2026-06-26-web-admin-product-spec-field-design.md 10.0 KB

Web Admin 商品「规格」字段设计

背景

商品管理(#/base/product)当前通过 计量(measure_value)+ 单位(unit_id)描述售卖数量维度(如 500 克、1 个)。业务还需要一个独立的 规格 文本属性,用于补充包装、形态等自由描述(如「礼盒装」「约 2 斤/袋」),且 非必填。

查看弹窗 product-view-dialog 曾将「计量+单位」误标为「规格」,需与新字段区分,避免语义混淆。

目标(用户可见行为)

  1. 新增/编辑商品:在基本信息区(计量/单位旁或下一行)增加「规格」输入框(el-input),可清空,不做必填校验。
  2. 列表模式:凡同时展示「计量」「单位」的表格,单独增加「规格」列;空值显示 —。
  3. 行内/详情模式:合并展示为 计量 + 单位 +(规格) 形式,例如 500 克(礼盒装);无规格时不显示括号。
  4. 商品管理列表:计量 / 单位 / 规格 三列并列;导出 Excel 同步增加「规格」列。

非目标

  • 不改造「参数配置」(分类关联动态参数)体系来承载规格。
  • 不在 biz_order_item 等订单行表增加规格快照(历史订单展示仍实时读取 bas_product.spec;若商品后续改规格,历史订单展示会跟随变化)。
  • 本轮不改小程序端(mini_client / mini_supplier / mini_deliverer)。
  • 不改仅展示「单位」、无「计量」的页面(价格清单、报价、询价等)。

数据模型

新增字段

表 字段 类型 约束 说明
bas_product spec VARCHAR(100) NULL 商品规格(自由文本,非必填)
bas_product_his spec VARCHAR(100) NULL 与主表一致,UPDATE 时写入历史快照

字段命名约定:

层级 命名
数据库 spec
后端 Model spec
API 请求/响应 spec(驼峰与蛇形均为单字词,一致)

长度上限 100 字符;前端输入框 maxlength=100 + show-word-limit(可选)。

迁移脚本

  • 路径:backend/scripts/migration_20260626_bas_product_spec.sql
  • 内容:ALTER TABLE bas_product ADD COLUMN spec ...;ALTER TABLE bas_product_his ADD COLUMN spec ...
  • 存量数据默认 NULL,无需回填。

历史表机制

backend/app/crud/product.py 中 update_with_history、soft_delete_with_history 的快照字典需补充 spec(以及现有已缺失的 sku_code、detail_content、price_coefficient 可不在本轮强制补齐,但 spec 必须写入)。


展示规范

公共格式化函数(建议)

新增 web-admin/src/utils/product-measure-display.ts(或等价路径):

/** 列表:计量单元格(保持现有逻辑) */
formatMeasureCell(measureValue): string  // 如 "500"、"—"

/** 行内合并:计量 + 单位 +(规格) */
formatMeasureUnitSpec(measureValue, unitName, spec?): string

formatMeasureUnitSpec 规则:

measure unit spec 输出
500 克 礼盒装 500 克(礼盒装)
500 克 空 500 克
空 克 礼盒装 克(礼盒装)
空 空 礼盒装 (礼盒装)
空 空 空 —
  • 计量经 formatMeasureCell 格式化(去尾零,与现有一致)。
  • 单位名 trim 后拼接;计量与单位之间空格分隔。
  • 仅当 spec 非空 时追加 (${spec})。
  • 括号使用中文全角 ()。

数量 × 计量单位(规格)(订单已配送等场景):

{qty}×{formatMeasureUnitSpec(measure, unit, spec)}

示例:10×500 克(礼盒装);无规格时 10×500 克。

列表 vs 行内

场景 展示方式
表格列表 计量、单位、规格 三列独立
弹窗 Hero、描述行、价格后缀、配送数量文案 formatMeasureUnitSpec 合并

命名修正

product-view-dialog 中原「规格:500 克」改为:

  • Hero / 基本信息行内:formatMeasureUnitSpec(...) 或标签改 「计量/单位(规格)」 与合并文案一致。
  • 列表式 KV 若需拆分:计量/单位 与 规格 可各占一行,或合并一行展示——推荐合并一行 500 克(礼盒装),标签写 「计量/单位(规格)」。

接口变更

商品 CRUD(必改)

文件:backend/app/api/admin.py

接口 变更
GET /web/admin/products 列表项增加 spec
GET /web/admin/products/{id} 详情增加 spec
POST /web/admin/products 接收 spec(可省略或 null / 空串,存 NULL)
PUT /web/admin/products/{id} 同上

空串处理:后端统一 strip 后,空串写 NULL。

关联业务接口(展示联动)

凡响应中已有 measureValue + unitName 的组装逻辑,同步从 product.spec 带出 spec:

模块 文件 说明
订单明细 backend/app/api/admin.py 订单详情 items
客户结算树 backend/app/api/admin.py settlement line children
供应商结算 backend/app/api/admin.py 同上
配送 backend/app/api/delivery.py 配送任务商品行

原则:前端业务页不额外请求商品详情补 spec;由列表/详情接口一次返回。


前端改造清单

P0 — 商品管理核心

文件 改动
web-admin/src/views/system/product/index.vue 表单 formData.spec;提交/回填/复制/重置;列表加「规格」列;导出列
web-admin/src/components/business/product-view-dialog/index.vue 合并展示;修正原「规格」标签
web-admin/src/api/admin.ts fetchCreateProduct / fetchUpdateProduct 类型增加 spec?: string
web-admin/src/utils/product-measure-display.ts 新建 公共格式化函数

表单布局建议(基本信息区):

[商品分类]          [计量/单位(现有)]
[规格输入框]        [调价系数 / 其它]

规格表单项:无 required 规则;placeholder 如「如:礼盒装、约 2 斤/袋」。

P1 — 业务列表(有计量+单位列)

文件 列表改动 行内改动
web-admin/src/views/system/order/index.vue 加「规格」列 formatDeliveredQtyMeasureUnit 等改用合并格式
web-admin/src/views/system/delivery/index.vue 加「规格」列 行内文案合并
web-admin/src/views/system/delivery-warning/index.vue 同上 同上
web-admin/src/views/system/inquiry-quotation/index.vue 同上 同上
web-admin/src/views/system/client-settlement/index.vue 同上 同上
web-admin/src/views/system/supplier-settlement/index.vue 同上 同上
web-admin/src/components/settlement/SettlementSheetDetailPanel.vue 同上 同上

各页本地重复的 formatMeasureCell 可逐步改为引用公共工具,但 不强制本轮全量重构;至少新增列与行内展示使用 formatMeasureUnitSpec。

不改(本轮)

  • price-list/index.vue、price-list/version.vue(仅单位)
  • quotation/index.vue、quotation/compare.vue
  • inquiry/index.vue
  • order-adjust/index.vue(仅单位,无计量列)

后端改造清单

文件 改动
backend/scripts/migration_20260626_bas_product_spec.sql 新建
backend/app/models/models.py BasProduct、BasProductHis 增加 spec 列
backend/app/crud/product.py 历史快照写入 spec
backend/app/api/admin.py 商品 CRUD + 订单/结算等 join 处
backend/app/api/delivery.py 配送商品行

backend/app/schemas/product.py 为旧版占位 Schema,实际 Admin 接口使用 dict + CRUD 字段映射;若后续统一 Schema,再补 spec 字段。

backend/scripts/import_products_from_excel.py:可选在本轮为导入数据留空 spec,非阻塞。


实施顺序

  1. 数据库迁移 + Model + CRUD 历史快照
  2. Admin 商品 CRUD 接口(含空串 → NULL)
  3. 前端公共 formatMeasureUnitSpec + 商品管理页(表单/列表/导出)+ 查看弹窗
  4. Admin 关联接口补 spec
  5. 各业务页列表列 + 行内文案
  6. 手工回归(见下)

测试建议(手工验证)

商品管理

  1. 新增商品:只填计量/单位,规格留空 → 保存成功;列表规格列为 —。
  2. 新增商品:填规格「礼盒装」→ 列表三列分别为计量、单位、礼盒装。
  3. 编辑商品:修改/清空规格 → 保存后列表与查看弹窗一致。
  4. 复制商品:规格应随详情复制(与计量/单位一致)。
  5. 导出 Excel:含「规格」列。

展示格式

  1. 查看弹窗 Hero:500 克(礼盒装);无规格时 500 克。
  2. 订单明细列表:独立「规格」列;已配送文案 10×500 克(礼盒装)。

边界

  1. 规格 100 字截断/校验(若前端 maxlength)。
  2. 规格含特殊字符(括号、空格)原样展示,不再嵌套括号。

风险与遗留

项 说明
历史订单规格 无订单行快照,改商品规格会影响历史订单展示
小程序未覆盖 客户端若需展示规格,需另开需求
查看弹窗旧文案 必须将「规格=计量+单位」改为合并标签,避免与新字段混淆

关联页面路由

  • 商品管理:http://localhost:3006/#/base/product
  • 设计规范:Fresh Mint(docs/web-admin-ui-spec-fresh-mint.md),新增列表列与表单项对齐现有筛选/表单高度(36px)与 label 样式。

实施状态(2026-06-26)

  • Task 1–5:DB 迁移脚本、Model、CRUD、Admin/Delivery API
  • Task 6–9:前端工具函数、商品管理页、查看弹窗
  • Task 10–13:订单、配送、询价报价、结算等业务页
  • Task 14:需在开发环境执行迁移并手工回归