## 一、 普通员工 (Employee) 角色视图：全功能交互与字段穷举

普通员工拥有 26 个基础页面的完整发起、查看个人流水、修改草稿、执行撤回等完整闭环功能。

### 1. 框架与导航页（4 页）

#### 页面 1：Appshell 底部导航外壳页 (`/`)



* **页面组件内容**：
* 底部固定挂载 `TDTabBar`（包含 3 个 Tab：消息、工作台、我的）。


* 主内容区：由 `GoRouter` 驱动的 `IndexedStack` 视图容器。


* **微观组件交互**：
* 点击“消息”Tab：触发路由切换到 `/messages`，对应图标变为激活态颜色 `#00ABF3`。


* 点击“工作台”Tab：触发路由切换到 `/home`，对应图标变为激活态。


* 点击”我的”Tab：触发路由切换到 `/profile`，对应图标变为激活态。

* **重复点击当前 Tab**：对应页面的列表（若存在）自动滚动至顶部（ScrollToTop），非列表页面无额外行为。
* **消息 Tab 未读角标**：”消息”Tab 右上角挂载未读消息数角标 `TDBadge`，数据由 `GET /api/messages/unread-count` 提供，进入 Appshell 时首次拉取，之后每 30 秒轮询刷新或由宿主 Push 事件触发刷新。





#### 页面 2：消息通知聚合页 (`/messages`)



* **页面组件内容**：
* 顶部 `TDNavbar` 标题居中展示：”消息通知”。右侧挂载”全部已读”文字按钮（仅当存在未读消息时显示），点击调用 `/api/messages/read-all`，成功后所有 `TDBadge` 红点同时动画淡出，按钮消失。
* 中部 `TDListView` 滚动区域。
* 单条消息卡片卡片组件字段包括：消息小图标（审批待办/审批结果/系统公告）、消息大标题、发送人、发送时间（格式化为：MM-DD HH:mm）、摘要文本（如：”您的单据已通过”）、未读红点组件 `TDBadge`。


* **微观组件交互**：
* **向左横向滑动单条卡片**：使用 `flutter_slidable` 插件组件，卡片右侧向左平滑滑出 `[标记已读]`（蓝色按钮）与 `[删除]`（红色按钮）。已读消息不可左滑（无操作按钮）。

* 点击 `[标记已读]` 按钮：触发 Dio 异步请求 `/api/messages/:id/read`，成功后本地状态更新，`TDBadge` 红点动画淡出消失，卡片整体透明度降至 0.6 表示已读状态。若请求失败，`TDToast`（红色）提示”操作失败，请重试”，红点不消失。

* 点击 `[删除]` 按钮：弹出 `TDDialog` 二次确认弹窗（标题”确认删除”，正文”删除后该消息将从列表中移除，不可恢复。”，确定按钮红色”确认删除”）。点击”确定”则触发删除接口，卡片伴随向上收起动画从列表中移除。

* **下拉刷新**：列表顶部出现圆圈旋转 `RefreshIndicator`，重置分页 `page=1` 重新拉取最新消息。

* **上拉加载更多**：滑动列表到底部触发 `page++`，列表底部显示小菊花 + “加载中...”，静默追加历史消息卡片。

* **页间跳转交互**：
* 点击”公告通知”卡片：路由跳转至 `/announcement/detail/:id`。

* 点击”审批待办”卡片：根据消息体内的 `bizType` 路由跳转至对应的单据详情页（如 `/expense/detail/:id`），详情页底部直接展示审批操作栏。

* 点击”审批结果”卡片：根据消息体内的 `bizType`，路由精准跳转至对应的单据详情页（如 `/expense/detail/:id`）。







#### 页面 3：工作台（员工标准版）页 (`/home`)



* **页面组件内容**：
* 顶部 `TDNavbar` 标题：“TBOSS 工作台”。
* 第一层：`TDRotation` 轮播图组件，展示企业近期行政头条照片。
* 第二层：功能金刚区（`TDGrid` 宫格组件，4列布局），分两行：
  * **第一行（发起）**：事前申请 → `/expense-apply/apply`、费用报销 → `/expense/apply`、用车申请 → `/vehicle/apply`、加班申请 → `/overtime/apply`。
  * **第二行（记录）**：申请记录 → `/expense-apply/list`、报销记录 → `/expense/list`、外勤日志 → `/outing-log/list`、公司公告 → `/announcement/list`。
* 第三层：【我的快捷看板】卡片组件。字段包括：本月累计报销支出（大字文本）、本月已提单据总数、待处理单据总数。


* **微观组件交互**：
* **轮播图自动播放**：`TDRotation` 每 3 秒自动切换一张，循环播放。**点击轮播图片**：若该图片绑定了公告/活动链接则跳转对应详情页，无链接则全屏预览图片（支持双指缩放）。

* 点击快捷看板中的”本月已提单据总数”卡片：跳转至申请记录列表 `/expense-apply/list`（默认”全部”状态，涵盖事前申请、费用报销、加班、用车四类单据的发起统计入口）。

* 点击快捷看板中的”本月累计报销”卡片：卡片触发水波纹效果，直接跳转到个人的费用明细报表页（`/report/expense-detail`）。快捷看板数据由后端预计算汇总（缓存 5 分钟），**下拉刷新可强制穿透缓存**重新拉取。

* 点击快捷看板中的”待处理单据总数”卡片：跳转至消息列表页 `/messages`，并默认仅展示”审批待办”类的消息。

* **工作台整体下拉刷新**：支持下拉手势，刷新后重新拉取轮播图数据、快捷看板数据（强制穿透缓存）。

* **页间跳转交互**：
* 点击宫格内的各个图标：通过 `context.push()` 精准跳转至对应页面（第一行跳表单页，第二行跳列表页）。列表页默认展示”全部”状态，不预设筛选。





---

#### 页面 3.1：我的个人中心页 (`/profile`)

* **页面组件内容**：
  * 顶部个人信息卡片：用户头像（`TDAvatar`，点击可更换）、真实姓名、所属部门、岗位名称。
  * 中部功能列表（`TDListView` 分组样式）：
    * 【我的审批历史】— 查看本人发起的所有单据审批流水。
    * 【我的报表】— 进入五大明细报表聚合页（事前申请/费用报销/加班/用车/外勤日志），顶部 Tab 切换。
    * 【关于 TBOSS OA】— 版本号、用户协议、隐私政策。

* **微观组件交互**：
  * 点击头像：通过通道唤起原生相册选择或相机拍摄 → 裁剪(1:1, ≤2MB, JPG/PNG) → `PUT /api/user/avatar` 上传至服务器 → 成功则 `SysUser.AvatarUrl` 更新 + `TDToast`"头像已更新" + 全局刷新头像；失败则 `TDToast`（红色）"头像上传失败，请重试"。

* **登录态管理说明**：登录/登出/Token 刷新由宿主 App 统一管理，OA 模块不持有独立退出入口。模块需实现两个监听：
  * 收到宿主登出事件时，清除本地缓存（草稿、已读状态、全局 Provider 状态等）。
  * API 返回 401 时，通过 `MethodChannel` 通知宿主触发重新登录流程。

* **页间跳转交互**：
  * 点击列表项分别跳转至对应子页面（审批历史、我的报表、关于）。

#### 子页面：我的审批历史

* **页面组件内容**：
  * 顶部 `TDNavbar` 标题："我的审批历史"。
  * 中部 `TDListView` 按时间倒序展示该用户发起的所有单据的审批流水，按单据分组。每组卡片包含：单据编号（可点击跳详情页）、单据类型标签（事前申请/报销/加班/用车）、审批时间线摘要（如"王经理已通过 → 李会计审批中"）。支持下拉刷新（`page=1`）和上拉加载（`page++`）。无数据时展示 `TDEmpty`："暂无审批记录"。
  * **点击单据编号**：按单据类型跳转对应详情页（事前申请→`/expense-apply/detail/:id`、费用报销→`/expense/detail/:id`、加班→`/overtime/detail/:id`、用车→`/vehicle/detail/:id`）。
  * 数据来源：以当前用户 `ApplicantId` 反查 4 张业务主表，取其 `Id` 作为 `BizId`，JOIN `ApprovalRecord` 按 `ApprovalLevel` 排序渲染时间线摘要。

#### 子页面：我的报表聚合页

* **页面组件内容**：
  * 顶部 `TDNavbar` 标题："我的报表"。
  * 顶部 `TDTabBar` 五个 Tab：事前申请 / 费用报销 / 加班明细 / 用车明细 / 外勤日志，切换后下方内容区对应渲染页面 21~25 的个人报表视图。
  * 此举解决了报表页无统一入口的问题，员工和经理均可通过 `/profile` → "我的报表" 进入。


### 2. 费用控制与报销模块（6 页）

#### 页面 4：事前申请表单页 (`/expense-apply/apply`)



* **页面组件内容**：
* `FormSection`（基本信息分组）：申请人姓名（只读，取自当前登录用户 `SysUser.RealName`）、所属部门（只读，取自 `SysUser.DeptId → SysDepartment.DeptName`）、申请日期（只读，默认取当前日期 `DateTime.now()`，格式 `YYYY-MM-DD`）、紧急程度选择（`TDRadioGroup`：普通/紧急/特急，默认选中"普通"）、费用类型选择（`TDPicker` 下拉：差旅费/招待费/日常采购/活动经费，选择后联动过滤"预算科目"候选列表）、费用事由（`TDTextarea` 大文本输入框，限制 200 字）。
* `FormSection`（关联管控）：关联项目（`TDPicker` 级联选择：第一级选择项目 `SysProject.Name`，选定后第二级加载该项目下的预算科目 `SysBudgetSubject.Name`）、预算科目（`TDPicker` 级联选择）。
* `FormSection`（动态预估明细列表）：包含【+ 添加费用明细】文字按钮。每项明细卡片内含：费用类型、预估金额（`TDInput` 纯数字键盘）、明细说明。
* `FormSection`（支撑材料上传区）：`TDImageGrid` 宫格组件，支持最大 6 张图片或 PDF 文件（单张图片 ≤ 10MB，PDF ≤ 20MB，超限时 `TDToast` 提示"文件大小超过限制"）。员工可上传报价单、合同、出差审批截图等业务合理性证明材料（存入 `ExpenseApplicationAttachment`）。上传交互：点击【+】唤起原生相册/相机/文件选择器，上传成功后缩略图实时渲染，右上角带删除减号。附件满 6 张后【+】按钮自动隐藏，需先删除已有附件方可添加。点击缩略图可全屏预览：黑色背景，支持双指缩放（0.5x~3x）、左右滑动切换图片、右上角关闭按钮。PDF 文件点击后通过原生 PDF 查看器打开。点击缩略图右上角删除减号 → 弹出气泡确认"删除该附件？"→ 确定后缩略图缩小消失 + 异步删除服务器文件。
* 底部悬浮固定操作栏：编辑已有草稿时展示 `[重置]`（灰色文字按钮）+ `[存为草稿]`（浅蓝背景按钮）+ `[提交审批]`（主色 `#00ABF3` 按钮）。新建空白表单时仅展示 `[存为草稿]` + `[提交审批]`，不展示重置按钮。


