27 KiB
移动端 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菜单 →2dac9d6API →cf4994ewebview →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.vueweb-admin/src/views/oa-approval/instances/detail.vueweb-admin/src/api/oaApproval.tsweb-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):
- 无 token →
forceRelogin - 若
query.draftId或(query.appId且无instanceId)→buildOaStartUrl - 若
query.instanceId→buildOaDetailUrl({ instanceId, taskId, mode }) - 标题:
takeOaDetailTitle()/takeOaStartTitle() CLOSE_ACTIONS扩展:oa-detail-close、oa-detail-done、oa-detail-withdraw、oa-detail-restart等;H5 iframemessage与小程序@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: 手工验收
- 发起页暂存 → 草稿箱可见
- 编辑打开 H5 回填 → 再暂存/提交
- 列表提交成功后草稿消失
- 单删、批量删
- 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)、Webdetail.vue(progress 字段) -
Step 1: 注册路由
import OaDetail from './views/OaDetail.vue'
// ...
{ path: '/oa-detail', component: OaDetail }
- Step 2:
OaDetail.vue最小只读
captureTokenFromUrl(与 main/OaStart 一致)- 读
instanceId、mode(默认 view)、taskId fetchOaInstanceDetail→formSchemaJson+formData→schema-prep→<form-create-mobile disabled>- 渲染
progress时间线 - 顶栏状态 Tag(文案映射对齐 Web
statusText) - 底栏:
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 一致):
- H5:
postMessageaction=oa-detail-restart+appId+ 小程序再开 start webview,并用uni.setStorageSync('oa_restart_prefills', { formData, … }) - 或 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: 未读红点样式 —
OaTaskCardunread -
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
并行请求:
fetchOaInstanceDraftCountfetchOaPendingTaskCountfetchOaCcUnreadCount
返回 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 依赖);字段/流程组装对齐 Weboa-approval-print.ts(关联审批嵌套块简化为「已关联 N 条」)。 - 入口:我发起的 / 抄送列表「打印」→ 详情
autoPrint=1;详情底栏次要「打印」按钮(紧挨关闭)。 - 微信小程序 web-view:系统打印能力通常不可用;检测后
showToast:「微信内暂不支持系统打印,请用手机浏览器打开或到电脑端导出」。不阻塞其它功能。 - 建议验收:手机浏览器 / 电脑打开 H5 详情应能调起打印对话框;微信内应看到明确失败提示。