fix(gerp): 修复"空草稿"误判,只看用户可填列
草稿判定原来比对整个表单对象,隐藏列/父级注入列/自动时间戳(CreateTime 每次打开都不同)与多选列空数组规范化都会产生差异,导致明明是空草稿仍弹恢复提示。改为写入与恢复共用 hasUserInput:仅比对表单可填列(editableFields)且值非空、与基线不同;内容清空时同步清掉旧草稿。
This commit is contained in:
1 parent
0e45175bc8
commit
78866514e8
2 files changed
+44
-28
No files matched your search
@@ -209,11 +209,15 @@ const draft = useDraftAutosave({
|
||||
editingRow: editingRow as Record<string, unknown>,
|
||||
dialogVisible: () => dialogVisible.value,
|
||||
isEdit: () => isEdit.value,
|
||||
// 只有"表单里用户可填的列"变化才算草稿内容:
|
||||
// 隐藏列/父级注入列/自动时间戳(CreateTime)的变化一律忽略,避免生成"看起来是空的"草稿
|
||||
editableFields: () => formColumns.value.map((c) => c.field),
|
||||
})
|
||||
|
||||
// 新增:先走 useCrudTable 初始化,再注入父级上下文关联字段;
|
||||
// 自动时间戳字段(CreateTime 等)预填当前 UTC 时间——不要求用户输入(后端仍以服务器时间为准);
|
||||
// 若存在未提交草稿,提示恢复或放弃
|
||||
// 若存在未提交草稿(且草稿里有用户真正录入的内容),提示恢复或删除;
|
||||
// 空草稿(可填列没有有效录入)直接丢弃,不弹窗
|
||||
async function onHandleCreate(): Promise<void> {
|
||||
// 新增前拦截:父页面可通过 beforeCreate 前置校验(返回 false 阻止打开弹窗)
|
||||
if (props.beforeCreate && props.beforeCreate() === false) return
|
||||
|
||||
@@ -27,9 +27,15 @@ export function useDraftAutosave(options: {
|
||||
dialogVisible: () => boolean
|
||||
/** 是否编辑态(编辑态不存草稿) */
|
||||
isEdit: () => boolean
|
||||
/**
|
||||
* 用户真正可填的列(表单可见列 field 列表):**只有这些列的有效值才算"录入内容"**。
|
||||
* 隐藏列/上下文注入列/自动时间戳的变化(如 CreateTime 每次打开都不同、
|
||||
* 多选列由 '' 规范化为 [])一律忽略 —— 否则会生成"用户视角下明明是空的"草稿。
|
||||
*/
|
||||
editableFields: () => string[]
|
||||
}) {
|
||||
const draftKey = `gerp-draft:add:${options.table}`
|
||||
/** 打开新增弹窗时的表单基线(JSON),内容未改动时不写草稿,避免"空草稿"打扰 */
|
||||
/** 打开新增弹窗时的表单基线(JSON),用于判断某个可填列是否真的被改过 */
|
||||
let draftBaseline: string | null = null
|
||||
let draftTimer: ReturnType<typeof setTimeout> | null = null
|
||||
|
||||
@@ -46,11 +52,38 @@ export function useDraftAutosave(options: {
|
||||
localStorage.removeItem(draftKey)
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断表单数据里是否存在"用户真正录入"的内容(写入与恢复共用同一判定,保证对称)
|
||||
*
|
||||
* 判定规则:只看可填列(editableFields),且值非空、与本次打开表单的基线不同。
|
||||
* 基线为 null(草稿刚恢复、允许增量保存)时以"非空即可"为准。
|
||||
*/
|
||||
function hasUserInput(data: Record<string, unknown>): boolean {
|
||||
const fields = options.editableFields()
|
||||
let base: Record<string, unknown> = {}
|
||||
if (draftBaseline) {
|
||||
try {
|
||||
base = JSON.parse(draftBaseline) as Record<string, unknown>
|
||||
} catch {
|
||||
base = {}
|
||||
}
|
||||
}
|
||||
return fields.some((field) => {
|
||||
const value = data[field]
|
||||
if (value === null || value === undefined || value === '') return false
|
||||
if (Array.isArray(value) && value.length === 0) return false
|
||||
return JSON.stringify(value) !== JSON.stringify(base[field])
|
||||
})
|
||||
}
|
||||
|
||||
function writeDraft(): void {
|
||||
if (!options.dialogVisible() || options.isEdit()) return
|
||||
if (!hasUserInput(options.editingRow)) {
|
||||
// 用户已把内容清空 → 一并清掉旧草稿,不在 localStorage 里留"空草稿"
|
||||
clearDraft()
|
||||
return
|
||||
}
|
||||
try {
|
||||
const cur = JSON.stringify({ ...options.editingRow })
|
||||
if (draftBaseline !== null && cur === draftBaseline) return // 未录入任何内容,不存
|
||||
localStorage.setItem(
|
||||
draftKey,
|
||||
JSON.stringify({ data: { ...options.editingRow }, updatedAt: Date.now() } satisfies FormDraft),
|
||||
@@ -80,36 +113,15 @@ export function useDraftAutosave(options: {
|
||||
draftBaseline = JSON.stringify({ ...options.editingRow })
|
||||
}
|
||||
|
||||
/**
|
||||
* 草稿是否"为空":没有任何"用户真正录入"的内容 —— 全部是空值,
|
||||
* 或与本次打开表单的基线完全一致(只有 CreateTime 等系统预填字段,用户没动过)
|
||||
*/
|
||||
function isDraftEmpty(draft: FormDraft): boolean {
|
||||
const data = draft.data || {}
|
||||
let base: Record<string, unknown> = {}
|
||||
if (draftBaseline) {
|
||||
try {
|
||||
base = JSON.parse(draftBaseline) as Record<string, unknown>
|
||||
} 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<void> {
|
||||
const draft = readDraft()
|
||||
if (!draft || isDraftEmpty(draft)) {
|
||||
if (!draft || !hasUserInput(draft.data || {})) {
|
||||
clearDraft() // 空草稿:直接丢弃
|
||||
return
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user