* **微观组件交互**：
* **点击 `[重置]` 按钮**（仅编辑已有草稿时可见）：弹出确认弹窗”将清空当前已填写内容，是否继续？” → “确认重置” / “取消”。确认后表单恢复至初始状态（仅保留申请人姓名、部门、日期等只读字段，其余清空）。
* **点击”预算科目”选择器**：底部弹出 BottomSheet 列表。项目与科目均选定后，输入框下方异步加载出该【项目-科目】下的【当前可用预算余额：¥XXXX.xx】（数据来源：`SysProjectBudget.AvailableAmount`，由后端联合 ProjectId + SubjectId 查得）。
* **输入”预估金额”组件**：每输入一项明细的预估金额后，前端汇总所有明细行的预估金额合计，页面底部实时显示汇总金额。当合计金额大于当前可用的预算余额时，汇总区域变红并在下方实时弹出警告文本：”您的申请金额已超支，提交后将自动触发高管特批流程”。
* **点击【+ 添加费用明细】按钮**：明细区域平滑向下展开一个全新的明细子卡片，同时主页面自动向上平滑滚动以聚焦新表单。子卡片右上角带”✕”删除图标。
* **点击明细子卡片右上角”✕”图标**：弹出气泡确认”确认删除”，点击后该组明细卡片伴随向左折叠动画消失。
* **点击返回按钮（表单有未保存修改时）**：若表单已有任何修改（与初始空值或已保存草稿比对），弹出 `TDDialog`：”当前内容尚未保存，是否退出？” → “继续编辑” / “放弃并退出”（红色）。放弃后不保存直接返回。


* **页间流转交互**：
* 点击 `[存为草稿]` 按钮：按钮进入 Loading 态，Dio 异步调用 `PUT /expense-apply/draft`，成功后弹出 `TDToast`（绿色）”已保存为草稿，可在列表'草稿'分类中继续编辑”，使用 `context.replace('/expense-apply/list')` 跳转至列表页并默认激活”草稿”Chip。**若保存失败**：`TDToast`（红色）”保存失败，请稍后重试”，停留在当前页面，按钮恢复可点击态。

* 点击 `[提交审批]` 按钮：触发全局校验。若有未填项，页面平滑 `ScrollTo` 到该组件（300ms 缓动），组件边框变红并闪烁 2 秒，同时组件下方显示红色错误提示文字（如”请填写费用事由”），页面顶部弹出 `TDMessage`（红色横幅）”请完善必填信息后重新提交”。若校验全过，按钮进入 Loading 态，Dio 发送 `POST /expense-apply/submit`，成功后 `TDToast`”已提交，等待审批”，使用 `context.replace('/expense-apply/list')` 切换至列表页。**若提交失败**：`TDToast`（红色）”提交失败，请稍后重试”，停留在当前页面。





#### 页面 5：费用报销表单页 (`/expense/apply`)



* **页面组件内容**：
* 顶部特殊组件：【一键导入已通过的事前申请】蓝色高亮快捷链。
* 基本信息区：报销事由、关联项目、预算科目、关联成本中心（`TDPicker` 下拉，选项来自 `SysCostCenter`，可空）、报销总金额（只读变动组件）。
* **收款银行账户专区**：开户行全称（`TDInput` + 下拉联想，银行列表由后端 `/api/banks` 提供）、开户人户名（默认取当前用户姓名，可修改）、银行账号输入框（前端校验 16-19 位数字格式）。首次提交成功后，后端自动记录该账户为该用户的默认收款账户，下次进入表单时自动回填。
* **动态报销明细区**：发生日期选择（点击 → 底部 `TDDatePicker` 年月日选择器，无时分）、费用类别（`TDPicker` 下拉，选项来自 `SysCostCategory` 字典表：交通费/住宿费/餐饮费/办公用品等）、实际发票金额（`TDInput` 纯数字键盘）。
* **发票与附件上传区**：`TDImageGrid` 宫格组件，支持最大 9 张图片（单张 ≤ 10MB）。




* **微观组件交互**：
* **点击【一键导入已通过的事前申请】**：页面滑出半屏侧边抽屉，罗列该员工处于 `approved` 状态且**未被其他报销单导入过**的事前申请（后端排除 `SourceApplicationId` 已被引用的记录）。若无可用的事前申请，抽屉内展示 `TDEmpty`：”暂无可导入的事前申请”。若当前表单已有填写内容，点击某条记录时弹出确认弹窗：”导入事前申请将覆盖当前已填写的表单内容，是否继续？” → “继续导入” / “取消”。确认后，该条记录的事由、项目、科目、预估金额自动全量复制回填到当前报销单中。

* **发票明细金额联动**：员工在明细区每输入一项实际发票金额，页面底部的”报销总金额”组件便会实时进行数字上浮动画自动累加。

* **报销明细行增删**：点击【+ 添加费用明细】按钮平滑展开新明细卡片（复用页面 4 的交互模式）；点击明细行右上角”✕”图标 → 气泡确认”确认删除”→ 卡片折叠消失。

* **银行账号格式校验**：输入过程中前端即时校验，格式错误时输入框下方红色提示”请输入正确的银行账号”（16-19 位数字），提交按钮不置灰但提交时后端再次校验。

* **点击 `TDImageGrid` 中的【+】上传图标**：通过通道唤起宿主原生相册选择或直接相机拍摄。图片成功返回后异步上传服务器（单张 ≤ 10MB）。卡片格子实时渲染出缩略图，且缩略图右上角自带删除减号。

* **点击已上传的缩略图**：拉起纯黑底色全屏全景预览组件，支持手指双指放大、缩小、手势左右划动切图。

* **点击返回按钮（表单有未保存修改时）**：复用页面 4 的返回确认逻辑。


* **页间流转交互**：
* 底部操作栏：`[存为草稿]`（浅蓝背景按钮）+ `[提交审批]`（主色 `#00ABF3` 按钮）。存为草稿与提交审批逻辑复用页面 4（含 Loading 态、失败 Toast、返回确认）。
* 点击底部的 `[提交审批]`：执行逻辑与事前申请完全相同（含 ScrollTo 校验动画 + `TDMessage`），提交成功后直接切入 `/expense/list`。





#### 页面 6：事前申请列表页 (`/expense-apply/list`) / 页面 7：费用报销列表页 (`/expense/list`)



* **页面组件内容**：
* 顶部固定展示 `TDNavbar` 标题。
* 紧贴下方挂载滚动筛选条 `TDChip` 分组（全部 / 草稿 / 审批中 / 已通过 / 已拒绝 / 已撤回）。


* 中部为高阶定制化 `TDListView` 流水卡片列表。
* 单张卡片内含核心字段：单据唯一编号（页面 6 事前申请如 BXSQ-20260530-001，页面 7 费用报销如 BX-20260530-001）、总事由摘要、总金额（粗体高亮展现）、提单日期（YYYY-MM-DD）、当前状态标签组件 `StatusTag`（配合五种状态颜色：灰-草稿、橙-审批中、绿-已通过、红-已拒绝、灰-已撤回）。




* **微观组件交互**：
* **首次加载**：默认选中"全部"Chip，加载第一页数据。
* **点击顶部的任一 `TDChip` 状态标签**：被点击标签变为高亮 `#00ABF3`，列表触发重新请求后端，并伴随淡入淡出动画刷新显示该状态下的数据。若该状态下无数据，列表区展示 `TDEmpty`，文案随状态动态替换：暂无草稿/暂无审批中的单据/暂无已通过的单据/暂无已拒绝的单据。
* **执行下拉手势**：触发列表顶部出现圆圈旋转 Loading，重置分页 `page=1` 并重新拉取最新列表。
* **滑动列表到底部**：触发上拉加载更多，分页自增 `page++`，列表底部显示小菊花 + "加载中..."，静默追加新单据卡片。
* **对 `draft` 或 `rejected` 状态的卡片执行左滑**：卡片右侧滑出 `[编辑]`（蓝色）和 `[删除]`（红色）操作。对非 `draft`/`rejected` 状态的卡片左滑无反应（无操作按钮）。
* **点击 `[删除]` 按钮**：弹出 `TDDialog` 二次确认："确认删除该单据？删除后不可恢复。"→"确认删除"（红色）。确定后卡片向上收起移除 + 调用删除接口。
* **`StatusTag` 标签**：仅作视觉展示，不可点击。

* **页间跳转交互**：
* 点击左滑滑出的 `[编辑]`：路由调起对应表单编辑态（事前申请 → `/expense-apply/apply?id=xxx`，报销单 → `/expense/apply?id=xxx`）。

* 点击普通的卡片任意空白区域：携带该单据 ID 路由跳转至对应详情页（事前申请 → `/expense-apply/detail/:id`，费用报销 → `/expense/detail/:id`）。





#### 页面 8：事前申请详情页 (`/expense-apply/detail/:id`) / 页面 9：费用报销详情页 (`/expense/detail/:id`)



