瀏覽代碼

docs: add BX date column dual-caret sort design.

Co-authored-by: Cursor <cursoragent@cursor.com>
wuzilong 1 月之前
父節點
當前提交
e33b56797a
共有 1 個文件被更改,包括 138 次插入 和 0 次删除
  1. 138 0
      docs/superpowers/specs/2026-09-08-bx-date-column-sort-design.md

+ 138 - 0
docs/superpowers/specs/2026-09-08-bx-date-column-sort-design.md

@@ -0,0 +1,138 @@
+# 报销域日期列头双三角排序
+
+**日期**:2026-09-08  
+**状态**:待审阅  
+**对齐**:`docs/superpowers/specs/2026-09-08-crm-date-column-sort-design.md`(交互与参数一致)  
+**Demo**:`docs/crm-date-column-sort-demo.html`(样式 2 · 双三角)
+
+---
+
+## 1. 背景与目标
+
+CRM 客户 / 报价 / 合同已落地日期列双三角整库排序。报销管理下多份列表已有列头漏斗(`ColumnHeaderFilter` + `datetime`),但尚不能按日期列升/降序。
+
+### 目标
+
+1. 有日期列的报销相关列表:日期列头双三角,整库 `asc` / `desc`,取消后恢复现网默认序。  
+2. 与 CRM 同一套交互与查询参数:`sortField` / `sortOrder`。  
+3. 前端优先在现有 `ColumnHeaderFilter` 上扩展(报销页已直接使用该组件),避免整页改迁 `CrmColFilterHeader`。  
+4. 后端复用 `resolve_datetime_order_by` + 各 list 白名单。
+
+### 非目标
+
+- 费用项目管理、工作流配置(备用金/报销单)、发票夹管理(员工汇总)  
+- 文本 / 枚举 / 数字列排序  
+- 弹窗内嵌表明细日期(如费用明细 `occurDate`、冲抵记录子表)除非该列已是主列表带漏斗的 `datetime`  
+- 排序状态 URL / localStorage 持久化
+
+---
+
+## 2. 已确认产品决策
+
+| # | 决策 |
+|---|------|
+| D1 | 范围:仅「有日期列」的页面;费用项目 / 工作流 / 发票夹管理跳过 |
+| D2 | 交互与 CRM 相同:双三角;同向再点取消;仅 `datetime`;空值升序前、降序后 |
+| D3 | 架构:扩展 `ColumnHeaderFilter` + 页面 `useColumnSort`(方案 1);不用逐列手写三角 |
+| D4 | 参数:`sortField`(列 prop)+ `sortOrder`(`asc`\|`desc`);非法回退默认序 |
+
+---
+
+## 3. 本批页面与日期列
+
+| 菜单 | 前端路径 | 日期列 prop |
+|------|----------|-------------|
+| 备用金申请 | `views/bx/advance/apply/index.vue` | `applyTime` |
+| 备用金审批 | `views/bx/advance/approve/index.vue` | `applyTime` |
+| 备用金发放 | `views/bx/advance/issue/index.vue` | `applyTime`、`issueTime` |
+| 报销申请 | `views/bx/reimburse/apply/index.vue` | `applyTime` |
+| 报销审批 | `views/bx/reimburse/approve/index.vue` | `applyTime` |
+| 支付发放冲抵 | `views/bx/payment/index.vue` | `applyTime`、`payTime` |
+| 我的发票夹 | `views/bx/invoice-wallet/mine/index.vue` | `createTime`(发票表 + 流水表,各一套 filter/sort) |
+| 我的申请单 | `views/bx/view/mine/index.vue` | `applyTime` |
+| 待我审批 | `views/bx/view/todo/index.vue` | `applyTime` |
+| 全部申请单(总经理) | `views/bx/view/all/index.vue` | `applyTime` |
+
+**跳过**:`expense-item`、`workflow/advance`、`workflow/reimburse`、`invoice-wallet/admin`。
+
+---
+
+## 4. 产品与交互
+
+与 CRM 设计 §4 一致:
+
+- 列头顺序:标题 → 双三角(仅 datetime) → 漏斗。  
+- 同一时刻仅一列排序;藏列若为当前排序列则清除并排序重查。  
+- 过滤与排序 AND;导出与列表同一套 sort 参数(若该页有导出且走同一 query builder)。
+
+「申请时间」展示字段 `applyTime` 在后端多映射 `create_time`:排序白名单须 `applyTime` → `create_time`(或实体等价列),禁止按不存在的 `apply_time` 列动态拼接。
+
+---
+
+## 5. 架构
+
+### 5.1 前端 · `ColumnHeaderFilter`
+
+在 `column-header-filter/index.vue` 的漏斗按钮旁:
+
+- `inject('crmColumnSort')`(与 CRM 同 key,避免双套 inject;可选再兼容 `columnSort`)。  
+- `showSort = Boolean(sortApi && type === 'datetime')`。  
+- 样式复用已有 `.col-header-sort-pair` / `.col-header-sort-btn`。
+
+`CrmColFilterHeader` 已具备三角,行为不变。未 provide 排序的页面(含跳过页)不显示三角。
+
+### 5.2 前端 · 各列表页
+
+```ts
+const { setSort, clearSort, isSortActive, sortField, sortOrder, toQueryParams: toSortQueryParams, watchVisibleColumns: watchSortVisibleColumns } =
+  useColumnSort({ onChange: () => { /* page=1; reload */ } })
+
+provide('crmColumnSort', { setSort, clearSort, isSortActive, sortField, sortOrder })
+// list/export params: ...toSortQueryParams()
+// watchSortVisibleColumns(visibleFilterProps)
+```
+
+我的发票夹:发票 Tab / 流水 Tab 可各用一套 `useColumnSort`,inject key 用默认 + 可选第二 key,或切换 Tab 时共用一套(实现时选改动更小者;须保证当前可见表的 datetime 列能驱动正确 list API)。
+
+### 5.3 后端
+
+复用 `backend/app/services/crm/list_sort.py` 的 `resolve_datetime_order_by`(路径名含 crm,本批不强制搬家;若后续统一可迁到 `app/services/list_sort.py`,非本批必做)。
+
+各 list(及共用路径的 export)增加可选 `sortField` / `sortOrder`,白名单示例:
+
+| 域 | prop → Column |
+|----|----------------|
+| 备用金申请/审批/发放主列表 | `applyTime` → `BxAdvanceApply.create_time`;发放另加 `issueTime` → 实际发放时间列(以 model 为准,如 `issue_time` / `pay_time`) |
+| 报销申请/审批 | `applyTime` → `BxReimburseApply.create_time` |
+| 支付发放冲抵 | `applyTime` → 单据创建/申请时间列;`payTime` → 处理时间列(以现网字段为准) |
+| 视图 mine/todo/all | 按 biz 分支映射到 advance/reimburse 的 `create_time`,或统一视图查询的申请时间表达式 |
+| 我的发票夹 | `createTime` → 发票/流水表的 `create_time` |
+
+非法 `sortField` → 现网默认(一般为 `create_time DESC`)。
+
+---
+
+## 6. 测试要点
+
+1. 报销申请:`applyTime` 升/降/取消;与漏斗日期段 AND;换页保持序。  
+2. 备用金发放:`issueTime` 仅在已发放可见列时可用;藏列清排序。  
+3. 支付:`payTime` 在已处理 Tab。  
+4. 我的申请单 / 待我审批 / 全部:备用金与报销单混合列表排序正确。  
+5. 我的发票夹:上传时间/流水时间排序打整库。  
+6. 费用项目、工作流、发票夹管理:无双三角。  
+7. 抓包非法 `sortField` → 默认序、不 500。
+
+---
+
+## 7. 风险与后续
+
+- **风险**:视图页(mine/todo/all)若 SQL 为 union/多表,白名单须落到真实可排序表达式,避免只改前端。  
+- **风险**:`applyTime` 与 DB `create_time` 映射遗漏导致排序无效或报错——实现时对照 schema `validation_alias="create_time"`。  
+- **后续**:发票夹管理若补日期列再接线;可将 `list_sort` 抽到中性路径。
+
+---
+
+## 8. 文档
+
+- 本设计:`docs/superpowers/specs/2026-09-08-bx-date-column-sort-design.md`  
+- CRM 对照:`docs/superpowers/specs/2026-09-08-crm-date-column-sort-design.md`