refactor(form): 重构提交逻辑以支持跨端统一接管
diy-form 组件将提交事件机制改为函数 prop (onSubmit) 接管,避免 $listeners 在 MP 端不可用的问题。 formdata 页面使用 bind 预绑定 this,防止子组件调用时上下文漂移。 新增防重复提交标记复位逻辑,优化网络失败时的重试体验。
This commit is contained in:
@@ -22,13 +22,18 @@
|
|||||||
<!-- 提交按钮(外层 = 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)。
|
||||||
showSubmit=false 时由父级页面渲染自己的提交按钮并通过 ref 调用本组件的 verify()/getFormData()。 -->
|
showSubmit=false 时由父级页面渲染自己的提交按钮并通过 ref 调用本组件的 verify()/getFormData()。 -->
|
||||||
<view v-if="showSubmit" class="form-submit-wrap" :style="submitWrapStyle">
|
<view v-if="showSubmit" class="form-submit-wrap" :style="submitWrapStyle">
|
||||||
<view @click.stop="submitForm" class="form-submit" :style="submitStyle">{{ submitText }}</view>
|
<view @click.stop="handleSubmit" class="form-submit" :style="submitStyle">{{ submitText }}</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// 自定义表单
|
// 自定义表单
|
||||||
|
// 提交接管三档(不依赖 $listeners / 事件机制,跨 H5 / MP / 鸿蒙一致):
|
||||||
|
// 1) 默认 → showSubmit=true + 不传 onSubmit → 内置按钮 + 内置 defaultSubmit
|
||||||
|
// 2) 复用按钮、自定义逻辑 → showSubmit=true + :onSubmit="fn" → 内置按钮 + 父级 fn
|
||||||
|
// 3) 完全自定义 UI → showSubmit=false + :onSubmit="fn" 或 ref.verify()/getFormData() → 父级全控
|
||||||
|
// 注:type: Function prop 在 uni-app MP 端走 Vue runtime 注入,机制与 ns-navbar.customBack 一致
|
||||||
import DiyMinx from './minx.js'
|
import DiyMinx from './minx.js'
|
||||||
import NsNewform from '@/components/ns-newform/ns-newform.vue'
|
import NsNewform from '@/components/ns-newform/ns-newform.vue'
|
||||||
|
|
||||||
@@ -52,6 +57,12 @@ export default {
|
|||||||
showSubmit: {
|
showSubmit: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true
|
default: true
|
||||||
|
},
|
||||||
|
// 父级可选地接管提交:传入函数 prop 时,verify 通过后由它处理 formData;
|
||||||
|
// 不传时走组件内置 defaultSubmit(兜底)。default 必须是 null,不能写成函数返回 null
|
||||||
|
onSubmit: {
|
||||||
|
type: Function,
|
||||||
|
default: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mixins: [DiyMinx],
|
mixins: [DiyMinx],
|
||||||
@@ -148,6 +159,10 @@ export default {
|
|||||||
submitText() {
|
submitText() {
|
||||||
return this.mergedFormConfig.submitText || '提交';
|
return this.mergedFormConfig.submitText || '提交';
|
||||||
},
|
},
|
||||||
|
// 父级是否接管提交(集中判断,便于以后替换机制;避免多个调用点各自 typeof 校验)
|
||||||
|
hasCustomSubmit() {
|
||||||
|
return typeof this.onSubmit === 'function';
|
||||||
|
},
|
||||||
// 后端把表单样式配置统一存放在 data.formConfig 子对象中(与后端 design.js getDefaultFormData().formConfig 字段名一致)。
|
// 后端把表单样式配置统一存放在 data.formConfig 子对象中(与后端 design.js getDefaultFormData().formConfig 字段名一致)。
|
||||||
// 这里做一次归一化:优先取 formConfig;如果历史数据是扁平结构(直接挂在 value 上),也做兼容。
|
// 这里做一次归一化:优先取 formConfig;如果历史数据是扁平结构(直接挂在 value 上),也做兼容。
|
||||||
formCfg() {
|
formCfg() {
|
||||||
@@ -286,24 +301,26 @@ export default {
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
// 提交表单
|
// 统一提交入口:内置按钮 / 父级 ref 调用都走这里
|
||||||
submitForm() {
|
// 不依赖 $listeners / 事件机制(MP 端 $listeners 为 undefined),跨端一致
|
||||||
var self = this;
|
handleSubmit() {
|
||||||
// 校验(同时返回 formData 数组;校验失败时 ns-newform 内部 toast 提示)
|
// 校验(同时返回 formData 数组;校验失败时 ns-newform 内部 toast 提示)
|
||||||
if (!this.verify()) {
|
if (!this.verify()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const formData = this.getFormData();
|
||||||
// 若父级监听了 @submit,则把提交控制权交给父级(用于父级有自定义提交字段/反馈逻辑的页面,如 formdata.vue 传 uniacid)
|
// 父级显式接管:function prop 在 H5 / MP / 鸿蒙一致可用
|
||||||
if (this.$listeners && this.$listeners.submit) {
|
if (this.hasCustomSubmit) {
|
||||||
this.$emit('submit', this.getFormData());
|
return this.onSubmit(formData);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
// 兜底:内置 sendRequest 流程
|
||||||
|
this.defaultSubmit(formData);
|
||||||
|
},
|
||||||
|
|
||||||
// 获得保单数据
|
// 公共兜底实现:默认的 /form/api/form/create 流程;
|
||||||
const submitFormData = this.getFormData();
|
// 父级若需"先自定义、失败回退",可经 ref.defaultSubmit(formData) 调用
|
||||||
|
defaultSubmit(submitFormData) {
|
||||||
// 提交到后端
|
var self = this;
|
||||||
this.$api.sendRequest({
|
this.$api.sendRequest({
|
||||||
url: '/form/api/form/create',
|
url: '/form/api/form/create',
|
||||||
data: {
|
data: {
|
||||||
|
|||||||
@@ -978,6 +978,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
onShareClick() {
|
onShareClick() {
|
||||||
|
console.log('[推荐码分享]点击分享按钮,完整推广链接:', this.formatShareLink,'当前推荐码:',this.code);
|
||||||
const vm = this;
|
const vm = this;
|
||||||
const v = this.nc;
|
const v = this.nc;
|
||||||
const shareUrl = this.formatShareLink; // 改用后台拼接链接
|
const shareUrl = this.formatShareLink; // 改用后台拼接链接
|
||||||
@@ -1013,6 +1014,9 @@ onShareClick() {
|
|||||||
// #ifdef MP-WEIXIN
|
// #ifdef MP-WEIXIN
|
||||||
// open-type="share" 会自动触发 onShareAppMessage 弹出微信原生分享面板
|
// open-type="share" 会自动触发 onShareAppMessage 弹出微信原生分享面板
|
||||||
// 存储分享数据供页面级 onShareAppMessage 读取(组件级钩子会被页面级覆盖)
|
// 存储分享数据供页面级 onShareAppMessage 读取(组件级钩子会被页面级覆盖)
|
||||||
|
console.log('分享链接准备,原始配置', v);
|
||||||
|
console.log('生成的分享完整路径:', this.extractMiniProgramPath());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var shareData = {
|
var shareData = {
|
||||||
title: v.shareTitle || v.title || '邀您入驻低空经济产业平台',
|
title: v.shareTitle || v.title || '邀您入驻低空经济产业平台',
|
||||||
|
|||||||
@@ -2,16 +2,18 @@
|
|||||||
<view :style="themeColor">
|
<view :style="themeColor">
|
||||||
<block v-if="detail">
|
<block v-if="detail">
|
||||||
<view class="page">
|
<view class="page">
|
||||||
<view class="system-form-wrap">
|
<view class="system-form-wrap">
|
||||||
<!-- 复用 diy-form:banner / tip / 表单字段 / 提交按钮 全部由其内部渲染,样式(bannerPaddingX/tipColor/submitBgColor 等)由 form_config 控制。
|
<!-- 复用 diy-form:banner / tip / 表单字段 / 提交按钮 全部由其内部渲染,样式(bannerPaddingX/tipColor/submitBgColor 等)由 form_config 控制。
|
||||||
@submit 接管提交逻辑(带 uniacid + showModal) -->
|
:onSubmit 接管提交逻辑(带 uniacid + showModal);跨 H5 / MP 一致,不依赖 $listeners。
|
||||||
<diy-form
|
注意:create 重度依赖本组件 this(id/uniacid/memberInfo/$api 等),必须在 created 阶段 bind this,
|
||||||
ref="form"
|
否则 diy-form 通过 this.onSubmit(formData) 调用时 this 会漂移到子组件实例 -->
|
||||||
:value="diyValue"
|
<diy-form
|
||||||
dataMode="raw"
|
ref="form"
|
||||||
@submit="create"
|
:value="diyValue"
|
||||||
/>
|
dataMode="raw"
|
||||||
</view>
|
:onSubmit="handleSubmit"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</block>
|
</block>
|
||||||
<block v-else>
|
<block v-else>
|
||||||
@@ -44,6 +46,7 @@ export default {
|
|||||||
isFromMpWebView: false, // 是否是从微信小程序的WebView跳转过来的
|
isFromMpWebView: false, // 是否是从微信小程序的WebView跳转过来的
|
||||||
hideNavbar: false, // 查询参数中支持隐藏Navbar
|
hideNavbar: false, // 查询参数中支持隐藏Navbar
|
||||||
urlParams: {}, // 通用 URL 透传参数收集
|
urlParams: {}, // 通用 URL 透传参数收集
|
||||||
|
handleSubmit: null, // create 的 this 绑定版本,作为 :onSubmit 传给 diy-form(见 created)
|
||||||
|
|
||||||
// #ifdef H5
|
// #ifdef H5
|
||||||
formTitle: '', // 查询参数传入的Title
|
formTitle: '', // 查询参数传入的Title
|
||||||
@@ -51,6 +54,13 @@ export default {
|
|||||||
// #endif
|
// #endif
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
created() {
|
||||||
|
// 预绑定 this:create 重度依赖本组件 this(id/uniacid/memberInfo/$api/$lang/$util 等)。
|
||||||
|
// 若直接传 :onSubmit="create",diy-form 通过 this.onSubmit(formData) 调用时,
|
||||||
|
// 普通函数调用的 this 会指向调用者(子组件实例),导致字段全部读不到。
|
||||||
|
// bind 后无论子组件如何调用,this 始终是 formdata 实例。
|
||||||
|
this.handleSubmit = this.create.bind(this);
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
// 构造 diy-form raw 模式期望的 value 形态:
|
// 构造 diy-form raw 模式期望的 value 形态:
|
||||||
// - jsonData:透传给 ns-newform(已是 ns-newform 期望的 [{ controller, value, val }])
|
// - jsonData:透传给 ns-newform(已是 ns-newform 期望的 [{ controller, value, val }])
|
||||||
@@ -71,6 +81,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLoad(options) {
|
onLoad(options) {
|
||||||
|
console.log("=====表单页面====",options)
|
||||||
if (!options.uniacid) {
|
if (!options.uniacid) {
|
||||||
uni.showModal({
|
uni.showModal({
|
||||||
title: this.$lang('form.formdata.tip'),
|
title: this.$lang('form.formdata.tip'),
|
||||||
@@ -167,22 +178,31 @@ export default {
|
|||||||
// #endif
|
// #endif
|
||||||
uni.setNavigationBarTitle({ title: formName });
|
uni.setNavigationBarTitle({ title: formName });
|
||||||
},
|
},
|
||||||
// 由 diy-form 内置提交按钮触发:diy-form 已先做 verify(),仅在通过时才 emit('submit', formData)
|
// 由 diy-form 内置提交按钮触发:diy-form handleSubmit 已先做 verify(),
|
||||||
|
// 仅在通过时才通过 :onSubmit 函数 prop 调用本方法(跨 H5 / MP 一致,不走 $emit / $listeners)。
|
||||||
|
// 注意:本方法通过 created 中 this.handleSubmit = this.create.bind(this) 预绑定 this,
|
||||||
|
// 因此内部所有 this.xxx 都指向 formdata 实例,不会漂移到子组件。
|
||||||
create(formData) {
|
create(formData) {
|
||||||
|
// 防重复提交:console 在拦截之前,避免日志噪声;拦截后立即置位
|
||||||
if (this.isRepeat) return
|
if (this.isRepeat) return
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
form_id: this.id,
|
||||||
|
form_data: typeof formData === 'string' ? formData : JSON.stringify(formData),
|
||||||
|
uniacid: this.uniacid,
|
||||||
|
url_params: JSON.stringify(this.urlParams),
|
||||||
|
member_id: this.memberInfo ? this.memberInfo.id : 0
|
||||||
|
}
|
||||||
|
console.log(`=====表单提交====`, data);
|
||||||
|
|
||||||
|
|
||||||
this.isRepeat = true
|
this.isRepeat = true
|
||||||
this.$api.sendRequest({
|
this.$api.sendRequest({
|
||||||
url: '/form/api/form/create',
|
url: '/form/api/form/create',
|
||||||
data: {
|
data,
|
||||||
form_id: this.id,
|
|
||||||
form_data: typeof formData === 'string' ? formData : JSON.stringify(formData),
|
|
||||||
uniacid: this.uniacid,
|
|
||||||
url_params: JSON.stringify(this.urlParams),
|
|
||||||
member_id: this.memberInfo ? this.memberInfo.id : 0
|
|
||||||
},
|
|
||||||
success: res => {
|
success: res => {
|
||||||
if (res.code == 0) {
|
if (res.code == 0) {
|
||||||
|
this.complete = true
|
||||||
uni.showModal({
|
uni.showModal({
|
||||||
title: this.$lang('form.formdata.tip'),
|
title: this.$lang('form.formdata.tip'),
|
||||||
content: this.$lang('form.formdata.submitSuccess'),
|
content: this.$lang('form.formdata.submitSuccess'),
|
||||||
@@ -195,6 +215,10 @@ export default {
|
|||||||
title: res.message
|
title: res.message
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
fail: () => {
|
||||||
|
// 网络失败时复位防重复标记,允许用户重试
|
||||||
|
this.isRepeat = false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user