* **页面组件内容**：
* 顶部第一层：状态大色块横幅。内容包括大状态图标（⏳审批中、✅已通过、❌已拒绝、↩ 已撤回）、当前状态中文、当前节点审批人姓名。已撤回状态横幅底色为灰色（`--status-gray`）。
* 顶部基本信息区顶端以 `--fs-caption` 字号展示"提交时间：YYYY-MM-DD HH:mm"（取自 `CreateTime`）。
* 第二层：单据基础信息及费用明细表。以 `key-value` 形式双栏平铺。若明细超过 5 行，默认折叠仅显示前 5 行 + "展开全部"文字按钮，点击后平滑展开其余行。
* 第三层（页面 8：事前申请）：支撑材料附件区（`ExpenseApplicationAttachment`），展示员工上传的报价单、合同、出差审批截图等业务合理性证明（非发票）。附件以 `TDImageGrid` 宫格展示，点击缩略图 → 黑底全屏预览，双指缩放（0.5x~3x），左右滑动切图。
* 第三层（页面 9：费用报销）：发票及附件大图宫格（`ExpenseAttachment`），展示实际发票、付款凭证等。附件以 `TDImageGrid` 宫格展示，点击缩略图 → 黑底全屏预览，交互同上。
* 第四层：`TDTimeline` 审批流进程组件。纵向时间线首节点"发起人已提交"由业务主表的 `CreateTime` 与申请人信息拼装渲染，后续节点（审批中/已通过/已拒绝/已转交）来自 `ApprovalRecord` 表按 `ApprovalLevel` 升序排列。若单据为 `withdrawn` 状态，时间线末尾追加灰色虚线节点："申请人于 YYYY-MM-DD HH:mm 撤回了该申请"。
* 底部悬浮固定操作栏。




* **微观组件交互**：
* 若单据为 `draft`（草稿）：底部悬浮操作栏展现 `[编辑]`（进入表单编辑态）和 `[提交审批]` 两个按钮。点击 `[提交审批]`：直接校验后提交（不弹确认，因详情页已展示完整信息），逻辑复用页面 4。**若校验失败**（如缺少必填项），弹出 `TDDialog`："该单据信息不完整，是否进入编辑页补充？" → "去编辑"（跳转表单编辑态，回填已有数据） / "取消"（留在详情页）。
* 若单据为 `pending`（审批中）：底部悬浮操作栏仅展现一个 `[撤回申请]` 按钮。点击后，屏幕中央弹出 `TDDialog` 二次确认框（标题”确认撤回”，正文”撤回后该单据将变为已撤回状态，如需重新提交请重新编辑。”，确定按钮蓝色”确认撤回”，取消按钮”取消”）。确认后单据撤回，页面自动刷新重载，顶部大色块横幅变为灰色的”已撤回”状态，时间线末尾追加撤回节点。
* 若单据为 `rejected`（已拒绝）：底部操作栏展现一个 `[重新编辑并发起]` 按钮。
* 若单据为 `approved`（已通过）或 `withdrawn`（已撤回）：底部操作栏隐藏，仅展示单据详情。
* **审批时间线节点点击**：点击某节点，若该节点审批人为当前用户且状态为待审批，底部弹出审批操作选项（即触发经理版审批操作栏）；否则仅展开该节点的详细信息（审批人、审批时间、审批意见等），不弹出操作入口。
* **审批人姓名/头像点击**：点击状态横幅或时间线节点上的审批人姓名/头像（`ApprovalRecord.ApproverId → SysUser.RealName`），弹出 `TDDialog` 展示该审批人的基本信息卡片（姓名、部门、岗位），无更多操作入口。
* **附件宫格预览**：与表单页完全一致，黑底全屏预览，双指缩放，左右滑动，右上角关闭。


* **页间跳转交互**：
* 点击 `[编辑]`（draft 状态）：路由跳转并带入所有已填字段回填到表单页（事前申请 → `/expense-apply/apply?id=xxx`，报销单 → `/expense/apply?id=xxx`）。
* 点击 `[重新编辑并发起]`（rejected 状态）：路由跳转并带入所有历史字段回填到表单页，路径同上按单据类型区分。编辑提交后**更新原记录**（`Status` 从 `rejected` 重置为 `pending`，`CurrentApproverId` 重新写入首级审批人），同时**清空旧审批链**（`ApprovalRecord` 中该 `BizId` 的所有记录标记为已失效）。



---

### 3. 勤务考勤、用车调度与外勤外务模块（9 页）

#### 页面 10：加班申请表单页 (`/overtime/apply`)



* **页面组件内容**：
* 加班类型选择（`TDPicker`：工作日加班 / 休息日加班 / 法定节假日加班，默认"工作日加班"）、补偿核销方式（`TDRadio`：转调休工资 / 结算加班费 / 混合模式（部分转调休+部分结算加班费），默认"转调休工资"）。法定节假日加班时，各补偿方式旁显示参考费率标注（如"结算加班费（按 300% 计算）"）。
* 加班开始时间选择器、加班结束时间选择器、加班原因说明（大文本框）。
* 联动高亮组件：本次申请加班净工时（大字卡片）。


* **微观组件交互**：
* **点击开始/结束时间输入框**：从页面底部缓缓滑出滚轮式的 `TDDatePicker` 年月日时分选择器。
* **开始时间 > 结束时间校验**：若开始时间晚于结束时间，输入框边框变红 + 下方红色提示”开始时间不能晚于结束时间”，底部提交按钮置灰。
* **净工时为 0 处理**：若计算出的净工时 ≤ 0，中间大字卡片显示”0 小时”并变红，提交按钮置灰，下方红色警告”加班净工时不能为 0，请检查出勤时间”。
* **选择”混合模式”时**：补偿核销方式下方平滑展开一个比例滑块组件（`TDSlider`，范围 10%~90%，步长 10%，两端排除 0 和 100 以保证”混合”语义），滑块值表示”转为调休的比例”，剩余部分自动结算加班费。滑块右侧实时显示文案：如”30% 转调休 + 70% 结算加班费”。
* **时间联动自动计算**：一旦用户点击确认选定结束时间，前端立刻通过异步流调用规则（自动匹配扣除中午 12:00-13:00 和傍晚 18:00-18:30 的就餐休息考勤盲区，扣除时段不可前端修改），动态在中间的高亮大字卡片上输出计算好的实际净小时数（例如：”4.5 小时”）。
* **返回未保存确认**：复用页面 4 的返回确认逻辑。


* **页间跳转交互**：提交或存草稿逻辑复用页面 4 的存草稿/提交审批模式，成功后跳转回加班列表页。



#### 页面 11：加班申请列表页 (`/overtime/list`) / 页面 12：加班申请详情页 (`/overtime/detail/:id`)



* **页面组件内容**：
  * 顶部 `TDNavbar` 标题："加班记录"。
  * 筛选条 `TDChip` 分组：全部 / 草稿 / 审批中 / 已通过 / 已拒绝 / 已撤回。
  * 中部 `TDListView` 流水卡片。单张卡片内含：单据编号（如 JB-20260530-001）、加班类型标签（`TDCapsuleTag`：工作日/休息日/法定节假日）、加班日期（`StartTime` 格式化为 MM-DD）、净工时（`NetOtHours` 粗体高亮）、补偿方式（`overtime_pay`→结算加班费 / `comp_leave`→转调休 / `mixed`→"{CompLeaveRatio*100}%调休+{(1-CompLeaveRatio)*100}%结算"）、当前 StatusTag。
  * 支持下拉刷新（重置 `page=1`）和上拉加载（`page++`）。

* **微观组件交互**：
  * **首次加载**：默认选中"全部"Chip，加载第一页数据。
  * 点击 `TDChip` 状态标签：高亮切换，列表按所选状态重新请求，淡入淡出刷新。无数据时展示对应 `TDEmpty`。
  * 对 `draft` 或 `rejected` 状态的卡片执行左滑：滑出 `[编辑]` 和 `[删除]`。点击 `[删除]` 弹出 `TDDialog` 二次确认。非 `draft`/`rejected` 卡片左滑无反应。
  * 点击卡片空白区域：路由跳转至详情页 `/overtime/detail/:id`。
  * 详情页遵循通用时间线渲染规范（含撤回节点），员工端仅能执行 `[撤回]` 动作。时间线节点点击展开详细信息（审批人、时间、意见）。



#### 页面 13：用车申请表单页 (`/vehicle/apply`)



* **页面组件内容**：
* 基本信息：车牌号选择器（`TDPicker` 调取全公司车池，冲突车辆标记为红色"冲突"标签）、用车事由输入框。
* 行程参数：预计始发地（带原生定位匹配）、预计目的地（`TDInput` 文本输入 + 右侧地图图标按钮，点击后通过 MethodChannel 唤醒宿主地图组件选点回填地址和经纬度）、预计出车时间（`TDDatePicker` 年月日时分）、预计还车时间（`TDDatePicker` 年月日时分）。
* 人员安排：同行总人数（纯数字，输入 0 自动调整为 1）、随行同行人添加组件（标签网格区域）。


* **微观组件交互**：
* **点击”随行同行人”旁边的【+】图标按钮**：Flutter 模块通过通道异步唤醒宿主原生应用的通讯录多选控制器。员工勾选同事完毕后点击原生完成，数据回传给 Flutter，以漂亮的 `TDCapsuleTag` 胶囊标签动态追加展示在表单中。点击胶囊标签上的”x”可剔除该人员。若用户在原生通讯录中取消选择，Flutter 端无变化，不报错。

* **核心资产排期冲突校验**：当用户选好”车牌号”和”预计出车/还车时间”这两个核心字段后，输入框右侧会出现菊花转动进行异步调用。如果该车辆在所选时段已被其他部门预订，车牌选择组件下方立刻爆出红色文本：”注意：当前时段该车存在排期冲突！”，下方追加灰色建议文字：”请尝试选择其他可用车辆或调整用车时段”，并瞬间锁死底部的提交按钮。

* **还车时间校验**：还车时间必须晚于出车时间，否则红色提示 + 提交按钮置灰。

* **无可用车辆**：车牌号选择器下拉展示 `TDEmpty`：”暂无可用车辆，请联系行政管理员”。

* **返回未保存确认**：复用页面 4 的返回确认逻辑。


* **页间跳转交互**：底部操作栏 `[存为草稿]` + `[提交审批]`，存草稿/提交审批/校验逻辑均复用页面 4 的模式。提交成功后，路由替换至用车列表页 `/vehicle/list`。



#### 页面 14：用车申请列表页 (`/vehicle/list`)



* **页面组件内容**：
  * 顶部 `TDNavbar` 标题："用车记录"。
  * 筛选条 `TDChip` 分组：全部 / 草稿 / 审批中 / 已通过 / 已拒绝 / 已撤回 / 已还车。
  * 中部 `TDListView` 流水卡片。单张卡片内含：单据编号（如 YC-20260530-001）、预约车牌号（粗体高亮）、用车目的标签（客户接待/商务出行/公务）、行程摘要（始发地 → 目的地）、出车时间（MM-DD HH:mm）、当前 StatusTag。
  * 支持下拉刷新（重置 `page=1`）和上拉加载（`page++`）。

