# 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`。