编辑表单的数据回显与提交:新增、修改和重提如何共用页面

同一张表单常常同时承担新增、修改和驳回重提。真正麻烦的不是给 input 填值,而是让接口字段、页面字段、枚举、日期、图片和提交模式保持一致。
1. 先明确三种模式
const mode = computed(() => { if (isResubmitting.value) return 'resubmit' if (order.editingOrderId) return 'edit' return 'create'})模式决定标题、按钮文案、接口和部分校验,但不应改变字段本身的含义。
2. 回显函数是接口到表单的适配器
function fillFromOrder(data = {}) { editingOrderId.value = data.orderId || null courierCompany.value = normalizeEnumValue(COURIER_COMPANY_OPTIONS, data.expressCompanyName) courierNo.value = data.expressNo || '' packageWeight.value = data.cargoWeightKg == null ? '' : Number(data.cargoWeightKg) packageVolume.value = data.cargoVolume == null ? '' : String(data.cargoVolume)}空值不能一律写成 0;否则用户会误以为服务端保存了 0。枚举值也要经过兼容转换,处理旧数据可能返回中文名称或历史编码的情况。
3. 日期时间回显
接口时间可能包含 T、秒和时区,选择器需要的是固定格式。回显时统一截取,提交时再构造成完整日期时间:
arriveTime.value = data.expectedArriveTime ? String(data.expectedArriveTime).replace('T', ' ').trim().slice(0, 16) : ''页面显示格式和接口传输格式应由两个函数明确转换,避免到处 slice。
4. 异步加载顺序
编辑页面通常需要:订单详情、地址、枚举字典和凭证图片。订单详情与字典可以并行,表单回显完成后再处理依赖订单 ID 的凭证。
读取路由 orderId ↓并行加载订单详情和字典 ↓fillFromOrder / fillFromAddress ↓下载鉴权凭证并回显页面显示 editingLoading,避免数据尚未回显时用户已经提交空表单。
5. 构造提交 payload
回显与提交应成对出现:fillFromOrder 将服务端数据转成页面状态,buildCourierPayload 将页面状态转回接口模型。模板不参与字段拼装。
const response = mode.value === 'resubmit' ? await OrderApi.resubmitOrder(id, payload) : mode.value === 'edit' ? await OrderApi.updateOrder(payload) : await OrderApi.createOrder(payload)6. 图片做差异提交
已有远程图片与新选择的本地图片放在同一展示数组中,同时额外保存已有路径集合。提交时仅上传新增项,删除远程项时调用删除接口。它相当于一个小型 diff。
7. 表单状态清理
页面提交成功或主动离开新建流程后,应清理 Store 中的编辑 ID、字段和图片;但从地图选点或地址簿返回时不能清理,否则用户之前填写的内容会丢失。
因此清理动作不能简单放在每次 onUnload,而要根据导航目的和成功状态执行。
8. 后端验证仍是最终防线
前端校验用于即时反馈,API 层在提交前再次执行 normalize + validate,后端还要做最终业务校验。三层并不重复:页面重体验,API 层防调用遗漏,后端保数据安全。
9. 本篇小结
表单回显的最高境界不是“看起来填上了”,而是用户不改任何字段也能安全提交,改一个字段只改变那一个事实。新增、修改、重提共用页面以后,我最大的愿望很朴素:别让上一张订单的照片偷偷跟着下一张订单去旅行。
支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!





























