2026-08-12-mobile-oa-task-centers.md 27 KB

移动端 OA 任务中心 Implementation Plan

实现状态(Task 13 收尾)

  • Tasks 1–12:已落地(代码冒烟:pages/oa/start|drafts|pending|done|mine|cc|webview、OaDetail、oa-badge、oa/print、路由 /oa-detail 均存在)。
  • feat 提交(730cc99^..HEAD):730cc99 菜单 → 2dac9d6 API → cf4994e webview → 387e824 卡片 → 6b0fb7b 草稿 → 47d7a14/d827f55/76dab57 详情 → 29560b7 已办/我发起/抄送 → d528776 待办办理 → ecdf73f 撤销重开 → 06b1468 抄送增强 → 915253e 角标 → cbef681 打印。
  • Task 13:文件/路由冒烟完成;./scripts/update-graph.sh backend 已执行;真机/微信内全链路仍需人工 QA(见文末「剩余人工 QA」)。

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: 在小程序落地草稿箱 / 待办 / 已办 / 我发起的 / 抄送我的五中心:原生列表 + H5 详情办理(对齐 Web 业务,UI 对齐既有 mobile-h5 / mobile-oa-h5)。

Architecture: 列表在 mobile-h5;表单与办理在 mobile-oa-h5 新增 /oa-detail(mode=view|approve);草稿编辑继续 /oa-start?draftId=。API 复用 /web/admin/oa-approval/* + JWT。菜单种子挂「移动端菜单」根下。

Tech Stack: uni-app 微信小程序、web-view、Vue3、Vant4、@form-create/vant、FastAPI(菜单 SQL)

Spec: docs/superpowers/specs/2026-08-12-mobile-oa-task-centers-design.md

原则(强制): 功能语义对齐 web-admin OA 对应页与 instances/detail.vue;UI 对齐 mobile-h5(如 pages/demo/list、pages/oa/start)与 mobile-oa-h5(OaStart.vue、footer、picker),禁止照搬 Element 桌面布局。

已完成(勿重复): 五菜单 PNG → mobile-h5/static/menu-icons/oa-*.png;van-icon / menu-icon-colors.js / backend/app/api/mini_menu.py path→icon 映射。

Out of scope: 时间范围/分组/流程状态等已剔除筛选;Web 设计器改造;小程序内直挂 form-create。


File map

路径 职责
docs/sql/mobile_menu_oa_task_centers.sql 五菜单种子(OA002–OA006)
mobile-h5/utils/oa-api.js drafts / tasks / mark-read / counts
mobile-h5/utils/oa-h5.js buildOaDetailUrl;扩展 start 预填若需要
mobile-h5/utils/oa-badge.js 拉草稿/待办/抄送角标(可新建)
mobile-h5/pages.json 注册五列表 + 通用 webview(或扩 start/webview)
mobile-h5/pages/oa/drafts/index.vue 草稿箱
mobile-h5/pages/oa/pending/index.vue 待办
mobile-h5/pages/oa/done/index.vue 已办
mobile-h5/pages/oa/mine/index.vue 我发起的
mobile-h5/pages/oa/cc/index.vue 抄送我的
mobile-h5/pages/oa/webview/index.vue 通用 web-view(start + detail)或 扩展 start/webview.vue
mobile-h5/components/OaTaskCard.vue 任务/草稿卡片(移动端样式,非 BizListCard 业务 mock)
mobile-oa-h5/src/api/oa.js instance detail + task actions + withdraw + cc read
mobile-oa-h5/src/views/OaDetail.vue 详情只读 / 办理
mobile-oa-h5/src/oa/components/OaProgressTimeline.vue 审批进度(对齐 Web progress;可基于现有 Flow 组件)
mobile-oa-h5/src/oa/components/OaActionSheet*.vue 意见 / 加签 / 转交弹层(Vant Dialog/Popup)
mobile-oa-h5/src/router.js /oa-detail
mobile-oa-h5/src/oa/print/(可选) PDF 打印移植

Web 只读对照:

  • web-admin/src/views/oa-approval/{drafts,pending,done,mine,cc}/index.vue
  • web-admin/src/views/oa-approval/instances/detail.vue
  • web-admin/src/api/oaApproval.ts
  • web-admin/src/utils/oa-approval-print.ts(打印)

目标用户路径

首页宫格(授权菜单)
  → 五列表之一(原生)
  → 草稿编辑 → webview → /oa-start?draftId=
  → 待办办理 → webview → /oa-detail?mode=approve&instanceId=&taskId=
  → 已办/抄送/我发起查看 → /oa-detail?mode=view&instanceId=
  → postMessage → navigateBack → 列表 onShow 刷新 + 角标

Task 1: 菜单 SQL(S0)

Files:

  • Create: docs/sql/mobile_menu_oa_task_centers.sql

  • [ ] Step 1: 按 mobile_menu_oa_start.sql 写五条幂等 INSERT

完整内容:

-- 小程序 OA 五中心菜单种子(挂在「移动端菜单」根下)
-- 前置:docs/sql/mobile_menu_oa_start.sql(发起审批 OA001)可已执行
-- 角色授权请在 Web「角色管理」勾选本菜单。

SET @mini_root = (
  SELECT id FROM sys_menu
  WHERE del_flag = 0 AND parent_id = '0' AND menu_name LIKE '%移动端%'
  LIMIT 1
);

-- 草稿箱
INSERT INTO sys_menu (
  id, menu_name, parent_id, menu_type, path, component, perms,
  is_frame, visible, status, tenant_no, order_num, remark,
  create_by, create_dept_id, create_time, update_by, update_time, del_flag
)
SELECT
  'MINI000000000000000000000000OA002',
  '草稿箱',
  @mini_root,
  'C',
  '/pages/oa/drafts/index',
  NULL,
  'mini:oa:drafts',
  1,
  '1',
  '1',
  'zkxw',
  20,
  '小程序草稿箱',
  'admin',
  '',
  NOW(),
  NULL,
  NOW(),
  0
FROM DUAL
WHERE @mini_root IS NOT NULL
  AND NOT EXISTS (
    SELECT 1 FROM sys_menu WHERE id = 'MINI000000000000000000000000OA002' AND del_flag = 0
  );

-- 待办中心
INSERT INTO sys_menu (
  id, menu_name, parent_id, menu_type, path, component, perms,
  is_frame, visible, status, tenant_no, order_num, remark,
  create_by, create_dept_id, create_time, update_by, update_time, del_flag
)
SELECT
  'MINI000000000000000000000000OA003',
  '待办中心',
  @mini_root,
  'C',
  '/pages/oa/pending/index',
  NULL,
  'mini:oa:pending',
  1,
  '1',
  '1',
  'zkxw',
  30,
  '小程序待办中心',
  'admin',
  '',
  NOW(),
  NULL,
  NOW(),
  0
FROM DUAL
WHERE @mini_root IS NOT NULL
  AND NOT EXISTS (
    SELECT 1 FROM sys_menu WHERE id = 'MINI000000000000000000000000OA003' AND del_flag = 0
  );

-- 已办中心
INSERT INTO sys_menu (
  id, menu_name, parent_id, menu_type, path, component, perms,
  is_frame, visible, status, tenant_no, order_num, remark,
  create_by, create_dept_id, create_time, update_by, update_time, del_flag
)
SELECT
  'MINI000000000000000000000000OA004',
  '已办中心',
  @mini_root,
  'C',
  '/pages/oa/done/index',
  NULL,
  'mini:oa:done',
  1,
  '1',
  '1',
  'zkxw',
  40,
  '小程序已办中心',
  'admin',
  '',
  NOW(),
  NULL,
  NOW(),
  0
FROM DUAL
WHERE @mini_root IS NOT NULL
  AND NOT EXISTS (
    SELECT 1 FROM sys_menu WHERE id = 'MINI000000000000000000000000OA004' AND del_flag = 0
  );

-- 我发起的
INSERT INTO sys_menu (
  id, menu_name, parent_id, menu_type, path, component, perms,
  is_frame, visible, status, tenant_no, order_num, remark,
  create_by, create_dept_id, create_time, update_by, update_time, del_flag
)
SELECT
  'MINI000000000000000000000000OA005',
  '我发起的',
  @mini_root,
  'C',
  '/pages/oa/mine/index',
  NULL,
  'mini:oa:mine',
  1,
  '1',
  '1',
  'zkxw',
  50,
  '小程序我发起的',
  'admin',
  '',
  NOW(),
  NULL,
  NOW(),
  0
FROM DUAL
WHERE @mini_root IS NOT NULL
  AND NOT EXISTS (
    SELECT 1 FROM sys_menu WHERE id = 'MINI000000000000000000000000OA005' AND del_flag = 0
  );

-- 抄送我的
INSERT INTO sys_menu (
  id, menu_name, parent_id, menu_type, path, component, perms,
  is_frame, visible, status, tenant_no, order_num, remark,
  create_by, create_dept_id, create_time, update_by, update_time, del_flag
)
SELECT
  'MINI000000000000000000000000OA006',
  '抄送我的',
  @mini_root,
  'C',
  '/pages/oa/cc/index',
  NULL,
  'mini:oa:cc',
  1,
  '1',
  '1',
  'zkxw',
  60,
  '小程序抄送我的',
  'admin',
  '',
  NOW(),
  NULL,
  NOW(),
  0
FROM DUAL
WHERE @mini_root IS NOT NULL
  AND NOT EXISTS (
    SELECT 1 FROM sys_menu WHERE id = 'MINI000000000000000000000000OA006' AND del_flag = 0
  );
  • Step 2: 在测试库执行 SQL,Web 角色管理勾选后调 GET /mini/menus 应含 5 条 path

Expected: list 中出现 /pages/oa/drafts|pending|done|mine|cc/index,icon 分别为 oa-drafts|oa-pending|oa-done|oa-mine|oa-cc。

  • Step 3: Commit
git add docs/sql/mobile_menu_oa_task_centers.sql
git commit -m "$(cat <<'EOF'
feat(oa): add mobile menu seeds for five OA task centers

EOF
)"

Task 2: 扩展双端 OA API(S0/S1 前置)

Files:

  • Modify: mobile-h5/utils/oa-api.js
  • Modify: mobile-oa-h5/src/api/oa.js

  • [ ] Step 1: 在 mobile-h5/utils/oa-api.js 追加(保留现有 templates 方法;按项目 request.js 支持的 get/post/del 写法实现)

对齐 web-admin/src/api/oaApproval.ts:

// drafts
export function fetchOaInstanceDraftList(params) { /* GET /web/admin/oa-approval/drafts */ }
export function fetchOaInstanceDraftCount() { /* GET .../drafts/count */ }
export function fetchDeleteOaInstanceDraft(id) { /* DELETE .../drafts/{id} */ }
export function fetchBatchDeleteOaInstanceDrafts(ids) { /* POST .../drafts/batch-delete { ids } */ }
export function fetchSubmitOaInstanceDraft(id, data) { /* POST .../drafts/{id}/submit */ }

