From 9e1862f4e757653c6bdd9dd0f485c4b2119a8e83 Mon Sep 17 00:00:00 2001 From: wuzilong <704541015@qq.com> Date: Tue, 8 Sep 2026 22:31:35 +0800 Subject: [PATCH] docs: add DB weighbridge date column dual-caret sort design. Co-authored-by: Cursor --- .../2026-09-08-db-date-column-sort-design.md | 154 ++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 docs/superpowers/specs/2026-09-08-db-date-column-sort-design.md diff --git a/docs/superpowers/specs/2026-09-08-db-date-column-sort-design.md b/docs/superpowers/specs/2026-09-08-db-date-column-sort-design.md new file mode 100644 index 000000000..5655e846d --- /dev/null +++ b/docs/superpowers/specs/2026-09-08-db-date-column-sort-design.md @@ -0,0 +1,154 @@ +# 地磅域日期列头双三角排序(出入库 / 地磅单) + +**日期**:2026-09-08 +**状态**:待审阅 +**对齐**:`docs/superpowers/specs/2026-09-08-crm-date-column-sort-design.md`、`docs/superpowers/specs/2026-09-08-bx-date-column-sort-design.md` +**Demo**:`docs/crm-date-column-sort-demo.html`(样式 2 · 双三角) + +--- + +## 1. 背景与目标 + +地磅管理下「地磅出入库」「地磅单管理」已具备列头 `datetime` 漏斗,但日期列无法升/降序切换。交互需与 CRM / 报销一致。 + +### 目标 + +1. 仅对已有 `ColumnHeaderFilter` 且 `type === 'datetime'` 的列显示双三角。 +2. 整表(拉全量后的全集)按该列 `asc` / `desc`;取消后恢复现网默认倒序。 +3. 列表与导出顺序一致:导出走同一 `_fetch_*`,须识别 `sortField` / `sortOrder`。 +4. 参数与 inject 与报销相同:`useColumnSort` + `provide('crmColumnSort')`。 + +### 非目标 + +- **地磅计划**(本批无 datetime 漏斗列;起始/截止日期不加) +- 地磅单 `grossWeightTime` / `tareWeightTime`(无漏斗) +- 文本 / 枚举 / 数字列排序 +- 排序状态 URL / localStorage 持久化 +- 工作台 `db/dashboard` + +--- + +## 2. 已确认产品决策 + +| # | 决策 | +|---|------| +| D1 | 范围方案 **A**:只给已有 datetime 漏斗的列加双三角 | +| D2 | 页面:出入库 + 地磅单;**计划本批跳过** | +| D3 | 交互与 CRM/报销相同:双三角;同向再点取消;空值升序前、降序后 | +| D4 | 架构:复用 `ColumnHeaderFilter`(已支持 inject)+ `useColumnSort` + 后端白名单(方案 1) | + +--- + +## 3. 本批页面与日期列 + +| 菜单 | 前端路径 | 日期列 prop | 后端映射 | +|------|----------|-------------|---------| +| 地磅出入库 | `web-admin/src/views/db/inventory/index.vue` | `date` | `DbInboundOutbound.date` | +| 地磅单管理 | `web-admin/src/views/db/weighing/index.vue` | `inventoryDate` | 关联出入库 `DbInboundOutbound.date`(经 `inbound_outbound_id`;展示字段非地磅单本表列) | + +**跳过**:`views/db/plans/index.vue`、`views/db/dashboard/index.vue`。 + +--- + +## 4. 产品与交互 + +与 CRM / 报销 §4 一致: + +- 列头:标题 → 双三角(仅 datetime) → 漏斗。 +- 同一时刻仅一列排序;藏列若为当前排序列则清除。 +- 过滤、快捷搜索与排序 AND。 +- 导出 filter/body 合并 `toSortQueryParams()`。 + +--- + +## 5. 架构 + +### 5.1 现网列表形态(约束) + +两页均为: + +1. 后端 `_fetch_inventory` / `_fetch_weighing` **一次拉全量**(含列过滤); +2. 前端再 **固定按日期倒序**(出入库 `applySortAndPage`;地磅单 `loadData` 内对 `getRecordDate` 排序); +3. 前端切片分页。 + +因此: + +- **列表 UI 排序**:改前端现有固定倒序,接入 `useColumnSort`(无参时保持现网默认 `desc`)。 +- **导出 / 与后端默认序对齐**:`_fetch_*` 按 `sortField`/`sortOrder` 应用白名单 `order_by`,避免导出仍按硬编码序。 + +### 5.2 前端 + +`ColumnHeaderFilter` 已在报销任务中支持 `inject('crmColumnSort')` + `type==='datetime'` 显示双三角,**本批无需再改组件**。 + +各页: + +```ts +const { setSort, clearSort, isSortActive, sortField, sortOrder, + toQueryParams: toSortQueryParams, watchVisibleColumns: watchSortVisibleColumns } = + useColumnSort({ onChange: () => { /* 出入库:applySortAndPage;地磅单:重排 allData 或 loadData */ } }) + +provide('crmColumnSort', { setSort, clearSort, isSortActive, sortField, sortOrder }) +``` + +- `buildListQueryParams` / 导出 `getFilterParams` 合并 `...toSortQueryParams()`。 +- `watchSortVisibleColumns(visibleFilterProps)`。 +- 出入库:`applySortAndPage` 按 `sortField`(仅 `date`)与 `sortOrder` 排;缺省 `date` + `desc`。 +- 地磅单:替换现网固定 `getRecordDate` 倒序;缺省仍按展示日期 `desc`;有 `inventoryDate`+`asc|desc` 时按该序(空串按空值规则)。 + +### 5.3 后端 + +新建薄封装(对齐 `app/services/bx/list_sort.py`): + +- `app/services/db/list_sort.py` + - `INVENTORY_WHITELIST = {"date": DbInboundOutbound.date}` + - `WEIGHING_WHITELIST`:`inventoryDate` → 需 **outerjoin** 出入库表后对 `DbInboundOutbound.date` 排序(见下) + - 复用 `resolve_datetime_order_by`;`parse_sort_params` 可复用 bx 或同文件复制一行。 + +**出入库** `_fetch_inventory`: + +- 默认:`date.desc(), id.desc()`(现网)。 +- 有合法 sort:`order_by(resolve(...))`;建议次键仍 `id.desc()` 保稳定。 + +**地磅单** `_fetch_weighing`: + +- 默认:`gross_weight_time.desc()`(现网;与列头「日期」展示字段不完全同一列,保持取消排序后行为不变)。 +- 排序 `inventoryDate`:`outerjoin(DbInboundOutbound, ...)` 后按 `DbInboundOutbound.date` asc/desc;无关联单时 date 为空,依赖 MySQL NULL 次序(升序前、降序后)。 +- 注意 join 后勿重复行:按 `inbound_outbound_id` 一对一关联即可。 + +导出 body(若已有 export schema)显式或 `extra=allow` 透传 `sortField`/`sortOrder`。 + +### 5.4 测试 + +- `backend/tests/test_db_list_sort.py`:白名单 / 非法回退。 +- 前端:复用已有 `useColumnSort.test.ts`,无需新测除非抽纯函数。 +- 手工:出入库 `date` 升/降/取消;地磅单 `inventoryDate`;导出序与列表一致;计划页无三角。 + +--- + +## 6. 风险与注意 + +| 风险 | 处理 | +|------|------| +| `inventoryDate` 非本表列 | JOIN 排序;禁止臆造 `inventory_date` 列名 | +| 前端已拉全量 | 列表改客户端排序即可;仍须后端排序服务导出 | +| 取消排序后默认序 | 出入库回 `date desc`;地磅单回 `gross_weight_time desc`(后端)与展示日期 desc(前端,与现网 loadData 一致) | +| 与未提交的 BX 排序改动并存 | 本能力独立;提交时勿混入无关 dirty 文件 | + +--- + +## 7. 验收 + +1. 出入库「日期」列头有双三角;升/降/取消行为正确。 +2. 地磅单「日期」列头有双三角;按关联出入库日期排序。 +3. 地磅计划无双三角。 +4. 导出顺序与当前列表排序一致。 +5. 列过滤 + 排序可叠加。 + +--- + +## 8. 实现顺序(摘要) + +1. `services/db/list_sort.py` + 单测 +2. `_fetch_inventory` / `_fetch_weighing`(及 export params)接排序 +3. 接线 `inventory/index.vue`、`weighing/index.vue` +4. 回归 + `./scripts/update-graph.sh`