Files
shanxia2.0/docs/superpowers/specs/2026-09-08-db-date-column-sort-design.md
T

6.4 KiB
Raw Blame History

地磅域日期列头双三角排序(出入库 / 地磅单)

日期: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' 显示双三角,本批无需再改组件。

各页:

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