// tasks
export function fetchOaTasks(kind, params) {
  // kind: pending|done|mine|cc
  // GET .../tasks/{kind}  query: page,pageSize,appName,initiatorName,readStatus(cc)
}
export function fetchOaPendingTaskCount() { /* GET .../tasks/pending/count */ }
export function fetchOaCcUnreadCount() { /* GET .../tasks/cc/unread-count */ }
export function fetchMarkOaCcTaskRead(taskId) { /* POST .../tasks/{id}/read */ }
export function fetchBatchMarkOaCcTasksRead(ids) { /* POST .../tasks/cc/read-batch { ids } */ }

实现时复用文件内已有 get / authHeader;若 request.js 无 del/post,按同目录其它 utils 写法调用。每个函数必须带 Authorization: Bearer。

  • Step 2: 在 mobile-oa-h5/src/api/oa.js 追加
export function fetchOaInstanceDetail(id) {
  return get(`/web/admin/oa-approval/instances/${encodeURIComponent(id)}`)
}

export function fetchApproveOaTask(taskId, comment) {
  return post(`/web/admin/oa-approval/tasks/${encodeURIComponent(taskId)}/approve`, { comment })
}

export function fetchRejectOaTask(taskId, comment) {
  return post(`/web/admin/oa-approval/tasks/${encodeURIComponent(taskId)}/reject`, { comment })
}