* **微观组件交互**：
  * **首次加载**：默认选中"全部"Chip。
  * 点击 `TDChip` 状态标签：高亮切换，列表按所选状态重新请求。无数据时展示对应 `TDEmpty`。
  * 对 `draft` 或 `rejected` 状态的卡片执行左滑：滑出 `[编辑]` 和 `[删除]`。点击 `[删除]` 弹出 `TDDialog` 二次确认。
  * 对 `approved` 状态的卡片执行左滑：滑出 `[确认还车]` 快捷入口，直接拉起还车登记半屏抽屉（复用页面 15 的还车核销表单）。
  * 对 `已还车` 状态的卡片执行左滑：无操作按钮（已归档不可操作）。
  * 点击卡片空白区域：路由跳转至详情页 `/vehicle/detail/:id`。



#### 页面 15：用车申请详情页 (`/vehicle/detail/:id`)



* **页面组件内容**：单据行程明细、时间线节点。若有 GPS 经纬度数据，展示始发地/目的地微缩静态地图标记图。

* **独有流程终点闭环交互**：
* 如果用车单据状态属于 `approved`，详情页的底部操作栏会一直展示一个长条形的 **`[确认还车并登记]`** 主色按钮（不限制当前时间必须晚于 EndTime）。若当前时间早于 `EndTime`，按钮旁附带灰字提示文案”提前还车”，点击后核销表单中额外展示灰色只读字段”原计划还车时间：YYYY-MM-DD HH:mm”（仅作对比）。若当前时间晚于 `EndTime`，按钮旁附带红色提示文案”已超出原计划还车时间”。

* **点击 `[确认还车并登记]` 按钮**：页面底部优雅滑出半屏核销抽屉。要求用户补填：实际归还时间选择（`TDDatePicker` 年月日时分）、出车前里程表读数（`TDInput` 纯数字键盘）、还车后里程表读数（`TDInput` 纯数字键盘，**前端即时校验：必须 ≥ 出车前里程**，否则提交按钮置灰 + 红色提示”还车里程不能小于出车里程”）、期间产生费用备注（路桥费/停车费）。

* 核销表单填完后点击”确认提交还车”：弹出 `TDDialog` 二次确认（”提交后单据将归档为已还车状态，里程和费用数据不可再修改。”），确认后单据状态变更为”已还车归档”。页面重新加载：底部操作栏变为灰色只读栏展示”已还车归档于 YYYY-MM-DD HH:mm”，正文区追加核销信息区块（实际里程、实际费用），还车登记操作按钮消失。



#### 页面 16：业务员外出日志创建页 (`/outing-log/create`)



* **页面组件内容**：
* 外勤定位区：当前 GPS 精准定位经纬度及逆地理编码出的具体街道写实地址文本（组件左侧带有一枚绿色的 TDesign 盾牌安全防护图标）。定位精度 > 100 米时，定位文本下方显示黄色警告"当前定位精度较低（{精度}米），请移至开阔区域"。
* 业务汇报区：关联拜访客户名称（`TDInput`，输入时自动联想匹配客户池库，无匹配项时支持手动输入自由文本，后端自动创建客户档案）、今日工作核心总结（`TDTextarea`）、后续推进计划大文本框。
* 现场防伪核验区：【现场强制拍照】组件网格，最少 1 张、最多 4 张。


* **微观组件交互（全方位反作弊交互）**：
* **页面初始化自动请求定位**：用户切入本页面时，Flutter 模块强制通过 `MethodChannel` 向底层宿主申请最高精度 GPS 原生模块。**解析出来的当前地理位置文本组件完全封装为 `IsReadOnly=true` 状态，彻底屏蔽并杜绝了员工手动修改微调地址的可能性。**

* **定位失败/被拒降级处理**：GPS 定位失败或被用户拒绝时，定位区展示 `TDEmpty` 占位：”无法获取当前位置，请检查定位权限”。此时”现场拍照”仍可正常使用（照片水印仅含服务器授时，不含定位信息），但**外勤日志不可在无定位时提交**，底部提交按钮置灰 + 红色提示”请先获取当前位置”。

* **点击”现场强制拍照”区域**：直接跳过系统相册挑选功能，通过通道强制直接唤醒原生相机硬件。拍摄画面成功后，原生端或 Flutter 插件在图片右下角实时渲染出包含”当前不可篡改的服务器授时时间+精准 GPS 地址经纬度”的半透明防伪数字水印，确保外勤日志真实性。

* **相机权限被拒**：弹出 `TDDialog`：”无法访问相机，请在系统设置中开启相机权限”→ 提供”前往设置”按钮（通过 MethodChannel 跳转系统设置）+ “取消”按钮。

* **拍照数量限制**：少于 1 张时提交按钮置灰 + 红色提示”请至少拍摄一张现场照片”；满 4 张后拍照入口隐藏。

* **存草稿支持**：底部操作栏提供 `[存为草稿]` 和 `[提交]` 两个按钮，存草稿逻辑复用页面 4。草稿可在外出日志列表中找到并继续编辑。

* **返回未保存确认**：复用页面 4 的返回确认逻辑。


* **页间跳转交互**：提交完毕后，路由跳转至外出日志列表页。



#### 页面 17：外出日志列表页 (`/outing-log/list`) / 页面 18：外出日志详情页 (`/outing-log/detail/:id`)



* **页面组件内容（列表页）**：
  * 顶部 `TDNavbar` 标题："外勤记录"。
  * 筛选条 `TDChip` 分组：全部 / 本月。
  * 中部 `TDListView` 流水卡片，按 `CreateTime` 倒序层叠展开。单张卡片内含：拜访编号（如 VST-20260530-001）、客户名称（粗体高亮）、拜访日期（YYYY-MM-DD）、签到地址摘要（`CheckInAddress` 截断一行）、工作摘要（`VisitSummary` 截取前 50 字）、StatusTag（`completed`已完成→`--success` 绿色、`draft`草稿→`--status-gray` 灰色）。**若有主管新点评**（`OutingLogComment.CreateTime > COALESCE(OutingLog.LastViewedTime, '1900-01-01')` 且 `CommenterId != SalespersonId`），卡片右侧挂载橙色 `TDBadge`"新点评"标记。
  * 支持下拉刷新（重置 `page=1`）和上拉加载（`page++`）。

* **微观组件交互（列表页）**：
  * 对 `draft` 状态的卡片执行左滑：滑出 `[编辑]` 和 `[删除]`。点击 `[删除]` 弹出 `TDDialog` 二次确认。`completed` 状态不可编辑不可删除（左滑无反应）。
  * 点击卡片空白区域：路由跳转至详情页 `/outing-log/detail/:id`。

* **页面组件内容（详情页）**：
  * 百度/高德微缩静态地图定位标记图（由 `CheckInLongitude`/`CheckInLatitude` 渲染）。**点击微缩地图**：通过 MethodChannel 唤醒宿主原生地图 App（高德/百度/苹果地图），以该经纬度为中心打开外部导航。
  * 带防伪水印的现场照片墙（`OutingLogAttachment` 宫格预览，支持点击放大全屏，交互同其他附件预览）。
  * 工作总结汇报正文区（`VisitSummary` + `NextPlan`）。
  * 页面最底部【日志互动点评历史流水区】（`OutingLogComment` 按时间升序渲染气泡样式）。

* **页内微观交互**：员工进入详情页时，前端无感发送 `PUT /api/outing-log/:id/view`，后端更新 `OutingLog.LastViewedTime = NOW()`，列表页"新点评"红点随之消失。员工可点击照片墙任意单张放大全屏查阅，浏览主管星级打分和指导文字。**员工不可回复主管点评**（点评为单向：主管→员工），`completed` 状态日志完全只读。

---

### 4. 行政通知公告与个人效能报表模块（7 页）

#### 页面 19：公司公告列表页 (`/announcement/list`) / 页面 20：公司公告详情页 (`/announcement/detail/:id`)



* **页面组件内容（列表页）**：
  * 顶部 `TDNavbar` 标题："公司公告"。
  * 紧贴导航栏下方 `TDChip` 类型筛选条：全部 / 通知公告 / 人事与制度 / 放假与活动 / 我的草稿。**"我的草稿"Chip 仅管理员可见**（筛选 `Status='draft' AND PublisherId=当前用户`），用于管理员找回已存草稿的公告。
  * 中部 `TDListView` 按 `PublishTime` 倒序排列。**排序规则**：置顶公告（`IsTop=1`）> 未过期公告（按发布时间倒序）> 已过期公告（按发布时间倒序）。
  * 单张卡片内含：公告标题（粗体）、类型标签（`notice`通知公告 / `policy`人事与制度 / `activity`放假与活动）、发布部门（`PublisherId → SysUser.DeptId → SysDepartment.DeptName`）、发布时间（MM-DD HH:mm）、未读红点 `TDBadge`（由 `AnnouncementReadLog.IsRead=0` 驱动）。已过期的公告卡片（`ExpiryDate < NOW`）整体置灰并在标题末尾标注"已过期"。
  * 支持下拉刷新和上拉加载。**不支持搜索**。

* **微观组件交互（列表页）**：
  * 点击卡片进入详情页 `/announcement/detail/:id`。
  * 从详情页返回后，已读公告的 `TDBadge` 红点自动消失。

* **页面组件内容（详情页）**：
  * 严谨的企业公文级通知样式：大标题、发布部门（二级关联得出）、红头文件分界线、公告正文 HTML/Markdown 渲染。若公告已过期，顶部展示红色横幅"该公告已于 YYYY-MM-DD HH:mm 过期"。
  * 附件下载专区（`AnnouncementAttachment` 列表）：若附件列表为空，整个"附件下载专区"区块隐藏。点击附件图标通过原生浏览器/下载管理器打开，若文件类型不支持或下载失败，`TDToast`："文件下载失败，请联系管理员"。

* **微观组件交互（详情页）**：
  * 停留超过 2 秒，前端无感发送 `/api/announcements/:id/read`，标记 `AnnouncementReadLog.IsRead=1`。若用户在 2 秒内返回，不发送已读请求（红点保留）。
  * 从详情页返回后，已读公告的 `TDBadge` 红点自动消失。



#### 页面 21~25：个人五大明细报表页 (`/report/..`)



