Appearance
TL;DR
- 取消裁剪 ≠ 失败:用户放弃本次选择,应视为未选择,而不是上传失败。
- 占位项策略:选择文件时先写入一条
uploading占位;确认时就地改为done,失败改为error,取消则移除占位。 - 校验触发:取消或完成后调用表单
onFieldChange(),让必填/失败规则即时生效。 - 规则分治:
change阶段只提示(warning),submit阶段严格拦截(error)。
交互流转(状态机)
- 选择文件 → 写入占位:
{ uid, name, status:'uploading', percent:0 } - 弹出裁剪框
- 确认裁剪 → 压缩 → 上传成功:占位 就地更新
status:'done', url/thumbUrl, percent:100 - 上传失败:占位 就地更新
status:'error', response - 取消裁剪:移除占位,并触发表单校验(若必填则提示“请至少上传 1 张”)
校验最佳实践
目标:用户行为清晰,错误信息不重复、不误导。
变更阶段(
change):- 仅对明确失败
error做 warning(不置红,不阻断)。 - 对
uploading(即便percent=100)不报错。
- 仅对明确失败
提交阶段(
submit):- 若存在
error:报“上传失败”; - 若存在
uploading:报“仍在上传,请稍候”; - 若必填但无
done:报“请至少成功上传 1 张图片”。
- 若存在
配置上可在
Form.Item开启validateFirst,避免同周期多条错误叠加。
仅需调整的关键代码片段
1) 打开裁剪框前写入占位项
ts
// 复杂逻辑:写入 uploading 占位,UI/校验均能感知到“已选择”
_fileList.value = [
..._fileList.value.filter((f) => f.uid !== file.uid),
{
uid: file.uid,
name: file.name,
status: "uploading",
percent: 0,
originFileObj: file,
url: "",
thumbUrl: "",
},
];2) 确认裁剪成功后就地更新
ts
// 复杂逻辑:占位就地变更为成功
_fileList.value = _fileList.value.map((f) =>
f.uid === uid ? { ...f, status: "done", url, thumbUrl: url, percent: 100 } : f
);
nextTick(() => ctx && ctx.onFieldChange());3) 上传失败就地更新
ts
// 复杂逻辑:占位就地变更为失败(供 submit 阶段拦截)
_fileList.value = _fileList.value.map((f) =>
f.uid === uid ? { ...f, status: "error", response: e.message } : f
);
nextTick(() => ctx && ctx.onFieldChange());4) 取消裁剪时移除占位并校验
ts
// 复杂逻辑:取消裁剪=放弃本次选择,移除占位,触发必填校验
_fileList.value = _fileList.value.filter(
(f) => f.uid !== selectedFile.value?.uid
);
URL.revokeObjectURL(imageUrl.value);
open.value = false;
selectedFile.value = null;
nextTick(() => ctx && ctx.onFieldChange());5) Modal 右上角关闭也走取消逻辑
vue
<!-- 复杂逻辑:任意关闭动作都归一到 cancel -->
<a-modal v-model:open="open" @cancel="cancelCrop" />文案与可用性建议
取消不弹红色错误,最多给信息提示(如“已取消裁剪”)。
失败要可读:来源(网络/格式/大小)+ 重试建议。
体验细节:
- 始终
revokeObjectURL,避免内存泄漏; - 压缩失败可回退原图上传;
- 占位项上显示微进度/Loading,让用户感知状态。
- 始终
适配多文件/并发
- 多文件独立
uid管理,占位与就地更新一一对应; - 压缩/上传并发时,仍以
uid精确更新,不互相污染; - 可加“清除失败项”入口(移除所有
status:'error'的项)。
快速检查清单(Checklist)
- [ ] 选择后立刻写入
uploading占位 - [ ] 确认/失败 就地更新,不新建条目
- [ ] 取消 移除占位,并
onFieldChange() - [ ]
change=warningOnly,submit=error - [ ]
validateFirst=true - [ ]
revokeObjectURL在finally/取消中调用
小结
这套方案把取消与失败严格区分,既避免“误报失败”,又能在提交时可靠拦截未完成/未满足的情况;配合占位项与表单钩子,整体体验顺滑、实现简单、可维护性高。