大型后台管理系统如何把业务操作做可靠(17):复杂编辑器的状态、命令与协作
运营用后台编辑一张门店平面图。他拖动货架、批量对齐、撤销,然后切换到另一张图。此时需要同时管理文档、选区、手势、异步保存和不同版本的数据。富文本、流程图和合同编排也会遇到类似问题。
把文档状态与交互状态分开
文档保存业务实体和稳定 ID,例如货架的位置、大小和所属区域。选区、悬停、缩放和拖动过程是当前会话的状态。保存时序列化文档,避免把鼠标坐标和打开的面板写进业务数据。坐标需要区分屏幕坐标和文档坐标,缩放后用明确的变换计算落点。
把交互转成有边界的命令
拖动过程中只更新临时预览,释放指针后提交一次移动命令。下面的命令示例保存变更前后的位置,供本地撤销使用。
{
"type": "moveShelf",
"shelfId": "S42",
"before": { "x": 20, "y": 40 },
"after": { "x": 30, "y": 40 }
}
命令应用前检查对象是否存在、是否锁定和目标位置是否有效。多选移动可以合成一项可撤销操作。撤销修改本地文档历史,已提交到服务端并产生业务影响的动作需要独立恢复规则。异步保存携带文档版本,成功时只确认已经保存的快照,用户随后做的编辑仍保持待保存状态。
根据冲突代价选协作机制
低冲突场景可以用版本检查和冲突比较。高价值文档可以加有期限的编辑租约,并在服务端核对租约。确实需要多人实时改同一文档时,再评估 OT 或 CRDT 一类协作模型。需要事先定义节点删除与子节点更新冲突、离线重连、权限撤销以及本地撤销的语义。富文本能合并字符,不代表所有业务约束都能自动合并。
按一条拖动操作走完状态变化
用户按下货架 S42 时记住它的初始位置和指针起点。移动时将屏幕坐标转换到文档坐标,算出临时位移并预览。松手时验证新位置,生成一项移动命令,再把临时手势清掉。取消指针操作则只清理预览。选择多个货架时先校验整个组合是否可以移动,再以一个命令提交,撤销也按一次用户意图处理。
pointerUp(gesture, pointer):
next = toDocumentPoint(pointer) - gesture.grabOffset
if gesture.cancelled or next == gesture.initialPosition:
discardPreview()
return
command = moveShelf(gesture.id, gesture.initialPosition, next)
if validate(command, currentDocument):
apply(command)
appendToUndoHistory(command)
discardPreview()
文档保存可以从当前修订创建不可变快照。假设保存第 10 版时,用户又编辑出第 11 版。响应回来只把第 10 版标记为已确认,第 11 版仍是未保存。服务端拒绝旧版本写入时,把已保存版本、当前文档和本地待提交命令分开比较。已被远端删除的货架不能直接应用本地移动,页面应显示冲突或明确跳过原因。
决定撤销撤的是什么
单人本地撤销可以应用一条命令的反向变更。多人协作时,另一个人可能在同一对象上做了更新。直接恢复旧位置会覆盖他的操作。可以按命令归属和版本判断是否能执行反向命令,也可以要求人工选择。对已经发布且影响库存、门店配置的修改,应提供有审计的恢复动作。
检查案例时从四条路径出发。拖动中按 Esc,文档不变。拖动后连续保存两次,旧响应不应清掉新草稿。远端删除选中节点,本地选区应失效。撤销批量对齐,参与节点应作为一次操作恢复。这些断言检查的是文档结果和编辑状态,而非某个内部事件是否触发。
分别测输入、渲染和保存
高频拖动可以把绘制合并到动画帧,拖动中避免序列化整个文档。数据量大时先测命中检测、布局、重绘和保存的耗时,再决定是否采用空间索引、分区渲染或增量保存。补键盘操作和清晰的可访问名称。验收时用同一张复杂图测试拖动取消、多选撤销、外部更新、保存失败和页面重开,核对每一步文档状态与选区。
用具体状态确认保存与撤销
可以给编辑器留一份小型验证文档,包含两个货架、一个分组和一个锁定区域。让运营把两个货架一起移动到新的位置,再尝试移入锁定区。若一个节点不满足规则,整组移动应按约定取消,或者界面明确给出逐项结果。保存结果应保持文档的业务约束,例如货架不能落在所属门店的边界以外。
| 操作序列 | 应保留的状态 |
|---|---|
| 拖动预览后取消 | 文档不变,临时位置清除 |
| 合法移动后撤销 | 恢复文档位置,撤销栈推进一次 |
| 保存版本 10 后继续编辑 | 版本 11 保持待保存 |
| 保存响应乱序到达 | 不用旧响应覆盖新的文档与确认基线 |
| 远端删除本地选中对象 | 选区失效,后续命令重新校验 |
保存请求可以将文档修订和持久化基线分别记录。若同一客户端只允许一项保存进行,后续编辑积累为新的待保存快照。若允许并行,就要定义服务器拒绝旧写入的规则,客户端也要追踪每个响应确认的快照。高风险编辑器往往先实现容易解释的串行保存,再根据任务耗时和并发需求决定是否引入更复杂的协作机制。
文档发布与编辑保存还可以分开。草稿保存成功,只说明内容已持久化。让门店使用新平面图,需要单独的发布动作与版本记录。发现发布内容有问题时,可基于历史版本创建恢复计划,并重新检查当前门店配置是否允许应用。
总结
复杂交互适合从文档结构、临时手势和持久命令分别建模。明确每个动作何时生效、如何撤销与恢复,再根据实际协作需求决定同步机制。