From a4a0b41ac75be2fa7da3c12e43cd5f6233c6bbb4 Mon Sep 17 00:00:00 2001 From: zhouz <> Date: Wed, 22 Jul 2026 09:19:46 +0800 Subject: [PATCH 01/28] =?UTF-8?q?docs:=20=E5=87=BA=E5=BA=93=E5=8D=95?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E9=A1=B5=E9=9D=A2=E8=AE=BE=E8=AE=A1=E8=A7=84?= =?UTF-8?q?=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2026-07-22-outbound-create-design.md | 256 ++++++++++++++++++ 1 file changed, 256 insertions(+) create mode 100644 docs/superpowers/specs/2026-07-22-outbound-create-design.md diff --git a/docs/superpowers/specs/2026-07-22-outbound-create-design.md b/docs/superpowers/specs/2026-07-22-outbound-create-design.md new file mode 100644 index 00000000..b41f0264 --- /dev/null +++ b/docs/superpowers/specs/2026-07-22-outbound-create-design.md @@ -0,0 +1,256 @@ +# 出库单新增页面设计 + +## 概述 + +将出入库单主表(`iostorinv`)的新增弹窗从简单表单重构为出库单新增页面,包含主表表单 + 出库明细 table。参考原型图 `.image/出库新增.png`。 + +### 范围 + +- 仅涉及**出库**场景(`ioType = "OUT"`) +- 后端:新增 `createWithDetails` 接口,新增库存查询 Mapper SQL +- 前端:重构 `form.vue`,新增库存选择弹窗和手动新增汇总弹窗 +- **不新增数据库字段** + +--- + +## 主表字段映射 + +| 页面字段 | 数据来源/逻辑 | 对应 DO 字段 | 必填 | 可编辑 | +|---------|-------------|-------------|------|-------| +| 仓库 | `wms_bsrealstorattr` 启用列表 | `storId / storCode / storName` | 是 | 是 | +| 业务类型 | 字典 `out_bill_type` | `billType` | 是 | 是 | +| 单据号 | `CodeGenApi.generate("IO_CODE")` | `billCode` | 是 | 否(自动生成) | +| 业务日期 | 默认当天 | `bizDate` | 是 | 是 | +| 单据状态 | 固定"生成" | `billStatus` | 是 | 否 | +| 明细数 | `detailTable.length` | `detailCount` | 是 | 否(自动联动) | +| 总重量 | `sum(detail.planQty)` | `totalWeight` | 是 | 否(自动联动) | +| 备注 | 手动输入 | `remark` | 否 | 是 | +| 出入类型 | 固定 "OUT" | `ioType` | 是 | 否 | + +--- + +## 出库明细字段映射(仅使用现有 DO 字段) + +| 列 | 对应 DO 字段 | 数据来源 | 说明 | +|----|------------|---------|------| +| 序号 | `seqNo` | 自动编号 1,2,3... | | +| 箱号 | *(不存)* | `wms_group_plate.vehicle_code` | 仅页面展示,不入库 | +| 物料编码 | `materialCode` | 库存带入 / 手动选择 | | +| 物料名称 | *(不存)* | `MaterialBaseApi` 实时反查 | 仅页面展示 | +| 子卷号 | `pcsn` | `wms_group_plate.pcsn` 或手动输入 | 方式 B 时为空 | +| 出库重量 | `planQty` | 库存 `qty - frozenQty`(可编辑)或手动输入 | | +| 未分配数量 | `unassignQty` | 初始 = `planQty` | | +| 已分配数量 | `assignQty` | 初始 = 0 | | +| 单位 | `qtyUnitName` | 库存带入 / 物料默认单位 | | +| 批次 | `pcsn` | 同子卷号 | | +| 备注 | `remark` | 可选输入 | | +| 来源单据号 | `sourceBillCode` | `group_plate.extCode` | 库存带入时有值 | +| 来源单据类型 | `sourceBillType` | `group_plate.extType` | 库存带入时有值 | +| 来源单据明细 | `sourceBilldtlId` | `group_plate.extDtlCode` | 库存带入时有值 | + +--- + +## 明细新增的两种方式 + +### 方式 A:从库存选择带入 + +1. 查询逻辑: + - 通过 `wms_structattr.storagevehicle_code = wms_group_plate.vehicle_code` 关联仓库 + - 过滤条件:`storId` = 主表所选仓库、`status` = 可用、`qty - frozenQty > 0` + - 支持物料编码、批次、载具编码等可选筛选条件 +2. **一对多联动**:一个 `vehicle_code` 对应多个 `pcsn`,勾选其中一条时,同一 `vehicle_code` 的所有记录自动勾选并带入明细 +3. 出库重量默认带入可用数量(`qty - frozenQty`),用户可编辑 + +### 方式 B:手动新增汇总 + +1. 弹出简单表单:选择物料、输入出库重量、批次(可选)、备注(可选) +2. 单位自动带出物料的基本计量单位 +3. 箱号、子卷号为空(仅页面不展示,不存 `pcsn`) + +--- + +## 后端设计 + +### 新增接口 + +**`POST /wms/iostor-inv/create-with-details`** + +```java +@PostMapping("/create-with-details") +@Operation(summary = "创建出库单(含明细)") +@PreAuthorize("@ss.hasPermission('wms:iostor-inv:create')") +public CommonResult createIostorInvWithDetails( + @Valid @RequestBody IostorInvWithDetailsSaveReqVO reqVO) +``` + +### 请求 VO + +```java +public class IostorInvWithDetailsSaveReqVO { + // 主表字段 + private String billType; // 业务类型 + private String storId; // 仓库标识 + private String storCode; // 仓库编码 + private String storName; // 仓库名称 + private LocalDateTime bizDate; // 业务日期 + private String remark; // 备注 + + // 明细列表 + private List details; +} + +public class DetailVO { + private String materialCode; + private String materialId; + private String pcsn; // 子卷号/批次 + private BigDecimal planQty; // 出库重量 + private String qtyUnitId; + private String qtyUnitName; + private String remark; + private String sourceBillCode; // 来源单据号 + private String sourceBillType; // 来源单据类型 + private String sourceBilldtlId; // 来源单据明细标识 +} +``` + +### Service 层逻辑 + +1. 调用 `CodeGenApi.generate("IO_CODE")` 生成单据号 +2. 设置 `ioType = "OUT"`、`billStatus = "生成"` +3. 计算 `detailCount = details.size()`、`totalWeight = sum(details.planQty)` +4. 插入主表 `wms_iostorinv` +5. 批量插入明细 `wms_iostorinvdtl`: + - `planQty` = 出库重量 + - `unassignQty` = `planQty`(初始等于计划数量) + - `assignQty` = 0 + - `seqNo` 自动编号 +6. `@Transactional` 保证原子性 + +### 库存查询(Mapper XML) + +在 `IostorinvDtlMapper.xml` 中新增通用查询 SQL,方便后续扩展: + +```xml + +``` + +Controller 通过 Service → Mapper 调用,不新增独立接口。 + +### 数据关联图 + +``` +wms_bsrealstorattr (仓库) + │ + │ storId + ▼ +wms_structattr (仓位) + │ storagevehicle_code + │ + ▼ +wms_group_plate (库存/组盘) + │ + │ 选择带入 + ▼ +wms_iostorinvdtl (出库明细) + │ + │ iostorinvId + ▼ +wms_iostorinv (出库单主表) +``` + +--- + +## 前端设计 + +### 文件结构 + +``` +nl-ui/.../views/wms/iostorinv/ +├── index.vue # 列表页 (不改) +├── data.ts # 列表 schema (不改) +├── modules/ +│ ├── form.vue # [重构] 大弹窗:主表表单 + 明细 table +│ ├── inventory-select.vue # [新增] 库存选择弹窗 +│ └── manual-detail.vue # [新增] 手动新增汇总弹窗 + +nl-ui/.../api/wms/iostorinv/ +└── index.ts # [扩展] 新增 createIostorInvWithDetails +``` + +### form.vue 重构 + +- 弹窗宽度 1200px 或全屏 +- 主表区域 `useVbenForm`,水平布局,字段排列参照原型图 +- 明细 table 内嵌在表单下方,使用 vxe-table +- 联动: + - 仓库切换 → 清空明细 + - 明细增删 → 自动更新明细数、总重量 + - 单据号在弹窗打开时通过预览接口展示,提交时由后端生成 + +### inventory-select.vue(库存选择弹窗) + +- 搜索区:物料编码、批次、载具编码 +- 表格多选 + 同 vehicle_code 联动勾选 +- 可编辑出库重量列(默认可用数量) +- 确认后返回选中数据 + +### manual-detail.vue(手动新增弹窗) + +- 物料选择器(可复用 MaterialSelectModal) +- 出库重量输入 +- 批次输入(可选) +- 备注输入(可选) + +--- + +## 错误处理 + +| 场景 | 处理方式 | +|------|---------| +| 单据号生成失败(CodeGenApi 不可用) | 抛出异常,事务回滚,前端提示"单据号生成失败" | +| 未选择仓库时点"新增明细(方式A)" | 前端拦截,提示"请先选择仓库" | +| 提交时明细为空 | 后端校验,返回"请至少添加一条明细" | +| 库存已被其他单据消耗(并发) | 提交时校验可用数量,不足则回滚提示 | +| Feign 调用超时 | 兜底:打印日志 + 抛出业务异常 | + +--- + +## 不考虑的内容 + +- 入库单场景(本次只做出库) +- 编辑/修改出库单明细(本次只做新增) +- 明细的分配/确认流程 +- 回传/上传逻辑 From c6edf4b8d90bf4a26506f564f67ed65aeebf944c Mon Sep 17 00:00:00 2001 From: zhouz <> Date: Wed, 22 Jul 2026 09:24:49 +0800 Subject: [PATCH 02/28] =?UTF-8?q?docs:=20=E5=87=BA=E5=BA=93=E5=8D=95?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E9=A1=B5=E9=9D=A2=E5=AE=9E=E7=8E=B0=E8=AE=A1?= =?UTF-8?q?=E5=88=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plans/2026-07-22-outbound-create-plan.md | 1325 +++++++++++++++++ 1 file changed, 1325 insertions(+) create mode 100644 docs/superpowers/plans/2026-07-22-outbound-create-plan.md diff --git a/docs/superpowers/plans/2026-07-22-outbound-create-plan.md b/docs/superpowers/plans/2026-07-22-outbound-create-plan.md new file mode 100644 index 00000000..850fc5dc --- /dev/null +++ b/docs/superpowers/plans/2026-07-22-outbound-create-plan.md @@ -0,0 +1,1325 @@ +# 出库单新增页面 实现计划 + +> **面向 AI 代理的工作者:** 必需子技能:使用 superpowers:subagent-driven-development(推荐)或 superpowers:executing-plans 逐任务实现此计划。步骤使用复选框(`- [ ]`)语法来跟踪进度。 + +**目标:** 将出入库单主表的新增弹窗重构为出库单新增页面,包含主表表单 + 出库明细 table,支持从库存选择带入和手动新增汇总两种方式。 + +**架构:** 前端大弹窗页面(form.vue 重构),主表用 `useVbenForm` 水平布局,明细用 vxe-table 内嵌,新增库存选择弹窗和手动汇总弹窗两个子组件。后端新增 `createWithDetails` 接口,事务写入主表 + 明细,库存查询 SQL 写在 Mapper XML 中。通过 Feign 调用 base 模块的 CodeGenApi 生成单据号。 + +**技术栈:** Java 17, Spring Boot 3, MyBatis-Plus, OpenFeign, Vue 3, Ant Design Vue Next, Vxe Table + +--- + +### 文件结构 + +``` +# 后端 — 新增/修改 +nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/ +├── framework/rpc/config/RpcConfiguration.java [新增] Feign 客户端配置 +├── controller/admin/iostorinv/vo/ +│ ├── IostorInvWithDetailsSaveReqVO.java [新增] 带明细的创建请求 VO +│ └── AvailableInventoryVO.java [新增] 可用库存查询结果 VO +├── dal/mysql/iostorinvdtl/IostorinvDtlMapper.java [修改] 新增 selectAvailableInventory 方法 +├── resources/mapper/iostorinvdtl/IostorinvDtlMapper.xml [修改] 新增库存查询 SQL +├── service/iostorinv/IostorInvService.java [修改] 新增 createWithDetails 方法 +├── service/iostorinv/IostorInvServiceImpl.java [修改] 实现 createWithDetails +├── service/iostorinvdtl/IostorinvDtlService.java [修改] 新增 batchInsert 方法 +├── service/iostorinvdtl/IostorinvDtlServiceImpl.java [修改] 实现 batchInsert +└── controller/admin/iostorinv/IostorInvController.java [修改] 新增 createWithDetails 端点 + +# 前端 — 新增/修改 +nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/ +├── api/wms/iostorinv/index.ts [修改] 新增类型和 API +├── views/wms/iostorinv/modules/ +│ ├── form.vue [重构] 大弹窗 + 主表 + 明细 +│ ├── inventory-select.vue [新增] 库存选择弹窗 +│ └── manual-detail.vue [新增] 手动新增汇总弹窗 +└── packages/constants/src/dict-enum.ts [修改] 新增 out_bill_type 字典类型 +``` + +--- + +### 任务 1:后端 — Feign 客户端配置 + +**文件:** +- 创建:`nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/framework/rpc/config/RpcConfiguration.java` + +- [ ] **步骤 1:创建 RpcConfiguration,注册 CodeGenApi Feign 客户端** + +```java +package cn.code.nl.module.wms.framework.rpc.config; + +import cn.code.nl.module.base.api.codegen.CodeGenApi; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.annotation.Configuration; + +/** + * WMS 模块 RPC 配置 + * + * @author zhouz + */ +@Configuration(value = "wmsRpcConfiguration", proxyBeanMethods = false) +@EnableFeignClients(clients = {CodeGenApi.class}) +public class RpcConfiguration { +} +``` + +> **注意:** 需要确认 `nl-module-wms-server` 的 pom.xml 已依赖 `nl-module-base-api`,否则 Feign 接口编译不到。如缺失,添加依赖: +> ```xml +> +> cn.code.nl +> nl-module-base-api +> ${project.version} +> +> ``` + +- [ ] **步骤 2:Commit** + +```bash +git add nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/framework/rpc/config/RpcConfiguration.java +git commit -m "feat: 添加 WMS 模块 Feign 客户端配置,注册 CodeGenApi" +``` + +--- + +### 任务 2:后端 — 新增请求 VO 和库存查询 VO + +**文件:** +- 创建:`nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/vo/IostorInvWithDetailsSaveReqVO.java` +- 创建:`nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/vo/AvailableInventoryVO.java` + +- [ ] **步骤 1:创建带明细的创建请求 VO** + +```java +package cn.code.nl.module.wms.controller.admin.iostorinv.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +/** + * 管理后台 - 出库单新增(含明细)Request VO + * + * @author zhouz + */ +@Schema(description = "管理后台 - 出库单新增(含明细)Request VO") +@Data +public class IostorInvWithDetailsSaveReqVO { + + @Schema(description = "单据类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "生产领料出库") + @NotEmpty(message = "单据类型不能为空") + private String billType; + + @Schema(description = "仓库标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "12635") + @NotEmpty(message = "仓库标识不能为空") + private String storId; + + @Schema(description = "仓库编码") + private String storCode; + + @Schema(description = "仓库名称", example = "原料仓库") + private String storName; + + @Schema(description = "业务日期", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "业务日期不能为空") + private LocalDateTime bizDate; + + @Schema(description = "备注") + private String remark; + + @Schema(description = "出库明细列表", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "请至少添加一条出库明细") + @Valid + private List details; + + /** + * 出库明细 VO + */ + @Data + public static class DetailVO { + + @Schema(description = "物料编码", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "物料编码不能为空") + private String materialCode; + + @Schema(description = "物料标识") + private String materialId; + + @Schema(description = "批次/子卷号") + private String pcsn; + + @Schema(description = "出库重量", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "出库重量不能为空") + private BigDecimal planQty; + + @Schema(description = "计量单位标识") + private String qtyUnitId; + + @Schema(description = "计量单位名称") + private String qtyUnitName; + + @Schema(description = "备注") + private String remark; + + @Schema(description = "来源单据号") + private String sourceBillCode; + + @Schema(description = "来源单据类型") + private String sourceBillType; + + @Schema(description = "来源单据明细标识") + private String sourceBilldtlId; + } +} +``` + +- [ ] **步骤 2:创建可用库存查询结果 VO** + +```java +package cn.code.nl.module.wms.controller.admin.iostorinv.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 管理后台 - 可用库存查询结果 VO + * + * @author zhouz + */ +@Schema(description = "管理后台 - 可用库存查询结果 VO") +@Data +public class AvailableInventoryVO { + + @Schema(description = "载具编码(箱号)") + private String vehicleCode; + + @Schema(description = "批次/子卷号") + private String pcsn; + + @Schema(description = "物料编码") + private String materialCode; + + @Schema(description = "物料标识") + private String materialId; + + @Schema(description = "组盘数量") + private BigDecimal qty; + + @Schema(description = "冻结数量") + private BigDecimal frozenQty; + + @Schema(description = "可用数量") + private BigDecimal availableQty; + + @Schema(description = "计量单位标识") + private String qtyUnitId; + + @Schema(description = "计量单位名称") + private String qtyUnitName; + + @Schema(description = "来源单据号") + private String extCode; + + @Schema(description = "来源单据类型") + private String extType; + + @Schema(description = "来源单据明细号") + private String extDtlCode; + + @Schema(description = "仓库标识") + private String storId; + + @Schema(description = "仓库编码") + private String storCode; + + @Schema(description = "仓库名称") + private String storName; +} +``` + +- [ ] **步骤 3:Commit** + +```bash +git add nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/vo/IostorInvWithDetailsSaveReqVO.java nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/vo/AvailableInventoryVO.java +git commit -m "feat: 新增出库单带明细创建请求 VO 和可用库存查询 VO" +``` + +--- + +### 任务 3:后端 — Mapper XML 新增库存查询 SQL + +**文件:** +- 修改:`nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/mysql/iostorinvdtl/IostorinvDtlMapper.java` +- 修改:`nl-module-wms/nl-module-wms-server/src/main/resources/mapper/iostorinvdtl/IostorinvDtlMapper.xml` + +- [ ] **步骤 1:在 IostorinvDtlMapper 接口中新增方法** + +在文件末尾(`}` 之前)添加: + +```java + /** + * 查询可用库存(wms_group_plate 关联 wms_structattr) + * + * @param storId 仓库标识(必传) + * @param materialCode 物料编码(可选) + * @param pcsn 批次(可选) + * @param vehicleCode 载具编码(可选) + * @return 可用库存列表 + */ + List selectAvailableInventory( + @Param("storId") String storId, + @Param("materialCode") String materialCode, + @Param("pcsn") String pcsn, + @Param("vehicleCode") String vehicleCode); +``` + +同时新增 import: + +```java +import cn.code.nl.module.wms.controller.admin.iostorinv.vo.AvailableInventoryVO; +import org.apache.ibatis.annotations.Param; +``` + +- [ ] **步骤 2:在 IostorinvDtlMapper.xml 中新增 SQL** + +替换空的 XML 文件内容为: + +```xml + + + + + + + + + +``` + +- [ ] **步骤 3:Commit** + +```bash +git add nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/mysql/iostorinvdtl/IostorinvDtlMapper.java nl-module-wms/nl-module-wms-server/src/main/resources/mapper/iostorinvdtl/IostorinvDtlMapper.xml +git commit -m "feat: 新增可用库存查询 SQL(group_plate 关联 structattr)" +``` + +--- + +### 任务 4:后端 — Service 层新增 createWithDetails 和 batchInsert + +**文件:** +- 修改:`nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/iostorinv/IostorInvService.java` +- 修改:`nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/iostorinv/IostorInvServiceImpl.java` +- 修改:`nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/iostorinvdtl/IostorinvDtlService.java` +- 修改:`nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/iostorinvdtl/IostorinvDtlServiceImpl.java` + +- [ ] **步骤 1:在 IostorInvService 接口中新增方法声明** + +在文件末尾的 `}` 之前添加: + +```java + /** + * 创建出库单(含明细) + * + * @param reqVO 创建请求(含明细列表) + * @return 出入单标识 + */ + String createIostorInvWithDetails(@Valid IostorInvWithDetailsSaveReqVO reqVO); +``` + +新增 import: + +```java +import cn.code.nl.module.wms.controller.admin.iostorinv.vo.IostorInvWithDetailsSaveReqVO; +``` + +- [ ] **步骤 2:在 IostorInvServiceImpl 中实现 createIostorInvWithDetails** + +添加 `@Resource` 注入: + +```java + @Resource + private cn.code.nl.module.base.api.codegen.CodeGenApi codeGenApi; + + @Resource + private cn.code.nl.module.wms.dal.mysql.iostorinvdtl.IostorinvDtlMapper iostorinvDtlMapper; +``` + +新增方法实现(在文件末尾 `}` 之前): + +```java + @Override + @Transactional(rollbackFor = Exception.class) + public String createIostorInvWithDetails(IostorInvWithDetailsSaveReqDTO reqVO) { + // 1. 调用 base 模块生成单据号 + CodeGenerateReqDTO codeReq = new CodeGenerateReqDTO(); + codeReq.setRuleCode("IO_CODE"); + CommonResult codeResult = codeGenApi.generate(codeReq); + if (codeResult == null || codeResult.getData() == null) { + throw exception(IOSTOR_INV_BILL_CODE_GENERATE_FAIL); + } + String billCode = codeResult.getData(); + + // 2. 构建主表数据 + IostorInvDO master = new IostorInvDO(); + master.setBillCode(billCode); + master.setIoType("OUT"); + master.setBillType(reqVO.getBillType()); + master.setBizDate(reqVO.getBizDate()); + master.setStorId(reqVO.getStorId()); + master.setStorCode(reqVO.getStorCode()); + master.setStorName(reqVO.getStorName()); + master.setBillStatus("生成"); + master.setRemark(reqVO.getRemark()); + master.setDetailCount(reqVO.getDetails().size()); + + // 3. 计算总重量 + BigDecimal totalWeight = reqVO.getDetails().stream() + .map(IostorInvWithDetailsSaveReqVO.DetailVO::getPlanQty) + .reduce(BigDecimal.ZERO, BigDecimal::add); + master.setTotalWeight(totalWeight); + + // 4. 插入主表 + iostorInvMapper.insert(master); + + // 5. 批量插入明细 + String iostorinvId = master.getIostorinvId(); + int seqNo = 1; + for (IostorInvWithDetailsSaveReqVO.DetailVO detail : reqVO.getDetails()) { + IostorinvDtlDO dtl = new IostorinvDtlDO(); + dtl.setIostorinvId(iostorinvId); + dtl.setSeqNo(seqNo++); + dtl.setMaterialCode(detail.getMaterialCode()); + dtl.setMaterialId(detail.getMaterialId()); + dtl.setPcsn(detail.getPcsn()); + dtl.setPlanQty(detail.getPlanQty()); + dtl.setUnassignQty(detail.getPlanQty()); // 初始未分配 = 计划数量 + dtl.setAssignQty(BigDecimal.ZERO); + dtl.setQtyUnitId(detail.getQtyUnitId()); + dtl.setQtyUnitName(detail.getQtyUnitName()); + dtl.setRemark(detail.getRemark()); + dtl.setSourceBillCode(detail.getSourceBillCode()); + dtl.setSourceBillType(detail.getSourceBillType()); + dtl.setSourceBilldtlId(detail.getSourceBilldtlId()); + iostorinvDtlMapper.insert(dtl); + } + + return iostorinvId; + } +``` + +新增 import: + +```java +import cn.code.nl.module.base.api.codegen.CodeGenApi; +import cn.code.nl.module.base.api.codegen.dto.CodeGenerateReqDTO; +import cn.code.nl.module.framework.common.pojo.CommonResult; +import cn.code.nl.module.wms.controller.admin.iostorinv.vo.IostorInvWithDetailsSaveReqVO; +import cn.code.nl.module.wms.dal.dataobject.iostorinvdtl.IostorinvDtlDO; +import cn.code.nl.module.wms.dal.mysql.iostorinvdtl.IostorinvDtlMapper; +import java.math.BigDecimal; +``` + +- [ ] **步骤 3:在 ErrorCodeConstants 中新增错误码** + +修改 `nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/enums/ErrorCodeConstants.java`,在文件末尾添加: + +```java + // ========== 出入库单 1-030-001-000 ========== + ErrorCode IOSTOR_INV_BILL_CODE_GENERATE_FAIL = new ErrorCode(1_030_001_000, "单据号生成失败,请稍后重试"); +``` + +> **注意:** 确认错误码 `1_030_001_000` 不与已有错误码冲突。如冲突则顺延。 + +- [ ] **步骤 4:IostorinvDtlService 和 Impl 暂不改动** + +明细的批量插入直接在 `IostorInvServiceImpl` 中通过 `iostorinvDtlMapper.insert()` 完成,无需改动 DtlService 接口。跳过此改动。 + +- [ ] **步骤 5:Commit** + +```bash +git add nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/iostorinv/IostorInvService.java nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/iostorinv/IostorInvServiceImpl.java nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/enums/ErrorCodeConstants.java +git commit -m "feat: 实现出库单带明细创建 Service(含 CodeGen 单据号生成)" +``` + +--- + +### 任务 5:后端 — Controller 新增 createWithDetails 端点 + +**文件:** +- 修改:`nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/IostorInvController.java` + +- [ ] **步骤 1:新增 createWithDetails 方法和可用库存查询方法** + +在类末尾的 `}` 之前添加: + +```java + @PostMapping("/create-with-details") + @Operation(summary = "创建出库单(含明细)") + @PreAuthorize("@ss.hasPermission('wms:iostor-inv:create')") + public CommonResult createIostorInvWithDetails( + @Valid @RequestBody IostorInvWithDetailsSaveReqVO reqVO) { + return success(iostorInvService.createIostorInvWithDetails(reqVO)); + } +``` + +新增 import: + +```java +import cn.code.nl.module.wms.controller.admin.iostorinv.vo.IostorInvWithDetailsSaveReqVO; +``` + +- [ ] **步骤 2:Commit** + +```bash +git add nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/IostorInvController.java +git commit -m "feat: 新增出库单带明细创建 API 端点 POST /create-with-details" +``` + +--- + +### 任务 6:前端 — API 层扩展 + +**文件:** +- 修改:`nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/api/wms/iostorinv/index.ts` + +- [ ] **步骤 1:新增请求类型和 API 方法** + +在文件末尾的 `export function exportIostorInv` 之前添加类型定义,并在最后添加 API 方法: + +```typescript +// --- 出库单带明细创建 --- + +/** 出库明细项 */ +export interface OutboundDetail { + materialCode: string; + materialId?: string; + pcsn?: string; + planQty: number; + qtyUnitId?: string; + qtyUnitName?: string; + remark?: string; + sourceBillCode?: string; + sourceBillType?: string; + sourceBilldtlId?: string; +} + +/** 出库单创建请求(含明细) */ +export interface OutboundCreateReq { + billType: string; + storId: string; + storCode?: string; + storName?: string; + bizDate: string; + remark?: string; + details: OutboundDetail[]; +} + +/** 可用库存项 */ +export interface AvailableInventory { + vehicleCode: string; + pcsn: string; + materialCode: string; + materialId: string; + qty: number; + frozenQty: number; + availableQty: number; + qtyUnitId: string; + qtyUnitName: string; + extCode: string; + extType: string; + extDtlCode: string; + storId: string; + storCode: string; + storName: string; +} + +/** 可用库存查询参数 */ +export interface AvailableInventoryQuery { + storId: string; + materialCode?: string; + pcsn?: string; + vehicleCode?: string; +} +``` + +在文件末尾(`export function exportIostorInv` 之后)添加: + +```typescript +/** 创建出库单(含明细) */ +export function createIostorInvWithDetails(data: OutboundCreateReq) { + return requestClient.post('/wms/iostor-inv/create-with-details', data); +} + +/** 查询可用库存 */ +export function getAvailableInventory(params: AvailableInventoryQuery) { + return requestClient.get( + '/wms/iostorinvdtl/available-inventory', + { params }, + ); +} +``` + +- [ ] **步骤 2:Commit** + +```bash +git add nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/api/wms/iostorinv/index.ts +git commit -m "feat: 前端 API 层新增出库单带明细创建和可用库存查询" +``` + +--- + +### 任务 7:后端 — 可用库存查询 Controller(补充) + +> 前面任务 3 只在 Mapper 加了 SQL,但前端需要一个 HTTP 接口调用。需要在 Controller 或现有 Controller 中暴露。 + +**文件:** +- 修改:`nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinvdtl/IostorinvDtlController.java` + +- [ ] **步骤 1:在 IostorinvDtlController 中新增可用库存查询端点** + +在类末尾 `}` 之前添加: + +```java + @GetMapping("/available-inventory") + @Operation(summary = "查询可用库存(组盘关联仓位)") + @PreAuthorize("@ss.hasPermission('wms:iostor-inv:query')") + public CommonResult> getAvailableInventory( + @RequestParam("storId") String storId, + @RequestParam(value = "materialCode", required = false) String materialCode, + @RequestParam(value = "pcsn", required = false) String pcsn, + @RequestParam(value = "vehicleCode", required = false) String vehicleCode) { + List list = iostorinvDtlMapper.selectAvailableInventory( + storId, materialCode, pcsn, vehicleCode); + return success(list); + } +``` + +注入 Mapper: + +```java + @Resource + private IostorinvDtlMapper iostorinvDtlMapper; +``` + +新增 import: + +```java +import cn.code.nl.module.wms.controller.admin.iostorinv.vo.AvailableInventoryVO; +import cn.code.nl.module.wms.dal.mysql.iostorinvdtl.IostorinvDtlMapper; +import jakarta.annotation.Resource; +``` + +- [ ] **步骤 2:Commit** + +```bash +git add nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinvdtl/IostorinvDtlController.java +git commit -m "feat: 新增可用库存查询端点 GET /available-inventory" +``` + +--- + +### 任务 8:前端 — 重构 form.vue(主表表单 + 明细 Table) + +**文件:** +- 修改:`nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/iostorinv/modules/form.vue` + +- [ ] **步骤 1:重写 form.vue — 脚本部分** + +```vue + +``` + +- [ ] **步骤 2:重写 form.vue — 模板部分** + +```vue + +``` + +> **注意:** 模板中使用了 `VxeTable` 组件,需要确认该组件在项目中的实际名称和用法。参考 `index.vue` 中 `useVbenVxeGrid` 的使用方式。如 `VxeTable` 不可用,改用 `vxe-grid` 直接绑定。 + +- [ ] **步骤 3:Commit** + +```bash +git add nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/iostorinv/modules/form.vue +git commit -m "feat: 重构出库单新增表单为带明细的单页弹窗" +``` + +--- + +### 任务 9:前端 — 新增库存选择弹窗组件 + +**文件:** +- 创建:`nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/iostorinv/modules/inventory-select.vue` + +- [ ] **步骤 1:创建 inventory-select.vue** + +```vue + + + +``` + +> **注意:** `getMaterialBaseList` 接口参数需要确认。如接口仅支持 `MaterialBaseListReqDTO` 条件查询而非批量按编码查询,可能需要使用循环调用 `getMaterialBaseByCode` 或调整查询方式。此处为简化说明假设支持批量查询。 + +- [ ] **步骤 2:Commit** + +```bash +git add nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/iostorinv/modules/inventory-select.vue +git commit -m "feat: 新增库存选择弹窗组件(支持同箱号联动勾选)" +``` + +--- + +### 任务 10:前端 — 新增手动新增汇总弹窗组件 + +**文件:** +- 创建:`nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/iostorinv/modules/manual-detail.vue` + +- [ ] **步骤 1:创建 manual-detail.vue** + +```vue + + + +``` + +> **注意:** MaterialSelectModal 在此处的集成方式需要参考原组件的 `useVbenModal` 用法。当前简化示例——实际集成时应在 script 中用 `useVbenModal({ connectedComponent: MaterialSelectModal })` 打开物料选择弹窗。 + +- [ ] **步骤 2:Commit** + +```bash +git add nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/iostorinv/modules/manual-detail.vue +git commit -m "feat: 新增手动新增汇总弹窗组件" +``` + +--- + +### 任务 11:前端 — 字典常量补充 & 最终联调验证 + +**文件:** +- 修改:`nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/packages/constants/src/dict-enum.ts`(可选,取决于实际使用方式) +- 不强制修改,因为已在 form.vue 中通过 `getSimpleDictDataList()` 动态过滤 `out_bill_type` + +- [ ] **步骤 1:验证 checklist** + +1. 确认 `nl-module-wms-server` 的 pom.xml 包含 `nl-module-base-api` 依赖 +2. 确认 `IostorinvDtlController` 文件存在,且注入 `IostorinvDtlMapper` 无编译错误 +3. 启动后端服务,调用 `POST /wms/iostor-inv/create-with-details` 接口验证 +4. 调用 `GET /wms/iostorinvdtl/available-inventory?storId=xxx` 验证库存查询 +5. 前端页面打开"出库新增"弹窗,验证: + - 仓库下拉加载正确 + - 业务类型下拉加载正确(out_bill_type 字典) + - 单据号显示占位提示 + - "从库存选择"弹窗:只显示对应仓库的库存,同箱号联动勾选 + - "手动新增汇总"弹窗:选物料、填重量 + - 明细增删后,明细数和总重量自动更新 + - 提交保存成功 + +--- + +## 自检 + +**1. 规格覆盖度:** +- [x] 主表字段映射 → 任务 8(form.vue schema)+ 任务 4(ServiceImpl 字段赋值) +- [x] 明细字段映射 → 任务 8(detailList 结构)+ 任务 4(dtl.setXxx) +- [x] 方式 A 从库存选择 → 任务 9(inventory-select.vue)+ 任务 3(Mapper SQL)+ 任务 7(Controller) +- [x] 方式 B 手动新增 → 任务 10(manual-detail.vue) +- [x] 单据号 CodeGenApi → 任务 1(RpcConfig)+ 任务 4(ServiceImpl 调用) +- [x] 一对多联动 → 任务 9(gridEvents.checkboxChange) +- [x] planQty/unassignQty/assignQty → 任务 4(ServiceImpl) +- [x] 箱号不存 → 任务 8(vehicleCode 仅 detailList 前端字段) +- [x] 库存查询 Mapper XML → 任务 3 +- [x] 错误处理 → 任务 4(异常抛出)+ 任务 8(前端校验) +- [x] 不考虑的内容(入库/编辑/分配/回传)→ 已明确范围,无遗漏 + +**2. 占位符扫描:** 无待定/TODO/占位符 + +**3. 类型一致性:** +- `OutboundCreateReq` / `OutboundDetail` → 前端 API 层(任务 6)和 form.vue(任务 8)一致 +- `IostorInvWithDetailsSaveReqVO` → 后端 Controller(任务 5)和 Service(任务 4)一致 +- `AvailableInventoryVO` → Mapper(任务 3)、Controller(任务 7)、前端 API(任务 6)一致 +- `AvailableInventory` → 前端 API(任务 6)和 inventory-select(任务 9)一致 From d45feffe429da76cf9dfd3d5db7a26cfc35ededa Mon Sep 17 00:00:00 2001 From: zhouz <> Date: Wed, 22 Jul 2026 09:30:45 +0800 Subject: [PATCH 03/28] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=20WMS=20?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=20RPC=20Feign=20=E5=AE=A2=E6=88=B7=E7=AB=AF?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 创建 RpcConfiguration,注册 CodeGenApi Feign 客户端 - 添加 nl-module-base-api Maven 依赖 --- nl-module-wms/nl-module-wms-server/pom.xml | 5 +++++ .../framework/rpc/config/RpcConfiguration.java | 15 +++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/framework/rpc/config/RpcConfiguration.java diff --git a/nl-module-wms/nl-module-wms-server/pom.xml b/nl-module-wms/nl-module-wms-server/pom.xml index ef12b750..be10ce4c 100644 --- a/nl-module-wms/nl-module-wms-server/pom.xml +++ b/nl-module-wms/nl-module-wms-server/pom.xml @@ -42,6 +42,11 @@ nl-module-task-api ${revision} + + cn.nl.cloud + nl-module-base-api + ${revision} + diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/framework/rpc/config/RpcConfiguration.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/framework/rpc/config/RpcConfiguration.java new file mode 100644 index 00000000..dfe143ac --- /dev/null +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/framework/rpc/config/RpcConfiguration.java @@ -0,0 +1,15 @@ +package cn.code.nl.module.wms.framework.rpc.config; + +import cn.code.nl.module.base.api.codegen.CodeGenApi; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.annotation.Configuration; + +/** + * WMS 模块 RPC 配置 + * + * @author zhouz + */ +@Configuration(value = "wmsRpcConfiguration", proxyBeanMethods = false) +@EnableFeignClients(clients = {CodeGenApi.class}) +public class RpcConfiguration { +} From 4d8110fa26cb12916558618b313aa2ce2c1af609 Mon Sep 17 00:00:00 2001 From: zhouz <> Date: Wed, 22 Jul 2026 09:35:55 +0800 Subject: [PATCH 04/28] =?UTF-8?q?feat:=E6=96=B0=E5=A2=9E=E5=87=BA=E5=BA=93?= =?UTF-8?q?=E5=8D=95=E5=90=AB=E6=98=8E=E7=BB=86=E8=AF=B7=E6=B1=82VO?= =?UTF-8?q?=E5=92=8C=E5=8F=AF=E7=94=A8=E5=BA=93=E5=AD=98=E6=9F=A5=E8=AF=A2?= =?UTF-8?q?=E7=BB=93=E6=9E=9CVO?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iostorinv/vo/AvailableInventoryVO.java | 61 +++++++++++++ .../vo/IostorInvWithDetailsSaveReqVO.java | 85 +++++++++++++++++++ 2 files changed, 146 insertions(+) create mode 100644 nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/vo/AvailableInventoryVO.java create mode 100644 nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/vo/IostorInvWithDetailsSaveReqVO.java diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/vo/AvailableInventoryVO.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/vo/AvailableInventoryVO.java new file mode 100644 index 00000000..b07a9632 --- /dev/null +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/vo/AvailableInventoryVO.java @@ -0,0 +1,61 @@ +package cn.code.nl.module.wms.controller.admin.iostorinv.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; + +import java.math.BigDecimal; + +/** + * 管理后台 - 可用库存查询结果 VO + * + * @author zhouz + */ +@Schema(description = "管理后台 - 可用库存查询结果 VO") +@Data +public class AvailableInventoryVO { + + @Schema(description = "载具编码(箱号)") + private String vehicleCode; + + @Schema(description = "批次/子卷号") + private String pcsn; + + @Schema(description = "物料编码") + private String materialCode; + + @Schema(description = "物料标识") + private String materialId; + + @Schema(description = "组盘数量") + private BigDecimal qty; + + @Schema(description = "冻结数量") + private BigDecimal frozenQty; + + @Schema(description = "可用数量") + private BigDecimal availableQty; + + @Schema(description = "计量单位标识") + private String qtyUnitId; + + @Schema(description = "计量单位名称") + private String qtyUnitName; + + @Schema(description = "来源单据号") + private String extCode; + + @Schema(description = "来源单据类型") + private String extType; + + @Schema(description = "来源单据明细号") + private String extDtlCode; + + @Schema(description = "仓库标识") + private String storId; + + @Schema(description = "仓库编码") + private String storCode; + + @Schema(description = "仓库名称") + private String storName; +} diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/vo/IostorInvWithDetailsSaveReqVO.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/vo/IostorInvWithDetailsSaveReqVO.java new file mode 100644 index 00000000..a9dfb7e3 --- /dev/null +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/vo/IostorInvWithDetailsSaveReqVO.java @@ -0,0 +1,85 @@ +package cn.code.nl.module.wms.controller.admin.iostorinv.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import lombok.*; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.*; + +/** + * 管理后台 - 出库单新增(含明细)Request VO + * + * @author zhouz + */ +@Schema(description = "管理后台 - 出库单新增(含明细)Request VO") +@Data +public class IostorInvWithDetailsSaveReqVO { + + @Schema(description = "单据类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "生产领料出库") + @NotEmpty(message = "单据类型不能为空") + private String billType; + + @Schema(description = "仓库标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "12635") + @NotEmpty(message = "仓库标识不能为空") + private String storId; + + @Schema(description = "仓库编码") + private String storCode; + + @Schema(description = "仓库名称", example = "原料仓库") + private String storName; + + @Schema(description = "业务日期", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "业务日期不能为空") + private LocalDateTime bizDate; + + @Schema(description = "备注") + private String remark; + + @Schema(description = "出库明细列表", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "请至少添加一条出库明细") + @Valid + private List details; + + /** + * 出库明细 VO + */ + @Data + public static class DetailVO { + + @Schema(description = "物料编码", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "物料编码不能为空") + private String materialCode; + + @Schema(description = "物料标识") + private String materialId; + + @Schema(description = "批次/子卷号") + private String pcsn; + + @Schema(description = "出库重量", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "出库重量不能为空") + private BigDecimal planQty; + + @Schema(description = "计量单位标识") + private String qtyUnitId; + + @Schema(description = "计量单位名称") + private String qtyUnitName; + + @Schema(description = "备注") + private String remark; + + @Schema(description = "来源单据号") + private String sourceBillCode; + + @Schema(description = "来源单据类型") + private String sourceBillType; + + @Schema(description = "来源单据明细标识") + private String sourceBilldtlId; + } +} From 6c8020d1c37a1385d97d159ab7bfbdd57964ea14 Mon Sep 17 00:00:00 2001 From: zhouz <> Date: Wed, 22 Jul 2026 09:43:09 +0800 Subject: [PATCH 05/28] =?UTF-8?q?feat:=E6=96=B0=E5=A2=9E=E5=8F=AF=E7=94=A8?= =?UTF-8?q?=E5=BA=93=E5=AD=98=E6=9F=A5=E8=AF=A2Mapper=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E5=8F=8ASQL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../iostorinvdtl/IostorinvDtlMapper.java | 16 +++++++++ .../iostorinvdtl/IostorinvDtlMapper.xml | 34 +++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/mysql/iostorinvdtl/IostorinvDtlMapper.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/mysql/iostorinvdtl/IostorinvDtlMapper.java index df00b8ea..c3f7714d 100644 --- a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/mysql/iostorinvdtl/IostorinvDtlMapper.java +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/dal/mysql/iostorinvdtl/IostorinvDtlMapper.java @@ -5,8 +5,10 @@ import java.util.*; import cn.code.nl.framework.common.pojo.PageResult; import cn.code.nl.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.code.nl.framework.mybatis.core.mapper.BaseMapperX; +import cn.code.nl.module.wms.controller.admin.iostorinv.vo.AvailableInventoryVO; import cn.code.nl.module.wms.dal.dataobject.iostorinvdtl.IostorinvDtlDO; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; import cn.code.nl.module.wms.controller.admin.iostorinvdtl.vo.*; /** @@ -41,4 +43,18 @@ public interface IostorinvDtlMapper extends BaseMapperX { .orderByDesc(IostorinvDtlDO::getIostorinvdtlId)); } + /** + * 查询可用库存(wms_group_plate 关联 wms_structattr) + * + * @param storId 仓库标识(必传) + * @param materialCode 物料编码(可选) + * @param pcsn 批次(可选) + * @param vehicleCode 载具编码(可选) + * @return 可用库存列表 + */ + List selectAvailableInventory( + @Param("storId") String storId, + @Param("materialCode") String materialCode, + @Param("pcsn") String pcsn, + @Param("vehicleCode") String vehicleCode); } \ No newline at end of file diff --git a/nl-module-wms/nl-module-wms-server/src/main/resources/mapper/iostorinvdtl/IostorinvDtlMapper.xml b/nl-module-wms/nl-module-wms-server/src/main/resources/mapper/iostorinvdtl/IostorinvDtlMapper.xml index 9e1b610f..a58111ac 100644 --- a/nl-module-wms/nl-module-wms-server/src/main/resources/mapper/iostorinvdtl/IostorinvDtlMapper.xml +++ b/nl-module-wms/nl-module-wms-server/src/main/resources/mapper/iostorinvdtl/IostorinvDtlMapper.xml @@ -9,4 +9,38 @@ 文档可见:https://www.iocoder.cn/MyBatis/x-plugins/ --> + \ No newline at end of file From a8d18e304c4add998c5000cbe4f09cf7a34942ea Mon Sep 17 00:00:00 2001 From: zhouz <> Date: Wed, 22 Jul 2026 09:49:07 +0800 Subject: [PATCH 06/28] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0=E5=87=BA?= =?UTF-8?q?=E5=BA=93=E5=8D=95=E5=B8=A6=E6=98=8E=E7=BB=86=E5=88=9B=E5=BB=BA?= =?UTF-8?q?=20Service=EF=BC=88=E5=90=AB=20CodeGen=20=E5=8D=95=E6=8D=AE?= =?UTF-8?q?=E5=8F=B7=E7=94=9F=E6=88=90=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - IostorInvService 接口新增 createIostorInvWithDetails 方法 - IostorInvServiceImpl 实现:单据号生成、主表+明细事务写入 - ErrorCodeConstants 新增 IOSTOR_INV_BILL_CODE_GENERATE_FAIL --- .../module/wms/enums/ErrorCodeConstants.java | 3 + .../service/iostorinv/IostorInvService.java | 9 +++ .../iostorinv/IostorInvServiceImpl.java | 72 +++++++++++++++++++ 3 files changed, 84 insertions(+) diff --git a/nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/enums/ErrorCodeConstants.java b/nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/enums/ErrorCodeConstants.java index 6205362e..ede70320 100644 --- a/nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/enums/ErrorCodeConstants.java +++ b/nl-module-wms/nl-module-wms-api/src/main/java/cn/code/nl/module/wms/enums/ErrorCodeConstants.java @@ -25,4 +25,7 @@ public interface ErrorCodeConstants { // ================ 组盘记录相关错误码 ================= ErrorCode GROUP_PLATE_NOT_EXISTS = new ErrorCode(9, "组盘记录不存在"); + + // ========== 出入库单 1-030-001-000 ========== + ErrorCode IOSTOR_INV_BILL_CODE_GENERATE_FAIL = new ErrorCode(1_030_001_000, "单据号生成失败,请稍后重试"); } diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/iostorinv/IostorInvService.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/iostorinv/IostorInvService.java index 42203b6d..4835cf75 100644 --- a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/iostorinv/IostorInvService.java +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/iostorinv/IostorInvService.java @@ -4,6 +4,7 @@ import java.util.*; import jakarta.validation.*; import cn.code.nl.module.wms.controller.admin.iostorinv.vo.*; import cn.code.nl.module.wms.dal.dataobject.iostorinv.IostorInvDO; +import cn.code.nl.module.wms.controller.admin.iostorinv.vo.IostorInvWithDetailsSaveReqVO; import cn.code.nl.framework.common.pojo.PageResult; import cn.code.nl.framework.common.pojo.PageParam; @@ -59,4 +60,12 @@ public interface IostorInvService { */ PageResult getIostorInvPage(IostorInvPageReqVO pageReqVO); + /** + * 创建出库单(含明细) + * + * @param reqVO 创建请求(含明细列表) + * @return 出入单标识 + */ + String createIostorInvWithDetails(@Valid IostorInvWithDetailsSaveReqVO reqVO); + } diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/iostorinv/IostorInvServiceImpl.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/iostorinv/IostorInvServiceImpl.java index f6d75cb2..33165cd1 100644 --- a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/iostorinv/IostorInvServiceImpl.java +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/service/iostorinv/IostorInvServiceImpl.java @@ -14,6 +14,13 @@ import cn.code.nl.framework.common.pojo.PageParam; import cn.code.nl.framework.common.util.object.BeanUtils; import cn.code.nl.module.wms.dal.mysql.iostorinv.IostorInvMapper; +import cn.code.nl.module.base.api.codegen.CodeGenApi; +import cn.code.nl.module.base.api.codegen.dto.CodeGenerateReqDTO; +import cn.code.nl.module.wms.dal.mysql.iostorinvdtl.IostorinvDtlMapper; +import cn.code.nl.module.wms.dal.dataobject.iostorinvdtl.IostorinvDtlDO; +import cn.code.nl.framework.common.pojo.CommonResult; +import cn.code.nl.module.wms.controller.admin.iostorinv.vo.IostorInvWithDetailsSaveReqVO; +import java.math.BigDecimal; import static cn.code.nl.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.code.nl.framework.common.util.collection.CollectionUtils.convertList; @@ -32,6 +39,12 @@ public class IostorInvServiceImpl implements IostorInvService { @Resource private IostorInvMapper iostorInvMapper; + @Resource + private CodeGenApi codeGenApi; + + @Resource + private IostorinvDtlMapper iostorinvDtlMapper; + @Override public String createIostorInv(IostorInvSaveReqVO createReqVO) { // 插入 @@ -82,4 +95,63 @@ public class IostorInvServiceImpl implements IostorInvService { return iostorInvMapper.selectPage(pageReqVO); } + @Override + @Transactional(rollbackFor = Exception.class) + public String createIostorInvWithDetails(IostorInvWithDetailsSaveReqVO reqVO) { + // 1. 调用 base 模块生成单据号 + CodeGenerateReqDTO codeReq = new CodeGenerateReqDTO(); + codeReq.setRuleCode("IO_CODE"); + CommonResult codeResult = codeGenApi.generate(codeReq); + if (codeResult == null || codeResult.getData() == null) { + throw exception(IOSTOR_INV_BILL_CODE_GENERATE_FAIL); + } + String billCode = codeResult.getData(); + + // 2. 构建主表数据 + IostorInvDO master = new IostorInvDO(); + master.setBillCode(billCode); + master.setIoType("OUT"); + master.setBillType(reqVO.getBillType()); + master.setBizDate(reqVO.getBizDate()); + master.setStorId(reqVO.getStorId()); + master.setStorCode(reqVO.getStorCode()); + master.setStorName(reqVO.getStorName()); + master.setBillStatus("生成"); + master.setRemark(reqVO.getRemark()); + master.setDetailCount(reqVO.getDetails().size()); + + // 3. 计算总重量 + BigDecimal totalWeight = reqVO.getDetails().stream() + .map(IostorInvWithDetailsSaveReqVO.DetailVO::getPlanQty) + .reduce(BigDecimal.ZERO, BigDecimal::add); + master.setTotalWeight(totalWeight); + + // 4. 插入主表 + iostorInvMapper.insert(master); + + // 5. 批量插入明细 + String iostorinvId = master.getIostorinvId(); + int seqNo = 1; + for (IostorInvWithDetailsSaveReqVO.DetailVO detail : reqVO.getDetails()) { + IostorinvDtlDO dtl = new IostorinvDtlDO(); + dtl.setIostorinvId(iostorinvId); + dtl.setSeqNo(seqNo++); + dtl.setMaterialCode(detail.getMaterialCode()); + dtl.setMaterialId(detail.getMaterialId()); + dtl.setPcsn(detail.getPcsn()); + dtl.setPlanQty(detail.getPlanQty()); + dtl.setUnassignQty(detail.getPlanQty()); // 初始未分配 = 计划数量 + dtl.setAssignQty(BigDecimal.ZERO); + dtl.setQtyUnitId(detail.getQtyUnitId()); + dtl.setQtyUnitName(detail.getQtyUnitName()); + dtl.setRemark(detail.getRemark()); + dtl.setSourceBillCode(detail.getSourceBillCode()); + dtl.setSourceBillType(detail.getSourceBillType()); + dtl.setSourceBilldtlId(detail.getSourceBilldtlId()); + iostorinvDtlMapper.insert(dtl); + } + + return iostorinvId; + } + } From 9e6a013a4ee5f43874710d276e3fc9329d48db63 Mon Sep 17 00:00:00 2001 From: zhouz <> Date: Wed, 22 Jul 2026 09:53:28 +0800 Subject: [PATCH 07/28] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=20createWithDe?= =?UTF-8?q?tails=20=E5=92=8C=20available-inventory=20=E7=AB=AF=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/iostorinv/IostorInvController.java | 8 ++++++++ .../iostorinvdtl/IostorinvDtlController.java | 16 ++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/IostorInvController.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/IostorInvController.java index 8f3cadd8..0740a4fe 100644 --- a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/IostorInvController.java +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinv/IostorInvController.java @@ -101,4 +101,12 @@ public class IostorInvController { BeanUtils.toBean(list, IostorInvRespVO.class)); } + @PostMapping("/create-with-details") + @Operation(summary = "创建出库单(含明细)") + @PreAuthorize("@ss.hasPermission('wms:iostor-inv:create')") + public CommonResult createIostorInvWithDetails( + @Valid @RequestBody IostorInvWithDetailsSaveReqVO reqVO) { + return success(iostorInvService.createIostorInvWithDetails(reqVO)); + } + } diff --git a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinvdtl/IostorinvDtlController.java b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinvdtl/IostorinvDtlController.java index eef62d4b..77932dd6 100644 --- a/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinvdtl/IostorinvDtlController.java +++ b/nl-module-wms/nl-module-wms-server/src/main/java/cn/code/nl/module/wms/controller/admin/iostorinvdtl/IostorinvDtlController.java @@ -38,6 +38,9 @@ public class IostorinvDtlController { @Resource private IostorinvDtlService iostorinvDtlService; + @Resource + private cn.code.nl.module.wms.dal.mysql.iostorinvdtl.IostorinvDtlMapper iostorinvDtlMapper; + @PostMapping("/create") @Operation(summary = "创建出入库单明细") @PreAuthorize("@ss.hasPermission('wms:iostorinv-dtl:create')") @@ -101,4 +104,17 @@ public class IostorinvDtlController { BeanUtils.toBean(list, IostorinvDtlRespVO.class)); } + @GetMapping("/available-inventory") + @Operation(summary = "查询可用库存(组盘关联仓位)") + @PreAuthorize("@ss.hasPermission('wms:iostor-inv:query')") + public CommonResult> getAvailableInventory( + @RequestParam("storId") String storId, + @RequestParam(value = "materialCode", required = false) String materialCode, + @RequestParam(value = "pcsn", required = false) String pcsn, + @RequestParam(value = "vehicleCode", required = false) String vehicleCode) { + List list = iostorinvDtlMapper.selectAvailableInventory( + storId, materialCode, pcsn, vehicleCode); + return success(list); + } + } \ No newline at end of file From c03e1a97cc446890e696e3ad726c9f4a70558d8e Mon Sep 17 00:00:00 2001 From: zhouz <> Date: Wed, 22 Jul 2026 09:53:50 +0800 Subject: [PATCH 08/28] =?UTF-8?q?feat:=20=E5=89=8D=E7=AB=AF=20API=20?= =?UTF-8?q?=E5=B1=82=E6=96=B0=E5=A2=9E=E5=87=BA=E5=BA=93=E5=8D=95=E5=B8=A6?= =?UTF-8?q?=E6=98=8E=E7=BB=86=E5=88=9B=E5=BB=BA=E5=92=8C=E5=8F=AF=E7=94=A8?= =?UTF-8?q?=E5=BA=93=E5=AD=98=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/api/wms/iostorinv/index.ts | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/api/wms/iostorinv/index.ts b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/api/wms/iostorinv/index.ts index 6afad210..654da506 100644 --- a/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/api/wms/iostorinv/index.ts +++ b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/api/wms/iostorinv/index.ts @@ -77,4 +77,69 @@ export function exportIostorInv(params: any) { return requestClient.download('/wms/iostor-inv/export-excel', { params }); } +// ========== 出库单带明细创建 ========== +/** 出库明细项 */ +export interface OutboundDetail { + materialCode: string; + materialId?: string; + pcsn?: string; + planQty: number; + qtyUnitId?: string; + qtyUnitName?: string; + remark?: string; + sourceBillCode?: string; + sourceBillType?: string; + sourceBilldtlId?: string; +} + +/** 出库单创建请求(含明细) */ +export interface OutboundCreateReq { + billType: string; + storId: string; + storCode?: string; + storName?: string; + bizDate: string; + remark?: string; + details: OutboundDetail[]; +} + +/** 可用库存项 */ +export interface AvailableInventory { + vehicleCode: string; + pcsn: string; + materialCode: string; + materialId: string; + qty: number; + frozenQty: number; + availableQty: number; + qtyUnitId: string; + qtyUnitName: string; + extCode: string; + extType: string; + extDtlCode: string; + storId: string; + storCode: string; + storName: string; +} + +/** 可用库存查询参数 */ +export interface AvailableInventoryQuery { + storId: string; + materialCode?: string; + pcsn?: string; + vehicleCode?: string; +} + +/** 创建出库单(含明细) */ +export function createIostorInvWithDetails(data: OutboundCreateReq) { + return requestClient.post('/wms/iostor-inv/create-with-details', data); +} + +/** 查询可用库存 */ +export function getAvailableInventory(params: AvailableInventoryQuery) { + return requestClient.get( + '/wms/iostorinv-dtl/available-inventory', + { params }, + ); +} \ No newline at end of file From e5e12fb799c02374c9428ff084501185e2533dc8 Mon Sep 17 00:00:00 2001 From: zhouz <> Date: Wed, 22 Jul 2026 09:56:04 +0800 Subject: [PATCH 09/28] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E6=89=8B?= =?UTF-8?q?=E5=8A=A8=E6=96=B0=E5=A2=9E=E6=B1=87=E6=80=BB=E5=BC=B9=E7=AA=97?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=20manual-detail.vue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 使用 useVbenModal + useVbenForm 组合 - 集成 MaterialSelectModal 进行物料选择 - 支持物料、出库重量、单位、批次、备注字段 - 单位自动从物料信息带出 - 弹窗打开时重置表单 --- .../wms/iostorinv/modules/manual-detail.vue | 150 ++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/iostorinv/modules/manual-detail.vue diff --git a/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/iostorinv/modules/manual-detail.vue b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/iostorinv/modules/manual-detail.vue new file mode 100644 index 00000000..a49ac7a1 --- /dev/null +++ b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/iostorinv/modules/manual-detail.vue @@ -0,0 +1,150 @@ + + + From ea9ae910896350011756f081a9a28bc3157a362d Mon Sep 17 00:00:00 2001 From: zhouz <> Date: Wed, 22 Jul 2026 09:57:44 +0800 Subject: [PATCH 10/28] =?UTF-8?q?feat:=20=E9=87=8D=E6=9E=84=E5=87=BA?= =?UTF-8?q?=E5=BA=93=E5=8D=95=E6=96=B0=E5=A2=9E=E9=A1=B5=E9=9D=A2=EF=BC=88?= =?UTF-8?q?form.vue=20+=20=E5=BA=93=E5=AD=98=E9=80=89=E6=8B=A9=20+=20?= =?UTF-8?q?=E6=89=8B=E5=8A=A8=E6=B1=87=E6=80=BB=E7=BB=84=E4=BB=B6=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/views/wms/iostorinv/modules/form.vue | 298 ++++++++++++++++-- .../iostorinv/modules/inventory-select.vue | 110 +++++++ 2 files changed, 373 insertions(+), 35 deletions(-) create mode 100644 nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/iostorinv/modules/inventory-select.vue diff --git a/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/iostorinv/modules/form.vue b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/iostorinv/modules/form.vue index dc52fb96..c38f46fb 100644 --- a/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/iostorinv/modules/form.vue +++ b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/iostorinv/modules/form.vue @@ -1,82 +1,310 @@ diff --git a/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/iostorinv/modules/inventory-select.vue b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/iostorinv/modules/inventory-select.vue new file mode 100644 index 00000000..fb95bcb1 --- /dev/null +++ b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/iostorinv/modules/inventory-select.vue @@ -0,0 +1,110 @@ + + + From 31a3906e24de6b8e5baeecf08f8c16c19dba98de Mon Sep 17 00:00:00 2001 From: zhouz <> Date: Wed, 22 Jul 2026 10:35:46 +0800 Subject: [PATCH 11/28] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E8=A1=A8?= =?UTF-8?q?=E5=8D=95=E5=B8=83=E5=B1=80=E3=80=81=E6=97=A5=E6=9C=9F=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E5=80=BC=E3=80=81=E5=AD=90=E5=BC=B9=E7=AA=97=E6=89=93?= =?UTF-8?q?=E5=BC=80/=E5=85=B3=E9=97=AD=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 业务日期默认当天(dayjs) - 表单 col-span-1 多列布局 - 子弹窗改为 ref + defineExpose 调用,解决 connectedComponent 冲突 - 物料选择弹窗路径修正 --- .../src/views/wms/iostorinv/modules/form.vue | 62 ++++++++++++------- .../iostorinv/modules/inventory-select.vue | 6 ++ .../wms/iostorinv/modules/manual-detail.vue | 23 +++---- 3 files changed, 57 insertions(+), 34 deletions(-) diff --git a/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/iostorinv/modules/form.vue b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/iostorinv/modules/form.vue index c38f46fb..66005e0c 100644 --- a/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/iostorinv/modules/form.vue +++ b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/iostorinv/modules/form.vue @@ -5,10 +5,10 @@ import { computed, ref, watch } from 'vue'; import { useVbenModal } from '@vben/common-ui'; +import dayjs from 'dayjs'; import { message } from 'antdv-next'; import { useVbenForm } from '#/adapter/form'; -import { useVbenVxeGrid } from '#/adapter/vxe-table'; import { type OutboundCreateReq, type OutboundDetail, @@ -18,11 +18,10 @@ import { getBsrealStorAttrSimpleList } from '#/api/wms/bsrealstorattr'; import { getSimpleDictDataList } from '#/api/system/dict/data'; import { $t } from '#/locales'; -import InventorySelect from './inventory-select.vue'; -import ManualDetail from './manual-detail.vue'; +import InventorySelectModalComponent from './inventory-select.vue'; +import ManualDetailModalComponent from './manual-detail.vue'; const emit = defineEmits(['success']); -const formData = ref(); // ========== 仓库选项 ========== const storOptions = ref< @@ -51,7 +50,7 @@ async function loadBillTypeOptions() { const [Form, formApi] = useVbenForm({ commonConfig: { componentProps: { class: 'w-full' }, - formItemClass: 'col-span-2', + formItemClass: 'col-span-1', labelWidth: 80, }, layout: 'horizontal', @@ -87,7 +86,6 @@ const [Form, formApi] = useVbenForm({ fieldName: 'bizDate', label: '业务日期', rules: 'required', - defaultValue: new Date(), component: 'DatePicker', componentProps: { showTime: true, @@ -127,7 +125,8 @@ const [Form, formApi] = useVbenForm({ }); // ========== 明细数据 ========== -const detailList = ref<(OutboundDetail & { vehicleCode?: string; materialName?: string })[]>([]); +type DetailRow = OutboundDetail & { vehicleCode?: string; materialName?: string }; +const detailList = ref([]); function updateSummary() { const totalWeight = detailList.value @@ -149,6 +148,7 @@ watch( ); // ========== 明细表格 ========== +const vxeGridRef = ref(); const gridOptions = computed(() => ({ columns: [ { type: 'seq', title: '序号', width: 50 }, @@ -164,6 +164,8 @@ const gridOptions = computed(() => ({ data: detailList.value, height: 'auto', rowConfig: { keyField: 'materialCode', isHover: true }, + toolbarConfig: false, + pagerConfig: false, })); function handleDeleteDetail(index: number) { @@ -171,11 +173,8 @@ function handleDeleteDetail(index: number) { updateSummary(); } -// ========== 库存选择弹窗 ========== -const [InventorySelectModal, inventorySelectModalApi] = useVbenModal({ - connectedComponent: InventorySelect, - destroyOnClose: true, -}); +// ========== 库存选择弹窗(子组件自带 Modal,用 ref 调用 open) ========== +const inventorySelectRef = ref>(); function handleSelectFromInventory() { const storId = formApi.getValues().storId; @@ -183,7 +182,7 @@ function handleSelectFromInventory() { message.warning('请先选择仓库'); return; } - inventorySelectModalApi.setData({ storId }).open(); + inventorySelectRef.value?.open({ storId }); } function onInventorySelected(rows: any[]) { @@ -205,18 +204,24 @@ function onInventorySelected(rows: any[]) { updateSummary(); } -// ========== 手动新增弹窗 ========== -const [ManualDetailModal, manualDetailModalApi] = useVbenModal({ - connectedComponent: ManualDetail, - destroyOnClose: true, -}); +// ========== 手动新增弹窗(子组件自带 Modal,用 ref 调用 open) ========== +const manualDetailRef = ref>(); function handleManualAdd() { - manualDetailModalApi.setData({}).open(); + manualDetailRef.value?.open(); } function onManualAdded(detail: any) { - detailList.value.push(detail); + detailList.value.push({ + materialCode: detail.materialCode, + materialId: String(detail.materialId ?? ''), + materialName: detail.materialName, + planQty: detail.planQty, + qtyUnitId: String(detail.qtyUnitId ?? ''), + qtyUnitName: detail.qtyUnitName, + pcsn: detail.pcsn, + remark: detail.remark, + }); updateSummary(); } @@ -269,6 +274,10 @@ const [Modal, modalApi] = useVbenModal({ if (isOpen) { await loadStorOptions(); await loadBillTypeOptions(); + // 业务日期默认当天 + formApi.setValues({ + bizDate: dayjs().format('YYYY-MM-DD HH:mm:ss'), + }); } if (!isOpen) { detailList.value = []; @@ -294,7 +303,7 @@ const [Modal, modalApi] = useVbenModal({ - + diff --git a/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/iostorinv/modules/inventory-select.vue b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/iostorinv/modules/inventory-select.vue index fb95bcb1..c9081143 100644 --- a/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/iostorinv/modules/inventory-select.vue +++ b/nl-ui/nl-ui-admin-vben/yudao-ui-admin-vben/apps/web-antdv-next/src/views/wms/iostorinv/modules/inventory-select.vue @@ -101,6 +101,12 @@ const [Modal, modalApi] = useVbenModal({ } }, }); + +/** 暴露给父组件调用 */ +function open(data: { storId: string }) { + modalApi.setData(data).open(); +} +defineExpose({ open });