hookehuyr

fix(plan): 修复表单重置不稳定问题

问题描述:
- 提交成功后,有时表单会保留上次的数据
- 表现不稳定:大部分时候正常,偶尔会有残留数据
- 影响用户体验,需要手动清除数据

根本原因:
- 依赖 watch 监听器重置表单存在竞态条件
- watch(visible) 只在弹窗打开时重置,关闭时不重置
- 组件可能被缓存(v-show),状态未清理
- 响应式更新时序问题导致残留

解决方案:
1. 在 submit() 成功后立即调用 resetForm()
2. 在 close() 关闭弹窗前调用 resetForm()
3. 优化 watch(visible) 作为安全网,检测残留数据
4. 为所有模板组件添加 clearErrors() 方法

技术改进:
- 新增 resetForm() 函数:重置表单和错误状态
- submit() 中调用 resetForm(),确保提交后立即清空
- close() 中调用 resetForm(),确保关闭时清空
- watch(visible) 仅在检测到残留数据时才重置
- 统一模板组件接口:validate + clearErrors

影响文件:
- src/components/PlanFormContainer.vue
- src/components/PlanTemplates/SavingsTemplate.vue
- src/components/PlanTemplates/LifeInsuranceTemplate.vue
- src/components/PlanTemplates/CriticalIllnessTemplate.vue

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
...@@ -164,36 +164,31 @@ const formData = ref({}) ...@@ -164,36 +164,31 @@ const formData = ref({})
164 const templateRef = ref(null) 164 const templateRef = ref(null)
165 165
166 /** 166 /**
167 - * 监听产品变化,重置表单数据 167 + * 监听显示状态变化,弹窗打开时确保表单是干净的
168 - */ 168 + * @description 这是最后的安全网,确保弹窗打开时表单一定是空的
169 -watch(
170 - () => props.product,
171 - (newProduct) => {
172 - if (newProduct) {
173 - // 重置表单数据
174 - formData.value = {}
175 - }
176 - },
177 - { immediate: true }
178 -)
179 -
180 -/**
181 - * 监听显示状态变化,弹窗打开时重置表单
182 */ 169 */
183 watch( 170 watch(
184 () => props.visible, 171 () => props.visible,
185 (newVal) => { 172 (newVal) => {
186 - if (newVal) { 173 + if (newVal && Object.keys(formData.value).length > 0) {
187 - // 弹窗打开时重置表单 174 + // 弹窗打开且表单有数据时,强制重置
188 - formData.value = {} 175 + console.log('[PlanFormContainer] 弹窗打开时检测到残留数据,强制重置')
176 + resetForm()
189 } 177 }
190 } 178 }
191 ) 179 )
192 180
193 /** 181 /**
194 * 关闭弹窗 182 * 关闭弹窗
183 + * @description 关闭时重置表单数据,避免下次打开时保留旧数据
195 */ 184 */
196 const close = () => { 185 const close = () => {
186 + console.log('[PlanFormContainer] 关闭弹窗,重置表单')
187 +
188 + // 关闭前重置表单
189 + resetForm()
190 +
191 + // 触发关闭事件
197 emit('close') 192 emit('close')
198 } 193 }
199 194
...@@ -217,11 +212,31 @@ const submit = () => { ...@@ -217,11 +212,31 @@ const submit = () => {
217 form_data: formData.value 212 form_data: formData.value
218 }) 213 })
219 214
215 + // 发送提交事件
220 emit('submit', { 216 emit('submit', {
221 product_id: props.product.id, 217 product_id: props.product.id,
222 form_sn: props.product.form_sn, 218 form_sn: props.product.form_sn,
223 form_data: formData.value 219 form_data: formData.value
224 }) 220 })
221 +
222 + // ✅ 提交成功后立即重置表单,避免下次打开时保留旧数据
223 + resetForm()
224 +}
225 +
226 +/**
227 + * 重置表单数据
228 + * @description 清空表单数据和错误状态
229 + */
230 +const resetForm = () => {
231 + console.log('[PlanFormContainer] 重置表单数据')
232 +
233 + // 重置表单数据
234 + formData.value = {}
235 +
236 + // 重置子组件的验证状态(如果有)
237 + if (templateRef.value && templateRef.value.clearErrors) {
238 + templateRef.value.clearErrors()
239 + }
225 } 240 }
226 </script> 241 </script>
227 242
......
...@@ -197,8 +197,19 @@ const validate = () => { ...@@ -197,8 +197,19 @@ const validate = () => {
197 return true 197 return true
198 } 198 }
199 199
200 +/**
201 + * 清除验证错误
202 + * @description 由于使用 Toast 显示错误,无需清除状态
203 + * 保留此方法以保持接口一致性
204 + */
205 +const clearErrors = () => {
206 + // 当前使用 Toast 显示错误,无需清除错误状态
207 + // 如果将来改用内联错误提示,可以在这里清除错误状态
208 +}
209 +
200 defineExpose({ 210 defineExpose({
201 - validate 211 + validate,
212 + clearErrors
202 }) 213 })
203 </script> 214 </script>
204 215
......
...@@ -197,8 +197,19 @@ const validate = () => { ...@@ -197,8 +197,19 @@ const validate = () => {
197 return true 197 return true
198 } 198 }
199 199
200 +/**
201 + * 清除验证错误
202 + * @description 由于使用 Toast 显示错误,无需清除状态
203 + * 保留此方法以保持接口一致性
204 + */
205 +const clearErrors = () => {
206 + // 当前使用 Toast 显示错误,无需清除错误状态
207 + // 如果将来改用内联错误提示,可以在这里清除错误状态
208 +}
209 +
200 defineExpose({ 210 defineExpose({
201 - validate 211 + validate,
212 + clearErrors
202 }) 213 })
203 </script> 214 </script>
204 215
......
...@@ -461,8 +461,19 @@ const validate = () => { ...@@ -461,8 +461,19 @@ const validate = () => {
461 return true 461 return true
462 } 462 }
463 463
464 +/**
465 + * 清除验证错误
466 + * @description 由于使用 Toast 显示错误,无需清除状态
467 + * 保留此方法以保持接口一致性
468 + */
469 +const clearErrors = () => {
470 + // 当前使用 Toast 显示错误,无需清除错误状态
471 + // 如果将来改用内联错误提示,可以在这里清除错误状态
472 +}
473 +
464 defineExpose({ 474 defineExpose({
465 - validate 475 + validate,
476 + clearErrors
466 }) 477 })
467 </script> 478 </script>
468 479
......