refactor(diy-form): 兼容前后端命名格式,优化表单配置处理

1.  重构mergedFormConfig逻辑,同时支持camelCase和snake_case格式的配置项,camelCase优先读取
2.  调整ns-newform的mode参数读取mergedFormConfig的formLayout
3.  优化提示语样式,新增tipBorderColor支持显示底部边框
4.  修复submitText读取逻辑,改为从mergedFormConfig获取
5.  移除多余的console日志,更新注释说明
This commit is contained in:
2026-06-12 12:00:10 +08:00
parent fe9cbcd21d
commit 48235c18a5
2 changed files with 55 additions and 40 deletions

View File

@@ -7,10 +7,10 @@
<!-- 提示语 -->
<view v-if="value.show_tip && value.form_tip" class="form-tip" :style="tipStyle">{{ value.form_tip }}</view>
<!-- 表单组件 -->
<ns-newform
:data="formComponents"
:customAttr="value"
:mode="formCfg.form_layout === 'vertical' ? 'vertical' : 'horizontal'"
<ns-newform
:data="formComponents"
:customAttr="value"
:mode="mergedFormConfig.formLayout || 'horizontal'"
:formConfig="mergedFormConfig"
ref="nsNewform"
></ns-newform>
@@ -90,15 +90,20 @@ export default {
padding: `0 ${this._num(this.mergedFormConfig && this.mergedFormConfig.bannerPaddingX, 0)}rpx`
});
},
// 提示语样式:padding-x、font-size、color、bg-color(与后端 .form-preview-tip 对齐)
// 提示语样式:padding-x、font-size、color、bg-color、border-bottom(与后端 .form-preview-tip 对齐)
tipStyle() {
const cfg = this.mergedFormConfig || {};
return this.$util.objToStyle({
const style = {
padding: `${this._num(cfg.rowPaddingY, 4)}rpx ${this._num(cfg.tipPaddingX, 14)}rpx`,
'font-size': `${this._num(cfg.tipFontSize, 24)}rpx`,
'color': cfg.tipColor || '#666666',
'background-color': cfg.tipBgColor || 'transparent'
});
};
// 仅在配置了 tipBorderColor 时添加底线,避免 transparent 占位仍增加 2rpx 高度
if (cfg.tipBorderColor) {
style['border-bottom'] = `2rpx solid ${cfg.tipBorderColor}`;
}
return this.$util.objToStyle(style);
},
// 提交按钮外层:与后端 .form-preview-submit 对齐(padding: row-padding-y form-padding-x;background: form-bg-color)
submitWrapStyle() {
@@ -131,8 +136,9 @@ export default {
});
},
// 提交按钮文字:与后端 design.html <button>提交</button> 默认对齐,开放给 formConfig 覆盖
// 走 mergedFormConfig(兼容 camelCase / snake_case),让 formConfig.submitText 能正确覆盖
submitText() {
return this.formCfg.submit_text || '提交';
return this.mergedFormConfig.submitText || '提交';
},
// 后端把表单样式配置统一存放在 data.formConfig 子对象中(与后端 design.js getDefaultFormData().formConfig 字段名一致)。
// 这里做一次归一化:优先取 formConfig;如果历史数据是扁平结构(直接挂在 value 上),也做兼容。
@@ -147,34 +153,45 @@ export default {
mergedFormConfig() {
const v = this.value || {};
const fc = this.formCfg;
// 后端实际返回 camelCase(如 formPaddingX / formBgColor / labelFontSize),
// 但旧版 design.js / 部分序列化路径可能用 snake_case(form_padding_x)。这里同时读两种写法,camelCase 优先
const self = this;
const pick = function(camel, snake) {
var v1 = fc[camel];
if (v1 !== undefined && v1 !== null && v1 !== '') return v1;
return fc[snake];
};
// 通用全局配置(与后端 design.js getDefaultFormData().formConfig 字段名一致)
const base = {
formPaddingX: fc.form_padding_x,
bannerPaddingX: fc.banner_padding_x,
tipPaddingX: fc.tip_padding_x,
rowPaddingY: fc.row_padding_y,
rowMinHeight: fc.row_min_height,
labelGap: fc.label_gap,
formBgColor: fc.form_bg_color,
rowDividerColor: fc.row_divider_color,
showRowDivider: this._coerceBool(fc.show_row_divider),
inputHeight: fc.input_height,
inputRadius: fc.input_radius,
labelWidthMode: fc.label_width_mode,
labelFixedWidth: fc.label_fixed_width,
labelFontSize: fc.label_font_size,
labelColor: fc.label_color,
requiredColor: fc.required_color,
checkedColor: fc.checked_color,
submitColor: fc.submit_color,
submitBgColor: fc.submit_bg_color,
formPaddingX: pick('formPaddingX', 'form_padding_x'),
bannerPaddingX: pick('bannerPaddingX', 'banner_padding_x'),
tipPaddingX: pick('tipPaddingX', 'tip_padding_x'),
rowPaddingY: pick('rowPaddingY', 'row_padding_y'),
rowMinHeight: pick('rowMinHeight', 'row_min_height'),
labelGap: pick('labelGap', 'label_gap'),
formBgColor: pick('formBgColor', 'form_bg_color'),
rowDividerColor: pick('rowDividerColor', 'row_divider_color'),
showRowDivider: self._coerceBool(pick('showRowDivider', 'show_row_divider')),
inputHeight: pick('inputHeight', 'input_height'),
inputRadius: pick('inputRadius', 'input_radius'),
formLayout: pick('formLayout', 'form_layout'),
labelWidthMode: pick('labelWidthMode', 'label_width_mode'),
labelFixedWidth: pick('labelFixedWidth', 'label_fixed_width'),
labelFontSize: pick('labelFontSize', 'label_font_size'),
labelColor: pick('labelColor', 'label_color'),
requiredColor: pick('requiredColor', 'required_color'),
checkedColor: pick('checkedColor', 'checked_color'),
submitText: pick('submitText', 'submit_text'),
submitColor: pick('submitColor', 'submit_color'),
submitBgColor: pick('submitBgColor', 'submit_bg_color'),
// 提交按钮尺寸:宽度百分比 + 尺寸预设(与后端 design.js 对齐)
submitWidthPercent: fc.submit_width_percent,
submitSize: fc.submit_size,
submitWidthPercent: pick('submitWidthPercent', 'submit_width_percent'),
submitSize: pick('submitSize', 'submit_size'),
// 提示语
tipFontSize: fc.tip_font_size,
tipColor: fc.tip_color,
tipBgColor: fc.tip_bg_color
tipFontSize: pick('tipFontSize', 'tip_font_size'),
tipColor: pick('tipColor', 'tip_color'),
tipBgColor: pick('tipBgColor', 'tip_bg_color'),
tipBorderColor: pick('tipBorderColor', 'tip_border_color')
};
// 移除 undefined / 空字符串(保留 0/false)
Object.keys(base).forEach(function(k) {
@@ -182,10 +199,10 @@ export default {
delete base[k];
}
});
// 平台差异化配置(与后端 formConfig.terminal 字段名对齐)
// 优先级:value.form_config_terminal > value.terminal > value.form_config_platform
// 平台差异化配置(terminal 块在 formConfig 子对象内)
// 优先级:v.formConfig.terminal > v.form_config_terminal > v.terminal > v.form_config_platform
const result = Object.assign({}, base);
const termBlock = v.form_config_terminal || v.terminal || v.form_config_platform;
const termBlock = (v.formConfig && v.formConfig.terminal) || v.form_config_terminal || v.terminal || v.form_config_platform;
if (termBlock && typeof termBlock === 'object') {
Object.keys(termBlock).forEach(function(platform) {
var src = termBlock[platform];
@@ -193,8 +210,7 @@ export default {
// 嵌套值若为 snake_case(部分序列化路径),在此统一转 camelCase
var translated = {};
Object.keys(src).forEach(function(k) {
var camel = k.replace(/_([a-z])/g, function(_, c) { return c.toUpperCase(); });
translated[camel] = src[k];
translated[self.$util.snakeToCamel(k)] = src[k];
});
result[platform] = translated;
});

View File

@@ -60,12 +60,12 @@ export default {
},
formTip() {
const tip = this.detail?.form_tip
console.log('formTip computed, tip value:', tip, 'truthy:', !!tip)
return tip ? tip : '请填写表单所需信息'
},
// 构造 diy-form raw 模式期望的 value 形态:
// - jsonData:透传给 ns-newform(已是 ns-newform 期望的 [{ controller, value, val }])
// - formConfig:snake_case 表单配置,diy-form 内部 mergedFormConfig 会做 camelCase 转换
// - formConfig:后端返回 camelCase(如 formPaddingX / formBgColor / labelFontSize),
// diy-form 内部 mergedFormConfig 同时兼容 snake_case 后再交给 ns-newform
// - 不传 show_banner / show_tip:让 diy-form 自带的 banner/tip 不渲染(页面已自渲染)
// 注:本 computed 只在 <block v-if="detail"> 内被消费,无需 null 守卫
diyValue() {
@@ -138,7 +138,6 @@ export default {
success: res => {
if (res.code == 0 && res.data) {
this.detail = res.data
console.log(`form data =>`, this.detail)
this.updateNavbarTitle();
}
this.$refs.loadingCover && this.$refs.loadingCover.hide()