TBOSS OA 模块 — 交互设计
版本:v1.0 | 日期:2026-06-03 | 基于 tboss-oa-product-strategy.md
覆盖 27 个页面的所有可交互元素及其操作逻辑。数据源标注遵循 v2.0 架构(审批走 ERP、用户/组织/客户走 ERP、消息走 .NET 服务端、权限走 OA ACL)。
1. 全局数据源
| 数据 |
来源 |
说明 |
| 用户信息(姓名/部门/岗位/头像) |
.NET GET /api/user/{id} |
非本地表 |
| 组织架构树 |
.NET GET /api/dept/tree |
非本地表 |
| 客户数据 |
.NET GET /api/customer/search?q= |
非本地表 |
| 审批流程(状态/时间线/待办) |
.NET GET/POST /api/oa/approval/* |
服务端转发 ERP |
| 消息通知 |
.NET 消息模块 API |
服务端已有模块 |
| OA 权限 |
本地 OaUserPermission JOIN OaPermission |
OA 自管 |
| 业务单据 |
本地 OA 业务表 |
OA 自管 |
| 车辆池 |
本地 SysVehicle |
OA 自管 |
| 费用类别 |
本地 SysCostCategory |
OA 自管 |
| 轮播图 |
本地 SysBanner |
OA 自管 |
| 银行列表 |
.NET GET /api/dict/banks |
服务端字典 |
2. 页面跳转关系总图
/ (Appshell)
├─ /messages ──────────────────────────────────────────────────────
│ └─ (点击) → /announcement/detail/:id
│ → /expense-apply/detail/:id /expense/detail/:id
│ → /overtime/detail/:id /vehicle/detail/:id
│
├─ /home ──────────────────────────────────────────────────────────
│ ├─ (轮播图) → 公告/活动链接 或 全屏预览
│ ├─ (金刚区-发起) → /expense-apply/apply /expense/apply
│ │ → /vehicle/apply /overtime/apply
│ ├─ (金刚区-记录) → /expense-apply/list /expense/list
│ │ → /outing-log/list /announcement/list
│ ├─ (看板) → /expense-apply/list /report/expense-detail /messages
│
└─ /profile
├─ 审批历史 → (子页面) → 各详情页
├─ 我的报表 → (子页面) → /report/.. (5 Tab)
└─ 关于 → (子页面)
表单页 ──(存草稿/提交)──→ 列表页 ──(点击卡片)──→ 详情页
↑ │ │
└──(左滑编辑/draft编辑)───┘ (rejected→重新编辑)┘
/admin/permissions (管理员)
/announcement/create (管理员)
3. 页面详细交互
页面 4.0:借款/备用金表单 (/loan/apply)
| 元素 |
事件 |
行为 |
数据来源 |
| 借款类型 TDPicker |
选择 |
备用金/差旅借款 |
Loan.LoanType |
| 借款金额 TDInput |
输入 |
纯数字 |
Loan.Amount |
| 借款事由 TDTextarea |
输入 |
|
Loan.Purpose |
| [存为草稿]/[提交审批] |
点击 |
复用标准逻辑,提交调 POST /api/oa/loan/submit |
— |
页面 4.0a:借款列表 (/loan/list)
| 元素 |
事件 |
行为 |
数据来源 |
| TDChip(全部/草稿/审批中/已通过/已拒绝/已撤回) |
点击 |
标准筛选 |
ApprovalStatus 缓存 |
| 卡片额外展示 |
— |
还款状态标签(未还/部分已还/已还清/已核销) |
Loan.RepaymentStatus |
| 已通过+未还清卡片左滑 |
手势 |
[登记还款] → 选还款方式+金额 → POST /api/oa/loan/repay |
— |
页面 4.0b:借款详情 (/loan/detail/:id)
已通过且未还清的借款底部展示还款记录列表(LoanRepayment 按时间倒序)。
页面 1:Appshell 底部导航 (/)
| 元素 |
事件 |
行为 |
数据来源 |
| 消息 Tab |
点击 |
路由 → /messages,图标变 #00ABF3 |
— |
| 消息 Tab 角标 |
数据绑定 |
未读消息数;进入时拉取,30s 轮询或 Push 刷新 |
.NET 消息模块 API |
| 工作台 Tab |
点击 |
路由 → /home |
— |
| 我的 Tab |
点击 |
路由 → /profile |
— |
| 当前激活 Tab |
重复点击 |
若含列表 → ScrollToTop |
— |
页面 2:消息通知聚合页 (/messages)
| 元素 |
事件 |
行为 |
数据来源 |
| 导航栏标题 |
— |
"消息通知" |
— |
| "全部已读" |
点击 |
仅未读>0 时显示;调用 API,红点淡出,按钮消失 |
.NET 消息模块 API |
| 消息卡片(公告类) |
点击 |
→ /announcement/detail/:id |
MsgType='announcement' |
| 消息卡片(审批待办) |
点击 |
按 BizType 路由 → 对应详情页,底部展审批操作栏 |
BizType 决定路由 |
| 消息卡片(审批结果) |
点击 |
按 BizType 路由 → 对应详情页 |
BizType 决定路由 |
| 已读消息卡片 |
— |
透明度 0.6,无红点,不可左滑 |
IsRead=1 |
| 未读卡片-左滑 |
手势 |
滑出 [标记已读](蓝) + [删除](红) |
— |
[标记已读] |
点击 |
成功→红点淡出+透明度↓;失败→Toast"操作失败,请重试" |
.NET 消息模块 API |
[删除] |
点击 |
TDDialog 确认 → 卡片收起 |
.NET 消息模块 API |
| 列表 |
下拉/上拉 |
刷新 page=1 / page++ 追加 |
.NET 消息模块 API |
| 空列表 |
— |
TDEmpty + 铃铛:"暂无消息通知" |
— |
页面 3:工作台 (/home)
角色判定(决定工作台变体):查 OaUserPermission:
- 有
oa.admin.* → 管理员版
- 无 admin 但有
oa.expense.mark_paid → 财务版
- 无上述但有
oa.*.approve 或 oa.*.view_dept → 经理版
- 其余 → 员工版
员工版
| 元素 |
事件 |
行为 |
数据来源 |
| 轮播图 |
自动/点击 |
3s 切换;有 LinkUrl→跳转,无→全屏预览(双指缩放) |
SysBanner (IsActive=1, IsDeleted=0) |
| 金刚区-事前申请 |
点击 |
→ /expense-apply/apply |
— |
| 金刚区-费用报销 |
点击 |
→ /expense/apply |
— |
| 金刚区-用车申请 |
点击 |
→ /vehicle/apply |
— |
| 金刚区-加班申请 |
点击 |
→ /overtime/apply |
— |
| 金刚区-申请记录 |
点击 |
→ /expense-apply/list(默认"全部") |
— |
| 金刚区-报销记录 |
点击 |
→ /expense/list |
— |
| 金刚区-外勤日志 |
点击 |
→ /outing-log/list |
— |
| 金刚区-公司公告 |
点击 |
→ /announcement/list |
— |
| 看板-已提单据 |
点击 |
→ /expense-apply/list |
.NET 预计算(缓存 5min) |
| 看板-本月报销 |
点击 |
→ /report/expense-detail |
.NET 预计算 |
| 看板-待处理 |
点击 |
→ /messages(筛选 approval_notice) |
.NET 预计算 |
| 页面整体 |
下拉 |
强制穿透缓存刷新 |
— |
经理版增量
| 元素 |
事件 |
行为 |
数据来源 |
| 待审批卡片 TDBadge |
— |
红色数字展示待办件数 |
GET /api/oa/approval/pending-count |
| 待审批卡片 |
点击 |
→ /messages(筛选 approval_notice) |
— |
财务版增量
| 元素 |
事件 |
行为 |
数据来源 |
| 财务看盘卡片 |
— |
已支付流水/待付款总额/异常退回数 |
.NET 全公司聚合查询 |
页面 3.1:个人中心 (/profile)
| 元素 |
事件 |
行为 |
数据来源 |
| 头像 TDAvatar |
点击 |
唤起相册/相机→裁剪(1:1,≤2MB)→上传→全局刷新 |
PUT /api/user/avatar → .NET → ERP |
| 个人信息卡片 |
— |
姓名/部门/岗位 |
GET /api/user/{erpUserId} |
| 我的审批历史 |
点击 |
→ 子页面 |
— |
| 我的报表 |
点击 |
→ 子页面(5 Tab) |
— |
| 关于 |
点击 |
→ 版本号/用户协议/隐私政策 |
— |
子页面:我的审批历史
| 元素 |
事件 |
行为 |
数据来源 |
| 单据卡片-编号 |
点击 |
按 BizType 跳转详情页 |
GET /api/oa/approval/my-history |
| 列表 |
下拉/上拉 |
刷新/加载更多 |
同上 |
| 空列表 |
— |
TDEmpty:"暂无审批记录" |
— |
页面 4:事前申请表单 (/expense-apply/apply)
| 元素 |
事件 |
行为 |
数据来源 |
| 申请人姓名 |
— |
只读 |
GET /api/user/{erpUserId} → RealName |
| 所属部门 |
— |
只读 |
GET /api/user/{erpUserId} → DeptName |
| 申请日期 |
— |
只读,DateTime.now(),YYYY-MM-DD |
— |
| 紧急程度 TDRadioGroup |
选择 |
普通/紧急/特急,默认"普通" |
ExpenseApplication.Urgency |
| 费用类型 TDCheckboxGroup |
多选 |
至少一项,联动过滤科目候选 |
ExpenseApplication.ExpenseTypes |
| 费用事由 TDTextarea |
输入 |
≤200 字 |
ExpenseApplication.Purpose |
| 关联项目 TDPicker |
级联 |
一级→ProjectService;二级→SubjectService |
ExpenseApplication.ProjectId |
| 预算科目 TDPicker |
选择 |
选定后调 BudgetService 加载可用余额 |
ExpenseApplication.BudgetSubjectId |
| 预算余额 |
— |
只读 ¥XXXX.xx,ERP 无预算→隐藏区块 |
.NET → ERP BudgetService |
| 预估金额 TDInput |
输入 |
纯数字键盘;汇总超预算→变红+警告 |
ExpenseAppDetail.EstimatedAmount |
| [+ 添加费用明细] |
点击 |
展开新卡片,页面滚动聚焦 |
— |
| 明细卡片 ✕ |
点击 |
气泡确认→折叠消失 |
— |
| 附件上传 [+] |
点击 |
相册/相机/文件;≤10MB(图)/≤20MB(PDF) |
Attachment(BizType='expense_apply') |
| 附件缩略图 |
点击 |
黑底全屏预览,双指缩放(0.5x~3x),左右滑动 |
— |
| 附件 ⊖ |
点击 |
气泡确认→删除服务器文件 |
— |
| PDF 附件 |
点击 |
原生 PDF 查看器 |
— |
| [重置] |
点击 |
仅编辑草稿时可见;确认弹窗→清空 |
— |
| [存为草稿] |
点击 |
Loading → PUT → Toast 绿→跳列表(激活"草稿"Chip) |
ExpenseApplication (Status='draft') |
| [提交审批] |
点击 |
校验→ScrollTo+红闪+TDMessage → Loading → POST → .NET → ERP 创建实例 → 存 ApprovalInstanceId → Toast → 跳列表 |
.NET POST /api/oa/expense-apply/submit |
| 返回(有未保存修改) |
点击 |
TDDialog:"是否退出?" |
— |
页面 5:费用报销表单 (/expense/apply)
| 元素 |
事件 |
行为 |
数据来源 |
| [导入事前申请] |
点击 |
半屏多选抽屉:approved 且尚有额度的申请,每条填导入金额;可追加多条;空→TDEmpty |
.NET API → ExpenseApplication (Status='approved', UsageStatus IN unused/partially_used) |
| 对公/对私 TDSwitch |
切换 |
personal→收款人账户;corporate→供应商名称+对公账户 |
Expense.PaymentType |
| 报销事由 |
输入 |
可导入回填或手工 |
Expense.Purpose |
| 成本中心 TDPicker |
选择 |
可空 |
.NET → ERP CostCenterService |
| 报销总金额 |
— |
只读,明细累加上浮动画 |
Expense.TotalAmount |
| 开户行/户名/账号 |
输入 |
对私展示;前端校验账号 16-19 位 |
Expense.BankName/AccountName/BankAccount |
| 供应商名称/账户 |
输入 |
对公展示 |
Expense.SupplierName |
| 币种 TDPicker |
选择 |
默认 CNY,选外币→自动从 ERP 填汇率 |
ExpenseDetail.CurrencyCode/ExchangeRate |
| 费用类别 TDPicker |
选择 |
叶子节点 |
SysCostCategory |
| 原币金额/本币金额 |
输入/只读 |
原币×汇率=本币(服务端算) |
ExpenseDetail.Amount/BaseAmount |
| 分摊设置 |
输入 |
比例+目标部门/项目;不分摊留空 |
ExpenseDetail.AllocationPercent/AllocationDeptId/AllocationProjectId |
| 借款冲销 |
提交时 |
.NET 查未还借款→弹窗确认冲销明细 |
GET /api/oa/loan/outstanding |
| [+ 添加费用明细] |
点击 |
平滑展开新卡片 |
— |
| 明细行 ✕ |
点击 |
气泡确认→折叠 |
— |
| 发票上传 [+] |
点击 |
相册/相机,≤10MB,满 9 隐藏;可绑定明细行 |
Attachment(BizType='expense') |
| 发票缩略图 |
点击 |
黑底全屏预览 |
— |
| [存为草稿]/[提交审批] |
点击 |
复用页面 4 逻辑 |
— |
| 返回(有修改) |
点击 |
复用页面 4 逻辑 |
— |
页面 6/7:事前申请/费用报销列表页
| 元素 |
事件 |
行为 |
数据来源 |
| 导航栏 |
— |
"申请记录" / "报销记录" |
— |
| TDChip(全部/草稿/审批中/已通过/已拒绝/已撤回) |
点击 |
高亮 #00ABF3,列表淡入淡出刷新;无数据→TDEmpty(文案随 Chip) |
ApprovalStatus 缓存 |
| 列表 |
下拉/上拉 |
刷新/追加 |
本地 OA 表 + 后台静默刷新 ApprovalStatus |
| 卡片空白区 |
点击 |
→ 详情页 |
— |
| 卡片左滑-[编辑] |
点击 |
仅 draft/rejected → 表单编辑态 |
— |
| 卡片左滑-[删除] |
点击 |
仅 draft/rejected → TDDialog 确认 → 软删除 |
— |
| StatusTag |
— |
仅展示(灰/橙/绿/红/灰) |
— |
经理版增量:
| 元素 |
事件 |
行为 |
数据来源 |
范围标签 [我的发起] |
点击 |
员工版逻辑 |
— |
范围标签 [下属审批] |
点击 |
骨架屏→加载部门下属单据 |
GET /api/oa/approval/subordinates |
| 下属卡片左滑-[一键同意] |
点击 |
仅 pending;绿色按钮,就地审批 |
POST /api/oa/approval/action |
财务版增量(仅页面 7):
| 元素 |
事件 |
行为 |
数据来源 |
[待付款] Chip |
点击 |
筛选 Status='approved' AND PaymentStatus='unpaid' |
本地 Expense 表 |
| 卡片左滑-[线下已付款] |
点击 |
标记 PaymentStatus='paid' |
本地 Expense 表 |
页面 8/9:事前申请/费用报销详情页
员工版
| 元素 |
事件 |
行为 |
数据来源 |
| 状态横幅 |
— |
图标 + 状态中文 + 当前审批人姓名 |
ApprovalStatus + 审批人姓名从 .NET API 获取 |
| 提交时间 |
— |
YYYY-MM-DD HH:mm |
CreateTime |
| 费用明细"展开全部" |
点击 |
>5 行时显示;平滑展开 |
本地子表 |
| 附件缩略图 |
点击 |
黑底全屏预览 |
本地附件表 |
| 审批时间线 |
— |
纵向时间线渲染 |
GET /api/oa/approval/timeline?bizType=&bizId= → .NET → ERP |
| 时间线节点 |
点击 |
当前用户待审批→弹出审批操作;否则→展开详情 |
— |
| 审批人姓名/头像 |
点击 |
TDDialog 展示基本信息卡片 |
GET /api/user/{approverId} |
| [编辑](draft) |
点击 |
→ 表单编辑态 |
— |
| [提交审批](draft) |
点击 |
校验→提交;失败→TDDialog 引导 |
POST /api/oa/approval/action?action=submit |
| [撤回申请](pending) |
点击 |
TDDialog 确认 → Status='withdrawn' → 旧 instanceId 追加到 PreviousInstanceIds |
POST /api/oa/approval/withdraw |
| [重新编辑并发起](rejected) |
点击 |
→ 表单编辑态;提交后更新原记录+旧审批链失效 |
— |
经理版底部操作栏(pending 状态 + 当前审批人=该经理)
| 元素 |
事件 |
行为 |
数据来源 |
| [拒绝] |
点击 |
TDDialog 输入理由(≥5 字解锁)→ POST |
POST /api/oa/approval/action {action: reject} |
| [转交] |
点击 |
MethodChannel 唤起通讯录单选 → POST |
POST /api/oa/approval/action {action: transfer} |
| [同意] |
点击 |
Loading → POST → 节点变绿 → 流转 |
POST /api/oa/approval/action {action: approve} |
| 风控参考看板 |
— |
申请人本月同类单据数 + 预算消耗进度条 |
本地查询 + .NET 转发 ERP |
管理员版底部操作栏
| 元素 |
事件 |
行为 |
| [强制终止] |
点击 |
TDDialog 确认 → Status='withdrawn' + 写入终止原因 |
| [查看审批链] |
点击 |
展开完整审批历史(含历史 instanceId 的流程) |
财务版增量(仅页面 9)
| 元素 |
事件 |
行为 |
数据来源 |
| 合规复选框 ×3 |
勾选 |
全部勾选→解锁 [确认打款] 按钮 |
Expense.IsInvoiceVerified/IsTaxIdMatched/IsCategoryCompliant |
| [退回修改] |
点击 |
选退回节点(员工/经理)→状态倒流 |
.NET API |
| [确认已线下打款并归档] |
点击 |
解锁后;滑出凭证表单(电汇流水号+记账凭证号必填)→提交→Paid |
Expense.BankTransferNo + VoucherNo |
| [下一笔待付款] |
点击 |
归档后替换显示;跳转下一条 approved+unpaid;无→"已无待付款" |
— |
页面 10:加班申请表单 (/overtime/apply)
| 元素 |
事件 |
行为 |
数据来源 |
| 加班类型 TDPicker |
选择 |
工作日/休息日/节假日,默认"工作日";节假日标注费率 |
Overtime.OtType |
| 补偿方式 TDRadio |
选择 |
转调休/结算加班费/混合,默认"转调休" |
Overtime.CompensationType |
| 混合模式 TDSlider |
拖动 |
10%~90%,步长 10%;右侧实时比例文案 |
Overtime.CompLeaveRatio |
| 开始/结束时间 |
点击 |
TDDatePicker 年月日时分;确认后自动算净工时(扣除 12:00-13:00/18:00-18:30) |
Overtime.StartTime/EndTime → NetOtHours |
| 净工时卡片 |
— |
只读大字;≤0→变红+提交按钮置灰 |
Overtime.NetOtHours |
| 开始>结束校验 |
— |
边框变红+提示+提交按钮置灰 |
— |
| 加班原因 TDTextarea |
输入 |
Overtime.Reason |
— |
| [存为草稿]/[提交审批] |
点击 |
复用页面 4 逻辑;提交时 .NET 创建 ERP 审批实例 |
— |
| 返回(有修改) |
点击 |
复用页面 4 逻辑 |
— |
页面 11:加班列表 / 页面 12:加班详情
交互模式同页面 6/7 和 8/9。加班类型标签(工作日/休息日/节假日)、补偿方式展示(加班费/转调休/"X%调休+Y%结算")。员工端仅 [撤回]。
页面 13:用车申请表单 (/vehicle/apply)
| 元素 |
事件 |
行为 |
数据来源 |
| 车牌号 TDPicker |
选择 |
全公司车池;冲突车辆标红"冲突";无可用→TDEmpty |
SysVehicle (IsDeleted=0) |
| 车牌+出车/还车时间 |
失焦 |
异步排期冲突检测;冲突→红色文本+锁死提交按钮 |
Vehicle (Status IN pending,approved) |
| 用车事由 |
输入 |
|
Vehicle.Reason |
| 始发地 |
定位/输入 |
原生定位匹配 |
Vehicle.Origin + 经纬度 |
| 目的地+地图图标 |
输入/点击 |
MethodChannel 唤醒地图选点→回填地址+经纬度 |
Vehicle.Destination + 经纬度 |
| 出车/还车时间 |
点击 |
TDDatePicker 年月日时分 |
Vehicle.StartTime/EndTime |
| 还车>出车校验 |
— |
否则红色提示+按钮置灰 |
— |
| 同行总人数 |
输入 |
纯数字,0→1 |
Vehicle.PassengerCount |
| 随行同行人 [+] |
点击 |
MethodChannel 唤醒通讯录多选→胶囊标签;取消→无变化 |
VehiclePassenger |
| 胶囊标签 x |
点击 |
剔除人员 |
— |
| [存为草稿]/[提交审批] |
点击 |
复用页面 4 逻辑 |
— |
页面 14:用车列表 / 页面 15:用车详情
| 元素 |
事件 |
行为 |
数据来源 |
| TDChip(全部/草稿/审批中/已通过/已拒绝/已撤回/已还车) |
点击 |
标准逻辑 |
ApprovalStatus 缓存 |
| 卡片左滑-[编辑]/[删除] |
点击 |
仅 draft/rejected |
— |
| 卡片左滑-[确认还车] |
点击 |
仅 approved;拉起半屏核销抽屉 |
— |
| 微缩地图 |
点击 |
唤醒原生地图导航 |
本地经纬度 |
| [确认还车并登记] |
点击 |
滑出核销抽屉:实还时间/出车前里程/还车后里程/费用备注 |
Vehicle 还车字段 |
| 还车里程校验 |
— |
还车后 ≥ 出车前,否则按钮置灰+红提示 |
— |
| 确认提交还车 |
点击 |
TDDialog 确认→Status='returned'→页面刷新 |
— |
页面 16:外勤日志创建 (/outing-log/create)
| 元素 |
事件 |
行为 |
数据来源 |
| GPS 定位区 |
页面初始化 |
强制高精度 GPS → ReadOnly 展示地址;精度>100m→黄色警告 |
MethodChannel → OutingLog.CheckIn* |
| GPS 定位失败 |
— |
TDEmpty"无法获取当前位置";提交按钮置灰 |
— |
| 客户名称 TDInput |
输入 |
自动联想;无匹配→自由文本(提交时 .NET 自动创建 ERP 客户) |
GET /api/customer/search?q= |
| 工作总结 TDTextarea |
输入 |
|
OutingLog.VisitSummary |
| 后续计划 |
输入 |
|
OutingLog.NextPlan |
| 现场拍照 |
点击 |
跳过相册,直接相机;水印=服务器授时+GPS;最少1张最多4张 |
Attachment(BizType='outing_log') |
| 相机权限被拒 |
— |
TDDialog + "前往设置" |
— |
| [存为草稿]/[提交] |
点击 |
校验 GPS+照片≥1 |
— |
| 返回(有修改) |
点击 |
复用页面 4 逻辑 |
— |
页面 17:外勤日志列表 / 页面 18:外勤日志详情
| 元素 |
事件 |
行为 |
数据来源 |
| TDChip(全部/本月) |
点击 |
切换刷新 |
— |
| 卡片"新点评" TDBadge |
— |
橙色标记 |
OutingLogComment.CreateTime > COALESCE(LastViewedTime, '1900-01-01') 且评论者≠本人 |
| 卡片左滑-[编辑]/[删除] |
点击 |
仅 draft |
— |
| 微缩地图 |
点击 |
唤醒原生地图导航 |
本地经纬度 |
| 照片墙 |
点击 |
全屏预览 |
Attachment(BizType='outing_log') |
| 页面进入 |
— |
无感 PUT 更新 LastViewedTime,列表"新点评"消失 |
PUT /api/oa/outing-log/:id/view |
| 主管点评区(员工端) |
— |
只读浏览,不可回复 |
OutingLogComment |
经理版增量:
| 元素 |
事件 |
行为 |
数据来源 |
| TDRate 星级 |
点选 |
1-5 星 |
OutingLogComment.RatingStars |
| 点评文本框 |
输入 |
批示文字 |
OutingLogComment.CommentText |
| 发送按钮 |
点击 |
气泡追加+动画 |
POST /api/oa/outing-log/:id/comment |
页面 19:公告列表 (/announcement/list)
| 元素 |
事件 |
行为 |
数据来源 |
| TDChip 类型筛选 |
点击 |
全部/通知公告/人事与制度/放假与活动/我的草稿(仅管理员) |
Announcement.Type + Status |
| 排序规则 |
— |
置顶→未过期(PublishTime DESC)→已过期(PublishTime DESC) |
IsTop, PublishTime, ExpiryDate |
| 已过期卡片 |
— |
整体置灰+标题末尾"已过期" |
ExpiryDate < NOW |
| 未读红点 |
— |
返回列表后红点消失 |
AnnouncementReadLog.IsRead=0 |
| 公告卡片 |
点击 |
→ /announcement/detail/:id |
— |
| 搜索 |
— |
不支持 |
— |
页面 20:公告详情 (/announcement/detail/:id)
| 元素 |
事件 |
行为 |
数据来源 |
| 正文 |
— |
HTML/Markdown 渲染 |
Announcement.Content |
| 已过期横幅 |
— |
红色"该公告已于 YYYY-MM-DD HH:mm 过期" |
Announcement.ExpiryDate |
| 附件图标 |
点击 |
原生浏览器/下载管理器;失败→Toast |
Attachment(BizType='announcement') |
| 停留 ≥2s |
— |
无感标记已读;<2s 返回不发送 |
POST /api/oa/announcement/:id/read |
管理员版增量:
| 元素 |
事件 |
行为 |
数据来源 |
| [已读 N 人] Chip |
点击 |
展开已读员工列表(头像+部门) |
AnnouncementReadLog (IsRead=1) + .NET 用户 API |
| [未读 N 人] Chip |
点击 |
展开未读员工列表 |
AnnouncementReadLog (IsRead=0) |
| [一键 DING] |
点击 |
震动反馈→MethodChannel 强推 Push/短信 |
AnnouncementReadLog.IsUrged=1 |
页面 21~25:五大明细报表 (/report/..)
| 元素 |
事件 |
行为 |
| TDDropdownMenu(本月/本季/本年) |
选择 |
数值卡片+图表 300ms 渐变刷新 |
| 数值卡片 |
— |
只读;空→0/¥0.00 |
| fl_chart 数据点 |
长按 |
tooltip(日期+数值) |
| fl_chart 图表 |
水平滑动 |
查看历史数据 |
| 图表为空 |
— |
TDEmpty:"所选时间范围内暂无数据" |
经理版增量:柱状图柱体点击 → 下方列表联动过滤该员工本月单据 → 可穿透跳详情页。
财务版增量(仅页面 22):部门树/项目组级联筛选 + [数据流水一键导出] → 后端生成 Excel → MethodChannel 原生分享。
页面 26:公告发布 (/announcement/create)
| 元素 |
事件 |
行为 |
数据来源 |
| 公告标题 TDInput |
输入 |
|
Announcement.Title |
| 分类下拉 |
选择 |
notice/policy/activity |
Announcement.Type |
| 富文本编辑域 |
操作 |
加粗/斜体/下划线/列表/图片/链接/字号 |
Announcement.Content |
| 附件上传 [+] |
点击 |
≤5 个,PDF/图片/Word/Excel,≤20MB |
Attachment(BizType='announcement') |
| 置顶 TDSwitch |
切换 |
默认关闭 |
Announcement.IsTop |
| 有效期 TDDatePicker |
选择 |
非必填,不填永不过期 |
Announcement.ExpiryDate |
| 接收范围选择器 |
点击 |
右侧滑出部门树多选(Checkbox);默认全员;底部统计人数 |
AnnouncementTarget |
| [预览] |
点击 |
TDDialog 全屏模拟详情页 |
— |
| [存为草稿] |
点击 |
保存(仅创建者+管理员可见) |
Announcement.Status='draft' |
| [发布] |
点击 |
确认弹窗 → 写入 + 异步初始化 AnnouncementReadLog → Toast |
Announcement.Status='published' |
| 返回(有修改) |
点击 |
复用页面 4 逻辑 |
— |
页面 27:权限管理 (/admin/permissions)
| 元素 |
事件 |
行为 |
数据来源 |
| TDSearchBar |
输入 |
300ms 防抖模糊搜索→骨架屏→刷新;无结果→TDEmpty |
GET /api/user/search?q= |
| 员工列表 |
下拉/上拉 |
刷新/每页 20 条 |
默认按部门+姓名排序 |
| 员工卡片 |
点击 |
右侧滑出 TDDrawer 权限编辑抽屉 |
— |
| 快捷套餐按钮 ×4 |
点击 |
一键赋予标准权限集(员工/审批人/财务/管理员) |
OaUserPermission |
| 权限点复选框 |
勾选 |
逐项加减 |
OaUserPermission |
| 启用/禁用 TDSwitch |
切换 |
经理有待审批时警告确认 |
OA 权限状态 |
| [确认保存] |
点击 |
Loading → PUT → Toast + 写审计日志 |
OaUserPermission + OaPermissionChangeLog |
| 【变更记录】折叠区 |
展开 |
TDTimeline 展示最近 20 条 |
OaPermissionChangeLog |
| 抽屉关闭(有未保存修改) |
— |
确认弹窗"是否放弃?" |
— |
| 自保护 |
— |
无法取消自己的 admin 权限 → Toast |
— |
| 最后管理员保护 |
— |
后端拒绝移除最后一个 admin |
— |
4. API 端点汇总
OA 业务 API(.NET 服务端新增)
# 权限
GET /api/oa/permissions
GET /api/oa/user-permissions?userId=
PUT /api/oa/user-permissions
GET /api/oa/permission-changelog?userId=&page=
# 业务单据
POST /api/oa/expense-apply/submit
POST /api/oa/expense/submit
POST /api/oa/overtime/submit
POST /api/oa/vehicle/submit
PUT /api/oa/{bizType}/draft
GET /api/oa/{bizType}/list?status=&page=
GET /api/oa/{bizType}/detail/:id
DELETE /api/oa/{bizType}/:id
# 外勤日志
PUT /api/oa/outing-log/submit
PUT /api/oa/outing-log/:id/view
POST /api/oa/outing-log/:id/comment
# 审批代理
GET /api/oa/approval/pending?userId=&bizType=&page=
GET /api/oa/approval/pending-count?userId=
GET /api/oa/approval/timeline?bizType=&bizId=
POST /api/oa/approval/action
POST /api/oa/approval/withdraw
GET /api/oa/approval/my-history?userId=&page=
GET /api/oa/approval/subordinates?approverId=&bizType=&page=
# 公告
POST /api/oa/announcement/publish
POST /api/oa/announcement/:id/read
POST /api/oa/announcement/:id/ding
GET /api/oa/announcement/read-stats/:id
# 报表
GET /api/oa/report/{type}?range=&userId=
POST /api/oa/report/expense/export
# 车辆/轮播图
GET /api/oa/vehicles
POST /api/oa/vehicles
PUT /api/oa/vehicles/:id
GET /api/oa/banners
复用已有 API
GET /api/user/{id}
GET /api/user/search?q=&page=
GET /api/dept/tree
PUT /api/user/avatar
GET /api/customer/search?q=
GET /api/messages?page=
GET /api/messages/unread-count
PUT /api/messages/:id/read
POST /api/messages/read-all
DELETE /api/messages/:id
GET /api/dict/banks
GET /api/dict/cost-categories
5. 全局交互规范
5.1 空状态
| 场景 |
文案 |
| 消息列表为空 |
"暂无消息通知" |
| 申请列表为空 |
"暂无记录,点击下方按钮发起申请" |
| 审批列表为空 |
"暂无待审批单据" |
| 搜索无结果 |
"未找到匹配的员工,试试其他关键词" |
| 公告列表为空 |
"暂无行政公告" |
5.2 加载态
| 场景 |
方式 |
| 页面首次加载 |
TDSkeleton 骨架屏(最长 8s 超时转网络异常态) |
| 下拉刷新 |
RefreshIndicator |
| 上拉加载更多 |
底部菊花 + "加载中..." |
| 按钮提交中 |
按钮替换为 TDLoading 小菊花 + 禁用态 |
| 附件上传中 |
缩略图叠加半透明蒙层 + 中心菊花 |
5.3 网络异常态
| 场景 |
方式 |
| 列表加载失败 |
TDEmpty + 断网图标 + [点击重试] |
| 提交失败 |
TDToast(红)"提交失败,请稍后重试" |
| 接口超时(>15s) |
TDEmpty + [点击重试] |
| 401 未授权 |
静默触发宿主登录流程 |
5.4 全局交互
- 横竖屏:仅支持竖屏
- 键盘:聚焦时页面上移;金额类弹数字键盘;文本类弹默认键盘
- 返回手势:iOS 边缘右滑;Android 系统返回键;表单页有未保存修改时拦截确认
- Deep Link:
tboss://oa/{path} → GoRouter 路由
- 并发冲突:编辑/审批冲突 → TDDialog 提示 → 刷新
- 字体缩放:sp 单位,最小 11sp,导航栏标题不溢出
5.5 设计 Token
| Token |
值 |
用途 |
--primary |
#00ABF3 |
主按钮/激活态/高亮/链接 |
--success |
#00A870 |
已通过/同意/金额正面 |
--warning |
#E37318 |
审批中/超支警告/冲突提示 |
--danger |
#D54941 |
已拒绝/拒绝/删除 |
--text-primary |
#1A1A1A |
标题/关键金额 |
--text-secondary |
#666666 |
摘要/辅助说明 |
--bg-page |
#F5F5F5 |
页面背景 |
--bg-card |
#FFFFFF |
卡片/表单底色 |
--border |
#E7E7E7 |
分割线/描边 |
文档版本:v1.0 | 日期:2026-06-03