export function fetchReturnOaTask(taskId, comment) {
  return post(`/web/admin/oa-approval/tasks/${encodeURIComponent(taskId)}/return`, { comment })
}

export function fetchAddSignOaTask(taskId, data) {
  // data: { userIds: string[], comment?: string }
  return post(`/web/admin/oa-approval/tasks/${encodeURIComponent(taskId)}/add-sign`, data)
}

export function fetchTransferOaTask(taskId, data) {
  // data: { targetUserId: string, comment?: string }
  return post(`/web/admin/oa-approval/tasks/${encodeURIComponent(taskId)}/transfer`, data)
}

export function fetchWithdrawOaInstance(instanceId) {
  return post(`/web/admin/oa-approval/instances/${encodeURIComponent(instanceId)}/withdraw`, {})
}

export function fetchMarkOaCcTaskRead(taskId) {
  return post(`/web/admin/oa-approval/tasks/${encodeURIComponent(taskId)}/read`, {})
}
  • [ ] Step 3: 本地用已登录 token 在浏览器/控制台或临时页面各打一条 drafts list / tasks pending 验证 200

  • [ ] Step 4: Commit

git add mobile-h5/utils/oa-api.js mobile-oa-h5/src/api/oa.js
git commit -m "$(cat <<'EOF'
feat(oa): extend mobile OA APIs for drafts, tasks, and detail actions

