Capture the approved design for sidebar unread counts and read/batch-read flows on 抄送我的, with an interactive HTML demo for UI reference. Co-authored-by: Cursor <cursoragent@cursor.com>
5.9 KiB
5.9 KiB
OA「抄送我的」未读角标 Design
| 项 | 内容 |
|---|---|
| 日期 | 2026-08-06 |
| 状态 | 已定稿(UI 参考 docs/demos/oa-cc-unread-badge-demo.html) |
| 范围 | 侧栏「抄送我的」未读数角标 + 列表阅读状态筛选 / 已读 / 批量已读 / 查看即已读 |
背景
「抄送我的」目前仅列出抄送任务,无法区分是否已读,侧栏也无未读提示。产品要求对齐「质检工单」菜单上的红色数字角标,展示抄送给我且尚未阅读的数量;列表支持标记已读,点击「查看」默认已读。
现有 oa_task 抄送任务创建时 status=done(不阻断流程),没有阅读态字段,需扩展。
目标与非目标
目标
- 侧栏「抄送我的」(
menuCode: oa:cc)展示未读抄送数;样式对齐质检.art-text-badge;未读为 0 时隐藏角标。 - 列表:筛选下拉「阅读状态」(未读 / 已读 / 全部),默认仅未读;无 Tab。
- 操作列「已读」+ 工具栏「批量已读」;「查看」进入详情前自动标记已读。
- 历史抄送记录上线后全部视为未读。
非目标
- 不改造小程序端。
- 「打印」不自动已读。
- 不引入独立已读表或前端 localStorage 持久化。
- 不把阅读态与审批
status(pending/done)混用。
方案对比与结论
| 方案 | 做法 | 优点 | 缺点 |
|---|---|---|---|
| 1(选定) | oa_task 增加 is_read / read_time |
改动小;列表与角标查询简单 | 审批任务行也会多两列(可忽略) |
| 2 | 独立 oa_cc_read 表 |
不改 oa_task |
多 join;实现更绕 |
| 3 | localStorage | 无后端 | 换设备丢失;多端不一致 |
选定方案 1。
数据模型
表:oa_task 增量字段:
| 字段 | 类型 | 说明 |
|---|---|---|
is_read |
TINYINT NOT NULL DEFAULT 0 | 0 未读 / 1 已读;仅 task_type=cc 有业务意义 |
read_time |
DATETIME NULL | 首次标记已读时间;重复标记不覆盖 |
行为:
- 新建抄送任务:显式
is_read=0,read_time=NULL。 - 历史数据迁移:全部
is_read=0(产品确认)。 - 标记已读:本人 +
task_type=cc+ 未删除;已读则幂等成功。 - 审批任务(
task_type=approval)不读写这两字段的业务逻辑。
SQL:docs/sql/oa_task_cc_is_read.sql(ALTER + 注释;迁移默认未读即靠 DEFAULT / 显式 UPDATE)。
API(/api/admin/oa-approval)
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /tasks/cc/unread-count |
当前用户未读抄送数 { count: number } |
| GET | /tasks/cc |
扩展:query readStatus=unread|read|all,缺省 unread;列表项含 isRead、readTime(及现有字段) |
| POST | /tasks/{task_id}/read |
单条已读;仅本人抄送任务;幂等 |
| POST | /tasks/cc/read-batch |
body { ids: string[] };仅处理本人、抄送、未删任务;返回 { updated: number } |
约束(全部 SHALL):
- 租户隔离:
tenant_no= 当前用户租户。 - 归属:
assignee_user_id= 当前用户。 - 类型:
task_type=cc,del_flag=0。 - 非本人 / 非抄送 / 不存在:单条 400;批量跳过非法 id,只更新合法未读行。
说明:若现网
GET /tasks/{kind}且kind=cc已存在,则在该路径上增加readStatus,不要为列表另起冲突路径;unread-count与read-batch使用更具体路径,避免与{kind}路由抢配(实现时注意 FastAPI 路由注册顺序:更具体路径先于{kind})。
前端
「抄送我的」页(web-admin/src/views/oa-approval/cc/index.vue)
- 筛选
filter-block:el-select「阅读状态」选项未读 / 已读 / 全部;默认unread;重置回到未读。 - 工具栏:已选 N 项 →「批量已读」(中性描边,非主色);无选中或所选均已读时禁用。
- 表格:
selection;「阅读状态」列用el-tag(未读danger,已读info);未读行流程名可选红点。 - 操作列:查看 | 已读 | 打印;已读行禁用「已读」;查看:先
POST .../read再跳转实例详情;打印不改已读。 - 成功标记后:刷新列表 +
refreshOaMenuBadges();onActivated同样刷新。
对齐列表模板:/#/demo/unit(筛选 + 工具栏 + 勾选 + 批量)。
侧栏角标
- 新增 store(如
oa-menu-badge)+refreshOaMenuBadges(),模式对齐qc-menu-badge。 - 解析
menuCode === 'oa:cc'(及路由名OaApprovalCc兜底)。 SidebarSubmenu/ 横向菜单接入;数字格式与质检一致(>99→99+);count===0不渲染角标。- 登录后路由守卫中与质检角标一并刷新。
UI Demo:docs/demos/oa-cc-unread-badge-demo.html(交互示意;实现时补筛选与批量勾选,以本 spec 为准)。
错误与边界
| 场景 | 行为 |
|---|---|
| 重复已读 | 成功,不改 read_time |
| 批量含已读 / 非法 id | 跳过;合法未读照常更新;updated 为实际更新数 |
| 默认筛选未读且全部已读 | 列表空;角标隐藏;用户可改筛选看已读/全部 |
| 角标接口失败 | 角标视为未加载或 0(不阻塞菜单) |
测试要点
- 新抄送产生后角标 +1;列表默认能看到该未读行。
- 「已读」/「批量已读」后角标与列表同步。
- 「查看」后该条变为已读,角标减少。
- 「打印」后仍为未读。
- 筛选已读 / 全部行为正确;重置回到未读。
- 历史数据上线后均为未读。
- 非抄送任务调
/read应失败。
决策记录
| 决策 | 结论 |
|---|---|
| 未读为 0 | 隐藏角标 |
| 已读入口 | 操作列 + 工具栏批量已读 |
| 历史数据 | 全部未读 |
| 列表筛选 | 下拉「阅读状态」,无 Tab,默认未读 |
| 查看 | 默认已读 |
| 打印 | 不自动已读 |
| 存储 | oa_task.is_read / read_time |