跟单合同模块逐步实现计划

5次阅读

共计 7961 个字符,预计需要花费 20 分钟才能阅读完成。

跟单合同模块逐步实现计划

日期:2026-06-02

对应设计文档:docs/merchandiser-production-contract-design.md

目标:按尽量轻量、可验证、低风险的顺序,把“生产单解析 + 采购合同 + 人工报价 + 导出”模块完整落地。

1. 实施原则

  • 新功能独立开发,不改现有备货、销量核对、同步、大屏逻辑。
  • 先落数据库和后端规则,再做前端编辑器。
  • 先满足首期核心链路,再考虑提效增强项。
  • 所有关键节点都要可验证、可回滚、可定位问题。

2. 最终交付范围

首期必须交付:

  • 生产单 Excel 导入、解析、落库
  • 按生产单号或 SKU 查询生产单明细
  • 人工报价 Excel 批量上传、逐行覆盖确认、按 SKU 查询价格
  • 合同模板管理
  • 采购合同新建、保存、复制、作废、打开、导出
  • 合同明细导入生产单数据
  • 合同编辑页图片手动上传
  • 合同导出前自动保存
  • 作废合同只读,禁止再次编辑和导入明细
  • 导入确认使用临时批次存储,不回传整份解析结果

首期不做:

  • 从生产单 Excel 中提取内嵌图片
  • 报价表中携带图片
  • 批量自动配图
  • 外部合同 Excel 反向导入

3. 交付顺序

第 1 步:建表和模型骨架

目标:

  • 先把数据库结构定住,让后续服务、接口、页面都能围绕稳定表结构展开。

执行项:

  • 新增 migration:
  • scm_contract_templates
  • scm_production_orders
  • scm_production_order_items
  • scm_purchase_contracts
  • scm_purchase_contract_items
  • scm_manual_price_quotes
  • scm_import_batches
  • scm_import_batch_rows
  • 为以上表新增 Eloquent Model
  • 在 migration 中落实关键约束:
  • scm_production_orders.production_order_no 唯一
  • scm_purchase_contracts.contract_no 唯一
  • scm_manual_price_quotessku 建索引
  • 在合同表中增加:
  • packing_text
  • sign_text_json
  • ui_version
  • 中立化 sheet_state_json
  • 明确合同状态字段枚举:draftsavedvoid
  • 为临时批次表明确过期策略:
  • pending_confirm 超过 24 小时可清理

验证:

  • php artisan migrate
  • 检查表结构、索引、唯一键是否符合设计

第 1.5 步:中立化 JSON 契约对齐

目标:

  • 在前后端同时开工前,把 sheet_state_json 的最小数据契约对齐,避免后期一边开发一边猜结构。

执行项:

  • 用 10 分钟对齐一版中立 JSON 结构
  • 明确只保存:
  • 单元格坐标
  • 单元格值
  • 基础样式
  • 行高
  • 列宽
  • 合并信息
  • 明确不保存:
  • 表格引擎类名
  • 渲染函数名
  • 前端运行时对象
  • 把契约样例写进设计文档,作为后续实现依据

验证:

  • 前后端都能用同一份样例结构读写一份最小合同

第 2 步:角色与路由入口准备

目标:

  • 在不动现有核心模块的前提下,为新模块单独开入口和权限边界。

