Add approval instance drafts with menu badges, dual-track user/role read model and role bound names, form-designer outline docs, and bare-metal test deploy scripts. Ignore local test-server credential file. Co-authored-by: Cursor <cursoragent@cursor.com>
6.1 KiB
6.1 KiB
OA 发起审批草稿箱 Design
| 项 | 内容 |
|---|---|
| 日期 | 2026-08-06 |
| 状态 | 已定稿(UI 以 docs/demos/oa-approval-draft-box-demo.html 为准) |
| 范围 | 发起审批「暂存草稿」+ 侧栏「草稿箱」列表(再编辑 / 保存 / 提交 / 删除) |
背景
现状「发起审批」弹窗仅有「取消」「提交」,填到一半关闭即丢失。客户要求可暂存草稿,并在「发起审批」下方增加「草稿箱」菜单集中管理。
现有 oa_app_version.status=draft 仅为模板设计草稿,与本需求的发起人填写草稿无关,不得混用。
目标与非目标
目标
- 发起弹窗可「暂存草稿」,不进入审批流、不产生待办。
- 侧栏「草稿箱」列出当前用户草稿;支持编辑、再次保存、提交审批、删除与批量删除。
- 草稿仅本人可见(租户内 + 发起人 = 当前用户)。
非目标
- 不与「我发起的」实例混排;草稿不是
oa_instance。 - 不做跨用户共享草稿、不做审批人代填。
- 不做自动定时暂存(仅显式点击「暂存草稿 / 保存草稿」)。
- 不改造小程序端(本期仅 web-admin)。
方案对比与结论
| 方案 | 做法 | 优点 | 缺点 |
|---|---|---|---|
| A(推荐) | 独立表 oa_instance_draft |
与引擎解耦;「我发起的」无需过滤 draft;语义清晰 | 多一张表与一套 CRUD |
| B | oa_instance.status=draft |
少表 | 引擎/列表/关联审批处处要排除;误发起风险高 |
| C | 仅浏览器 localStorage | 无后端 | 换设备丢失;无法做菜单级权限与审计 |
选定 A。 提交时调用现有 start_instance,成功后软删对应草稿。
数据模型
表名:oa_instance_draft
| 字段 | 说明 |
|---|---|
id |
VARCHAR(64) PK,UUID |
app_id |
审批应用 ID |
app_version_id |
暂存时锁定的已发布版本(提交时优先用最新已发布版,见下) |
title |
可选标题(可由应用名或表单字段推导,可空) |
form_data_json |
表单数据 JSON |
initiator_select_json |
发起人自选 {nodeId:[userId,...]} |
initiator_dept_id |
发起部门(与发起接口一致) |
owner_user_id |
草稿所属人(= 创建人) |
| 公共字段 | tenant_no … del_flag(遵循 database-conventions) |
约束与行为:
- 同一用户对同一
app_id允许多条草稿(与 Demo 列表一致)。 - 列表默认
create_time DESC不适用时,用update_time DESC(最近保存优先)。 - 删除:软删
del_flag=1。 - 暂存不做完整必填校验;提交走与正式发起相同的校验。
版本策略
- 暂存:写入当时「最新已发布」
app_version_id。 - 编辑打开:用该草稿的
app_id取当前最新已发布模板渲染;form_data/initiator_select按 field 名回填(字段已删则忽略多余键)。 - 若应用已无已发布版本:编辑/提交时提示「该审批单已下架或未发布」,禁止提交,允许删除草稿。
API(/api/admin/oa-approval)
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /drafts |
当前用户草稿分页;query:keyword(流程名)、groupId、updateTimeFrom/updateTimeTo、page/pageSize |
| GET | /drafts/{id} |
草稿详情(含 formData、initiatorSelect、app 基础信息) |
| POST | /drafts |
新建草稿(appId + formData + initiatorSelect + initiatorDeptId + title?) |
| PUT | /drafts/{id} |
更新草稿(仅 owner) |
| DELETE | /drafts/{id} |
软删单条 |
| POST | /drafts/batch-delete |
body: { ids: string[] } 批量软删 |
| POST | /drafts/{id}/submit |
校验 + start_instance;成功后软删草稿;返回实例信息 |
权限:均需登录管理端用户;读写仅限 owner_user_id == 当前用户 且同 tenant_no。菜单码:oa:drafts(列表/读写统一挂此码,与 oa:start 同级授权习惯)。
前端
菜单与路由
- 侧栏顺序:… → 发起审批 → 草稿箱 → 待办中心 → …
- 路由:
/#/system-manage/oa-approval/drafts,组件web-admin/src/views/oa-approval/drafts/index.vue - 前端路由模块
sqxp.ts与库表sys_menu(SQL 脚本)同步增加菜单项。
发起页 start/index.vue
- 弹窗底栏:取消 | 暂存草稿 | 提交(主色仅「提交」)。
- 「暂存草稿」:调用 POST/PUT drafts;成功提示「已存入草稿箱」并关弹窗(或保持打开——关弹窗,与 Demo 心智一致)。
- 若路由带
draftId(从草稿箱「编辑」跳入):打开对应模板并回填,暂存走 PUT。
草稿箱页(对齐「我发起的」列表模式)
列:序号、流程名称、模板版本、最近保存、状态(Tag「草稿」warning)、操作(编辑 / 提交审批 / 删除)。
- 编辑:
router.push到发起页并带draftId(或本页弹窗复用发起表单——推荐跳转发页 + query,少复制逻辑;若发起页已是弹窗模式,则在草稿箱页复用同一套 dialog 逻辑亦可。实现择一:草稿箱页内嵌与 start 相同的发起弹窗,避免整页跳转丢上下文)。 - 提交审批:调用
POST /drafts/{id}/submit;若校验失败,打开编辑弹窗并展示错误。 - 删除 / 批量删除:二次确认后调用删除接口。
筛选:流程名称、保存时间范围、模板分组(与 Demo 一致)。
错误处理
- 非 owner / 已删:404 或 403,文案「草稿不存在或无权访问」。
- 提交时应用不可用:400 + 明确文案。
- 批量删除:跳过无权 id,返回成功数(或全部失败则报错)——采用 全部校验通过再删,任一无权则整批失败。
测试要点
- 暂存后草稿箱可见;他人不可见。
- 更新草稿只改本人记录。
- 提交成功产生
oa_instance且草稿软删;「我发起的」出现新实例。 - 暂存不产生
oa_task。 - 无发布版本时禁止提交。
UI 权威
docs/demos/oa-approval-draft-box-demo.html;视觉遵循 Fresh Mint / docs/web-admin-ui-spec-fresh-mint.md。