EOF
)"

Task 3: 通用 web-view + buildOaDetailUrl(S0)

Files:

  • Modify: mobile-h5/utils/oa-h5.js
  • Create or Modify: mobile-h5/pages/oa/webview/index.vue(推荐新建通用页)或 扩展 pages/oa/start/webview.vue
  • Modify: mobile-h5/pages.json

  • [ ] Step 1: 在 oa-h5.js 增加

/** 审批详情 / 办理页 */
export function buildOaDetailUrl({ instanceId, taskId, mode } = {}) {
  const token = getToken()
  const params = {}
  if (instanceId) params.instanceId = instanceId
  if (taskId) params.taskId = taskId
  if (mode) params.mode = mode // view | approve
  if (token) params.token = token
  return withQuery('/oa-detail', params)
}

const DETAIL_TITLE_KEY = 'oa_detail_nav_title'

export function stashOaDetailTitle(name) { /* 同 stashOaStartTitle 模式 */ }
export function takeOaDetailTitle() { /* 同 takeOaStartTitle */ }
  • Step 2: 通用 webview 页逻辑

onLoad(query):

  1. 无 token → forceRelogin
  2. 若 query.draftId 或(query.appId 且无 instanceId)→ buildOaStartUrl
  3. 若 query.instanceId → buildOaDetailUrl({ instanceId, taskId, mode })
  4. 标题:takeOaDetailTitle() / takeOaStartTitle()
  5. CLOSE_ACTIONS 扩展:oa-detail-close、oa-detail-done、oa-detail-withdraw、oa-detail-restart 等;H5 iframe message 与小程序 @message 行为对齐现有 start webview
  • Step 3: pages.json 注册五列表空壳路径 + webview(标题用中文业务名)

示例条目:

{
  "path": "pages/oa/drafts/index",
  "style": {
    "navigationBarTitleText": "草稿箱",
    "navigationBarBackgroundColor": "#ecfdf5",
    "navigationBarTextStyle": "black"
  }
}

