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

View File

@@ -60,12 +60,12 @@ export default {
}, },
formTip() { formTip() {
const tip = this.detail?.form_tip const tip = this.detail?.form_tip
console.log('formTip computed, tip value:', tip, 'truthy:', !!tip)
return tip ? tip : '请填写表单所需信息' return tip ? tip : '请填写表单所需信息'
}, },
// 构造 diy-form raw 模式期望的 value 形态: // 构造 diy-form raw 模式期望的 value 形态:
// - jsonData:透传给 ns-newform(已是 ns-newform 期望的 [{ controller, value, val }]) // - 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 不渲染(页面已自渲染) // - 不传 show_banner / show_tip:让 diy-form 自带的 banner/tip 不渲染(页面已自渲染)
// 注:本 computed 只在 <block v-if="detail"> 内被消费,无需 null 守卫 // 注:本 computed 只在 <block v-if="detail"> 内被消费,无需 null 守卫
diyValue() { diyValue() {
@@ -138,7 +138,6 @@ export default {
success: res => { success: res => {
if (res.code == 0 && res.data) { if (res.code == 0 && res.data) {
this.detail = res.data this.detail = res.data
console.log(`form data =>`, this.detail)
this.updateNavbarTitle(); this.updateNavbarTitle();
} }
this.$refs.loadingCover && this.$refs.loadingCover.hide() this.$refs.loadingCover && this.$refs.loadingCover.hide()