* 每个报表页独立渲染，共享以下统一框架，内容按单据类型差异化：

**统一框架**：
* 顶部挂载全局维度筛选按钮 `TDDropdownMenu`（按本月 / 本季 / 本年快速过滤），**不支持自定义日期范围**。
* 中部平铺【我发起的统计核心数值卡片】（内容因报表而异，见下）。数值卡片**仅作数据展示，不可点击**。数据为空时显示 0/¥0.00。
* 下方大面积渲染基于 `fl_chart` 的趋势图表（内容因报表而异，见下）。`fl_chart` 图表交互：长按数据点显示 tooltip（日期 + 数值）、水平滑动查看历史数据。**员工版不支持点击穿透下钻**（仅经理版支持）。
* 切换筛选维度时，数值卡片和折线图均伴随波动渐变动画（300ms 过渡）就地刷新。
* 报表数据为空时，图表区展示 `TDEmpty`："所选时间范围内暂无数据"。
* **员工版不支持数据导出**（仅财务角色在页面 22 有导出按钮）。

**各报表内容明细**：

##### 页面 21：事前申请明细报表 (`/report/expense-apply-detail`)

| 项目 | 内容 |
|------|------|
| 数值卡片 | 本年累计申请总金额（¥）、本月申请笔数、已通过笔数、已通过金额 |
| 趋势图 | 近 12 个月「申请金额 vs 已通过金额」双折线对比图 |
| 数据来源 | `ExpenseApplication` 主表 + SUM(`ExpenseAppDetail.EstimatedAmount`) |

##### 页面 22：费用报销明细报表 (`/report/expense-detail`)

| 项目 | 内容 |
|------|------|
| 数值卡片 | 本年累计已核销总金额（¥，`PaymentStatus='paid'` 的 SUM(`TotalAmount`)）、本月报销笔数、待审批笔数（`Status='pending'`）、已通过待付款笔数（`Status='approved' AND PaymentStatus='unpaid'`） |
| 趋势图 | 近 12 个月「报销金额 vs 审批通过金额」双折线对比图 |
| 数据来源 | `Expense` 主表 + SUM(`ExpenseDetail.TotalAmount`) |

##### 页面 23：加班明细报表 (`/report/overtime-detail`)

| 项目 | 内容 |
|------|------|
| 数值卡片 | 本月累计加班净工时（小时）、本月加班次数、累计调休小时数、加班费结算次数 |
| 趋势图 | 近 12 个月「加班工时」柱状趋势图（按月汇总 `NetOtHours`，按加班类型分色堆叠） |
| 数据来源 | `Overtime` 主表汇总 `NetOtHours` |

##### 页面 24：用车明细报表 (`/report/vehicle-detail`)

| 项目 | 内容 |
|------|------|
| 数值卡片 | 本月用车次数、本月累计里程（km）、本月路桥停车费合计（¥）、未还车数量 |
| 趋势图 | 近 12 个月「用车次数 vs 累计费用」双轴折线图（左轴次数/右轴金额） |
| 数据来源 | `Vehicle` 主表汇总 `EndOdometer - StartOdometer` 和 `ActualCost` |

##### 页面 25：外勤日志明细报表 (`/report/outing-log-detail`)

| 项目 | 内容 |
|------|------|
| 数值卡片 | 本月拜访次数、本月拜访客户数（去重）、平均客户评分（⭐）、未点评日志数 |
| 趋势图 | 近 12 个月「拜访次数 vs 平均评分」双轴折线图（左轴次数/右轴星数） |
| 数据来源 | `OutingLog` 主表 LEFT JOIN `OutingLogComment` 计算平均 `RatingStars` |

---

## 二、 审批人/经理 (Approver) 角色视图：下属审阅与决策动态操作栏

审批人具备与员工完全对称的 26 个页面路由体系，但通过权限角色判定，工作台、五大业务列表、四大业务详情及报表页将**自动发生深度 UI 组件变体与指令闭环**。

### 0. 复合角色优先级规则

当用户在 `SysUserRole` 表中同时拥有多个角色时，系统按以下规则决定 UI 变体：

1. **工作台聚合优先级**：`Admin > Finance > Approver > Employee`。高优先级角色的专属功能区块（如管理员公告发布入口、财务全公司看盘、经理待办卡片）逐层叠加，低优先级角色的基础功能块保持不变。
2. **列表页/详情页**：按当前路由的业务上下文切换。列表页顶部同时展示 `[我的发起]` / `[下属审批]` / `[全公司单据]` 等该用户所有角色对应的范围标签。
3. **全局状态管理**：角色列表在 App 启动时一次性从 `GET /api/user/roles` 拉取并写入全局 Provider，不依赖路由参数传递。

### 1. 工作台与列表页的管理层权限变体

#### 页面 3（经理版变体）：工作台首页看板 (`/home`)



* **UI 升级组件内容**：
* 在原员工版的基础上，金刚区宫格图标上方，强势强行插入一个专属的 **【待我处理的审批】高规格动态卡片区**。
* 该卡片区右侧挂载红色大字数字通知 `TDBadge`（如“8 件待办”）。卡片内以轮播跑马灯形式滚动展示最新提交上来的下属申请简要。


* **微观交互逻辑**：点击【待我处理的审批】大卡片区块：系统产生水波纹扩散效果，跳转至消息列表页 `/messages` 并默认筛选 `MsgType='approval_notice'` 类待办消息（按单据类型分组展示），用户再点击具体消息进入对应单据详情页进行审批。

#### 页面 6、7、11、14、17（经理版变体）：五大业务单据列表页



* **UI 升级组件内容**：
  * 顶部导航栏下方多出一层特异化的**数据访问范围切换标签**（`[我的发起]` / `[下属审批]`）。
  * 经理切换到 `[下属审批]` 后，各列表页的卡片追加以下信息：
    * 页面 6/7（事前申请/费用报销）：卡片左上角追加【申请人姓名 - 部门】。
    * 页面 11（加班）：卡片左上角追加【申请人姓名】，加班类型标签后追加补偿方式。
    * 页面 14（用车）：卡片左上角追加【申请人姓名】，车牌号后追加用车目的标签。
    * 页面 17（外勤日志）：卡片左上角追加【业务员姓名】，签到地址后追加拜访客户名称。

* **微观交互逻辑**：
  * 经理点击 `[我的发起]`：展现的内容、左滑删除等逻辑与普通员工角色完全等同。
  * **经理点击切换到 `[下属审批]` 标签**：列表卡片立刻清空，并伴随骨架屏动画刷新展示整个部门或管辖范围内所有下属员工提交的单据流水。
  * 经理对处于 `Pending` 状态的下属卡片向左划动：滑出快捷菜单 `[一键同意]`（绿色按钮）。点击一键同意，无需进入详情页即可就地对该单据进行秒审批准予通过。



---

### 2. 审批详情页的“三元控制动态操作栏”与页间闭环

#### 页面 8、9、12、15（经理版变体）：四大业务单据详情页



* **UI 升级组件内容**：
* 键值对信息区上方额外塞入一个专属于管理者的【风控参考辅助看板】：直观展示该申请人本月已提交过几次此类单据、当前项目预算已消耗的百分比进度条（`TDProgress`）。
* **底部固定悬浮操作栏完全替换为：`[拒绝]`（白色卡片红字按钮）、`[转交]`（灰色背景按钮）、`[同意]`（主色 `#00ABF3` 大按钮）**。





```
┌────────────────────────────────────────────────────────┐
│ [拒绝] (必填理由)   │ [转交] (调原生通讯录) │   [同意] (下流转)  │
└────────────────────────────────────────────────────────┘

```

* **微观组件级微观交互（终极决策闭环）**：
1. **点击 `[拒绝]` 按钮**：页面中央立刻弹出强制性 `TDDialog` 对话框，提示：“请输入拒绝该单据的合理理由（必填）”。**输入框设置监听，当字数小于 5 个字时，弹窗的“确定”按钮持续保持置灰锁死态。** 只有填满理由点击确定，单据才会流转为 `rejected`，当前页面就地销毁弹回列表。
2. **点击 `[转交]` 按钮**：Flutter 模块通过 `MethodChannel` 异步召唤宿主原生应用的组织架构单选通讯录。经理勾选转交目标后，Flutter 调取 `/api/{bizType}/approve`（如 `/api/expense/approve`，参数：action=transfer, target=接收人Id），当前单据审批权移交，详情页销毁弹回。
3. **点击 `[同意]` 按钮**：页面出现短暂的菊花 Loading，调取 `/api/{bizType}/approve`（参数：action=approve）。成功后，时间线上对应节点变为✅绿色已通过，单据流转到下一审批节点。



#### 页面 18（经理版变体）：外出日志详情页



* **UI 升级组件内容**：页面底部常驻显示一个类似社交应用朋友圈风格的【主管批示及打分专属互动专区】（内含 5 星评级组件 `TDRate` 与文字输入框）。
* **微观交互逻辑**：主管在 `TDRate` 上点选 4 颗星，并在文本框输入点评。点击发送按钮，该内容伴随平滑过渡动画，以精致的气泡样式隔离追加挂载在日志正文正下方。点评数据写入 `OutingLogComment` 表后，该日志的 `UpdateTime` 自动更新为该点评的 `CreateTime`。

#### 页面 21~25（经理版变体）：五大管理维度报表页



* **UI 升级组件内容**：数据范围由个人数据自动跃升为“本部门团队聚合能耗分析”。图表自动渲染呈现出**部门内部员工之间的横向阶梯对比柱状图**（展现谁本月出差最多、谁用车最频繁、谁加班时长封顶）。


* **高级数据穿透闭环交互**：经理查阅柱状图时，**点击柱状图上代表“业务员张三”的那根彩色柱体：图表下方的信息列表会自动进行联动过滤，瞬间只呈现张三本月发起的全部单据流水。** 经理进一步点击流水中的任意单据，可直接穿透跳转路由切入到该单据的具体详情页进行细致核查，看完点击返回顺畅回退至报表层。

---

## 三、 财务人员 (Finance) 角色视图：财务专用查验核销区与流水导出

财务人员专注于单据的**资产真实性查验、线下付款确认以及财务凭证编号归档**。

### 1. 财务核销专属详情页与表单归档闭环

#### 页面 3（财务版变体）：工作台首页看板 (`/home`)



