news 2026/7/17 21:36:17

29-表单返回就丢内容-用草稿快照和离开守卫保住输入

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
29-表单返回就丢内容-用草稿快照和离开守卫保住输入

第29篇|表单返回就丢内容:用草稿快照和离开守卫保住输入

摘要:表单返回就丢内容,本质是把输入态当成页面临时变量。用户填了一半跳去选图片、看协议或接电话,页面重建后输入全没了。更稳的做法是把表单拆成输入态、草稿快照和提交结果:输入变化时更新脏状态,关键节点保存草稿,返回时用离开守卫确认,提交成功后再清理草稿。

我见过一个课程发布表单:标题、简介、封面都填完后,用户进入图片选择页再返回,页面重新创建,之前输入全部丢了。临时把字段塞进全局变量能救一条路径,但切账号、提交失败和重新进入又会出现旧草稿回潮。表单要按草稿链路设计,而不是靠页面实例活着。

这篇文章解决四个实际问题:

  1. 如何区分页面输入态、草稿态和已提交事实。
  2. 如何保存草稿快照而不把半成品当正式数据。
  3. 如何在返回时提示用户,避免误丢内容。
  4. 如何验证重进、提交失败、提交成功和清理路径。


先分清三种状态

状态说明生命周期
输入态当前页面输入框里的值页面内高频变化
草稿态可恢复的未提交快照跨页面、可过期
提交态已保存到业务数据长期事实

表单丢内容,通常是输入态没有及时变成草稿态;旧草稿回潮,则是提交成功后没有清理草稿态。

定义草稿快照

exportinterfaceCourseFormDraft{draftId:stringtitle:stringsummary:stringcoverUri:stringupdatedAt:number}exportfunctionisEmptyDraft(draft:CourseFormDraft):boolean{returndraft.title.trim().length===0&&draft.summary.trim().length===0&&draft.coverUri.length===0}

草稿只保存用户输入,不保存提交状态、Toast 文案和页面 loading。这样恢复时不会把旧交互状态也带回来。

DraftStore 只负责读写草稿

exportinterfaceDraftStore{load(draftId:string):Promise<CourseFormDraft|null>save(draft:CourseFormDraft):Promise<void>remove(draftId:string):Promise<void>}exportclassCourseDraftService{constructor(privatereadonlystore:DraftStore){}asyncsaveDraft(draft:CourseFormDraft):Promise<void>{if(isEmptyDraft(draft)){awaitthis.store.remove(draft.draftId)return}awaitthis.store.save({...draft,updatedAt:Date.now()})}}

空草稿直接清理,避免用户每次进入都看到没有意义的恢复提示。

页面输入变化只更新当前草稿

@Componentstruct CourseEditorPage{@Statedraft:CourseFormDraft=createEmptyCourseDraft()@Statedirty:boolean=falseprivatedraftService:CourseDraftService=createCourseDraftService()privateasyncupdateTitle(value:string):Promise<void>{this.draft={...this.draft,title:value}this.dirty=trueawaitthis.draftService.saveDraft(this.draft)}privatehasUnsavedInput():boolean{returnthis.dirty&&!isEmptyDraft(this.draft)}}

高频输入可以加防抖,但 owner 不变:页面持有输入态,DraftService 保存可恢复快照。

返回前用离开守卫

exporttypeLeaveDecision='leave'|'stay'|'save_then_leave'exportclassFormLeaveGuard{decide(dirty:boolean,draft:CourseFormDraft):LeaveDecision{if(!dirty||isEmptyDraft(draft)){return'leave'}return'stay'}}

守卫只做判断,不弹窗、不保存。页面根据结果决定是否展示确认弹窗或直接返回。

提交成功后清草稿

exportclassCoursePublishService{constructor(privatereadonlyrepository:CourseRepository,privatereadonlydraftStore:DraftStore){}asyncpublish(draft:CourseFormDraft):Promise<void>{awaitthis.repository.save({title:draft.title.trim(),summary:draft.summary.trim(),coverUri:draft.coverUri})awaitthis.draftStore.remove(draft.draftId)}}

提交失败时不要清草稿。用户修正后还能继续提交,这比让用户重新填写更符合真实使用路径。

验证清单

  1. 填一半进入图片选择页,返回后内容仍在。
  2. 关闭应用再打开编辑页,能恢复未提交草稿。
  3. 提交失败后草稿仍在,错误可见。
  4. 提交成功后再次进入,不出现旧草稿。
  5. 空表单返回,不弹无意义确认。
  6. 切账号后不会恢复另一个账号的草稿。

草稿要有过期和归属

草稿不是永久缓存。一个账号的草稿不能被另一个账号看到,太久以前的草稿也不一定适合自动恢复。

字段用途
ownerId区分账号或本地用户域
draftId区分新增、编辑、复制等入口
updatedAt判断是否过期
source区分手动编辑、导入恢复等来源

如果没有 owner,切账号后容易恢复旧草稿;如果没有 updatedAt,半年以前的半成品也可能突然弹出来。恢复草稿前要先判断它是否属于当前用户和当前入口。

恢复草稿前先做判断

exportinterfaceDraftRestorePolicy{shouldRestore(draft:CourseFormDraft,ownerId:string,now:number):boolean}exportclassDefaultDraftRestorePolicyimplementsDraftRestorePolicy{shouldRestore(draft:CourseFormDraft,ownerId:string,now:number):boolean{constexpired=now-draft.updatedAt>7*24*60*60*1000if(expired){returnfalse}returndraft.draftId.startsWith(ownerId)}}

恢复策略不要写在 UI 弹窗里。页面可以问“是否有可恢复草稿”,但判断草稿是否属于当前入口,应该交给策略或 Service。

