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>
Showing
4 changed files
with
69 additions
and
21 deletions
| ... | @@ -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 | ... | ... |
-
Please register or login to post a comment