# 出库单新增页面 实现计划 > **面向 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)一致