执行项:

  • 前端新增 /contracts 路由
  • 路由 meta 支持 adminmerchandiser
  • 导航增加“采购合同”入口
  • 后端新增新模块路由组:
  • /api/merchandiser/production-orders/*
  • /api/merchandiser/contracts/*
  • /api/merchandiser/contract-templates/*
  • /api/merchandiser/manual-price-quotes/*
  • /api/merchandiser/upload-image
  • 新增角色校验逻辑,明确只允许 adminmerchandiser

验证:

  • 非法角色访问返回 403
  • adminmerchandiser 均可进入新页面

第 3 步:合同模板 CRUD

目标:

  • 先把合同抬头模板做出来,后面新建合同才能一键回填。

执行项:

  • 后端实现模板接口:
  • 列表
  • 新增
  • 编辑
  • 设为默认
  • 模板字段至少包含:
  • party_a_*
  • party_b_*
  • 默认包装说明
  • 默认条款
  • 默认备注
  • 签章区固定文字 JSON
  • 前端新增模板管理页面或集成到现有系统设置中的子页面
  • 支持多模板下拉

验证:

  • 能新建多个模板
  • 能切换默认模板
  • 新建合同时可正确回填默认模板内容

第 4 步:生产单导入与覆盖机制

目标:

  • 先把生产单解析和主数据源做稳,这是后续合同导明细的基础。

执行项:

  • 首期不新增 Composer Excel 依赖,优先使用原生 ZipArchive + XML 解析样本 .xlsx
  • 实现 ProductionOrderImportService
  • 解析策略:
  • 读取首个有效工作表
  • 关键字游标定位表头
  • 提取头信息、明细、包装说明、PO、制单人、日期
  • 导入前按 production_order_no 判重
  • 如果重复:
  • 生成 import_batch_id
  • 将解析后的临时结果写入 scm_import_batchesscm_import_batch_rows
  • 返回重复记录列表与旧数据摘要
  • 等待前端只提交确认参数
  • 用户确认后覆盖:
  • 前端提交 batch_id
  • 覆盖生产单头表
  • 重建对应明细表
  • 不回写历史合同

接口建议:

  • POST /production-orders/import
  • POST /production-orders/import-confirm

验证:

  • 正常导入成功
  • 重复生产单触发覆盖提醒
  • 覆盖后生产单数据更新
  • 已生成合同内容不被联动改写

第 5 步:人工报价批量导入与逐行覆盖

目标:

  • 先把价格参考来源做出来,后面合同编辑页才能有价格联想。

执行项:

  • 新增人工报价导入服务 ManualQuoteService
  • 首期沿用原生 ZipArchive + XML 解析样本 .xlsx 报价表
  • 设计导入模板最少列:
  • SKU
  • 不含税单价
  • 实际判重规则:
  • 因为 SKU 已包含颜色信息,所以按 SKU 判重
  • 导入流程:
  • 上传 Excel
  • 解析全部行
  • 识别重复 SKU
  • 生成 import_batch_id
  • 将解析结果和旧价 / 新价差异写入临时批次表
  • 返回重复清单,展示旧价格和新价格
  • 前端逐行勾选需要覆盖的项
  • 前端只提交 batch_id 和待覆盖 SKU 列表
  • 提供查询接口供合同页单价联想使用

接口建议:

  • POST /manual-price-quotes/import
  • POST /manual-price-quotes/import-confirm
  • GET /manual-price-quotes
  • GET /sku-prices?sku=...(合同录入按客户 SKU 列匹配;选中建议后回填客户 SKU)
  • 若客户 SKU 失焦后仍未命中报价,前端提示手工录入,并将单价置为 0.00 后高亮价格格。

验证:

  • 新报价可批量导入
  • 重复报价能展示旧价 / 新价
  • 可逐行选择覆盖
  • 合同页可按 SKU 拉出价格建议

第 5.5 步:临时批次垃圾清理

目标:

  • 防止用户上传后未确认覆盖而造成临时批次表无限堆积。

执行项:

  • 新增一个清理命令,例如 CleanupImportBatchesCommand
  • 清理规则:
  • status = pending_confirm
  • created_atexpires_at 超过 24 小时
  • 同步清理:
  • scm_import_batches
  • scm_import_batch_rows
  • 按当前仓库实际方式,把调度注册到 backend/backend_laravel/routes/console.php
  • 每天凌晨执行一次即可

当前实现:

  • 已新增命令 imports:cleanup-batches
  • 已在 backend/backend_laravel/routes/console.php 注册为每天 02:10 执行
  • 清理范围覆盖 pending_confirm / expired 且已过期的临时批次,并兼容早期无 expires_atpending_confirm 数据

验证:

  • 过期临时批次会被清理
  • 已确认和已应用的批次不会被误删

第 6 步:合同编号服务

目标:

  • 先把合同号生成逻辑独立出来,避免后面合同创建和复制时逻辑分散。

执行项:

  • 实现 ContractSequenceService
  • 规则:
  • 固定前缀:ADL
  • 日期:YYMMDD
  • 两位流水号
  • 示例:ADL26060201
  • 支持创建新合同自动生成
  • 支持复制合同时重新生成
  • 支持手工改号,但保存时必须做唯一性校验
  • 并发控制建议:
  • 优先数据库事务 + 行级锁
  • 不额外引入 Redis 依赖

验证:

  • 同一天连续新建合同号递增
  • 复制合同生成新编号
  • 手工改成重复编号时被阻止

第 7 步:合同后端核心逻辑

目标:

  • 让合同有完整生命周期:新建、保存、复制、作废、导出前读取稳定状态。

执行项:

  • 实现合同接口:
  • 新建
  • 详情
  • 保存
  • 复制
  • 作废
  • 列表
  • 保存内容包括:
  • 合同头信息
  • packing_text
  • sign_text_json
  • 合同明细
  • ui_version
  • 中立化 sheet_state_json
  • merged_cells_json
  • amount_total
  • source_production_order_ids
  • 作废规则:
  • 作废后仍可查看和导出
  • 作废后不可保存
  • 作废后不可再导入生产单明细

验证:

  • 合同可完整保存并再次打开
  • 作废后保存接口返回阻断
  • 作废后导入接口返回阻断
  • 复制合同成功,且新合同号生效

第 8 步:生产单明细导入合同

目标:

  • 打通“查生产单 -> 选明细 -> 导入合同”的主业务链路。

执行项:

  • 实现生产单查询接口:
  • 按生产单号查
  • 按 SKU 查
  • 合同导入逻辑:
  • 勾选生产单明细
  • 转成合同明细结构
  • 保留 production_order_idproduction_order_item_id
  • 合并规则:
  • 同一个 SKU 不同颜色保留多行
  • 仅“货物描述”列做合并显示
  • 数量、单价、总价不合并

验证:

  • 能按生产单号导入
  • 能按 SKU 搜索并导入
  • 同 SKU 不同颜色显示规则正确

第 9 步:图片上传

目标:

  • 满足首期跟单手动配图。

执行项:

  • 实现单图上传接口
  • 文件落盘或对象存储路径要统一
  • 上传时限制单图体积,首期建议不超过 2M
  • 如有条件,在服务端做压缩或格式转换
  • 返回图片 URL
  • 合同明细行保存 image_url
  • 前端表格中支持点击图片单元格上传 / 替换图片
  • 确保图片访问地址支持当前站点域名访问,避免导出时跨域失败

首期范围:

  • 单条手动上传

二期建议:

  • 批量上传图片并按 SKU 自动匹配

当前实现补充:

  • 合同编辑器已补充“批量传图”入口
  • 前端按文件名去扩展名后与合同行 SKU / 客户 SKU 做匹配
  • 单个文件上传成功后,会同步更新所有命中的合同明细行 image_url

验证:

  • 可为单条明细上传图片
  • 图片重新打开合同后仍可显示

第 10 步:表格引擎与导出翻译层 Spike

目标:

  • 在正式投入表格页面开发前,先验证“表格状态 -> exceljs 导出”的可行性。

执行项:

  • 用候选表格方案做一个最小 Demo
  • Demo 至少覆盖:
  • 合并单元格
  • 基础边框和样式
  • 图片单元格
  • 文本编辑
  • 将 Demo 状态翻译为 exceljs 工作簿结构
  • 打开导出的 .xlsx,检查布局和样式是否可接受
  • 明确这个 Demo 只能输出中立 JSON,而不是直接依赖表格引擎内部原始状态

判定规则:

  • 如果 @wolf-table/table 的翻译层复杂度可控,则继续使用
  • 如果翻译层成本过高、样式错位频繁、图片不稳定,则优先切换到更易导出的轻量 DOM 表格方案

第 11 步:前端合同编辑器

目标:

  • 做出可编辑、可保存、可恢复的合同界面。

推荐方案:

  • 先做 @wolf-table/table Spike
  • Spike 通过后再正式接入
  • Spike 不通过时优先选用更易导出的轻量 DOM 表格方案

执行项:

  • 初始化采购合同表格结构
  • 顶部工具栏:
  • 模板选择
  • 合同号
  • 日期
  • 保存
  • 复制
  • 作废
  • 导出
  • 左侧面板:
  • 生产单号 / SKU 搜索
  • 查询结果列表
  • 勾选导入
  • 表格区:
  • 单元格编辑
  • 合并 / 取消合并
  • 图片上传
  • 单价联想
  • 总价自动计算
  • 状态保存:
  • ui_version
  • 中立化 sheet_state_json
  • merged_cells_json

验证:

  • 页面刷新后表格状态可恢复
  • 合并单元格持久化正确
  • 单价输入和自动计算正常

第 12 步:导出

目标:

  • 让用户拿到结构和样式尽量接近原采购单的 .xlsx

执行项:

  • 前端使用 exceljs
  • 导出前检查未保存状态
  • 若有未保存改动:
  • 先自动保存
  • 保存成功后导出
  • 导出内容包含:
  • 抬头
  • 明细
  • 图片链接或图片渲染
  • 合并单元格
  • 边框、字体、签章位置
  • 图片下载实现要求:
  • 若使用 fetch,必须读取 arrayBuffer()
  • 若使用 axios,必须显式设置 responseType: 'arraybuffer'
  • 必须对单张图片失败做 catch
  • 单张图片失败时仅跳过该图片,不中断整份合同导出
  • 大图和多图场景要做压力测试,观察浏览器内存占用
  • 若前端导出在多图场景下不稳定,记录为二期后端导出改造项

验证:

  • 未保存状态下点击导出,系统先保存再导出
  • 导出文件打开后布局接近 采购单.xls

第 13 步:测试与回归

目标:

  • 在不伤现有业务的前提下,把新模块测到可上线。

后端测试:

  • 生产单解析测试
  • 生产单重复覆盖测试
  • 人工报价重复覆盖测试
  • import_batch_id 临时批次测试
  • 合同号生成测试
  • 合同号重复校验测试
  • 合同复制测试
  • 合同作废后只读测试
  • 作废合同禁止导入测试
  • 角色访问控制测试
  • ui_version 持久化测试
  • 过期临时批次清理测试

当前实现补充:

  • 已补充 ui_version / sheet_state_json / merged_cells_json 持久化测试
  • 已补充 imports:cleanup-batches 命令测试
  • 已补充合同模块角色中间件测试

前端验证:

  • npm run build
  • 合同页手工联调:
  • 模板回填
  • 生产单查询导入
  • 图片上传
  • 单价联想
  • 合并单元格
  • 自动保存后导出
  • 多图合同导出稳定性
  • 单张图片失效时的导出容错

回归检查:

  • 登录
  • 用户角色判断
  • 原有 Dashboard / Sales Check / System 页面无回归

4. 最终上线顺序

  1. 提交 migration 和后端基础服务
  2. 跑本地测试和前端构建
  3. 合并并推送分支
  4. 服务器执行 migrate --force
  5. 发布前端构建产物
  6. optimize:clear
  7. 手工验证新模块主链路
  8. 如前端导出在多图场景失败,转入后端导出二期方案

5. 实现完成判定

满足以下条件即可认为首期完成:

  • 跟单和 Admin 都能进入合同页
  • 生产单可导入、重复可覆盖
  • 报价可批量上传、重复可逐行覆盖
  • 合同可新建、保存、复制、作废
  • 合同可导入生产单明细
  • 合同可手动上传图片
  • 合同可先保存后导出 .xlsx
  • 作废合同不可再编辑和导入
  • 现有 SCM 其他功能无明显回归

6. 2026-06-03 实现补充

  • 合同编辑器首期继续复用现有 Element Plus 表格,不额外引入新的前端表格引擎。
  • 描述合并的持久化结构正式定为:
  • [{column: 'spec_description', start_line_no, end_line_no}]
  • 导出方案正式采用前端 exceljs,并在导出前自动调用合同保存接口,保证数据库与导出文件一致。
  • 导出中的图片读取统一走:
  • /api/contract-images?path=...

这样可兼容当前站点根目录与 Laravel 子目录不一致的部署结构。

7. 2026-06-03 前端联想补充

  • 合同编辑器在 SKU 录入后,会自动查询人工报价。
  • 当某个 SKU 仅命中 1 条报价且当前行尚未录入价格时,系统自动回填单价并重算总价。
  • 当某个 SKU 命中多条报价时,仍保留手动展开和选择报价的交互。
  • 从生产单导入到合同的新行,也按同样规则尝试自动回填单价,但不会覆盖已有手填价格。

8. 2026-06-03 合同录入页 Excel 化全屏改造计划

第 1 步:路由与页面外壳切换

目标

  • 新增全屏编辑页路由与独立页面外壳,把合同编辑入口从弹窗切到 URL 可直达页面。

涉及范围

  • 前端路由
  • 合同列表页跳转逻辑
  • 新增全屏编辑页空壳

实现要点

  • 新增全屏编辑页路由:/contracts/:id/edit
  • 新增页面容器 ContractsEditView
  • 合同列表页的“查看 / 编辑”入口改为路由跳转
  • 新建草稿成功后自动跳转到该页面
  • 页面先只做空壳:
  • Top Bar
  • Ribbon 占位
  • Sheet Viewport 占位
  • 暂时继续保留原 ContractEditorDialog,作为回退参考,不删除

验证点

  • 能从合同列表进入全屏页
  • 返回列表正常
  • 多个合同可分别打开不同 URL

完成后停下等待测试

  • 本步只验证路由切换与全屏页面壳,不接合同详情数据链路。

第 2 步:数据加载与页面级操作迁移

目标

  • 将合同详情加载与保存 / 导出 / 复制 / 作废操作迁移到全屏页面容器。

涉及范围

  • ContractsEditView
  • 合同详情接口调用
  • 页面级 Top Bar 动作区

实现要点

  • 把原弹窗里的合同详情加载逻辑迁移到 ContractsEditView
  • 页面级接入:
  • 保存
  • 导出
  • 复制
  • 作废
  • Top Bar 显示:
  • 合同号
  • 状态
  • 返回按钮
  • 保存 / 导出 / 复制 / 作废
  • 先仍使用原弹窗内容作为页面主体嵌入,确认页面级数据链路正确

验证点

  • 全屏页中保存、复制、作废、导出都仍可用
  • 现有接口完全不变

完成后停下等待测试

  • 本步不改工作表样式,只完成全屏页的数据链路承接。

第 3 步:移动端与桌面端组件分发

目标

  • 为后续桌面工作表与移动表单分离打基础。

涉及范围

  • ContractsEditView
  • 桌面 / 移动子组件

实现要点

  • ContractsEditView 顶层引入 isMobile = window.innerWidth <= 768
  • 按设备动态加载两个组件:
  • ContractSheetDesktop
  • ContractEditorMobile
  • ContractEditorMobile 先直接复用当前弹窗主体逻辑对应的录入布局
  • 桌面端先渲染一个最小 sheet 占位版

验证点

  • 桌面端进入 sheet 视图
  • 移动端仍是旧表单式布局
  • resize 后切换逻辑正确

完成后停下等待测试

  • 本步只完成组件分发,不进入静态模板排版。

第 4 步:桌面工作表静态骨架

目标

  • 做出与模板接近的全屏静态合同工作表外观。

涉及范围

  • ContractSheetDesktop
  • sheet 布局常量

实现要点

  • 新增 contractSheetLayout 常量文件,固定:
  • 9 列宽
  • 顶部区块
  • 明细区块
  • 备注区块
  • 签章区块
  • ContractSheetDesktop 用 CSS Grid 做出整张合同静态骨架
  • 先只渲染:
  • 抬头文本
  • 合同号 / 日期
  • 甲乙方信息
  • 包装说明
  • 备注区
  • 签章区
  • 明细表头
  • 此步不接编辑能力,只做模板外观对齐

验证点

  • 页面视觉上已明显接近 purchase-contract-template.xlsx
  • 横向滚动与满屏布局正常

完成后停下等待测试

  • 本步只验静态外观,不接明细数据。

第 5 步:明细区改为工作表渲染

目标

  • 将桌面端明细区从 el-table 改为工作表网格。

涉及范围

  • ContractSheetDesktop
  • 明细行渲染
  • gutter 区

实现要点

  • 移除桌面端对 el-table 的依赖,改为工作表明细网格
  • 引入左侧 gutter:
  • 行号
  • 勾选
  • 删除
  • 来源信息
  • gutter 不属于模板列,但要 sticky 固定
  • 明细列固定为:
  • 工厂货号
  • 客户号
  • 图片
  • 货物规格描述
  • 数量
  • 单价
  • 总价
  • 备注
  • 默认单元格全部先渲染成静态文本

验证点

  • 明细数量、删除、新增展示正确
  • gutter sticky 正常
  • 明细表头 sticky 正常

完成后停下等待测试

  • 本步只完成静态明细展示,不进入单元格编辑。

第 6 步:Cell Editor 模式

目标

  • 让桌面工作表具备轻量、可控的单元格编辑能力。

涉及范围

  • ContractSheetDesktop
  • 单元格交互状态
  • 轻量 editor

实现要点

  • 为桌面工作表引入:
  • selectedCell
  • editingCell
  • 单元格交互规则固定:
  • 单击选中
  • 双击 / Enter / F2 进入编辑
  • Esc 取消编辑
  • 直接键入进入覆盖编辑
  • 顶部少量字段可常驻 editor
  • 明细区只允许单个活跃 editor,其他格都保持静态文本
  • 价格、数量、备注、描述分别用适合的轻量 editor

验证点

  • 单元格选中与编辑切换符合预期
  • 大量明细时输入不明显卡顿

完成后停下等待测试

  • 本步不接键盘导航自动移动。

第 7 步:键盘导航与自动滚动

目标

  • 补齐类 Excel 的核心键盘导航与可视区跟随体验。

涉及范围

  • ContractSheetDesktop
  • 键盘事件分发
  • 单元格焦点滚动

实现要点

  • 在桌面工作表里统一接管键盘事件
  • 对以下按键执行 preventDefault()
  • Tab / Shift+Tab
  • Enter
  • ArrowUp / Down / Left / Right
  • F2
  • 基于布局定义计算下一个可编辑单元格
  • 焦点切换后调用目标单元格:
  • scrollIntoView({block: 'nearest', inline: 'nearest'})
  • 合并描述后的被吞并格不参与焦点导航

验证点

  • 连续按 Enter 可逐行向下
  • 离开可视区后自动滚动跟随
  • Tab 不会跳到顶部按钮

完成后停下等待测试

  • 本步不接粘贴与校验。

第 8 步:导入、合并、图片、单价联想回接

目标

  • 把现有合同明细相关能力全部挂回全屏工作表。

涉及范围

  • ContractsEditView
  • ContractSheetDesktop
  • 导入与图片入口

实现要点

  • 把现有能力全部回接到全屏 sheet:
  • 生产单导入
  • 批量传图
  • 单图上传
  • 单价联想
  • 单条报价自动回填
  • 描述合并 / 取消合并
  • 导入入口放在 Ribbon
  • 图片显示在图片格内
  • 合并描述继续使用现有 merged_cells_json

验证点

  • 旧功能在新 UI 下全部可用
  • 合并描述在工作表中显示正确

完成后停下等待测试

  • 本步不做单格粘贴和前端校验强化。

第 9 步:单格粘贴与轻量校验

目标

  • 增强类 Excel 体验,并补齐紧凑式错误反馈。

涉及范围

  • ContractSheetDesktop
  • 粘贴事件
  • 前端校验与错误样式

实现要点

  • 在工作表容器监听 paste
  • 若当前是 selected 且不在 editing
  • 把纯文本写入当前单元格
  • 触发格式化和联动计算
  • 加入前端最小校验:
  • 合同号必填
  • 日期必填
  • SKU 必填
  • 数量 / 价格非负
  • 错误展示方式固定:
  • 红色边框
  • 红色角标
  • tooltip 文案

验证点

  • Ctrl+V 对单格有效
  • 校验错误不撑高行

完成后停下等待测试

  • 本步不接 dirty 深比较与离开拦截。

第 10 步:Dirty 深比较与离开拦截

目标

  • 防止全屏页误返回、误刷新导致长合同录入内容丢失。

涉及范围

  • 前端依赖
  • ContractsEditView
  • 路由离开拦截

实现要点

  • 新增 lodash.isequal
  • 引入 sanitizePayload(),比较前抹平:
  • ''
  • null
  • undefined
  • 数值字符串格式
  • 基于“当前标准化 payload”与“最近一次成功加载 / 保存快照”计算 dirty
  • 增加:
  • onBeforeRouteLeave
  • beforeunload

验证点

  • 纯聚焦 / 失焦不触发假 dirty
  • 改过内容后返回 / 刷新会拦截

完成后停下等待测试

  • 本步不处理层级与全局收尾。

第 11 步:层级与收尾

目标

  • 补齐层级体系,清理旧入口,并完成文档闭环。

涉及范围

  • 全屏页样式层级
  • 浮层挂载方式
  • 相关文档

实现要点

  • 统一 z-index 常量:
  • 单元格 1
  • 选中 / 编辑 2
  • gutter 10
  • sticky 表头 11
  • 左上交叉区 12
  • Top Bar / Ribbon 20
  • 日期、下拉、价格建议层一律 append-to-body
  • 清理桌面端旧弹窗入口依赖
  • 更新文档:
  • docs/merchandiser-contract-implementation-plan.md
  • docs/merchandiser-production-contract-design.md
  • docs/runbooks/troubleshooting.md

验证点

  • 没有遮挡、错层、浮层被裁切问题
  • 主链路回归通过

完成后停下等待测试

  • 该步完成后,进入整体验收阶段。

2026-06-03 补充:生产单明细搜索交互收口

  • 导入生产单明细改为统一关键词搜索。
  • 删除前端“按生产单号 / 按 SKU”切换控件。
  • 同一输入框直接支持:
  • 生产单号
  • 工厂 SKU
  • 客户 SKU
  • 后端接口保持同一路径,搜索逻辑改为统一匹配三个字段,并保持大小写不敏感。

2026-06-04 Step 11 finalization

  • Unified full-page contract editor layer constants:
  • cell 1
  • active/editing 2
  • gutter 10
  • sticky header 11
  • sticky corner 12
  • Top Bar / Ribbon 20
  • Desktop price suggestion panel now renders through Teleport to="body".
  • Floating panel position now resyncs on selection change, edit-state change, scroll, and resize.
  • Full-page desktop flow no longer exposes the old contract-level copy action; the hidden legacy editor remains only as a save/export bridge.
  • Step 11 closes the structural refactor and moves the workstream into acceptance / bug-fix mode.

2026-06-04 Void contract delete rule

  • Void contracts can now be deleted from the contract draft list and the full-page editor.
  • Deletion is restricted to status=void only.
  • Delete removes the contract header and all contract detail rows together.

2026-06-04 合同页面重排专项计划入口

  • 本轮合同工作台重排与功能调整已拆成专项计划,主文档位于:
  • E:\programdata\Seafile\AI\scm_hbdWC\scm_merge_integration\docs\contract-page-redesign-plan.md
  • 后续按专项计划逐步实施,不在本文件继续展开逐步细节。
  • 专项计划覆盖范围包括:
  • 标题级点击切换器
  • 合同搜索与分页
  • 生产单多文件导入与 .xls 支持
  • 已导入生产单搜索、分页、查看
  • 报价查询分页与图片上传
  • 合同图片 / 报价图片超限自动压缩
正文完
 0
hermes
版权声明:本站原创文章,由 hermes 于2026-06-06发表,共计7961字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。