同样注册 pending/done/mine/cc;webview 标题默认「审批详情」。

  • [ ] Step 4: 真机/开发者工具打开任意 webview URL 带 token,确认能加载 H5(detail 可先 404,本 Task 只要求 start 路径不回归)

  • [ ] Step 5: Commit

git add mobile-h5/utils/oa-h5.js mobile-h5/pages.json mobile-h5/pages/oa/webview/
git commit -m "$(cat <<'EOF'
feat(oa): add shared OA webview and detail URL builder

EOF
)"

Task 4: 共用任务卡片 + 列表壳(UI)

Files:

  • Create: mobile-h5/components/OaTaskCard.vue
  • Create: mobile-h5/styles/components/oa-task-list.scss(若项目按组件拆 scss)
  • Reference UI: mobile-h5/pages/demo/list.vue、pages/oa/start/index.vue、styles/tokens.scss

  • [ ] Step 1: 实现 OaTaskCard

Props 建议:

{
  title: String,          // 流程名
  statusText: String,
  statusTone: String,     // warn|success|danger|info
  metaLines: Array,       // [{ label, value }]
  unread: Boolean,        // 抄送红点
  actions: Array          // [{ key, label, primary?, danger? }]
}

Emits: action with { key, ... };整卡点击可 emit open。

样式:三墙 Token;卡片圆角/间距对齐 start 列表与 demo list;不要复制 Web 表格列。

  • Step 2: 抽可复用 list mixin 或 utils/oa-list.js(可选)

提供:page/pageSize/loading/finished、loadMore、onPullDownRefresh 重置。若不想抽 mixin,五页内复制最小分页逻辑亦可(YAGNI:先两页共用后再抽)。

  • Step 3: Commit
git add mobile-h5/components/OaTaskCard.vue mobile-h5/styles/components/oa-task-list.scss
git commit -m "$(cat <<'EOF'
feat(oa): add mobile OA task card for center lists

EOF
)"

Task 5: 草稿箱列表(S1)

Files:

  • Create: mobile-h5/pages/oa/drafts/index.vue
  • Modify: mobile-h5/pages.json(若 Task3 已注册可跳过)

功能清单(规格 §2.1): 列表、分页、流程名筛选、编辑、列表提交、单删、批量删、角标(角标可先调 count API,宫格角标 UI 放 Task 11)。

  • Step 1: 实现页面

结构参考 pages/demo/list.vue:顶区搜索(仅关键词)+ 列表 + 多选模式批量删。

关键方法:

async loadList(reset) {
  // fetchOaInstanceDraftList({ keyword, page, pageSize })
}
openEdit(row) {
  stashOaStartTitle(row.appName || row.title)
  uni.navigateTo({
    url: `/pages/oa/webview/index?draftId=${row.id}&appId=${row.appId || ''}`
    // 若仍用 start/webview:`/pages/oa/start/webview?draftId=...`
  })
}
async submitRow(row) {
  // uni.showModal 确认 → fetchSubmitOaInstanceDraft(row.id) → 刷新
  // 失败:可改为 openEdit 让用户改表单(对齐 Web)
}
async deleteRow(row) { /* confirm → fetchDeleteOaInstanceDraft */ }
async batchDelete() { /* selected ids → fetchBatchDeleteOaInstanceDrafts */ }
  • [ ] Step 2: onShow 刷新列表

  • [ ] Step 3: 手工验收

  1. 发起页暂存 → 草稿箱可见
  2. 编辑打开 H5 回填 → 再暂存/提交
  3. 列表提交成功后草稿消失
  4. 单删、批量删
  • Step 4: Commit
git add mobile-h5/pages/oa/drafts/
git commit -m "$(cat <<'EOF'
feat(oa): add mobile draft box list with edit submit delete

EOF
)"

Task 6: H5 /oa-detail 只读(S2)

