refactor(form, diy-form): 重构表单组件复用逻辑,新增raw模式适配现有表单数据
1. 新增diy-form组件的raw数据源模式,支持直接透传ns-newform格式的表单数据 2. 新增showSubmit配置项,支持隐藏内置提交按钮由父页面自行实现提交逻辑 3. 暴露verify和getFormData方法供父组件调用 4. 替换pages_tool/form/formdata.vue的旧表单组件为diy-form,适配新的调用方式 5. 新增样式穿透修复diy-form默认样式与原有页面风格不一致的问题
This commit is contained in:
@@ -14,8 +14,9 @@
|
|||||||
:formConfig="mergedFormConfig"
|
:formConfig="mergedFormConfig"
|
||||||
ref="nsNewform"
|
ref="nsNewform"
|
||||||
></ns-newform>
|
></ns-newform>
|
||||||
<!-- 提交按钮(外层 = form-bg + form-padding-x;按钮 = submit-bg + submit-color + input-height + input-radius) -->
|
<!-- 提交按钮(外层 = form-bg + form-padding-x;按钮 = submit-bg + submit-color + input-height + input-radius)。
|
||||||
<view class="form-submit-wrap" :style="submitWrapStyle">
|
showSubmit=false 时由父级页面渲染自己的提交按钮并通过 ref 调用本组件的 verify()/getFormData()。 -->
|
||||||
|
<view v-if="showSubmit" class="form-submit-wrap" :style="submitWrapStyle">
|
||||||
<view @click.stop="submitForm" class="form-submit" :style="submitStyle">{{ submitText }}</view>
|
<view @click.stop="submitForm" class="form-submit" :style="submitStyle">{{ submitText }}</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -32,6 +33,20 @@ export default {
|
|||||||
props: {
|
props: {
|
||||||
value: {
|
value: {
|
||||||
type: Object
|
type: Object
|
||||||
|
},
|
||||||
|
// 数据源模式:
|
||||||
|
// 'diy'(默认):value.formComponents 是 [{ type, value }],组件内部转换为 ns-newform 期望的 [{ controller, value, val }]
|
||||||
|
// 'raw' :value.jsonData / value.json_data 已经是 ns-newform 期望的格式,直接透传给 ns-newform
|
||||||
|
// 用途:让 formdata 等"已是 ns-newform 格式"的页面也能复用本组件的 banner/tip/样式系统
|
||||||
|
dataMode: {
|
||||||
|
type: String,
|
||||||
|
default: 'diy',
|
||||||
|
validator: (v) => ['diy', 'raw'].includes(v)
|
||||||
|
},
|
||||||
|
// 是否渲染内置提交按钮。false 时由父级调用 ref 上的 verify()/getFormData() 自行实现提交
|
||||||
|
showSubmit: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mixins: [DiyMinx],
|
mixins: [DiyMinx],
|
||||||
@@ -47,6 +62,8 @@ export default {
|
|||||||
'value': {
|
'value': {
|
||||||
deep: true,
|
deep: true,
|
||||||
handler() {
|
handler() {
|
||||||
|
// raw 模式:formData 由父级透传,跳过重跑 initForm(避免每次父组件 re-render 触发 ns-newform 的深拷贝 initFormData)
|
||||||
|
if (this.dataMode === 'raw') return;
|
||||||
this.initForm();
|
this.initForm();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -192,6 +209,11 @@ export default {
|
|||||||
this.formComponents = [];
|
this.formComponents = [];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// raw 模式:value.jsonData / value.json_data 已经是 ns-newform 期望的 [{ controller, value, val }],直接透传
|
||||||
|
if (this.dataMode === 'raw') {
|
||||||
|
this.formComponents = this.value.jsonData || this.value.json_data || [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
var components = this.value.formComponents || this.value.form_components || [];
|
var components = this.value.formComponents || this.value.form_components || [];
|
||||||
// 转换格式:type -> controller
|
// 转换格式:type -> controller
|
||||||
this.formComponents = (components || []).map(function(item) {
|
this.formComponents = (components || []).map(function(item) {
|
||||||
@@ -203,6 +225,20 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 暴露给父级:触发 ns-newform 校验。校验失败时 ns-newform 内部会 toast;返回 false 表示失败,返回 formData 数组表示成功
|
||||||
|
verify() {
|
||||||
|
if (this.$refs.nsNewform && this.$refs.nsNewform.verify) {
|
||||||
|
return this.$refs.nsNewform.verify();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
// 暴露给父级:获取 ns-newform 当前的 formData 数组(含 val / option_lists / img_lists 等全部字段)
|
||||||
|
// 后端 /form/api/form/create 的 form_data 字段约定是 JSON.stringify 整个数组(与 form.vue 保持一致),
|
||||||
|
// 父级若需要 { index: val } 形式可自行 map
|
||||||
|
getFormData() {
|
||||||
|
return (this.$refs.nsNewform && this.$refs.nsNewform.formData) || [];
|
||||||
|
},
|
||||||
|
|
||||||
// 数字保底:undefined/空串/非数字时返回兜底值
|
// 数字保底:undefined/空串/非数字时返回兜底值
|
||||||
_num(v, fb) {
|
_num(v, fb) {
|
||||||
if (v === '' || v === null || v === undefined) return fb;
|
if (v === '' || v === null || v === undefined) return fb;
|
||||||
@@ -220,21 +256,16 @@ export default {
|
|||||||
// 提交表单
|
// 提交表单
|
||||||
submitForm() {
|
submitForm() {
|
||||||
var self = this;
|
var self = this;
|
||||||
// 使用 ns-newform 的验证
|
// 校验(同时返回 formData 数组;校验失败时 ns-newform 内部 toast 提示)
|
||||||
if (this.$refs.nsNewform && this.$refs.nsNewform.verify) {
|
if (!this.verify()) {
|
||||||
var verifyResult = this.$refs.nsNewform.verify();
|
return;
|
||||||
if (!verifyResult) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取表单数据
|
// 转为 { index: val } 提交格式(后端 /form/api/form/create 期望的紧凑结构)
|
||||||
var submitData = {};
|
var submitData = {};
|
||||||
if (this.$refs.nsNewform && this.$refs.nsNewform.formData) {
|
this.getFormData().forEach(function(item, index) {
|
||||||
this.$refs.nsNewform.formData.forEach(function(item, index) {
|
submitData[index] = item.val;
|
||||||
submitData[index] = item.val;
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 提交到后端
|
// 提交到后端
|
||||||
this.$api.sendRequest({
|
this.$api.sendRequest({
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
</view>
|
</view>
|
||||||
<view class="system-form-wrap">
|
<view class="system-form-wrap">
|
||||||
<view class="form-title">请填写表单所需信息</view>
|
<view class="form-title">请填写表单所需信息</view>
|
||||||
<ns-form :data="detail.json_data" ref="form"></ns-form>
|
<ns-form :data="detail.json_data" :formConfig="detail.form_config" ref="form"></ns-form>
|
||||||
<button type="primary" size="mini" class="button mini" @click="create()">提交</button>
|
<button type="primary" size="mini" class="button mini" @click="create()">提交</button>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -9,7 +9,13 @@
|
|||||||
<view class="system-form-tip" v-if="detail.show_tip > 0">{{ formTip }}</view>
|
<view class="system-form-tip" v-if="detail.show_tip > 0">{{ formTip }}</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="system-form-wrap">
|
<view class="system-form-wrap">
|
||||||
<ns-newform ref="form" :data="detail.json_data" @submit="create"></ns-newform>
|
<!-- 复用 diy-form 渲染表单字段:raw 模式直接透传 detail.json_data,隐藏内置 banner/tip/提交按钮(页面自己渲染) -->
|
||||||
|
<diy-form
|
||||||
|
ref="form"
|
||||||
|
:value="diyValue"
|
||||||
|
dataMode="raw"
|
||||||
|
:showSubmit="false"
|
||||||
|
/>
|
||||||
<button class="button mini" style="font-size:32rpx;" type="primary" size="mini"
|
<button class="button mini" style="font-size:32rpx;" type="primary" size="mini"
|
||||||
@click="create">提交</button>
|
@click="create">提交</button>
|
||||||
</view>
|
</view>
|
||||||
@@ -25,11 +31,11 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
import nsNewform from '@/components/ns-newform/ns-newform.vue';
|
import diyForm from '@/components-diy/diy-form.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
nsNewform
|
diyForm
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -56,6 +62,17 @@ export default {
|
|||||||
const tip = this.detail?.form_tip
|
const tip = this.detail?.form_tip
|
||||||
console.log('formTip computed, tip value:', tip, 'truthy:', !!tip)
|
console.log('formTip computed, tip value:', tip, 'truthy:', !!tip)
|
||||||
return tip ? tip : '请填写表单所需信息'
|
return tip ? tip : '请填写表单所需信息'
|
||||||
|
},
|
||||||
|
// 构造 diy-form raw 模式期望的 value 形态:
|
||||||
|
// - jsonData:透传给 ns-newform(已是 ns-newform 期望的 [{ controller, value, val }])
|
||||||
|
// - formConfig:snake_case 表单配置,diy-form 内部 mergedFormConfig 会做 camelCase 转换
|
||||||
|
// - 不传 show_banner / show_tip:让 diy-form 自带的 banner/tip 不渲染(页面已自渲染)
|
||||||
|
// 注:本 computed 只在 <block v-if="detail"> 内被消费,无需 null 守卫
|
||||||
|
diyValue() {
|
||||||
|
return {
|
||||||
|
jsonData: this.detail.json_data,
|
||||||
|
formConfig: this.detail.form_config
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLoad(options) {
|
onLoad(options) {
|
||||||
@@ -142,6 +159,7 @@ export default {
|
|||||||
uni.setNavigationBarTitle({ title: formName });
|
uni.setNavigationBarTitle({ title: formName });
|
||||||
},
|
},
|
||||||
create() {
|
create() {
|
||||||
|
// 校验(ns-newform 校验失败时会内部 toast;返回 false 表示失败)
|
||||||
if (!this.$refs.form.verify()) return
|
if (!this.$refs.form.verify()) return
|
||||||
|
|
||||||
if (this.isRepeat) return
|
if (this.isRepeat) return
|
||||||
@@ -151,7 +169,7 @@ export default {
|
|||||||
url: '/form/api/form/create',
|
url: '/form/api/form/create',
|
||||||
data: {
|
data: {
|
||||||
form_id: this.id,
|
form_id: this.id,
|
||||||
form_data: JSON.stringify(this.$refs.form.formData),
|
form_data: JSON.stringify(this.$refs.form.getFormData()),
|
||||||
uniacid: this.uniacid
|
uniacid: this.uniacid
|
||||||
},
|
},
|
||||||
success: res => {
|
success: res => {
|
||||||
@@ -225,4 +243,11 @@ page {
|
|||||||
padding: 30rpx;
|
padding: 30rpx;
|
||||||
border-radius: 32rpx;
|
border-radius: 32rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 穿透 diy-form 包装层:消除其自带的 10rpx 圆角与白色背景,
|
||||||
|
// 让 form 内容区域保持与原始 ns-newform 接入时一致的视觉
|
||||||
|
.system-form-wrap ::v-deep .diy-from {
|
||||||
|
background: transparent;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user