* **UI 升级组件内容**：顶部核心资产卡片替换展示为**全公司级大财务看盘**（本月公司已支付总现金流水、当前全公司处于待付款状态的单据总额、本周异常退回发票数）。

#### 页面 7（财务版变体）：费用报销列表页 (`/expense/list`)



* **UI 升级组件内容**：财务角色具备全公司单据的检索洞察权。状态筛选条中独家多出一个 **`[待付款]` 专属筛选 Chip 标签**。
* **微观交互逻辑**：点击 `[待付款]`，卡片卡流全面刷新为已经过了所有经理层级审批、正等待财务打款核销的单据。对卡片执行左滑，露出快捷标记 `[线下已付款]` 选项。

> **财务对其他单据列表的访问**：财务在事前申请列表（`/expense-apply/list`）、加班列表（`/overtime/list`）、用车列表（`/vehicle/list`）中为**全公司只读访问**（不限制 `ApplicantId`，但无专属操作按钮），用于关联核查报销单对应的原始申请和审批记录。

#### 页面 9（财务版变体）：费用报销详情页（核心财务核销专区）



* **UI 升级组件内容**：
* 在原有的单据明细与发票大图宫格正下方，硬性切入渲染出一个极其严谨的 **【发票真伪及税务合规核销自查区】**。内含多个 `TDCheckbox` 勾选框：“发票已经全国增值税发票系统查验真实”、“发票抬头与公司税号完全一致”、“报销类目与发票项目符合合规管控”。


* **底部固定悬浮操作栏变更为：`[退回修改]`（红色字按钮）与 `[确认已线下打款并归档]`（主色 `#00ABF3` 大按钮）**。





```
┌────────────────────────────────────────────────────────┐
│             发票合规查验区 (全部勾选方可解锁打款)       │
│ [ ] 发票已验真    [ ] 税号一致    [ ] 经营类目合规     │
├────────────────────────────────────────────────────────┤
│ [退回修改]              │       [确认已线下打款并归档] (解锁) │
└────────────────────────────────────────────────────────┘

```

* **页内组件级微观交互（严密财务控制闭环）**：
1. **复选框与按钮状态强制联动锁死**：页面加载时，底部的 `[确认已线下打款并归档]` 按钮默认处于绝对的灰暗锁死状态。**财务人员必须逐一点击点击勾选完上述所有的合规自查复选框，底部的绿色付款大按钮才会被正式激活解锁。** 只要有一项漏勾，付款按钮持续锁定，从根源上阻断误操作。
2. **点击 `[退回修改]` 按钮**：拉起弹窗，要求选择退回节点（退回给员工重填 / 退回给经理重审），提交后单据状态倒流：

| 退回到 | 数据库操作 |
|--------|-----------|
| 员工重填 | `Status = 'draft'`, `CurrentApproverId = NULL`，员工可在列表草稿中找到并重新编辑 |
| 经理重审 | `Status = 'pending'`, `CurrentApproverId` 回退为原经理 ID，经理端待审批列表重新出现该单据 |
3. **点击已激活的 `[确认已线下打款并归档]` 按钮**：页面底部优雅滑出带有金蝶系统风格的【财务凭证编号归档专属表单】。**在其中强行限制必须录入银行电子打款的【公司转账电汇流水号】以及本地账目的【财务记账凭证号】**。


4. 凭证表单校验通过后点击最终确定：单据在全局状态机中流转变为最终完结态 `Paid`（已付款），页面就地刷新渲染出醒目的红色”已解付/已归档”财务既定盖章图标，核销流程完美闭环。归档完成后，底部操作栏自动替换为 `[下一笔待付款]` 快捷按钮，点击后跳转到下一笔 `Status='approved' AND PaymentStatus='unpaid'` 的报销单详情页，实现财务连续核销，无需退回列表。**若无下一笔待付款单据**，底部显示灰色只读文字”已无待付款单据”并持续 2 秒后自动 `TDToast`”全部待付款单据已处理完毕”。





#### 页面 22（财务版变体）：公司费用明细全量报表页 (`/report/expense-detail`)



* **UI 升级组件内容**：
* 顶部除了时间范围外，全面开通【全公司任意部门树】、【任意关联项目组】的多维组合级联级筛选。
* 右上角长期常驻挂载一个醒目的 **`[数据流水一键导出]`（绿色带 Excel 专属图标的圆形悬浮按钮）**。


* **微观组件与系统级交互闭环**：
* 财务调配好筛选参数（如：2026年第二季度 - 销售部 - 招待费占比饼图），点击右上角的 `[数据流水一键导出]` 按钮。
* **系统底层链条响应交互**：该按钮立刻进入圆圈转动 Loading 态，Flutter 异步向 .NET 4.8 后端接口发起大批量数据清洗请求，后端在内存中生成标准 Excel 报表二进制文件并回传。Flutter 收到数据后通过 `MethodChannel` 强行唤醒 iOS 的 `UIActivityViewController` 或 Android 的原生文件分享管理面板，允许财务人员流畅地将该全量明细 Excel 账单流文件一键转发到原生的企业微信、钉钉或系统自带的隔空投送（AirDrop），完美切入财务日常办公应用生态。



---

## 四、 系统管理员 (Admin) 角色视图：公告触达率审计与专属权限配置

系统管理员除了对所有单据具有最高行政干预终止权外，独家独立拥有 **2 个全新管理专属页**（页面 26：系统公告发布页、页面 27：用户权限设置页），并对公告详情页享有完整的全员触达率审计权。

### 0. 管理员专属操作栏

#### 页面 8、9、12、15（管理员版变体）：四大业务单据详情页

管理员在这些详情页的底部操作栏为**专属双按钮**：`[强制终止]`（红底白字，直接将该单据置为 `withdrawn` 并写入强制终止原因）与 `[查看审批链]`（展开当前单据的完整 ApprovalRecord 历史，支持直接跳转到任意审批节点查看详情）。管理员不参与正常审批流，不展示经理版的同意/拒绝/转交按钮。

### 1. 公告审计与专属管理发布页

#### 页面 20（管理员版变体）：系统公告详情页（触达率审计与强催办）



* **UI 升级组件内容**：
* 在标准的行政公告正文最下方，专为管理员追加渲染一个名为 **【全员行政触达率审计追踪卡片】** 的胶囊式数据统计区。
* 卡片内部使用 `TDChip` 展示核心指标：`[已读 45 人]`（绿色）、`[未读 5 人]`（灰色）。未读人头像列表右侧，挂载一个标志性的 **`[一键强力 DING 一下 / 催办]` 高亮功能按钮**。




* **微观组件与系统原生级交互闭环（催办触达闭环）**：
1. 管理员点击 `[已读 45 人]` 胶囊标签：下方平滑展开已查阅该公告的员工名单列表（带头像与所属部门），数据来源 `AnnouncementReadLog WHERE IsRead=1` JOIN `SysUser`。
2. 管理员点击 `[未读 5 人]` 胶囊标签：下方平滑展开这 5 位至今尚未查阅该行政公告的员工名单列表（带头像与所属部门），数据来源 `AnnouncementReadLog WHERE IsRead=0` JOIN `SysUser`。
3. **点击 `[一键强力 DING 一下 / 催办]` 按钮**：系统触发震动反馈，Flutter 模块迅速封装这 5 位未读员工的 `UserIds` 集合，通过 `MethodChannel` 强行激活底层宿主原生应用的**系统级高级 Push 推送网络层或原生短信网关通道**。
4. **最终闭环效果**：这 5 位员工的手机锁屏终端会在几秒钟内无视 APP 是否被后台杀死，强行弹出一条由原生宿主推送出来的**高优先级系统通知弹窗**：”【行政紧急催办】系统有一条关于全员必读的行政公告需要您立即查阅，请点击进入”。



#### 页面 26（全新独有）：系统公告发布表单页 (`/announcement/create`)



* **页面组件内容**：
* 输入组件：公告标题输入框（`TDInput`）、公告分类下拉选择（`notice`通知公告 / `policy`人事与制度 / `activity`放假与活动安排）。
* 正文编辑区：富文本编辑域，工具栏包含：加粗、斜体、下划线、有序列表、无序列表、插入图片（从相册或相机）、插入链接、字号选择（H1/H2/H3/正文）。
* 附件上传区：`TDImageGrid` 宫格组件，支持最大 5 个文件（PDF/图片/Word/Excel，单文件 ≤ 20MB）。
* 配置区：【置顶公告】`TDSwitch`（默认关闭，开启后 `IsTop=1`）、【公告有效期至】`TDDatePicker`（非必填，不填则永不过期）。
* 接收控制区：【发布接收范围选择器】（默认为全员，点击可进行树形勾选）。


* **微观交互逻辑**：
* **点击接收范围**：页面右侧平滑滑出全公司组织架构部门树（多选 Checkbox 树形结构，子节点全选时父节点自动勾选）。底部实时统计”已选 {N} 个部门，覆盖 {M} 名员工”。
* **点击右上角【预览】按钮**：以 `TDDialog` 全屏弹窗模拟公告详情页最终展示效果（含标题、部门、正文渲染、附件列表）。
* **底部操作栏**：`[存为草稿]`（浅蓝背景按钮）、`[预览]`（灰色按钮）、`[发布]`（主色 `#00ABF3` 按钮）。
* **点击 `[存为草稿]`**：存为草稿后跳转公告列表，草稿仅创建者和管理员可见。
* **点击 `[发布]`**：弹出确认弹窗：”确认向 {M} 名员工发布该公告？发布后不可撤回。”→”确认发布” / “取消”。确认后数据入库，对应范围内员工的消息 Tab 即时挂载红点提醒，`TDToast`”公告已发布，已推送至 {M} 名员工”。
* **返回未保存确认**：复用页面 4 的返回确认逻辑。

---

### 2. 权限分配与专属系统配置页

#### 页面 27（全新独有）：全局用户权限配置及角色映射设置页 (`/admin/permissions`)



* **页面组件内容**：
* 顶部常驻挂载 `TDSearchBar` 全员姓名/工号精准动态模糊搜索栏。
* 中部为基于 `TDListView` 的全量员工镜像列表，默认按部门 + 姓名排序，搜索结果按匹配度排序。支持下拉刷新和上拉加载更多（每页 20 条）。无头像用户展示系统默认头像占位图（姓名首字母 avatar）。
* 员工卡片上罗列的核心展示字段包括：员工头像、标准工号、姓名、主属部门、账号状态（启用/禁用，由 `SysUser.IsActive` 决定，禁用用户卡片整体置灰）、**以及该员工当前在当前 OA 系统内部映射分配的专属系统角色标签组件（`TDCapsuleTag`）**。