Files:

  • Create: mobile-oa-h5/src/views/OaDetail.vue
  • Create: mobile-oa-h5/src/oa/components/OaProgressTimeline.vue(或复用/改造现有 flow 展示)
  • Modify: mobile-oa-h5/src/router.js
  • Reference: OaStart.vue(schema-prep、form-create disabled)、Web detail.vue(progress 字段)

  • [ ] Step 1: 注册路由

import OaDetail from './views/OaDetail.vue'
// ...
{ path: '/oa-detail', component: OaDetail }
  • Step 2: OaDetail.vue 最小只读
  1. captureTokenFromUrl(与 main/OaStart 一致)
  2. 读 instanceId、mode(默认 view)、taskId
  3. fetchOaInstanceDetail → formSchemaJson + formData → schema-prep → <form-create-mobile disabled>
  4. 渲染 progress 时间线
  5. 顶栏状态 Tag(文案映射对齐 Web statusText)
  6. 底栏:mode=view 且非发起人特殊操作时仅「关闭」→ postMessage / wx.miniProgram.navigateBack,action=oa-detail-close

UI:padding、底栏按钮类名对齐 OaStart / oa-form.css / footer demo。

  • Step 3: 浏览器直接打开

http://127.0.0.1:5178/oa-h5/oa-detail?instanceId=真实ID&token=...&mode=view
Expected: 表单只读可见、进度可见、关闭可返回。

  • Step 4: Commit
git add mobile-oa-h5/src/views/OaDetail.vue mobile-oa-h5/src/oa/components/OaProgressTimeline.vue mobile-oa-h5/src/router.js
git commit -m "$(cat <<'EOF'
feat(oa): add mobile OA detail page in read-only mode

EOF
)"

Task 7: 已办 / 我发起 / 抄送列表进 view(S2)

Files:

  • Create: mobile-h5/pages/oa/done/index.vue
  • Create: mobile-h5/pages/oa/mine/index.vue(本 Task 仅查看;打印/复制/撤销后续 Task)
  • Create: mobile-h5/pages/oa/cc/index.vue(本 Task:列表+打开已读+进详情;阅读筛选/批量可本 Task 一并做或见 Task 10)

  • [ ] Step 1: 已办页

筛选:appName、initiatorName(搜索框 + 可选第二条件,移动端可用一行双字段或展开筛选,不要 Web 日期/状态)。
fetchOaTasks('done', …) → 卡片 → stashOaDetailTitle → navigate webview instanceId&mode=view。

  • Step 2: 我发起的页(查看)

筛选仅 appName。fetchOaTasks('mine', …) → view。

  • Step 3: 抄送页(查看 + 打开已读)

打开前或打开时:fetchMarkOaCcTaskRead(taskId)(task 行上的抄送任务 id,字段名对齐 Web row.id)。
再进 mode=view。H5 详情可再兜底 mark-read。

  • [ ] Step 4: 验收三页均可进只读详情并返回刷新

  • [ ] Step 5: Commit

git add mobile-h5/pages/oa/done/ mobile-h5/pages/oa/mine/ mobile-h5/pages/oa/cc/
git commit -m "$(cat <<'EOF'
feat(oa): add done mine cc lists opening read-only OA detail

EOF
)"

Task 8: 待办列表 + 办理动作(S3)

Files:

  • Create: mobile-h5/pages/oa/pending/index.vue
  • Modify: mobile-oa-h5/src/views/OaDetail.vue
  • Create: 意见弹层 / 加签选人 / 转交选人(复用 OaPersonSelect 或 Vant Popup + 已有选人)

对照 Web detail.vue: myPendingTask、canApprove、allowReturn、allowAddSign、转交排除规则。

  • Step 1: 待办列表

筛选:流程名、申请人。进入:mode=approve&instanceId=&taskId=(taskId 用行上待办任务 id)。

  • Step 2: 详情底栏(mode=approve 且存在当前用户 pending task)
按钮 API
同意 fetchApproveOaTask(taskId, comment?)
拒绝 fetchRejectOaTask
打回 意见必填 → fetchReturnOaTask
加签 选多人 → fetchAddSignOaTask({ userIds, comment })
转交 选一人(排除本人与本节点已有待办)→ fetchTransferOaTask({ targetUserId, comment })

