From 0e45175bc87ebd98931387b7be5e94b4daa7df60 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Thu, 1 Oct 2026 16:13:18 +0800 Subject: [PATCH] =?UTF-8?q?feat(gerp):=20=E7=A9=BA=E8=8D=89=E7=A8=BF?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E4=B8=A2=E5=BC=83=E5=B9=B6=E6=96=B0=E5=A2=9E?= =?UTF-8?q?"=E5=88=A0=E9=99=A4=E8=8D=89=E7=A8=BF"=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit useDraftAutosave:打开新增表单时,草稿无有效内容(全空值或与表单基线一致)直接丢弃不弹窗;恢复提示按钮改为「恢复草稿/删除草稿」,右上角关闭视为稍后再看(保留草稿)。 --- src/components/CrudTable.vue | 2 +- src/composables/useDraftAutosave.ts | 51 +++++++++++++++++++++++++---- 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/src/components/CrudTable.vue b/src/components/CrudTable.vue index f3036f2..bce9df6 100644 --- a/src/components/CrudTable.vue +++ b/src/components/CrudTable.vue @@ -203,7 +203,7 @@ const formColumns = computed(() => { // ===== 新增表单自动草稿(独立 composable:useDraftAutosave) ===== // 防止误关闭/误刷新丢失录入内容;按表维度存 localStorage, -// 再次打开"新增"时提示恢复或放弃;提交成功后清除。 +// 再次打开"新增"时提示「恢复草稿 / 删除草稿」(空草稿直接丢弃,不打扰);提交成功后清除。 const draft = useDraftAutosave({ table: props.table, editingRow: editingRow as Record, diff --git a/src/composables/useDraftAutosave.ts b/src/composables/useDraftAutosave.ts index 02dee3c..6b37202 100644 --- a/src/composables/useDraftAutosave.ts +++ b/src/composables/useDraftAutosave.ts @@ -11,7 +11,10 @@ export interface FormDraft { * 新增表单自动草稿(防止误关闭/误刷新丢失录入内容) * * 按表维度存 localStorage:录入内容每 600ms 防抖自动保存; - * 再次打开"新增"时提示恢复或放弃;提交成功后清除。 + * 再次打开"新增"时提示「恢复草稿 / 删除草稿」(右上角关闭 = 稍后再看,保留草稿); + * 提交成功后清除。 + * + * 空草稿(没有任何用户录入内容,或与本次基线一致)直接丢弃,不弹窗打扰。 * * 从 CrudTable.vue 抽出的独立 composable(原 179-273 行逻辑)。 */ @@ -77,20 +80,56 @@ export function useDraftAutosave(options: { draftBaseline = JSON.stringify({ ...options.editingRow }) } - /** 检测并提示恢复/放弃草稿;恢复则写入表单(在新增弹窗初始化后调用) */ + /** + * 草稿是否"为空":没有任何"用户真正录入"的内容 —— 全部是空值, + * 或与本次打开表单的基线完全一致(只有 CreateTime 等系统预填字段,用户没动过) + */ + function isDraftEmpty(draft: FormDraft): boolean { + const data = draft.data || {} + let base: Record = {} + if (draftBaseline) { + try { + base = JSON.parse(draftBaseline) as Record + } catch { + base = {} + } + } + return !Object.entries(data).some(([key, value]) => { + if (value === null || value === undefined || value === '' || (Array.isArray(value) && value.length === 0)) { + return false + } + return JSON.stringify(value) !== JSON.stringify(base[key]) + }) + } + + /** + * 检测并提示恢复/删除草稿;恢复则写入表单(在新增弹窗初始化后调用)。 + * 空草稿(无有效内容/与基线一致)直接丢弃,不弹窗打扰用户。 + * 弹窗:确定=恢复草稿,取消=删除草稿,右上角关闭=稍后再看(保留草稿) + */ async function restoreDraftIfAny(): Promise { const draft = readDraft() - if (!draft || !draft.data || Object.keys(draft.data).length === 0) return + if (!draft || isDraftEmpty(draft)) { + clearDraft() // 空草稿:直接丢弃 + return + } try { await ElMessageBox.confirm( `检测到 ${new Date(draft.updatedAt).toLocaleString()} 未提交的草稿,是否恢复继续编辑?`, '恢复草稿', - { type: 'warning', confirmButtonText: '恢复草稿', cancelButtonText: '放弃' }, + { + type: 'warning', + confirmButtonText: '恢复草稿', + cancelButtonText: '删除草稿', + // 区分"取消"与"关闭":右上角 × 视为稍后再看,草稿保留不删 + distinguishCancelAndClose: true, + }, ) Object.assign(options.editingRow, draft.data) draftBaseline = null // 恢复后允许继续增量保存 - } catch { - clearDraft() // 用户放弃 → 清掉旧草稿 + } catch (action) { + if (action === 'cancel') clearDraft() // 选择"删除草稿" + // action === 'close'(右上角 ×):保留草稿,下次再提示 } }