```
┌────────────────────────────────────────────────────────┐
│ Search: [ 输入姓名或工号进行检索... ]                 │
├────────────────────────────────────────────────────────┤
│ ┌────────────────────────────────────────────────────┐ │
│ │ (头像)  工号: 0048    张三 · 销售部   [普通员工] › │ │
│ └────────────────────────────────────────────────────┘ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ (头像)  工号: 0012    李四 · 财务部   [财务人员] › │ │
│ └────────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────┘

```

* **微观组件级微观交互（权限即时分配与系统动态升级切换闭环）**：
1. **搜索栏实时动态响应**：管理员在顶部 `TDSearchBar` 中每输入一个字符，下方的员工卡片列表便会实时结合 Dio 发起防抖（Debounce，300ms）模糊检索。搜索过程中列表区展示骨架屏。搜索无结果时展示 `TDEmpty`："未找到匹配的员工，试试其他关键词"。

2. **点击某位员工卡片（触发权限编辑抽屉）**：页面右侧顺滑滑出一个占据半屏空间的 `TDDrawer` 权限控制抽屉。

3. 抽屉内部提供一个 `TDCheckboxGroup` 复选框矩阵，并清晰列出系统的四种核心业务身份：`[ ] 普通员工`、`[ ] 审批人(经理)`、`[ ] 财务人员`、`[ ] 系统管理员`。底部附带一个 `TDSwitch` 账号启用/禁用开关，直接映射 `SysUser.IsActive`。

4. **防护栏杆**：
   * **自保护**：管理员无法取消自己的 `admin` 角色，取消时弹出 `TDToast`"无法移除自身的系统管理员角色"。管理员**可以修改自己的非 admin 角色**（如给自己添加财务角色）。
   * **最后管理员保护**：移除其他用户的 `admin` 角色时，后端校验是否为系统最后一个 `admin`。若是，拒绝操作并返回"至少保留一名系统管理员"。
   * **审批中禁用警告**：当操作对象是经理且其名下有待审批单据时，切换 `IsActive` 为禁用前弹出警告"该员工名下还有 {N} 笔待审批单据，禁用后将无法处理"，需二次确认。若用户点击"取消"，开关回弹到原先状态。

5. **无角色用户行为**：若用户在所有角色均被取消（`SysUserRole` 无记录），系统视为**默认普通员工**（等同于仅拥有 `employee` 角色），可正常登录并使用员工基础功能。当管理员重新打开该用户的抽屉时，仅"普通员工"被勾选。

6. **审计日志**：每次点击 `[确认保存权限修改]` 时，后端在事务内同时写入 `SysRoleChangeLog`（快照变更前后角色列表和 `IsActive` 状态），操作人和时间戳不可篡改。

7. **变更记录查看**：抽屉底部设有 **【变更记录】折叠区**（默认收起），展开后以 `TDTimeline` 时间线形式展示该员工的历史角色变更记录。每条记录包含：操作时间、操作人头像和姓名、变更摘要（如"添加了财务人员角色"、"禁用了账号"）。数据来源为 `SysRoleChangeLog` 按 `CreateTime DESC` 排序，最多展示最近 20 条。

8. **抽屉关闭未保存确认**：若修改了抽屉中的角色设置但未点击保存即关闭抽屉，弹出确认弹窗："当前修改尚未保存，是否放弃？" → "放弃" / "继续编辑"。

9. **保存失败处理**：若 `PUT /api/admin/assign-role` 请求失败，`TDToast`（红色）"权限修改失败，请稍后重试"，抽屉不关闭。


10. **执行修改与全局权限刷新闭环**：管理员将原本是”普通员工”的张三额外勾选上了”财务人员”单选框，并点击抽屉底部的 `[确认保存权限修改]` 按钮。点击保存后，按钮触发 Loading，Dio 异步发送 `PUT /api/admin/assign-role` 瞬间刷新 .NET 4.8 后端数据库。

11. **跨端及路由级终极闭环流转**：当张三在自己的手机端点击打开宿主原生的这个 OA 模块时，其首屏工作台路由会在初始化注入权限判定阶段，直接由原先朴素的【员工版工作台】动态升级重构变体切换展示为全功能的【财务人员专用工作台】。张三无需重新下载 APP 或退出登录，其对应的页面权限、详情页核销专区以及全公司图表穿透下钻功能全部就地解锁，整套 OA 模块至此在架构、角色、页面、及核心微观组件的交互操作上达成了完美的逻辑大闭环。


## 五、 全局设计规范与边界态

### 5.1 视觉设计 Token

#### 5.1.1 色彩体系

| Token | 值 | 用途 |
|-------|------|------|
| `--primary` | `#00ABF3` | 主操作按钮、激活态 Tab 图标、高亮 Chip、链接文字 |
| `--primary-light` | `#E6F7FD` | 主色浅底（卡片高亮背景、选中项底色） |
| `--primary-active` | `#0089C4` | 主色按压态 |
| `--success` | `#00A870` | 已通过状态标签、同意按钮、金额正面展示 |
| `--warning` | `#E37318` | 审批中状态标签、超支警告文案、排期冲突提示 |
| `--danger` | `#D54941` | 已拒绝状态标签、拒绝按钮、强制终止按钮、删除按钮 |
| `--text-primary` | `#1A1A1A` | 页面标题、卡片大标题、关键金额数字 |
| `--text-secondary` | `#666666` | 摘要文本、辅助说明、时间戳 |
| `--text-placeholder` | `#BFBFBF` | 输入框占位符、禁用态文字 |
| `--bg-page` | `#F5F5F5` | 页面底层背景 |
| `--bg-card` | `#FFFFFF` | 卡片、表单分组、列表项底色 |
| `--border` | `#E7E7E7` | 分割线、卡片描边、输入框边框 |
| `--status-gray` | `#999999` | 草稿状态标签、已撤回状态标签、已撤回横幅底色 |

#### 5.1.2 字号层级

| Token | 字号 | 行高 | 用途 |
|-------|------|------|------|
| `--fs-title` | 18sp | 26px | 导航栏标题、详情页大标题 |
| `--fs-subtitle` | 16sp | 24px | 卡片标题、表单分组标题、金额大字、按钮文字 |
| `--fs-body` | 14sp | 22px | 正文、列表摘要、输入框内容、Chip 标签 |
| `--fs-caption` | 12sp | 18px | 辅助说明、时间戳、状态标签文字、未读红点数 |

#### 5.1.3 间距系统

| Token | 值 | 用途 |
|-------|------|------|
| `--space-xs` | 4px | 图标与文字间距、Chip 内边距 |
| `--space-sm` | 8px | 列表项内 padding、表单行间距 |
| `--space-md` | 16px | 卡片内边距、页面左右边距 |
| `--space-lg` | 24px | 表单分组间距、卡片间距 |
| `--space-xl` | 32px | 页面顶部/底部留白 |

#### 5.1.4 TDesign 图标映射

| 页面位置 | 使用图标 | TDesign Flutter Icon |
|---------|---------|---------------------|
| Tab "消息" | 铃铛 | `TDIcons.notification` |
| Tab "消息"（红点） | 实心圆 | `TDBadge` 组件 |
| Tab "工作台" | 方块四格 | `TDIcons.app` |
| Tab "我的" | 人像 | `TDIcons.user` |
| 消息-审批待办 | 文档+时钟 | `TDIcons.file-paste` |
| 消息-审批结果 | 对勾/叉号 | `TDIcons.check-circle` / `TDIcons.close-circle` |
| 消息-系统公告 | 喇叭 | `TDIcons.notification` |
| 金刚区-事前申请 | 文档+货币 | `TDIcons.money-circle` |
| 金刚区-费用报销 | 票据 | `TDIcons.receipt` |
| 金刚区-用车申请 | 汽车 | `TDIcons.car` |
| 金刚区-加班申请 | 时钟 | `TDIcons.time` |
| 金刚区-外勤日志 | 定位 | `TDIcons.location` |
| 金刚区-公司公告 | 喇叭 | `TDIcons.notification` |
| 金刚区-申请记录 | 文档列表 | `TDIcons.task` |
| 金刚区-报销记录 | 票据列表 | `TDIcons.receipt` |
| 详情-已通过 | 实心圆对勾 | `TDIcons.check-circle-filled` |
| 详情-已拒绝 | 实心圆叉号 | `TDIcons.close-circle-filled` |
| 详情-审批中 | 实心圆时钟 | `TDIcons.time-filled` |
| 详情-已撤回 | 实心圆回退 | `TDIcons.rollback` |
| 列表-左滑编辑 | 铅笔 | `TDIcons.edit` |
| 列表-左滑删除 | 垃圾桶 | `TDIcons.delete` |
| 列表-一键同意 | 对勾 | `TDIcons.check` |
| 工作台-待办 | 文档列表 | `TDIcons.task` |
| 财务-数据导出 | Excel 图标 | `TDIcons.download` |
| 附件上传【+】 | 加号 | `TDIcons.add` |
| 附件删除减号 | 减号圆 | `TDIcons.close-circle` |
| DING 催办 | 闪电/通知 | `TDIcons.notification` |

### 5.2 边界态交互规范

#### 5.2.1 空状态

| 场景 | 组件 | 文案 |
|------|------|------|
| 消息列表为空 | `TDEmpty` + 铃铛占位图 | "暂无消息通知" |
| 申请列表为空 | `TDEmpty` + 文档占位图 | "暂无记录，点击下方按钮发起申请" |
| 审批列表（下属审批）为空 | `TDEmpty` + 对勾占位图 | "暂无待审批单据" |
| 报销明细为空 | `TDEmpty` + 票据占位图 | "暂无报销明细，请先添加费用明细" |
| 照片墙为空 | `TDEmpty` + 图片占位图 | "暂无附件" |
| 搜索无结果 | `TDEmpty` + 搜索占位图 | "未找到匹配的员工，试试其他关键词" |
| 公告列表为空 | `TDEmpty` + 喇叭占位图 | "暂无行政公告" |

#### 5.2.2 加载态

