提交
This commit is contained in:
1 parent
3779de12ce
commit
fb85230d25
25 files changed
+6003
-234
No files matched your search
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-05-23
|
||||
@@ -0,0 +1,131 @@
|
||||
## Context
|
||||
|
||||
生产部门当前仅有耗材目录表(`biz_consumable`)和试剂目录表(`biz_reagent`),管理编码、名称、规格、标准用量等基础信息,无库存管理。市场部门和质检部门已有完整的库存管理体系,采用「主表含库存字段 + 变动记录表」的双表模式。
|
||||
|
||||
现有耗材/试剂被 10 张下游关联表(耗材包、工艺、工艺路线、生产任务步骤)引用,但全部使用快照复制(denormalized code/name),无 FK 依赖,对主表加字段零影响。
|
||||
|
||||
### 现有参照实现
|
||||
|
||||
- **市场物料**:`biz_material`(含 stock_qty)+ `biz_material_stock_log`(变动记录,rk/ck,正数)
|
||||
- **质检物料**:`biz_qc_material`(含 stock_qty)+ `biz_qc_material_usage`(变动记录,in/use,正负数,含 task_complete 自动扣减 + revert 回退)
|
||||
|
||||
### 步骤保存挂钩点
|
||||
|
||||
生产任务步骤的耗材/试剂保存入口:
|
||||
- `PUT /production-tasks/{task_id}/steps/{step_id}/reagents` → `replace_step_reagents()`
|
||||
- `PUT /production-tasks/{task_id}/steps/{step_id}/consumables` → `replace_step_consumables()`
|
||||
|
||||
这两个函数使用"软删旧 + 插入新"模式,需在此处加入 diff 结算逻辑。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- 为耗材和试剂增加库存管理能力(库存量、预警量、出入库操作)
|
||||
- 支持手动单个/批量入库出库,参照质检物料模式
|
||||
- 支持生产任务步骤保存时按 actual_usage 自动 diff 结算库存
|
||||
- 变动记录在耗材/试剂页面内以子弹窗和 Tab 形式展示
|
||||
|
||||
**Non-Goals:**
|
||||
- 不新建独立菜单页面(变动记录嵌入现有页面)
|
||||
- 不修改现有耗材/试剂目录字段(只加库存字段)
|
||||
- 不修改下游 10 张关联表
|
||||
- 不实现仓库/库位管理(单库存维度)
|
||||
- 不实现库存预警通知(仅记录 warning_qty 字段)
|
||||
- 不统一耗材/试剂为统一物料表(保持两个独立目录)
|
||||
|
||||
## Decisions
|
||||
|
||||
### Decision 1: 在现有目录表上加库存字段,不新建库存主表
|
||||
|
||||
**选择**:ALTER TABLE `biz_consumable` / `biz_reagent` 新增 `stock_qty`、`warning_qty`、`status` 字段。
|
||||
|
||||
**理由**:
|
||||
- 下游 10 张关联表全是快照复制,加字段零影响
|
||||
- 与市场/质检物料的「主表含库存字段」模式一致
|
||||
- 避免引入目录表→库存表的间接层和数据同步问题
|
||||
- 改动最小:复用现有 CRUD/API/前端,只需扩展
|
||||
|
||||
**替代方案**:新建 `biz_consumable_stock` 表,通过 consumable_id 关联。放弃原因:多一层间接、需维护目录-库存同步、与已有模式不一致。
|
||||
|
||||
### Decision 2: 新建独立的变动记录表
|
||||
|
||||
**选择**:新建 `biz_consumable_stock_log` 和 `biz_reagent_stock_log`,参照 `biz_material_stock_log` 结构。
|
||||
|
||||
**理由**:
|
||||
- 变动记录是独立的关注点,不应混在目录表中
|
||||
- 需要快照字段(code/name)和 before/after 库存
|
||||
- 需要溯源字段(source_type、step_id、task_id、task_no)
|
||||
- 参照 `biz_qc_material_usage` 的 source_type 模式:manual_in / manual_out / step_save
|
||||
|
||||
**关键设计**:参照质检物料采用正负数表示方向(入库正、出库负),同时保留 `change_type` 字段(in/use)。
|
||||
|
||||
### Decision 3: 步骤保存 diff 结算算法
|
||||
|
||||
**选择**:全量回退 + 重算模式(参照质检物料的 revert + deduct 模式)。
|
||||
|
||||
**算法**:
|
||||
1. 查出该步骤当前所有 source_type=step_save 的变动记录
|
||||
2. 按变动记录逐条回退库存(加回扣减量),软删变动记录
|
||||
3. 遍历新的步骤耗材/试剂列表,按 actual_usage 扣减库存,生成新变动记录
|
||||
4. 整体在同一事务中
|
||||
|
||||
**理由**:
|
||||
- 步骤保存频率不高,全量回退+重算简单可靠
|
||||
- 避免复杂的逐项 diff 计算(需处理编码变更等边界情况)
|
||||
- 与质检物料 deduct_for_task() 模式一致,代码可复用
|
||||
|
||||
**替代方案**:逐项 diff(旧用量 - 新用量 = 差额)。放弃原因:需处理编码变更(物料A改成物料B)、处理删除、新增等复杂场景,容易出 bug。全量回退更简单且已验证。
|
||||
|
||||
### Decision 4: 变动记录子弹窗 + Tab 双入口
|
||||
|
||||
**选择**:
|
||||
- 每行操作列「变动记录」按钮 → Dialog 弹窗,按 material_id 筛选
|
||||
- 页面顶部 Tab「全部变动记录」→ Tab 切换视图,展示所有物料的汇总记录
|
||||
|
||||
**理由**:
|
||||
- 不新增菜单位,保持生产管理菜单简洁
|
||||
- 行按钮满足"看某个物料历史"的快速需求
|
||||
- Tab 视图满足"看所有变动流水"的审计需求
|
||||
|
||||
### Decision 5: 挂钩点在 replace_step_reagents/consumables
|
||||
|
||||
**选择**:在 `replace_step_reagents()` 和 `replace_step_consumables()` 函数中,在 soft delete 旧记录之后、insert 新记录之前,执行库存 diff 结算。
|
||||
|
||||
**调用链**:
|
||||
```
|
||||
API: PUT /production-tasks/{task_id}/steps/{step_id}/consumables
|
||||
→ replace_step_consumables(db, step_id, tenant_no, user_id, items)
|
||||
→ 1. 查出步骤信息(step_id → task_id, task_no)
|
||||
→ 2. 调用 diff_settle_step_consumables(db, step_id, task_id, task_no, tenant_no, user_id)
|
||||
→ revert step 的所有 step_save 变动记录(回退库存)
|
||||
→ 3. soft delete 旧步骤耗材子记录
|
||||
→ 4. insert 新步骤耗材子记录
|
||||
→ 5. 调用 deduct_step_consumables(db, step_id, task_id, task_no, tenant_no, user_id, new_items)
|
||||
→ 按 actual_usage 扣减库存,生成 step_save 变动记录
|
||||
→ 6. commit
|
||||
```
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
### Risk: 步骤保存时库存不足
|
||||
**影响**:任务执行保存步骤时,若耗材/试剂库存不足,保存会失败。
|
||||
**缓解**:提供清晰的错误提示("物料「XX」库存不足,当前库存 N,需使用 M"),让用户先去入库。同时前端可在步骤编辑时显示当前库存量供参考。
|
||||
|
||||
### Risk: actual_usage 为空或非数字
|
||||
**影响**:步骤保存时 actual_usage 字段可能为空字符串或非数字。
|
||||
**缓解**:diff 结算时跳过 actual_usage 为空或无法转为正数的条目,仅处理有效数据。
|
||||
|
||||
### Risk: 并发保存同一物料库存
|
||||
**影响**:多个步骤同时保存涉及同一耗材/试剂。
|
||||
**缓解**:使用 `with_for_update()` 行锁,与质检物料一致。
|
||||
|
||||
### Trade-off: 全量回退 vs 逐项 diff
|
||||
**选择**全量回退,牺牲少量性能换取代码简洁性和正确性保证。步骤保存频率低,性能影响可忽略。
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. **数据库迁移**:ALTER TABLE 加字段(有默认值) + CREATE TABLE 建变动记录表
|
||||
2. **后端部署**:模型 → CRUD → API 顺序部署
|
||||
3. **前端部署**:扩展现有页面
|
||||
4. **无需数据迁移**:现有耗材/试剂的 stock_qty 默认为 0,warning_qty 默认为 0,status 默认为 '1'
|
||||
5. **回滚方案**:ALTER TABLE 删除新增字段,DROP TABLE 变动记录表,回滚代码
|
||||
@@ -0,0 +1,34 @@
|
||||
## Why
|
||||
|
||||
生产部门目前【耗材管理】和【试剂管理】仅维护目录数据(编码、名称、规格、单位等),无法追踪库存数量、出入库记录。市场部门和质检部门已有完整的物料库存管理体系(主表含库存字段 + 变动记录表),生产部门需要补齐这一能力,同时生产任务执行过程中的步骤保存需自动扣减耗材/试剂库存。
|
||||
|
||||
## What Changes
|
||||
|
||||
- 在 `biz_consumable` 和 `biz_reagent` 现有目录表上新增库存字段(stock_qty、warning_qty、status)
|
||||
- 新建 `biz_consumable_stock_log` 和 `biz_reagent_stock_log` 两张变动记录表(参照 `biz_material_stock_log` 结构)
|
||||
- 后端新增出入库 API(单个/批量入库出库),含行锁防超卖、出库校验、批量事务回滚
|
||||
- 后端新增生产任务步骤保存时的自动库存 diff 结算(增量扣减/回退)
|
||||
- 前端在现有耗材/试剂管理页面增加:库存列、入库/出库/批量入库/批量出库操作按钮及弹窗
|
||||
- 前端新增变动记录展示:行按钮弹窗(单物料历史)+ 顶部 Tab 全部变动记录视图
|
||||
- 变动记录区分 source_type:manual_in(手动入库)、manual_out(手动出库)、step_save(步骤保存自动扣减/回退)
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `consumable-inventory`: 耗材库存管理 — 库存字段扩展、手动出入库(单个+批量)、批量事务、库存校验、删除保护
|
||||
- `reagent-inventory`: 试剂库存管理 — 同上,独立于耗材的平行实现
|
||||
- `production-step-stock-deduction`: 生产任务步骤保存自动扣减 — diff 结算算法、增量扣减/回退、并发安全、溯源关联(step_id/task_id/task_no)
|
||||
- `material-stock-log-ui`: 变动记录子弹窗 UI — 行按钮单物料弹窗 + 顶部 Tab 全部记录视图、筛选、分页
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
(无需修改现有 spec 的行为要求)
|
||||
|
||||
## Impact
|
||||
|
||||
- **数据库**:`biz_consumable`、`biz_reagent` 各加 3 个字段(ALTER ADD COLUMN,有默认值,零风险);新建 2 张变动记录表
|
||||
- **后端**:修改 `models/models.py`(加字段+新 Model)、新建 `crud/consumable_stock_log.py` 和 `crud/reagent_stock_log.py`、扩展 `crud/consumable.py` 和 `crud/reagent.py` 出入库逻辑、扩展 `api/production.py` 出入库和变动记录 API、修改 `crud/production_task.py` 步骤保存加入 diff 扣减
|
||||
- **前端**:修改 `views/production/consumable/index.vue` 和 `views/production/reagent/index.vue`(增加库存列、出入库弹窗、变动记录 Tab/弹窗)、扩展 `api/production.ts` API 调用
|
||||
- **下游不受影响**:耗材包、工艺库、工艺路线、生产任务步骤等 10 张下游关联表均为快照复制,无 FK 依赖,加字段零影响
|
||||
- **SQL 迁移**:需在 `docs/sql/` 新增迁移脚本
|
||||
@@ -0,0 +1,107 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Consumable inventory fields
|
||||
系统 SHALL 在 `biz_consumable` 表上新增以下库存字段:
|
||||
- `stock_qty` DECIMAL(18,4) NOT NULL DEFAULT 0 — 当前库存量
|
||||
- `warning_qty` DECIMAL(18,4) NOT NULL DEFAULT 0 — 预警库存量
|
||||
- `status` CHAR(1) NOT NULL DEFAULT '1' — 启用/停用状态(1=启用,0=停用)
|
||||
|
||||
后端 SQLAlchemy Model `BizConsumable` SHALL 同步新增对应字段。前端列表和表单 SHALL 展示这些字段。
|
||||
|
||||
#### Scenario: Existing consumables have zero stock after migration
|
||||
- **WHEN** 数据库迁移完成,已有耗材记录存在
|
||||
- **THEN** 所有已有耗材的 stock_qty 为 0,warning_qty 为 0,status 为 '1'
|
||||
|
||||
#### Scenario: Consumable list shows stock info
|
||||
- **WHEN** 用户访问耗材管理列表
|
||||
- **THEN** 列表显示当前库存量、预警库存量、状态列
|
||||
|
||||
### Requirement: Manual stock-in for consumable
|
||||
系统 SHALL 支持对单个耗材进行手动入库操作。入库数量必须大于 0。入库后 stock_qty 增加,同时生成一条变动记录(source_type=manual_in,change_type=in)。
|
||||
|
||||
#### Scenario: Successful stock-in
|
||||
- **WHEN** 用户对库存为 10 的耗材入库 5
|
||||
- **THEN** 耗材 stock_qty 变为 15,生成变动记录 change_qty=5, stock_after=15, source_type=manual_in
|
||||
|
||||
#### Scenario: Stock-in with disabled consumable
|
||||
- **WHEN** 用户对已停用的耗材执行入库
|
||||
- **THEN** 系统拒绝并提示"物料「XX」已停用,无法入库"
|
||||
|
||||
### Requirement: Manual stock-out for consumable
|
||||
系统 SHALL 支持对单个耗材进行手动出库操作。出库数量必须大于 0,且不能超过当前库存。出库必须填写领用人。出库后 stock_qty 减少,同时生成一条变动记录(source_type=manual_out,change_type=use,change_qty 为负数)。
|
||||
|
||||
#### Scenario: Successful stock-out
|
||||
- **WHEN** 用户对库存为 10 的耗材出库 3,领用人为"张三"
|
||||
- **THEN** 耗材 stock_qty 变为 7,生成变动记录 change_qty=-3, stock_after=7, source_type=manual_out, receiver=张三
|
||||
|
||||
#### Scenario: Stock-out exceeds inventory
|
||||
- **WHEN** 用户对库存为 2 的耗材出库 5
|
||||
- **THEN** 系统拒绝并提示"物料「XX」库存不足,当前库存 2,需出库 5"
|
||||
|
||||
#### Scenario: Stock-out without receiver
|
||||
- **WHEN** 用户出库时不填写领用人
|
||||
- **THEN** 系统拒绝并提示"请选择领用人"
|
||||
|
||||
### Requirement: Batch stock-in for consumables
|
||||
系统 SHALL 支持对多个耗材批量入库。所有条目在同一事务内处理,任一条目失败则全部回滚。每条入库记录独立生成变动记录。
|
||||
|
||||
#### Scenario: Successful batch stock-in
|
||||
- **WHEN** 用户批量入库 3 种耗材,数量分别为 5、10、2
|
||||
- **THEN** 3 种耗材库存分别增加,生成 3 条变动记录,全部在同一事务内
|
||||
|
||||
#### Scenario: Batch stock-in with one invalid item
|
||||
- **WHEN** 用户批量入库 3 种耗材,其中一种已停用
|
||||
- **THEN** 全部回滚,没有任何耗材库存变化
|
||||
|
||||
### Requirement: Batch stock-out for consumables
|
||||
系统 SHALL 支持对多个耗材批量出库。批量出库需统一填写领用人和用途。所有条目在同一事务内处理,任一条目失败则全部回滚。
|
||||
|
||||
#### Scenario: Successful batch stock-out
|
||||
- **WHEN** 用户批量出库 3 种耗材,领用人"张三",数量分别为 2、1、3
|
||||
- **THEN** 3 种耗材库存分别减少,生成 3 条变动记录(source_type=manual_out)
|
||||
|
||||
#### Scenario: Batch stock-out with insufficient stock
|
||||
- **WHEN** 用户批量出库 3 种耗材,其中一种库存不足
|
||||
- **THEN** 全部回滚,提示具体哪个物料库存不足
|
||||
|
||||
### Requirement: Delete protection for stocked consumable
|
||||
系统 SHALL 禁止删除库存量大于 0 的耗材。
|
||||
|
||||
#### Scenario: Delete consumable with stock
|
||||
- **WHEN** 用户尝试删除库存为 5 的耗材
|
||||
- **THEN** 系统拒绝并提示"该物料仍有库存,无法删除"
|
||||
|
||||
#### Scenario: Delete consumable with zero stock
|
||||
- **WHEN** 用户删除库存为 0 的耗材
|
||||
- **THEN** 软删除成功(del_flag=1)
|
||||
|
||||
### Requirement: Consumable stock-out disabled status check
|
||||
出库和入库操作 SHALL 检查物料状态,已停用物料不允许出入库。
|
||||
|
||||
#### Scenario: Stock-out with disabled status
|
||||
- **WHEN** 用户对 status='0' 的耗材执行出库
|
||||
- **THEN** 系统拒绝并提示"物料「XX」已停用,无法出库"
|
||||
|
||||
### Requirement: Row-level locking for concurrent stock operations
|
||||
出入库操作 SHALL 使用 `with_for_update()` 行锁,防止并发操作导致库存数据不一致。
|
||||
|
||||
#### Scenario: Concurrent stock-in
|
||||
- **WHEN** 两个用户同时对同一耗材入库
|
||||
- **THEN** 行锁保证两次操作串行执行,库存结果正确
|
||||
|
||||
### Requirement: Consumable stock log table
|
||||
系统 SHALL 新建 `biz_consumable_stock_log` 变动记录表,字段参照 `biz_material_stock_log` 并增加生产溯源字段:
|
||||
- `material_id` — 耗材 ID
|
||||
- `material_code` / `material_name` — 快照字段
|
||||
- `change_type` — in(入库) / use(使用/出库)
|
||||
- `change_qty` — 变动数量(入库正数,出库负数)
|
||||
- `stock_before` — 变动前库存
|
||||
- `stock_after` — 变动后库存
|
||||
- `source_type` — manual_in / manual_out / step_save
|
||||
- `step_id` / `task_id` / `task_no` — 步骤溯源字段(step_save 时有值)
|
||||
- `receiver` / `purpose` — 领用人/用途(manual_out 时有值)
|
||||
- 公共字段
|
||||
|
||||
#### Scenario: Stock log created on stock-in
|
||||
- **WHEN** 耗材入库 5
|
||||
- **THEN** 变动记录 change_type=in, change_qty=5, source_type=manual_in, stock_before=旧库存, stock_after=新库存
|
||||
@@ -0,0 +1,80 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Per-row stock log dialog
|
||||
耗材/试剂管理列表每行操作列 SHALL 有一个「变动记录」按钮,点击弹出 Dialog,标题显示物料名称,列表仅展示该物料的变动记录,支持分页。
|
||||
|
||||
#### Scenario: Open stock log for single material
|
||||
- **WHEN** 用户点击耗材A行的「变动记录」按钮
|
||||
- **THEN** 弹出 Dialog,标题为"耗材A — 变动记录",列表按时间倒序展示该耗材的所有变动记录
|
||||
|
||||
#### Scenario: Stock log dialog pagination
|
||||
- **WHEN** 变动记录超过 20 条
|
||||
- **THEN** Dialog 内列表支持分页
|
||||
|
||||
### Requirement: Tab view for all stock logs
|
||||
耗材管理页面和试剂管理页面 SHALL 各自有一个顶部 Tab「全部变动记录」,切换后展示所有耗材(或试剂)的汇总变动记录列表,支持按物料名称/编码、变动类型、时间范围筛选,支持分页。
|
||||
|
||||
#### Scenario: Switch to all stock logs tab
|
||||
- **WHEN** 用户点击耗材管理页面顶部「全部变动记录」Tab
|
||||
- **THEN** 切换到全部变动记录视图,展示所有耗材的变动记录汇总列表
|
||||
|
||||
#### Scenario: Filter by material name
|
||||
- **WHEN** 用户在全部变动记录 Tab 中搜索物料名称"培养皿"
|
||||
- **THEN** 列表仅展示物料名称包含"培养皿"的变动记录
|
||||
|
||||
#### Scenario: Filter by change type
|
||||
- **WHEN** 用户筛选变动类型为"出库"
|
||||
- **THEN** 列表仅展示 change_type=use 的变动记录(含 manual_out 和 step_save)
|
||||
|
||||
#### Scenario: Filter by time range
|
||||
- **WHEN** 用户选择时间范围 2026-01-01 至 2026-03-31
|
||||
- **THEN** 列表仅展示该时间范围内的变动记录
|
||||
|
||||
### Requirement: Stock log display fields
|
||||
变动记录列表(弹窗和 Tab 视图)SHALL 展示以下字段:
|
||||
- 物料编码(material_code)
|
||||
- 物料名称(material_name)
|
||||
- 变动类型(change_type):入库/出库/步骤扣减,以 `<el-tag>` 展示
|
||||
- 变动数量(change_qty):正数绿色,负数红色
|
||||
- 变动前库存(stock_before)
|
||||
- 变动后库存(stock_after)
|
||||
- 来源(source_type):手动入库/手动出库/步骤保存,以 `<el-tag>` 展示
|
||||
- 关联任务(task_no):步骤扣减时展示任务编号
|
||||
- 领用人(receiver):手动出库时展示
|
||||
- 用途(purpose):手动出库时展示
|
||||
- 操作人(create_by_name)
|
||||
- 操作时间(create_time)
|
||||
|
||||
#### Scenario: Manual out log display
|
||||
- **WHEN** 变动记录为手动出库类型
|
||||
- **THEN** 展示领用人、用途、操作人、时间,来源显示"手动出库"
|
||||
|
||||
#### Scenario: Step save log display
|
||||
- **WHEN** 变动记录为步骤保存类型
|
||||
- **THEN** 展示关联任务编号、操作人、时间,来源显示"步骤扣减",不展示领用人/用途
|
||||
|
||||
### Requirement: Stock log API endpoints
|
||||
后端 SHALL 提供以下变动记录 API:
|
||||
- `GET /web/admin/consumables/{id}/stock-logs` — 单个耗材变动记录(分页)
|
||||
- `GET /web/admin/consumables/stock-logs` — 全部耗材变动记录(分页,支持筛选)
|
||||
- `GET /web/admin/reagents/{id}/stock-logs` — 单个试剂变动记录(分页)
|
||||
- `GET /web/admin/reagents/stock-logs` — 全部试剂变动记录(分页,支持筛选)
|
||||
|
||||
#### Scenario: Single material stock log API
|
||||
- **WHEN** 前端请求 `GET /web/admin/consumables/{id}/stock-logs?page=1&pageSize=20`
|
||||
- **THEN** 返回该耗材的变动记录列表和总数
|
||||
|
||||
#### Scenario: All stock logs API with filters
|
||||
- **WHEN** 前端请求 `GET /web/admin/consumables/stock-logs?keyword=培养皿&changeType=out&startTime=2026-01-01&endTime=2026-03-31`
|
||||
- **THEN** 返回筛选后的变动记录列表和总数
|
||||
|
||||
### Requirement: Status field display in list
|
||||
耗材/试剂管理列表的状态列 SHALL 使用 `<el-tag>` 展示:启用显示 success 类型,停用显示 info 类型。
|
||||
|
||||
#### Scenario: Enabled status display
|
||||
- **WHEN** 耗材状态为启用(status='1')
|
||||
- **THEN** 列表显示绿色 `<el-tag type="success" size="small">` 文本"启用"
|
||||
|
||||
#### Scenario: Disabled status display
|
||||
- **WHEN** 耗材状态为停用(status='0')
|
||||
- **THEN** 列表显示灰色 `<el-tag type="info" size="small">` 文本"停用"
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Step save auto stock deduction
|
||||
系统 SHALL 在生产任务步骤保存耗材/试剂时,根据 `actual_usage` 字段自动扣减对应耗材/试剂的库存。变动记录的 `source_type` SHALL 为 `step_save`。
|
||||
|
||||
#### Scenario: First save with actual usage
|
||||
- **WHEN** 用户首次保存步骤,耗材A 的 actual_usage=2,耗材B 的 actual_usage=3
|
||||
- **THEN** 耗材A 库存扣减 2,耗材B 库存扣减 3,生成 2 条变动记录 source_type=step_save
|
||||
|
||||
#### Scenario: Save with empty actual_usage
|
||||
- **WHEN** 用户保存步骤,某耗材的 actual_usage 为空或为 0
|
||||
- **THEN** 该耗材不扣减库存,不生成变动记录
|
||||
|
||||
#### Scenario: Save with non-numeric actual_usage
|
||||
- **WHEN** 用户保存步骤,某耗材的 actual_usage 为非数字字符串
|
||||
- **THEN** 系统跳过该条目,仅处理 actual_usage 为有效正数的条目
|
||||
|
||||
### Requirement: Step save diff settlement via full revert and re-deduct
|
||||
系统 SHALL 采用全量回退+重算模式处理步骤保存的库存变动:
|
||||
1. 查出该步骤此前所有 source_type=step_save 的变动记录
|
||||
2. 按变动记录逐条回退库存(加回扣减量),软删变动记录
|
||||
3. 遍历新的步骤耗材/试剂列表,按 actual_usage 扣减库存,生成新变动记录
|
||||
|
||||
#### Scenario: Modify usage upward
|
||||
- **WHEN** 步骤首次保存耗材A actual_usage=2,第二次保存改为 actual_usage=3
|
||||
- **THEN** 回退 2(库存+2),重新扣减 3(库存-3),净效果库存-1
|
||||
|
||||
#### Scenario: Modify usage downward
|
||||
- **WHEN** 步骤首次保存耗材A actual_usage=5,第二次保存改为 actual_usage=2
|
||||
- **THEN** 回退 5(库存+5),重新扣减 2(库存-2),净效果库存+3
|
||||
|
||||
#### Scenario: Remove material from step
|
||||
- **WHEN** 步骤首次保存耗材A actual_usage=3 和耗材B actual_usage=2,第二次保存仅保留耗材B actual_usage=2
|
||||
- **THEN** 回退耗材A 的 3 和耗材B 的 2,重新扣减耗材B 的 2。净效果:耗材A 库存+3,耗材B 不变
|
||||
|
||||
#### Scenario: Add new material to step
|
||||
- **WHEN** 步骤首次保存耗材A actual_usage=2,第二次保存新增耗材C actual_usage=4
|
||||
- **THEN** 回退耗材A 的 2,重新扣减耗材A 的 2 和耗材C 的 4。净效果:耗材A 不变,耗材C 库存-4
|
||||
|
||||
### Requirement: Step save stock deduction uses row-level locking
|
||||
diff 结算过程中 SHALL 使用 `with_for_update()` 行锁锁定涉及的耗材/试剂行。
|
||||
|
||||
#### Scenario: Concurrent step saves on same material
|
||||
- **WHEN** 两个步骤同时保存,都涉及耗材A
|
||||
- **THEN** 行锁保证串行执行,库存变动正确
|
||||
|
||||
### Requirement: Step save stock deduction in same transaction
|
||||
diff 结算 SHALL 在步骤耗材/试剂保存的同一数据库事务中执行。库存扣减失败时,步骤保存也回滚。
|
||||
|
||||
#### Scenario: Stock insufficient during step save
|
||||
- **WHEN** 用户保存步骤,耗材A actual_usage=10,但库存仅 3
|
||||
- **THEN** 整个步骤保存回滚,提示"物料「耗材A」库存不足,当前库存 3,需使用 10"
|
||||
|
||||
### Requirement: Stock deduction traceability
|
||||
每条 step_save 类型的变动记录 SHALL 包含以下溯源字段:
|
||||
- `step_id` — 生产任务步骤 ID
|
||||
- `task_id` — 生产任务 ID
|
||||
- `task_no` — 生产任务编号快照
|
||||
|
||||
#### Scenario: Stock log shows task info
|
||||
- **WHEN** 步骤保存触发库存扣减
|
||||
- **THEN** 变动记录包含 step_id、task_id、task_no,可追溯至具体任务和步骤
|
||||
|
||||
### Requirement: Deduction hooks in replace_step_reagents and replace_step_consumables
|
||||
库存 diff 结算 SHALL 在 `replace_step_reagents()` 和 `replace_step_consumables()` 函数中执行,调用时机为软删旧子记录之后、插入新子记录之前。
|
||||
|
||||
#### Scenario: replace_step_consumables triggers deduction
|
||||
- **WHEN** API `PUT /production-tasks/{task_id}/steps/{step_id}/consumables` 被调用
|
||||
- **THEN** 系统在替换步骤耗材的同时执行库存 diff 结算
|
||||
|
||||
### Requirement: Skip deduction for materials not in catalog
|
||||
如果步骤中的耗材编码或试剂编码在 `biz_consumable` / `biz_reagent` 中不存在(可能是手工录入的自定义物料),SHALL 跳过该条目的库存扣减,不报错。
|
||||
|
||||
#### Scenario: Custom material code in step
|
||||
- **WHEN** 步骤保存包含一个 consumable_code 不在 biz_consumable 表中的条目
|
||||
- **THEN** 系统跳过该条目的库存扣减,其他正常条目正常扣减
|
||||
@@ -0,0 +1,75 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Reagent inventory fields
|
||||
系统 SHALL 在 `biz_reagent` 表上新增以下库存字段:
|
||||
- `stock_qty` DECIMAL(18,4) NOT NULL DEFAULT 0 — 当前库存量
|
||||
- `warning_qty` DECIMAL(18,4) NOT NULL DEFAULT 0 — 预警库存量
|
||||
- `status` CHAR(1) NOT NULL DEFAULT '1' — 启用/停用状态(1=启用,0=停用)
|
||||
|
||||
后端 SQLAlchemy Model `BizReagent` SHALL 同步新增对应字段。前端列表和表单 SHALL 展示这些字段。
|
||||
|
||||
#### Scenario: Existing reagents have zero stock after migration
|
||||
- **WHEN** 数据库迁移完成,已有试剂记录存在
|
||||
- **THEN** 所有已有试剂的 stock_qty 为 0,warning_qty 为 0,status 为 '1'
|
||||
|
||||
#### Scenario: Reagent list shows stock info
|
||||
- **WHEN** 用户访问试剂管理列表
|
||||
- **THEN** 列表显示当前库存量、预警库存量、状态列
|
||||
|
||||
### Requirement: Manual stock-in for reagent
|
||||
系统 SHALL 支持对单个试剂进行手动入库操作。入库数量必须大于 0。入库后 stock_qty 增加,同时生成一条变动记录(source_type=manual_in,change_type=in)。
|
||||
|
||||
#### Scenario: Successful stock-in
|
||||
- **WHEN** 用户对库存为 10 的试剂入库 5
|
||||
- **THEN** 试剂 stock_qty 变为 15,生成变动记录 change_qty=5, stock_after=15, source_type=manual_in
|
||||
|
||||
### Requirement: Manual stock-out for reagent
|
||||
系统 SHALL 支持对单个试剂进行手动出库操作。出库数量必须大于 0,且不能超过当前库存。出库必须填写领用人。出库后 stock_qty 减少,同时生成一条变动记录(source_type=manual_out,change_type=use,change_qty 为负数)。
|
||||
|
||||
#### Scenario: Successful stock-out
|
||||
- **WHEN** 用户对库存为 10 的试剂出库 3,领用人"李四"
|
||||
- **THEN** 试剂 stock_qty 变为 7,生成变动记录 change_qty=-3, stock_after=7, source_type=manual_out, receiver=李四
|
||||
|
||||
#### Scenario: Stock-out exceeds inventory
|
||||
- **WHEN** 用户对库存为 2 的试剂出库 5
|
||||
- **THEN** 系统拒绝并提示库存不足
|
||||
|
||||
### Requirement: Batch stock-in for reagents
|
||||
系统 SHALL 支持对多个试剂批量入库。所有条目在同一事务内处理,任一条目失败则全部回滚。
|
||||
|
||||
#### Scenario: Successful batch stock-in
|
||||
- **WHEN** 用户批量入库 3 种试剂,数量分别为 5、10、2
|
||||
- **THEN** 3 种试剂库存分别增加,生成 3 条变动记录,全部在同一事务内
|
||||
|
||||
### Requirement: Batch stock-out for reagents
|
||||
系统 SHALL 支持对多个试剂批量出库。批量出库需统一填写领用人和用途。所有条目在同一事务内处理,任一条目失败则全部回滚。
|
||||
|
||||
#### Scenario: Successful batch stock-out
|
||||
- **WHEN** 用户批量出库 3 种试剂,领用人"李四"
|
||||
- **THEN** 3 种试剂库存分别减少,生成 3 条变动记录
|
||||
|
||||
### Requirement: Delete protection for stocked reagent
|
||||
系统 SHALL 禁止删除库存量大于 0 的试剂。
|
||||
|
||||
#### Scenario: Delete reagent with stock
|
||||
- **WHEN** 用户尝试删除库存为 5 的试剂
|
||||
- **THEN** 系统拒绝并提示"该物料仍有库存,无法删除"
|
||||
|
||||
### Requirement: Row-level locking for reagent stock operations
|
||||
出入库操作 SHALL 使用 `with_for_update()` 行锁,防止并发操作导致库存数据不一致。
|
||||
|
||||
### Requirement: Reagent stock log table
|
||||
系统 SHALL 新建 `biz_reagent_stock_log` 变动记录表,字段结构与 `biz_consumable_stock_log` 一致:
|
||||
- `material_id` — 试剂 ID
|
||||
- `material_code` / `material_name` — 快照字段
|
||||
- `change_type` — in(入库) / use(使用/出库)
|
||||
- `change_qty` — 变动数量(入库正数,出库负数)
|
||||
- `stock_before` / `stock_after` — 变动前后库存
|
||||
- `source_type` — manual_in / manual_out / step_save
|
||||
- `step_id` / `task_id` / `task_no` — 步骤溯源字段
|
||||
- `receiver` / `purpose` — 领用人/用途
|
||||
- 公共字段
|
||||
|
||||
#### Scenario: Stock log created on reagent stock-in
|
||||
- **WHEN** 试剂入库 5
|
||||
- **THEN** 变动记录 change_type=in, change_qty=5, source_type=manual_in, stock_before=旧库存, stock_after=新库存
|
||||
@@ -0,0 +1,56 @@
|
||||
## 1. Database Migration
|
||||
|
||||
- [x] 1.1 编写 SQL 迁移脚本 `docs/sql/production_material_inventory.sql`:ALTER TABLE `biz_consumable` 新增 stock_qty/warning_qty/status 字段,ALTER TABLE `biz_reagent` 新增同上字段,CREATE TABLE `biz_consumable_stock_log`,CREATE TABLE `biz_reagent_stock_log`
|
||||
- [ ] 1.2 在开发数据库执行迁移脚本,验证字段和表创建成功
|
||||
|
||||
## 2. Backend Model & CRUD — Consumable
|
||||
|
||||
- [x] 2.1 在 `backend/app/models/models.py` 中 `BizConsumable` 新增 stock_qty、warning_qty、status 字段;新建 `BizConsumableStockLog` Model(参照 `BizQcMaterialUsage`,增加 step_id/task_id/task_no 字段)
|
||||
- [x] 2.2 扩展 `backend/app/crud/consumable.py`:修改 `_row_to_dict` 返回库存字段,新增 `stock_in()`、`stock_out()`、`batch_stock_in()`、`batch_stock_out()` 函数(参照 `crud/qc_material.py`),修改 `soft_delete` 增加库存检查
|
||||
- [x] 2.3 新建 `backend/app/crud/consumable_stock_log.py`:`list_stock_logs()` 函数(参照 `crud/qc_material_usage.py` 的 `list_usages()`),支持按 material_id、change_type、时间范围筛选
|
||||
|
||||
## 3. Backend Model & CRUD — Reagent
|
||||
|
||||
- [x] 3.1 在 `backend/app/models/models.py` 中 `BizReagent` 新增 stock_qty、warning_qty、status 字段;新建 `BizReagentStockLog` Model(同 BizConsumableStockLog)
|
||||
- [x] 3.2 扩展 `backend/app/crud/reagent.py`:修改 `_row_to_dict` 返回库存字段,新增 `stock_in()`、`stock_out()`、`batch_stock_in()`、`batch_stock_out()` 函数,修改 `soft_delete` 增加库存检查
|
||||
- [x] 3.3 新建 `backend/app/crud/reagent_stock_log.py`:`list_stock_logs()` 函数,支持筛选
|
||||
|
||||
## 4. Backend API — Stock Operations & Log Endpoints
|
||||
|
||||
- [x] 4.1 在 `backend/app/api/production.py` 新增耗材出入库 API:`POST /consumables/{id}/stock-in`、`POST /consumables/{id}/stock-out`、`POST /consumables/batch-stock-in`、`POST /consumables/batch-stock-out`
|
||||
- [x] 4.2 在 `backend/app/api/production.py` 新增耗材变动记录 API:`GET /consumables/{id}/stock-logs`、`GET /consumables/stock-logs`
|
||||
- [x] 4.3 在 `backend/app/api/production.py` 新增试剂出入库 API:`POST /reagents/{id}/stock-in`、`POST /reagents/{id}/stock-out`、`POST /reagents/batch-stock-in`、`POST /reagents/batch-stock-out`
|
||||
- [x] 4.4 在 `backend/app/api/production.py` 新增试剂变动记录 API:`GET /reagents/{id}/stock-logs`、`GET /reagents/stock-logs`
|
||||
|
||||
## 5. Backend — Step Save Auto Deduction
|
||||
|
||||
- [x] 5.1 在 `backend/app/crud/consumable_stock_log.py` 新增 `revert_step_stock_changes()` 函数(参照 `qc_material_usage.py` 的 `revert_task_material_usages`,按 step_id + source_type=step_save 回退库存)
|
||||
- [x] 5.2 在 `backend/app/crud/consumable_stock_log.py` 新增 `deduct_for_step()` 函数(参照 `qc_material_usage.py` 的 `deduct_for_task`,按 actual_usage 扣减库存)
|
||||
- [x] 5.3 在 `backend/app/crud/reagent_stock_log.py` 新增 `revert_step_stock_changes()` 和 `deduct_for_step()` 函数(同上,试剂版)
|
||||
- [x] 5.4 修改 `backend/app/crud/production_task.py` 的 `replace_step_reagents()` 函数:在 soft delete 旧记录后、insert 新记录后,调用 `revert_step_stock_changes()` + `deduct_for_step()`
|
||||
- [x] 5.5 修改 `backend/app/crud/production_task.py` 的 `replace_step_consumables()` 函数:同上逻辑
|
||||
- [x] 5.6 修改 `backend/app/api/production.py` 中 `replace_task_step_reagents` 和 `replace_task_step_consumables` API,传入 task_id/task_no 以支持溯源
|
||||
|
||||
## 6. Frontend — Consumable Page Enhancement
|
||||
|
||||
- [x] 6.1 扩展 `web-admin/src/api/production.ts`:新增耗材出入库 API 调用函数(stockIn、stockOut、batchStockIn、batchStockOut、getStockLogs、getAllStockLogs)
|
||||
- [x] 6.2 修改 `web-admin/src/views/production/consumable/index.vue`:列表新增库存量、预警量、状态列,表单新增库存字段
|
||||
- [x] 6.3 在耗材页面新增操作按钮:单个入库、单个出库弹窗(参照质检物料 `qc-material/index.vue`)
|
||||
- [x] 6.4 在耗材页面新增批量入库、批量出库弹窗(参照质检物料批量操作)
|
||||
- [x] 6.5 在耗材页面行操作列新增「变动记录」按钮 → Dialog 弹窗,展示单物料变动记录列表
|
||||
- [x] 6.6 在耗材页面新增顶部 Tab「全部变动记录」视图,展示所有耗材汇总变动记录,支持筛选和分页
|
||||
|
||||
## 7. Frontend — Reagent Page Enhancement
|
||||
|
||||
- [x] 7.1 扩展 `web-admin/src/api/production.ts`:新增试剂量出入库 API 调用函数
|
||||
- [x] 7.2 修改 `web-admin/src/views/production/reagent/index.vue`:列表新增库存量、预警量、状态列,表单新增库存字段
|
||||
- [x] 7.3 在试剂页面新增操作按钮:单个入库、单个出库弹窗
|
||||
- [x] 7.4 在试剂页面新增批量入库、批量出库弹窗
|
||||
- [x] 7.5 在试剂页面行操作列新增「变动记录」按钮 → Dialog 弹窗
|
||||
- [x] 7.6 在试剂页面新增顶部 Tab「全部变动记录」视图
|
||||
|
||||
## 8. Verification
|
||||
|
||||
- [ ] 8.1 后端验证:启动后端服务,通过 Swagger 测试所有新增 API(CRUD + 出入库 + 变动记录)
|
||||
- [ ] 8.2 前端验证:启动前端开发服务,手动测试耗材/试剂页面的所有新增功能(入库、出库、批量操作、变动记录弹窗、变动记录 Tab)
|
||||
- [ ] 8.3 步骤扣减验证:创建生产任务,执行步骤保存,验证耗材/试剂库存自动扣减,修改用量后验证 diff 结算正确性
|
||||
@@ -575,23 +575,20 @@ import { Plus, Close, Check, Search, RefreshLeft, Edit, Delete, View } from '@el
|
||||
</el-table-column>
|
||||
```
|
||||
|
||||
**多状态映射标准写法(使用辅助函数):**
|
||||
**多状态映射标准写法(使用状态标签注册表):**
|
||||
|
||||
```html
|
||||
<el-table-column prop="workOrderStatus" label="状态" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="statusTagType(row.workOrderStatus)" size="small">
|
||||
{{ statusLabel(row.workOrderStatus) }}
|
||||
<el-tag :type="workOrderStatusTagType(row.workOrderStatus)" size="small">
|
||||
{{ workOrderStatusLabel(row.workOrderStatus) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
```
|
||||
|
||||
```typescript
|
||||
function statusTagType(s: string): string {
|
||||
const m: Record<string, string> = { ywc: 'success', ypz: 'warning', dpz: 'info' }
|
||||
return m[s] || 'info'
|
||||
}
|
||||
import { workOrderStatusLabel, workOrderStatusTagType } from '@/utils/status-tag-registry'
|
||||
```
|
||||
|
||||
#### 2. 查看弹窗(SystemViewDialog)中的 Tag 展示
|
||||
@@ -615,13 +612,15 @@ export interface ViewField {
|
||||
**标准写法:**
|
||||
|
||||
```typescript
|
||||
import { taskStatusLabel, taskStatusTagType, qcTaskConclusionLabel, qcTaskConclusionTagType } from '@/utils/status-tag-registry'
|
||||
|
||||
const viewSections = computed<ViewSection[]>(() => [
|
||||
{
|
||||
title: '任务信息',
|
||||
fields: [
|
||||
{ label: '任务号', value: d.taskNo },
|
||||
{ label: '状态', value: statusLabel(d.status), tagType: d.status === 'ywc' ? 'success' : d.status === 'jxz' ? 'warning' : 'info' },
|
||||
{ label: '检测结论', value: conclusionLabel(d.conclusion), tagType: d.conclusion === 'tg' ? 'success' : d.conclusion === 'btg' ? 'danger' : 'info' },
|
||||
{ label: '状态', value: taskStatusLabel(d.status), tagType: taskStatusTagType(d.status) },
|
||||
{ label: '检测结论', value: qcTaskConclusionLabel(d.conclusion), tagType: qcTaskConclusionTagType(d.conclusion) },
|
||||
// 普通字段不加 tagType,仍以纯文本展示
|
||||
{ label: '备注', value: d.remark || '—', span: true },
|
||||
]
|
||||
@@ -637,12 +636,16 @@ const viewSections = computed<ViewSection[]>(() => [
|
||||
|
||||
```html
|
||||
<dd>
|
||||
<el-tag :type="row.acceptConclusion === 'js' ? 'success' : 'danger'" size="small">
|
||||
<el-tag :type="acceptConclusionTagType(row.acceptConclusion)" size="small">
|
||||
{{ acceptConclusionLabel(row.acceptConclusion) }}
|
||||
</el-tag>
|
||||
</dd>
|
||||
```
|
||||
|
||||
```typescript
|
||||
import { acceptConclusionLabel, acceptConclusionTagType } from '@/utils/status-tag-registry'
|
||||
```
|
||||
|
||||
#### 4. Tag 类型语义色映射规范
|
||||
|
||||
| Tag Type | 颜色 | 适用语义 | 示例 |
|
||||
@@ -655,9 +658,72 @@ const viewSections = computed<ViewSection[]>(() => [
|
||||
|
||||
**对于启用/停用的状态字段**,SHALL 使用 `success`(启用)和 `info`(停用),SHALL NOT 使用 `danger` 表示停用状态。
|
||||
|
||||
#### 5. 公共标签映射函数
|
||||
#### 5. 状态标签注册表(集中定义 label + tagType)
|
||||
|
||||
多个页面复用的状态标签映射 SHALL 抽取为公共工具函数(如 `utils/protocol-payment-labels.ts`、`utils/qc-labels.ts`),同时提供 `xxxLabel`(文本)和 `xxxTagType`(tag type)两个配套函数。仅单个页面使用的状态标签 SHALL 在页面 `<script setup>` 中定义。
|
||||
`web-admin` 中所有状态、结论、类型等枚举字段的 **文案(label)与 Tag 语义色(tagType)SHALL 在单一注册表中集中定义**,权威文件为 `web-admin/src/utils/status-tag-registry.ts`。
|
||||
|
||||
**核心原则:**
|
||||
|
||||
1. **同一枚举值全局一致**:同一枚举 code 在列表、查看弹窗、导出、嵌套子列表等任何展示位置,SHALL 使用相同的 label 与 tagType;SHALL NOT 在页面内用三元表达式或局部 `Record` 重复定义映射。
|
||||
2. **成对导出**:每个枚举域 SHALL 导出 registry 对象及配套的 `xxxLabel(code)`、`xxxTagType(code)` 两个 getter;未知 code 时 label 回退为原值、`tagType` 回退为 `'info'`。
|
||||
3. **新增枚举先注册后使用**:新增业务枚举时,SHALL 先在 `status-tag-registry.ts` 追加 registry 与 getter,再在页面/组件中 import 使用;SHALL NOT 直接在 `.vue` 中写 `if/else` 或内联映射。
|
||||
4. **域内分组**:registry 内按业务域分区维护(协议/存储、质检、渠道、订单、系统通用等),便于检索与 code review。
|
||||
|
||||
**注册表结构(已实现):**
|
||||
|
||||
```typescript
|
||||
/** el-tag 的 type 属性值 */
|
||||
export type TagType = 'success' | 'warning' | 'danger' | 'info' | '' | 'primary'
|
||||
|
||||
export interface StatusItem {
|
||||
label: string
|
||||
tagType: TagType
|
||||
}
|
||||
|
||||
export type StatusRegistry = Record<string, StatusItem>
|
||||
|
||||
export const protocolStatus: StatusRegistry = {
|
||||
dsx: { label: '待生效', tagType: 'info' },
|
||||
sxz: { label: '生效中', tagType: 'success' },
|
||||
ygq: { label: '已过期', tagType: 'warning' },
|
||||
yzf: { label: '已作废', tagType: 'danger' },
|
||||
zky: { label: '转科研', tagType: 'primary' }
|
||||
}
|
||||
export const protocolStatusLabel = createLabelGetter(protocolStatus)
|
||||
export const protocolStatusTagType = createTagTypeGetter(protocolStatus)
|
||||
```
|
||||
|
||||
**页面使用方式:**
|
||||
|
||||
```typescript
|
||||
import { protocolStatusLabel, protocolStatusTagType } from '@/utils/status-tag-registry'
|
||||
```
|
||||
|
||||
```html
|
||||
<el-tag :type="protocolStatusTagType(row.status)" size="small">
|
||||
{{ protocolStatusLabel(row.status) }}
|
||||
</el-tag>
|
||||
```
|
||||
|
||||
**兼容 re-export(非新增入口):**
|
||||
|
||||
`utils/protocol-payment-labels.ts`、`utils/qc-labels.ts` 等历史文件 MAY 继续 re-export 注册表中的 getter 以保持既有 import 路径;**新增枚举 SHALL 直接写入 `status-tag-registry.ts`**,SHALL NOT 再新建独立的 label/tag 映射文件。
|
||||
|
||||
**禁止写法:**
|
||||
|
||||
- 页面 `<script setup>` 内定义 `function xxxTagType()` / `const xxxMap = { ... }` 映射已在或应在注册表中的枚举
|
||||
- 查看弹窗 `ViewField.tagType` 使用内联三元表达式(如 `d.status === 'ywc' ? 'success' : ...`)替代 registry getter
|
||||
- 同一枚举在不同页面使用不同 tagType
|
||||
|
||||
#### Scenario: 新增业务枚举的状态展示
|
||||
|
||||
- **WHEN** 新增页面需展示新的状态/结论/类型枚举字段
|
||||
- **THEN** 开发者 SHALL 先在 `status-tag-registry.ts` 注册该枚举的 label 与 tagType,并在页面中 import 对应的 `xxxLabel` / `xxxTagType` getter 使用
|
||||
|
||||
#### Scenario: 复用已有枚举的状态展示
|
||||
|
||||
- **WHEN** 页面需展示已在注册表中定义的枚举(如 `protocolStatus`、`taskStatus`、`enableStatus`)
|
||||
- **THEN** 页面 SHALL 直接从 `@/utils/status-tag-registry` import 并使用,SHALL NOT 在页面内重复定义映射
|
||||
|
||||
#### Scenario: 新增列表页包含状态列
|
||||
|
||||
@@ -672,7 +738,7 @@ const viewSections = computed<ViewSection[]>(() => [
|
||||
#### Scenario: Code review Tag 展示
|
||||
|
||||
- **WHEN** PR 修改列表页或查看弹窗中的状态/结论/类型字段展示
|
||||
- **THEN** 审查者 SHALL 确认使用 `<el-tag>` 组件(表格列)或 `tagType` 属性(SystemViewDialog),SHALL NOT 出现纯文本状态展示或自定义 `<span class="tag">`
|
||||
- **THEN** 审查者 SHALL 确认使用 `<el-tag>` 组件(表格列)或 `tagType` 属性(SystemViewDialog),SHALL NOT 出现纯文本状态展示或自定义 `<span class="tag">`;若枚举已在或应进入注册表,SHALL 确认映射来自 `status-tag-registry.ts` 而非页面内联定义
|
||||
|
||||
### Requirement: 嵌套弹窗与子弹窗内列表风格一致
|
||||
|
||||
@@ -701,3 +767,108 @@ const viewSections = computed<ViewSection[]>(() => [
|
||||
- **WHEN** PR 在弹窗、侧栏详情或嵌套面板中新增或修改 `el-table`
|
||||
- **THEN** 审查者 SHALL 确认未使用 `size="small"` / 紧凑字号覆盖,且列表风格与主列表页一致
|
||||
|
||||
### Requirement: web-admin 界面模式索引与参考页选型
|
||||
|
||||
`web-admin` 新增或改造业务页面时,开发者 SHALL 在编码前按 **功能特征 → 模式编号(P01–P27)→ 参考页** 完成界面选型;模式定义、决策表、各模式标准写法与全站页面映射的**详细说明**以 `docs/web-admin-ui-pattern-index.md` 为权威索引,本 Requirement 给出 SHALL 级门禁与摘要。
|
||||
|
||||
**选型流程(SHALL 按序执行):**
|
||||
|
||||
1. 对照下方「功能特征 → 模式」决策表或索引文档 §二,确定模式编号(如 P01、P12)。
|
||||
2. 打开该模式对应的 **Demo 模板页** 或 **业务参考页**(Hash + 源码路径见索引文档 §三)。
|
||||
3. 复制页面骨架与交互模式,替换 API、字段、`menuCode`;**SHALL NOT** 从零设计布局或另起一套类名习惯。
|
||||
4. 枚举字段 SHALL 注册至 `status-tag-registry.ts`(见本 spec「状态标签注册表」Requirement)。
|
||||
|
||||
**官方 Demo 模板(永远优先对照,SHALL 优先于业务页):**
|
||||
|
||||
| 用途 | Hash | 源码 |
|
||||
|------|------|------|
|
||||
| 标准列表 CRUD | `/#/demo/unit` | `web-admin/src/views/system/unit/index.vue` |
|
||||
| 复杂业务列表 | `/#/demo/order` | `web-admin/src/views/system/order/index.vue` |
|
||||
| 商品类 + 查看弹窗 | `/#/demo/product` | `web-admin/src/views/system/product/index.vue` |
|
||||
| 树形主数据 | `/#/demo/category` | `web-admin/src/views/system/category/index.vue` |
|
||||
| 用户类 CRUD | `/#/demo/user` | `web-admin/src/views/system/user/index.vue` |
|
||||
| 工作台说明 | `/#/demo/console` | `web-admin/src/views/dashboard/console/index.vue` |
|
||||
|
||||
**功能特征 → 模式编号(决策表):**
|
||||
|
||||
| 功能特征 | 模式 | 首选参考 |
|
||||
|----------|------|----------|
|
||||
| 单表主数据,增删改查 + 分页 + 关键词筛选 | **P01** | `/#/demo/unit` → `basic-info/customer` |
|
||||
| 列表多状态,操作按钮随状态变化 | **P02** | `/#/demo/order` → `cell-storage/protocol-audit` |
|
||||
| 树形结构,父子级联,无分页 | **P03** | `/#/demo/category` → `system/dept` |
|
||||
| 左侧组织/分类树 + 右侧分页列表 | **P04** | `/#/demo/user` → `system/user` |
|
||||
| 同页上下两块表(类型 + 明细) | **P05** | `system/dict` |
|
||||
| 主表列表 + 弹窗内维护子表 | **P06** | `basic-info/collection-region` |
|
||||
| 批量生成编号/编码 + 下发/收回 | **P07** | `basic-info/sample-code` |
|
||||
| 列表顶部 Tab 按状态切换(非路由 Tab) | **P08** | `cell-storage/payment-info` |
|
||||
| 只读查看,字段分 Section(≤20 字段) | **P09** | `SystemViewDialog` → `system/express` |
|
||||
| 只读查看,多 Tab/KPI/审核历史/附件 | **P10** | `ProtocolViewDialog` / `DonationViewDialog` |
|
||||
| 简单审核/状态变更(单表单 ~560px) | **P11** | `ProtocolAuditDialog` |
|
||||
| 多分区大表单(Tab 锚点 + 行内子表) | **P12** | `ProtocolFormDialog` / `DonationFormDialog` |
|
||||
| 财务/业务确认(只读明细 + 上传附件) | **P13** | `cell-storage/payment-confirm` |
|
||||
| 主表 + 嵌套配置大弹窗(子资源 CRUD) | **P14** | `basic-info/insurance-company` |
|
||||
| 主表 + 多 Tab 子模块(渠道/协议等) | **P15** | `pilot-service/channel` |
|
||||
| 三栏:资源库 \| 步骤列表 \| 步骤详情 | **P16** | `production/process-route` |
|
||||
| 工单列表 + 派发/执行弹窗链 | **P17** | `production/production-work-order` |
|
||||
| 任务执行(虚拟步骤 + 步骤状态机) | **P18** | `TaskExecutionDialog` + `useTaskStepEditor` |
|
||||
| 主数据 + append-to-body 选择器弹窗 | **P19** | `production/process` |
|
||||
| 库存主数据 + 入库/出库 + 流水只读页 | **P20** | `production/consumable` + `material-stock-log` |
|
||||
| 质检工单/任务(配置检测项) | **P21** | `quality-inspection/qc-work-order` |
|
||||
| 审核列表 + 专用审核弹窗 | **P22** | `quality-inspection/storage-release-review` |
|
||||
| 接收/登记(关联协议样本) | **P23** | `quality-inspection/sample-reception` |
|
||||
| 工作流配置 / 待办 / 审批 | **P24** | `system/workflow/*` |
|
||||
| 独立详情页(非弹窗) | **P25** | `system/user/detail` |
|
||||
| 角色/权限扩展(菜单树/数据范围) | **P26** | `system/role` |
|
||||
| 衍生列表(跳转主业务查看弹窗) | **P27** | `cell-storage/storage-cert` |
|
||||
|
||||
**各模式 SHALL 级要点(摘要):**
|
||||
|
||||
- **P01**:`page-head` → `filter-block` → `list-toolbar`(已选 N 项在前)→ `list-table stripe` → 分页;`dialog-form-layout` 编辑 + `SystemViewDialog` 查看。
|
||||
- **P02**:在 P01 基础上增加状态筛选、操作列多按钮、业务弹窗(审核/状态变更)。
|
||||
- **P03**:`el-table` 树形、无分页;路径展示遵循「树形主数据展示规则」Requirement。
|
||||
- **P04**:左 `el-tree` + 右 P01 列表;组织全路径用 `tree-path.ts`。
|
||||
- **P06**:主从平铺 + 子 API;**SHALL NOT** 误用树形主数据 UI。
|
||||
- **P07**:`/batch-generate`、`/batch-issue`、`/batch-revoke`;批量请求超时 SHALL ≥ 120s。
|
||||
- **P08**:`region-tabs` 切换列表筛选;各 Tab 可有不同批量操作。
|
||||
- **P09**:查看 SHALL 使用 `SystemViewDialog`;状态字段传 `tagType`。
|
||||
- **P10**:复杂查看 SHALL 使用独立 `*ViewDialog` 组件;与编辑弹窗 **同 Tab/section 命名**;只读附件复用 `ProtocolAttachmentReadonly` 等。
|
||||
- **P11/P22**:轻量审核弹窗 width ~560px;结论字段用 registry Tag。
|
||||
- **P12**:重表单 SHALL 拆为独立 `.vue` 组件;`DialogFormBody` + `detail-tabs` + `scroll-to-error`;附件卡片化布局遵循本 spec 附件 Requirement。
|
||||
- **P16**:三栏布局 SHALL 使用 `_route-config.scss` 的 `.route-config-*` 类;新三栏场景 SHALL 复用 `useTaskStepEditor` + `StepDetailPanel`,**SHALL NOT** 复制 `process-route` 内联逻辑。
|
||||
- **P17/P18**:工单/任务流 SHALL 对齐 `production-work-order` 弹窗链与 composable 模式。
|
||||
- **P27**:衍生列表查看 SHALL 打开主业务 ViewDialog(如 `ProtocolViewDialog`),**SHALL NOT** 重复实现完整查看 UI。
|
||||
|
||||
**维护约定:**
|
||||
|
||||
- 新增页面落地后,SHALL 在 `docs/web-admin-ui-pattern-index.md` §四补充「页面 → 模式」映射。
|
||||
- 出现现有 P01–P27 无法覆盖的新交互形态时,SHALL 先在索引文档新增模式编号与参考页,再更新本 Requirement 决策表。
|
||||
|
||||
#### Scenario: 新增标准主数据页
|
||||
|
||||
- **WHEN** 新增单表 CRUD 主数据页(如供应商、配置项)
|
||||
- **THEN** 开发者 SHALL 选定模式 **P01**,以 `/#/demo/unit` 或 `basic-info/customer` 为骨架参考
|
||||
- **THEN** PR 描述或设计说明中 SHALL 注明所选模式编号与参考页路径
|
||||
|
||||
#### Scenario: 新增复杂业务或工单页
|
||||
|
||||
- **WHEN** 新增含多状态操作、工单派发、任务执行或三栏编辑的页面
|
||||
- **THEN** 开发者 SHALL 从 P02、P16、P17、P18、P21 等决策表中选定模式,并对照索引文档 §三对应参考页实现
|
||||
- **THEN** SHALL NOT 仅参照 P01 标准列表强行裁剪复杂交互
|
||||
|
||||
#### Scenario: 新增重表单或专用查看弹窗
|
||||
|
||||
- **WHEN** 新增字段多、需 Tab 分区或 KPI 的业务表单/查看
|
||||
- **THEN** 编辑 SHALL 采用 **P12**(独立 FormDialog 组件);查看字段极多 SHALL 采用 **P10**(专用 ViewDialog)
|
||||
- **THEN** SHALL NOT 在 `index.vue` 内堆叠超过 500 行表单/查看模板
|
||||
|
||||
#### Scenario: Code review 界面模式选型
|
||||
|
||||
- **WHEN** PR 新增或大幅改造 `web-admin` 业务页面
|
||||
- **THEN** 审查者 SHALL 确认 PR 注明模式编号(Pxx)及参考页
|
||||
- **THEN** 页面骨架、工具栏顺序、弹窗类名与参考页一致;若偏离 SHALL 有书面理由
|
||||
|
||||
#### Scenario: 索引文档与 OpenSpec 同步
|
||||
|
||||
- **WHEN** 新增 P28 或更高级别模式,或全站页面映射变更
|
||||
- **THEN** 维护者 SHALL 同步更新 `docs/web-admin-ui-pattern-index.md` 与本 Requirement 决策表
|
||||
|
||||
Reference in new issue
Block a user