暂存用户打卡信息.md
8.08 KB
暂存用户打卡信息
背景
用户在“提交作业/打卡”页面输入了较长文字并上传了媒体,但在未点击提交时被中断(误触返回、微信进程被系统回收、来电/切后台等),再次进入页面内容丢失,导致体验断裂。
本规划目标是在不改动后端接口的前提下,在前端提供“草稿暂存(文本 + 已上传媒体信息)”能力,支持一周内自动过期清理,并在用户再次进入时提示恢复或删除。
涉及页面与核心逻辑参考:
- 打卡提交页:CheckinDetailPage.vue
- 打卡核心逻辑:useCheckin.js
- 环境变量约定:.env
需求拆解(逐条对齐)
- 缓存最多保存一周:写入时记录 saved_at,读写时执行过期清理(>7天删除)。
- 用户提交后清空缓存:提交成功(code===1)后删除对应草稿。
- 进入页面若存在未完成信息:弹框提示“继续/删除”,继续则回填,删除则清空。
- 功能开关放到 .env:新增 VITE_CHECKIN_DRAFT_CACHE(0/1),默认建议 1(也可先默认 0,灰度开启)。
范围与不做项(第一版)
- 覆盖:文字内容、已上传成功的媒体(含 url/meta_id/file_type/name)。
- 不覆盖:未上传完成的 File/Blob(localStorage 无法可靠持久化;要支持需 IndexedDB 存 Blob,复杂度与风险较高)。
- 编辑模式(route.query.status===edit):第一版建议默认不启用草稿恢复,避免与“编辑回显(来自后端)”冲突;若要覆盖编辑场景,采用独立 key(见“扩展”)。
关键设计
1) 存储介质
- 使用 localStorage:实现成本低,满足“一周”与“断网/切后台后仍可恢复”。
- 数据量控制:只存“已上传成功”的附件元数据;不存 File 本体。
2) 草稿 Key 设计(避免串号)
建议 key 包含用户与作业上下文,确保不同用户/不同作业互不影响:
- 前缀:CHECKIN_DRAFT_V1
- 维度:user_id、task_id、date、task_type、status
示例:
- CHECKIN_DRAFT_V1:{user_id}:{task_id}:{date}:{task_type}:{status}
其中:
- user_id:来自 currentUser(contexts/auth.js 本地持久化)
- task_id/date/task_type/status:来自路由 query(CheckinDetailPage 已使用 route.query.task_id/date/task_type/status)
3) 数据结构(建议)
{
"version": 1,
"saved_at": 1730000000000,
"expires_at": 1730000000000,
"context": {
"user_id": "123",
"task_id": "456",
"date": "2026-01-25",
"task_type": "upload",
"status": "create"
},
"payload": {
"message": "...",
"active_type": "image",
"subtask_id": "789",
"file_list": [
{
"meta_id": "xxx",
"url": "https://...",
"name": "a.jpg",
"file_type": "image"
}
],
"count": {
"gratitude_count": 1,
"gratitude_form_list": []
}
}
}
说明:
- file_list:仅保存 useCheckin.afterRead 上传成功后写入的字段(item.status===done 且 meta_id 存在)。
- count:来源于 CheckinDetailPage 的 selectedTargets/countValue(第一版可以先不存,或存但不影响非 count 类型)。
4) 触发保存的时机(自动暂存)
- 文本变化:watch(message) debounce 500ms 保存。
- 附件变化:watch(fileList) 深度监听 debounce 500ms 保存(仅保存 done 项)。
- 作业选择变化:watch(selectedTaskValue) debounce 200ms 保存。
- 页面离开兜底:beforeRouteLeave 或 window.pagehide/visibilitychange 时强制保存一次(避免最后一次变更没落盘)。
落盘时机要遵循开关:VITE_CHECKIN_DRAFT_CACHE === '1' 才启用。
5) 弹框提示与回填流程
进入 CheckinDetailPage.vue(且非 edit 模式)时:
- 读取 key 对应草稿;若不存在或已过期,直接进入正常流程。
- 若存在草稿且 payload 有实际内容(message 有非空或 file_list 非空):弹框提示。
- 用户选择:
- 继续:将草稿回填到 message / activeType / fileList / selectedTaskValue(以及 count 数据如启用),并立刻再保存一次(避免回填后又丢)。
- 删除:删除草稿并保持空表单。
弹框建议用 showConfirmDialog(Vant 4),取消分支需要 catch,避免控制台出现 Uncaught (in promise) cancel(Vant 文档:showConfirmDialog.then/catch)3。
6) 清理策略(“定期清除一周前”)
采用“惰性清理 + 低频全量清理”组合:
- 惰性清理:每次读/写草稿时,如果 expires_at < now 则删除。
- 低频全量:进入打卡页时,扫描 localStorage 中以 CHECKIN_DRAFT_V1: 开头的 key,删除所有过期项。
说明:localStorage 没有内建 TTL,必须业务侧维护 expires_at。
7) 提交成功后清空
清空动作必须绑定到“真正提交成功”之后:
- 建议在 useCheckin.onSubmit 中,当 add/edit API 返回 code===1 且后续逻辑准备 router.back 前,删除对应 key。
这样可覆盖“不同入口页复用 onSubmit”以及“提交后立即返回上一页”的场景。
开发步骤(可落地的实现顺序)
第 0 步:验证手段先行(TDD)
新增 Vitest 用例,先定义以下可验证点:
- 写入后能读取同一 key 的草稿;过期后读取返回空且自动删除。
- 仅保存 status===done 且含 meta_id 的附件。
- 清理函数能删除所有过期 key,不误删其他业务 localStorage。
- 提交成功时会调用清理(可通过 mock API 返回 code===1 验证)。
第 1 步:抽离草稿存储模块
位置建议:src/utils/checkinDraftCache.js(纯函数、无 UI 依赖)。
对外 API(示例):
- is_enabled(): boolean(读取 env + 可选 query override)
- build_key(context): string
- save_draft(key, draft)
- read_draft(key): draft|null(含 TTL 处理)
- clear_draft(key)
- cleanup_expired(prefix)
第 2 步:在 CheckinDetailPage 接入“检测 + 弹框 + 回填”
- onMounted:初始化后读取草稿并弹框。
- 回填时机:建议在任务详情/子任务列表加载完成后再回填 selectedTaskValue,避免 option 未加载导致显示异常。
第 3 步:在 CheckinDetailPage 接入“自动保存”
- 对 message/fileList/selectedTaskValue/countValue/selectedTargets 建立 watch + debounce。
- 页面离开事件兜底(pagehide/visibilitychange)。
第 4 步:在 useCheckin.onSubmit 接入“成功清理”
- onSubmit 成功分支清除草稿。
- 失败分支不清除,保留草稿以便重试。
边界条件与遗漏点梳理(建议补齐)
- 多用户切换:key 必须含 user_id,否则会串草稿。
- 多任务并存:key 必须含 task_id/date/task_type,否则会在不同作业之间误恢复。
- 附件未上传完成:
- 仅保存已上传成功的项;如果用户退出时仍有 uploading 项,恢复后无法找回该 File。
- 可在保存时统计未保存数量,并在恢复弹框里追加提示“有 X 个附件上传未完成未被暂存”。
- 关闭开关后的行为:
- 关闭后不再读/写;建议仍执行一次 cleanup_expired,避免历史堆积。
- 版本升级/数据结构变更:draft.version 不匹配时丢弃并清除,避免解析异常。
- localStorage 配额:图片多但只存 url/meta_id 一般不会超;仍需 try/catch JSON 与 setItem 异常。
- 编辑模式:
- 要支持“编辑中断恢复”,建议 key 加 post_id 维度,并在 initEditData 回显后再弹框询问是否覆盖当前表单。
环境变量(规划)
在 .env 增加:
- VITE_CHECKIN_DRAFT_CACHE = 1
约定:
- '1' 开启,'0' 关闭
- 可选增加 URL 覆盖用于灰度测试:?enable_draft=1 / ?enable_draft=0(模式同 VITE_CHECKIN_MULTI_ATTACHMENT)