| 场景 | 方式 |
|------|------|
| 页面首次加载 | `TDSkeleton` 骨架屏（列表页 3 行矩形 + 卡片页 1 个大矩形），最长 8 秒超时转网络异常态 |
| 下拉刷新 | 列表顶部圆圈旋转 `RefreshIndicator` |
| 上拉加载更多 | 列表底部小菊花 + "加载中..." 文案 |
| 按钮提交中 | 按钮文本替换为 `TDLoading` 小菊花，按钮进入禁用态 |
| 附件上传中 | 缩略图格子叠加半透明黑色蒙层 + 中心白色小菊花 |
| 一键导出中 | 圆形悬浮按钮旋转 + 文字变为"导出中..." |

#### 5.2.3 网络异常态

| 场景 | 组件 | 文案 |
|------|------|------|
| 列表加载失败 | `TDEmpty` + 断网图标 + `[点击重试]` 按钮 | "网络连接失败，请检查网络后重试" |
| 提交失败 | `TDToast`（红色） | "提交失败，请稍后重试" |
| 接口超时（>15s） | `TDEmpty` + 时钟图标 + `[点击重试]` | "请求超时，请稍后重试" |
| 服务端 500 | `TDToast` | "服务器繁忙，请稍后重试" |
| 401 未授权 | 无 UI 提示，静默触发宿主登录流程 | — |

#### 5.2.4 全局 Toast/弹窗文案穷举

##### 成功反馈

| 场景 | 类型 | 文案 |
|------|------|------|
| 存为草稿 | `TDToast`（绿色） | "已保存为草稿，可在列表'草稿'分类中继续编辑" |
| 提交审批 | `TDToast` | "已提交，等待审批" |
| 撤回成功 | `TDToast` | "已撤回" |
| 同意成功 | `TDToast` | "已批准" |
| 拒绝成功 | `TDToast` | "已拒绝" |
| 转交成功 | `TDToast` | "已转交至 {接收人姓名}" |
| 强制终止 | `TDToast` | "已强制终止该单据" |
| 还车登记成功 | `TDToast` | "还车已登记，单据归档" |
| 打款归档成功 | `TDToast` | "已归档，付款流水号已记录" |
| 一键同意成功 | `TDToast` | "{N} 笔单据已批量批准" |
| 线下已付款标记 | `TDToast` | "已标记为线下已付款" |
| 退回修改成功 | `TDToast` | "已退回至 {退回节点} 的待办中" |
| 头像上传成功 | `TDToast` | "头像已更新" |
| 公告发布成功 | `TDToast` | "公告已发布，已推送至 {N} 名员工" |
| 权限修改成功 | `TDToast` | "权限已更新" |
| DING 催办触发 | `TDToast` | "已向 {N} 名未读员工发送催办通知" |
| 附件上传成功 | —（缩略图直接渲染） | — |
| 明细删除 | —（卡片折叠动画消失） | — |

##### 二次确认弹窗

| 场景 | 标题 | 正文 | 确定按钮 |
|------|------|------|---------|
| 撤回申请 | "确认撤回" | "撤回后该单据将变为已撤回状态，如需重新提交请重新编辑。" | "确认撤回"（蓝色） |
| 删除消息 | "确认删除" | "删除后该消息将从列表中移除，不可恢复。" | "确认删除"（红色） |
| 删除明细 | "确认删除" | "删除后该明细项不可恢复。" | "删除"（红色） |
| 一键同意 | "确认批量同意" | "确定同意 {N} 笔单据吗？同意后将自动流转至下一审批节点。" | "确认同意"（绿色） |
| 还车提交 | "确认还车" | "提交后单据将归档为已还车状态，里程和费用数据不可再修改。" | "确认提交"（蓝色） |
| 强制终止 | "确认强制终止" | "您正在以管理员身份强制终止该单据。该操作不可撤销，请谨慎执行。" | "强制终止"（红色） |

##### 校验报错

| 场景 | 方式 | 文案 |
|------|------|------|
| 必填项未填 | `TDMessage`（红色横幅） + ScrollTo | "请填写 {字段名}" |
| 金额超预算 | 金额区域边框变红 + 下方红色警告文本 | "您的申请金额已超支，提交后将自动触发高管特批流程" |
| 拒绝理由不足 5 字 | 弹窗确认按钮置灰 | 输入框下方红色提示"请输入至少 5 个字的拒绝理由" |
| 发票合规未全勾选 | 打款按钮置灰 | —（视觉锁定，不弹文案） |
| 电汇流水号未填 | 凭证表单红色边框 + 下方提示 | "请输入公司转账电汇流水号" |
| 凭证号未填 | 凭证表单红色边框 + 下方提示 | "请输入财务记账凭证号" |
| 车辆排期冲突 | 车牌选择器下方红色文本 | "注意：当前时段该车存在排期冲突！" |
| 发票号缺失 | 发票查验区警告文本 | "明细行 {X} 缺少发票号码，请补充后重新查验" |

### 5.3 导航栏标题清单

| 页面编号 | 路由 | 标题文字 |
|---------|------|---------|
| 1 | `/` | —（Appshell，无独立标题） |
| 2 | `/messages` | "消息通知" |
| 3 | `/home` | "TBOSS 工作台" |
| 3.1 | `/profile` | "我的" |
| 4 | `/expense-apply/apply` | "事前申请" |
| 5 | `/expense/apply` | "费用报销" |
| 6 | `/expense-apply/list` | "申请记录" |
| 7 | `/expense/list` | "报销记录" |
| 8 | `/expense-apply/detail/:id` | "申请详情" |
| 9 | `/expense/detail/:id` | "报销详情" |
| 10 | `/overtime/apply` | "加班申请" |
| 11 | `/overtime/list` | "加班记录" |
| 12 | `/overtime/detail/:id` | "加班详情" |
| 13 | `/vehicle/apply` | "用车申请" |
| 14 | `/vehicle/list` | "用车记录" |
| 15 | `/vehicle/detail/:id` | "用车详情" |
| 16 | `/outing-log/create` | "外勤日志" |
| 17 | `/outing-log/list` | "外勤记录" |
| 18 | `/outing-log/detail/:id` | "日志详情" |
| 19 | `/announcement/list` | "公司公告" |
| 20 | `/announcement/detail/:id` | "公告详情" |
| 21 | `/report/expense-apply-detail` | "事前申请明细报表" |
| 22 | `/report/expense-detail` | "费用报销明细报表" |
| 23 | `/report/overtime-detail` | "加班明细报表" |
| 24 | `/report/vehicle-detail` | "用车明细报表" |
| 25 | `/report/outing-log-detail` | "外勤日志明细报表" |
| 26 | `/announcement/create` | "发布公告" |
| 27 | `/admin/permissions` | "权限管理" |
| — | 子页面：审批历史 | "我的审批历史" |
| — | 子页面：关于 | "关于 TBOSS OA" |

### 5.4 全局系统级交互规范

#### 5.4.1 并发编辑冲突

当用户在设备 A 打开草稿编辑时，该草稿在设备 B 上已被提交或删除：

| 场景 | 后端行为 | 前端行为 |
|------|---------|---------|
| 保存草稿时冲突 | 返回错误码 `CONCURRENCY_CONFLICT` | 弹出 `TDDialog`："该单据已被修改或删除，请刷新后重试"→ 点击"确定"返回列表页并刷新 |
| 提交审批时冲突 | 返回错误码 `CONCURRENCY_CONFLICT` | 同上 |

#### 5.4.2 并发审批冲突

当经理 A 审批单据时，该单据已被经理 B（同级审批）或转交目标处理：

| 场景 | 后端行为 | 前端行为 |
|------|---------|---------|
| 审批/转交时冲突 | 返回错误码 `APPROVAL_CONFLICT` | 详情页自动刷新展示最新状态 + `TDToast`："该单据状态已更新，请重新查看" |

#### 5.4.3 横竖屏适配

- **仅支持竖屏**：`SystemChrome.setPreferredOrientations` 锁定竖屏，表单页和报表页不做横屏适配。

#### 5.4.4 键盘弹出行为

- 表单输入框聚焦时，页面自动上移确保输入框不被键盘遮挡。
- 键盘收起后页面恢复原位。
- 金额类输入框（纯数字）弹出数字键盘（`TextInputType.number`）。
- 文本类输入框弹出默认键盘（`TextInputType.text`）。

#### 5.4.5 网络状态监听

| 事件 | 行为 |
|------|------|
| 网络断开 | 全局顶部展示 `TDMessage` 横幅"当前网络不可用"，背景色 `--warning` |
| 网络恢复 | 横幅自动消失，中断期间的 Dio 请求自动排队重试（最多 3 次，间隔 2 秒递增） |
| 持续断网（>30s） | 当前页面非列表页时，覆盖半透明蒙层提示"网络连接已中断" |

#### 5.4.6 字体缩放适配

- 所有文案使用 `sp` 单位，遵循系统字体大小设置。
- 最小缩放：系统字体设置为最小时，正文最小不小于 11sp。
- 最大缩放：系统字体设置为最大时，导航栏标题不溢出（超出部分省略号截断）。

#### 5.4.7 全局返回手势

- iOS：边缘右滑返回手势默认生效（`CupertinoPageRoute`）。
- Android：系统返回键默认执行 `Navigator.pop()`。
- **表单页拦截**：若有未保存修改，返回前弹出确认弹窗（复用页面 4 逻辑）。

#### 5.4.8 深链接（Deep Link）

| 链接格式 | 跳转目标 |
|---------|---------|
| `tboss://oa/messages` | 消息列表页 `/messages` |
| `tboss://oa/expense/detail/:id` | 费用报销详情页 `/expense/detail/:id` |
| `tboss://oa/expense-apply/detail/:id` | 事前申请详情页 `/expense-apply/detail/:id` |
| `tboss://oa/announcement/detail/:id` | 公告详情页 `/announcement/detail/:id` |
| `tboss://oa/overtime/detail/:id` | 加班详情页 `/overtime/detail/:id` |
| `tboss://oa/vehicle/detail/:id` | 用车详情页 `/vehicle/detail/:id` |
| `tboss://oa/outing-log/detail/:id` | 外勤日志详情页 `/outing-log/detail/:id` |

> 深链接由宿主 App 解析后通过 MethodChannel 传给 Flutter 模块，Flutter 端 `GoRouter` 处理路由跳转。若目标单据不存在（ID 无效或已删除），展示 `TDEmpty`："单据不存在或已删除" + "返回"按钮。