Files
shanxia2.0/docs/superpowers/plans/2026-10-11-column-facet-context-cascade.md
T
wuzilongandCursor 6a8cd90b98 feat: cascade column facet options by current list context
Make dynamic column-facet values respect other active filters (Excel-style), with backend facet context helpers, list-page wiring, slip print/sidebar fixes, and refreshed graphify graphs.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-11 22:04:20 +08:00

10 KiB
Raw Blame History

列头漏斗 Excel 级联可选值 — 分批 Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: 列头动态可选值按「当前列表上下文条件」去重统计(排除本列自身条件),对齐 Excel 级联筛选;前端一次铺管道,后端按模块分批点亮。

Architecture: useColumnFacetOptions 打开漏斗时附带 getContextParams()(固定条件 + 顶部筛选区 + 列头,并剥离本列 query 键);各模块 column-facet-values 复用列表 filter 构建,在过滤结果集上 group_by。未接入模块保持全集行为。

Batch 1 范围(已确认): 运输计划、运输归档、危化品证、合同完善(plan-contract)。

Tech Stack: Vue 3 composable、FastAPI、SQLAlchemy、build_plan_column_filters / 各模块 list filters、column_facet helper

Spec: docs/superpowers/specs/2026-10-11-column-facet-context-cascade-design.md


文件职责(总览)

文件 职责
web-admin/src/composables/useColumnFacetOptions.ts context 附带、本列键剥离、调用 fetchFacet
web-admin/src/composables/columnFacetContext.ts(新建,建议) 纯函数:按 ColumnFilterDef + prop 从 params 去掉本列键
web-admin/src/api/pl/index.ts 等 facet API 入参允许附加过滤字段
backend/app/api/pl/plan.py 计划 facet 吃列表同款 params,忽略本 field
backend/app/services/pl/plan_list_filters.py 复用,必要时抽「忽略某字段」钩子
backend/app/services/column_facet.py 可选:在已有 query 上做 facet
各列表 index.vue 传入 getContextParams(固定条件 + toQueryParams())

Batch 0:公共管道

Task 0.1: 本列 query 键剥离纯函数

Files:

  • Create: web-admin/src/composables/columnFacetContext.ts

  • Create: web-admin/src/composables/columnFacetContext.spec.ts(或项目既有单测位置)

  • Step 1: 实现 stripSelfColumnParams(params, prop, defs): Record<string, …>

    • 覆盖 enum / text / datetime / number / loadCustomers 别名
  • Step 2: 单测:承运商只剥 carrierName/carrierNameIn;装货客户剥 loadCustomers* 与 loadCustomer*

  • Step 3: Commit:feat(web-admin): add column facet context strip helper(待用户要求再提交)

Task 0.2: 扩展 useColumnFacetOptions

Files:

  • Modify: web-admin/src/composables/useColumnFacetOptions.ts

  • Step 1: 增加 getContextParams?: () => Record<string, unknown>

  • Step 2: onFilterOpen 合并 context → stripSelfColumnParams → 传入 fetchFacet

  • Step 3: 无 getContextParams 时行为与现网一致

  • Step 4: Commit:feat(web-admin): pass list context into column facet fetch(待用户要求再提交)

Task 0.3: 计划 facet API 类型放宽

Files:

  • Modify: web-admin/src/api/pl/index.ts(fetchPlanColumnFacetValues)

  • Step 1: data 类型改为 field + 可选过滤字段索引签名(或显式与列表 query 对齐的可选字段)

  • Step 2: Commit:chore(web-admin): allow context filters on plan facet API(待用户要求再提交)


Batch 1:运输计划族(优先交付)

Task 1.1: 后端计划 facet 吃上下文

Files:

  • Modify: backend/app/api/pl/plan.py(PlanColumnFacetBody、plan_column_facet_values)

  • Modify: backend/app/services/pl/plan_list_filters.py(若需 ignore_field)

  • Test: backend/tests/test_pl_plan_column_facet_context.py(新建)

  • Step 1: Body 增加与列表一致的可选过滤字段(或 extra: dict + 与 _filters_from_params 同源解析;优先复用现有 param 解析,避免双份字段表)

  • Step 2: 构建 filters 时忽略当前 field 对应键(carrierName→忽略 carrier_name/carrier_name_in 等)

  • Step 3: 各 field 分支在已过滤的 plan 查询上 group_by(改写现有仅租户过滤的 query)

  • Step 4: 单测:剥离 helper(carrier / loadCustomer 别名);打开 carrier 时不受自身 In 限制

  • Step 5: Commit:feat(backend): contextual filters for plan column-facet-values(待用户要求再提交)

Task 1.2: 运输归档页接入 context

Files:

  • Modify: web-admin/src/views/pl/plan-archive/index.vue

  • Step 1: getContextParams = 固定条件(如 transportStatus=arranged)+ 顶部筛选区(与列表同源)+ toQueryParams()

  • Step 2: fetchFacet 展开 payload 全部字段发给接口

  • Step 3: 手动验收:承运商 → 装货客户;装货客户 → 承运商;有顶部筛选时可选值同步收窄

  • Step 4: Commit:feat(web-admin): contextual facets on plan-archive(待用户要求再提交)

Task 1.3: 运输计划页 + 危化品证页接入