发布前回归路径

  1. 新增页草稿和编辑页草稿互不覆盖。
  2. 账号 A 的草稿不会在账号 B 下恢复。
  3. 过期草稿不自动弹出,但可以在草稿箱中手动找回。
  4. 提交成功后草稿删除,提交失败后草稿保留。
  5. 从图片选择页、协议页、系统后台返回,输入内容都不丢。

真实复现路径要包括跨页面回来

表单丢内容最容易在跨页面回来时出现。测试时不要只填标题然后按返回,要覆盖几个真实路径:填完标题去选封面图,回来后标题还在;填完简介去看协议页,回来后简介还在;输入一半接电话或切后台,再回到应用后草稿仍然能恢复。很多页面在普通返回时没问题,但跳转图片选择、系统权限页、协议页后会重新创建,这才是草稿链路真正要兜住的地方。

复现时按这三组入口分别走:

  1. 普通返回:编辑页直接按返回键。
  2. 跨页面返回:进入图片选择、协议页后再回来。
  3. 系统返回:切后台、锁屏、恢复应用后继续编辑。

草稿恢复也要给用户选择

自动恢复草稿不一定总是好事。用户可能只是想新建一条课程,却被上次半成品直接填满。更稳的交互是:发现可恢复草稿后给出提示,用户选择“继续编辑”再恢复,选择“丢弃”则清理草稿。这样既不会丢内容,也不会让旧草稿打扰新任务。

exporttypeDraftRestoreAction='restore'|'discard'|'ignore'exportinterfaceDraftRestorePrompt{title:stringupdatedText:stringaction:DraftRestoreAction}

这段模型把恢复草稿变成一次明确选择,而不是页面启动时偷偷覆盖输入框。

交付前验收表

验收项通过标准
草稿恢复只恢复当前用户、当前入口的未过期草稿
离开确认有内容才提示,空表单不打扰
提交失败草稿保留,错误可见
提交成功草稿删除,再进页面不回潮

这张表适合放到提测说明里。表单问题不是单个按钮能证明的,必须用跨页面返回和失败重试来确认。

小结:表单输入不是长期事实,但也不能只在页面里

表单要把输入态、草稿态和提交态分开。页面负责输入,DraftStore 负责可恢复快照,提交成功后再清草稿。这样返回、重进、失败重试和成功清理都有明确边界。
有内容才提示,空表单不打扰 |
| 提交失败 | 草稿保留,错误可见 |
| 提交成功 | 草稿删除,再进页面不回潮 |

这张表适合放到提测说明里。表单问题不是单个按钮能证明的,必须用跨页面返回和失败重试来确认。

小结:表单输入不是长期事实,但也不能只在页面里

表单要把输入态、草稿态和提交态分开。页面负责输入,DraftStore 负责可恢复快照,提交成功后再清草稿。这样返回、重进、失败重试和成功清理都有明确边界。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/17 21:31:43

SwinTransformer解析:窗口机制与视觉任务优化

1. 从视觉任务中的Transformer困境说起 第一次接触SwinTransformer是在2021年的一个图像分割项目里。当时团队正为如何平衡计算效率和模型性能发愁——传统的CNN backbone在密集预测任务中显存占用惊人&#xff0c;而初代Vision Transformer&#xff08;ViT&#xff09;虽然效果…

作者头像 李华
网站建设 2026/7/17 21:30:16

Anthropic的前世今生

2026年5月28日&#xff0c;Anthropic宣布完成H轮650亿美元融资&#xff0c;投后估值达9650亿美元。这一数字超越了OpenAI上一轮8520亿美元的估值&#xff0c;让Anthropic成为全球估值最高的AI创企。若以标普500企业市值排名&#xff0c;其规模已可跻身全美第13大企业&#xff0…

作者头像 李华
网站建设 2026/7/17 21:29:55

GraphRAG到底能不能干活?别只看 Demo 和跑分

聊《GraphRAG到底能不能干活&#xff1f;别只看 Demo 和跑分》之前&#xff0c;先说一句实在的&#xff1a;别急着背概念&#xff0c;先看它在真实项目里到底解决什么问题。摘要先把这篇文章的目标说清楚&#xff1a;看完之后&#xff0c;你应该能判断这件事值不值得做&#xf…

作者头像 李华
网站建设 2026/7/17 21:29:54

Linux系统安装配置与开发环境搭建指南

1. Linux操作系统概述Linux作为一款开源操作系统内核&#xff0c;自1991年由Linus Torvalds创建以来&#xff0c;已经发展成为服务器、嵌入式设备和桌面计算领域的重要力量。不同于商业操作系统&#xff0c;Linux采用GPL许可证&#xff0c;允许用户自由使用、修改和分发&#x…

作者头像 李华
网站建设 2026/7/17 21:29:49

计算机毕业设计之基于SpringBoot的学校设备管理系统的设计与实现

如今&#xff0c;在科学技术飞速发展的情况下&#xff0c;信息化的时代也已因为计算机的出现而来临&#xff0c;信息化也已经影响到了社会上的各个方面。它可以为人们提供许多便利之处&#xff0c;可以大大提高人们的工作效率。随着计算机技术的发展的普及&#xff0c;各个领域…

作者头像 李华
网站建设 2026/7/17 21:26:14

个人记录【Java后端面试】Java基础

Java基础总结基础概念多线程/并发集合框架网络编程&#xff08;netty&#xff09;场景题经典面试题总结 Java基础面试主要分为4个版块&#xff0c;基础概念&#xff08;面向对象&#xff09;、多线程/并发、集合框架以及网络编程。基础概念 这部分没啥需要记录的&#xff0c;…

作者头像 李华