成功后 toast + postMessage({ source:'zkxw-oa-h5', action:'oa-detail-done' }) + navigateBack。

意见输入用 Vant Dialog/Popup,样式对齐 mobile-oa-h5,不要 Element MessageBox。

  • [ ] Step 3: 五条办理主路径各测至少一次(可用测试租户)

  • [ ] Step 4: Commit

git add mobile-h5/pages/oa/pending/ mobile-oa-h5/src/views/OaDetail.vue mobile-oa-h5/src/oa/components/
git commit -m "$(cat <<'EOF'
feat(oa): add pending list and approve reject return add-sign transfer

EOF
)"

Task 9: 我发起的 — 撤销与复制重开(S4)

Files:

  • Modify: mobile-h5/pages/oa/mine/index.vue
  • Modify: mobile-oa-h5/src/views/OaDetail.vue
  • Modify: mobile-oa-h5/src/views/OaStart.vue(接收预填)
  • Modify: mobile-h5/utils/oa-h5.js(可选 storage 传 fromInstance 预填)

  • [ ] Step 1: 详情撤销

条件:当前用户是发起人且实例 running(与 Web canWithdraw 一致)。
fetchWithdrawOaInstance → done 回列表。

  • Step 2: 复制并重新发起

条件:发起人且 status ∈ returned|cancelled|rejected。
实现择一(优先与 Web 一致):

  1. H5:postMessage action=oa-detail-restart + appId + 小程序再开 start webview,并用 uni.setStorageSync('oa_restart_prefills', { formData, … })
  2. 或 H5 内 router.replace({ path:'/oa-start', query:{ appId, fromInstanceId } }),OaStart 拉实例详情回填

选定一种写清,并在 OaStart 实现回填;提交成功后勿留下旧 instanceId。

  • [ ] Step 3: 列表行也提供「复制并重新发起」入口(终态时)

  • [ ] Step 4: 验收撤销与复制提交新单

  • [ ] Step 5: Commit

git add mobile-h5/pages/oa/mine/ mobile-oa-h5/src/views/OaDetail.vue mobile-oa-h5/src/views/OaStart.vue mobile-h5/utils/oa-h5.js
git commit -m "$(cat <<'EOF'
feat(oa): support withdraw and copy-restart on mobile mine flows

EOF
)"

Task 10: 抄送增强(S5)

Files:

  • Modify: mobile-h5/pages/oa/cc/index.vue

  • [ ] Step 1: 阅读状态筛选 — readStatus: unread|read|all(默认 unread,对齐 Web)

  • [ ] Step 2: 未读红点样式 — OaTaskCard unread

  • [ ] Step 3: 单条已读 — 不打开详情也可 mark

  • [ ] Step 4: 批量已读 — 多选 + fetchBatchMarkOaCcTasksRead

  • [ ] Step 5: 验收后 Commit

git add mobile-h5/pages/oa/cc/
git commit -m "$(cat <<'EOF'
feat(oa): add cc read filters batch mark and unread styles

EOF
)"

Task 11: 角标(S1/S3/S5/S7)

Files:

  • Create: mobile-h5/utils/oa-badge.js
  • Modify: mobile-h5/components/MenuGrid.vue 和/或 pages/index/index.vue

  • [ ] Step 1: refreshOaMenuBadges

并行请求:

  • fetchOaInstanceDraftCount
  • fetchOaPendingTaskCount
  • fetchOaCcUnreadCount

返回 map:path → count(/pages/oa/drafts/index 等)。展示:>99 显示 99+。

  • Step 2: 首页加载菜单后合并 badge;onShow 刷新

MenuGrid 在 title 旁或图标右上角显示数字(样式轻量,对齐三墙,勿过重)。

  • [ ] **Step 3: 从 webview 返回后各列表 onShow 也可触发首页角标(通过 storage 事件或全局简单 bus;最小做法:仅首页 onShow 刷新即可)

  • [ ] Step 4: Commit