Files:

  • Modify: web-admin/src/views/pl/plan/index.vue

  • Modify: web-admin/src/views/pl/plan-cert/index.vue

  • Step 1: 同上:固定条件 + 顶部筛选区 + 列头;与各页 handleSearch / 列表 query 对齐(含 Tab、followStatus、危化相关)

  • Step 2: 手动抽查两页级联

  • Step 3: Commit:feat(web-admin): contextual facets on plan and plan-cert(待用户要求再提交)

Task 1.4: 合同完善页(plan-contract)接入

Files:

  • Modify: web-admin/src/views/pl/plan-contract/index.vue

  • Modify: web-admin/src/api/pl/index.ts(若合同完善走独立 facet API,一并放宽类型)

  • Step 1: getContextParams = 缺口页固定条件(contractGapOnly / gapType 等)+ 顶部筛选区 + toQueryParams();离散列改为动态 facet

  • Step 2: fetchFacet 带全量 context;后端计划 facet 支持 contractGapOnly / gapType

  • Step 3: 手动验收:先筛装/送货客户或产品,再开另一环动态列,可选值收窄且缺口条件不丢

  • Step 4: Commit:feat(web-admin): contextual facets on plan-contract(待用户要求再提交)

Task 1.5: Batch 1 验收清单

  • 归档:先筛承运商,装/送货客户可选值仅为该承运商下出现过的名称(需手动)
  • 归档:先筛装货客户,承运商可选值收窄;再开装货客户不受自身勾选锁死(需手动)
  • 归档/计划:顶部筛选有值时,打开任意动态列可选值受其约束(需手动)
  • 计划列表 / 危化证:同上逻辑 + 各自固定条件(需手动)
  • 合同完善:缺口固定条件 + 列头级联均生效(需手动)
  • 单测:test_pl_plan_column_facet_context + test_pl_plan_list_filters 通过
  • 未改页面(如 CRM 客户)仍为全集,无报错(需抽查)

Batch 2:PL 其它

Task 2.x(每个资源一组,可并行)

资源顺序建议: freight 未结算 → carrier → vehicle / self-vehicle → personnel → transport-address

对每个资源:

  • 后端对应 column-facet-values 复用该列表 filters + ignore field(facet_context.strip_simple_facet_self_params)
  • 前端页 getContextParams + API 类型放宽
  • 抽查 2 列级联(需手动)
  • Commit:feat: contextual facets on pl-<resource>(待用户要求再提交)

已接入:UnsettledPlanPanel、carrier、vehicle、self-vehicle、personnel、transport-address。


Batch 3:CRM

Task 3.x

顺序建议: customer / pool → clue → opportunity → quote → contract 族 → price-letter → supplier-hazard → product

  • 各 POST /web/crm/.../column-facet-values 吃 list 同款 params(facet_context.clear_facet_field_kwargs / facet_filter_kwargs_from_body + 各 build_*_column_filters)
  • 各列表 getContextParams(customer/pool、product、clue、opportunity、quote、contract/change/template/approval、price-letter、supplier-hazard)
  • 单测:tests/test_crm_facet_context.py
  • Commit 按子域拆分(勿一个巨大 commit;待用户要求再提交)

Batch 4:地磅 / 报销 / 系统

Task 4.x

顺序建议(可按投诉插队): db/plans、db/weighing、db/inventory → bx/* → admin users/tenants/roles… → workflow/leave/operlog

  • 同 Batch 2 模式(后端 extra="allow" + strip_simple_facet_self_params / 列表 filter 复用;前端 getContextParams + API Record<string, unknown>)
  • 地磅:db/plans、db/weighing、db/inventory
  • 报销:bx/reimburse/apply|approve、bx/payment、bx/advance/apply|approve|issue、bx/invoice-wallet/mine
  • 系统:system/user|tenant|role|post|dict|leave|operlog、workflow/task-pending|task-completed|my-applications
  • 全站「动态 facet 已接入 context」清单见下文「接入清单」
  • 抽查级联(需手动)
  • Commit(待用户要求再提交)

接入清单(动态 facet + getContextParams)

域 页面
PL plan、plan-archive、plan-cert、plan-contract、freight/UnsettledPlanPanel、carrier、vehicle、self-vehicle、personnel、transport-address
CRM customer、customer/pool、product、clue、opportunity、quote、contract 族、price-letter、supplier-hazard
DB plans、weighing、inventory
BX reimburse/apply|approve、payment、advance/apply|approve|issue、invoice-wallet/mine
System user、tenant、role、post、dict、leave、operlog、workflow pending/completed/mine

规范收尾(可与 Batch 1 同迭代)

Task N: 文档 / OpenSpec

Files:

  • Optional: openspec/changes/zkxw-engineering-standards/specs/ui-conventions/spec.md

  • Optional: docs/web-admin-ui-pattern-index.md 或 facet 相关说明一小段

  • SHALL:动态列头可选值请求须携带当前列表上下文,并排除本列条件

  • Commit:docs: excel-like contextual column facet convention


执行原则

  1. 先 Batch 0+1 上线,再扩其它域。
  2. 每批可独立合并、独立验收;未点亮后端的页保持旧行为。
  3. 禁止在未复用列表 filter 的情况下手写第二套条件(易漂移)。
  4. 实现时若 PlanColumnFacetBody 字段爆炸:优先「body 收 list 同款 dict / 与 request 合并」方案,与 plan.py 现有 _merge_query / _filters_from_params 对齐。