git add mobile-h5/utils/oa-badge.js mobile-h5/components/MenuGrid.vue mobile-h5/pages/index/index.vue
git commit -m "$(cat <<'EOF'
feat(oa): show draft pending cc badges on mobile home menu

EOF
)"

Task 12: 打印(S6)

Files:

  • 移植参考:web-admin/src/utils/oa-approval-print.ts
  • Create: mobile-oa-h5/src/oa/print/exportOaApprovalPdf.js(或等价)
  • Modify: OaDetail.vue + mine/cc 列表入口

  • [ ] Step 1: 在 H5 内实现 PDF 生成(优先 jspdf/html2canvas 等与 Web 同类方案;以 Web 工具函数逻辑为准)

  • [ ] Step 2: 详情与列表「打印」按钮:生成后尝试下载 / 新开预览;微信内若失败,showToast 明确文案(规格允许兜底,不阻塞其它功能)

  • [ ] **Step 3: 真机验收;记录限制到 docs/superpowers/specs/ 旁注或本 plan 底部「打印验收笔记」

  • [ ] Step 4: Commit

git add mobile-oa-h5/src/oa/print/ mobile-oa-h5/src/views/OaDetail.vue mobile-h5/pages/oa/mine/ mobile-h5/pages/oa/cc/
git commit -m "$(cat <<'EOF'
feat(oa): add mobile approval PDF print for mine and cc

EOF
)"

Task 13: 回归与图谱(收尾)

  • Step 1: 回归「发起审批」全链路未被破坏(列表→填写→草稿→提交) — 代码冒烟:pages/oa/start/* 仍在;真机全链路见下方人工 QA
  • Step 2: 按规格 §8 测试要点过一遍勾选 — 文件/路由冒烟完成;设备侧勾选交人工
  • **Step 3: 若改了 backend mini_menu.py(本计划默认已改完),执行 ./scripts/update-graph.sh backend
  • Step 4: 最终 commit 若有文档修补

剩余人工 QA

  • 发起审批:选流程 → 填表 → 存草稿 → 草稿箱编辑/提交/删除
  • 待办:同意 / 拒绝 / 打回 / 加签 / 转交;返回列表刷新与角标
  • 已办 / 我发起 / 抄送:只读详情;我发起撤销、复制重开;抄送已读/批量/未读样式
  • 首页角标:草稿 / 待办 / 抄送未读
  • 打印:手机浏览器可调起;微信 web-view 内明确失败提示

Spec coverage 自检

规格项 Task
菜单 SQL OA002–006 + 图标映射 Task 1(图标已完成)
功能对齐 Web / UI 对齐移动端 全文原则 + Task 4–8
草稿 1/2/3/6/7/8/9/10 Task 5 + Task 11
待办办理五动作 + 角标 Task 8 + 11
已办只读 Task 6–7
我发起撤销/复制/打印 Task 9 + 12
抄送阅读/批量/打印/角标 Task 7/10/11/12
/oa-detail Task 6 + 8 + 9
S0–S7 顺序 Task 1→12

执行说明

实现时每完成一个 Task 再开下一个;打印(Task 12)可与角标并行,但不得阻塞 Task 5–10 合并。


打印验收笔记(S6 / Task 12)

  • 实现:mobile-oa-h5 用可打印 HTML + window.print()(无 jspdf/html2canvas 依赖);字段/流程组装对齐 Web oa-approval-print.ts(关联审批嵌套块简化为「已关联 N 条」)。
  • 入口:我发起的 / 抄送列表「打印」→ 详情 autoPrint=1;详情底栏次要「打印」按钮(紧挨关闭)。
  • 微信小程序 web-view:系统打印能力通常不可用;检测后 showToast:「微信内暂不支持系统打印,请用手机浏览器打开或到电脑端导出」。不阻塞其它功能。
  • 建议验收:手机浏览器 / 电脑打开 H5 详情应能调起打印对话框;微信内应看到明确失败提示。