Browse Source

出入库信息维护

klzhangweiya 1 month ago
parent
commit
06fe1ca9bd
33 changed files with 2179 additions and 5 deletions
  1. 9 0
      SERVER/ChickenFarmV3/vb-common/vb-common-core/src/main/java/cn/vber/common/core/constant/CacheNames.java
  2. 9 0
      SERVER/ChickenFarmV3/vb-common/vb-common-core/src/main/java/cn/vber/common/core/service/ItemsService.java
  3. 6 0
      SERVER/ChickenFarmV3/vb-common/vb-common-core/src/main/java/cn/vber/common/core/service/ProjectService.java
  4. 12 0
      SERVER/ChickenFarmV3/vb-common/vb-common-core/src/main/java/cn/vber/common/core/service/StoreHouseService.java
  5. 6 0
      SERVER/ChickenFarmV3/vb-common/vb-common-core/src/main/java/cn/vber/common/core/service/StoreUserService.java
  6. 6 0
      SERVER/ChickenFarmV3/vb-common/vb-common-core/src/main/java/cn/vber/common/core/service/SupplierService.java
  7. 5 0
      SERVER/ChickenFarmV3/vb-common/vb-common-translation/src/main/java/cn/vber/common/translation/constant/TransConstant.java
  8. 27 0
      SERVER/ChickenFarmV3/vb-common/vb-common-translation/src/main/java/cn/vber/common/translation/core/impl/ItemsTranslationImpl.java
  9. 23 0
      SERVER/ChickenFarmV3/vb-common/vb-common-translation/src/main/java/cn/vber/common/translation/core/impl/ProjectTranslationImpl.java
  10. 31 0
      SERVER/ChickenFarmV3/vb-common/vb-common-translation/src/main/java/cn/vber/common/translation/core/impl/StoreHouseTranslationImpl.java
  11. 24 0
      SERVER/ChickenFarmV3/vb-common/vb-common-translation/src/main/java/cn/vber/common/translation/core/impl/StoreUserTranslationImpl.java
  12. 24 0
      SERVER/ChickenFarmV3/vb-common/vb-common-translation/src/main/java/cn/vber/common/translation/core/impl/SupplierTranslationImpl.java
  13. 6 1
      SERVER/ChickenFarmV3/vb-common/vb-common-translation/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  14. 112 0
      SERVER/ChickenFarmV3/vb-modules/vb-base/src/main/java/cn/vber/base/controller/UnitInfoController.java
  15. 69 0
      SERVER/ChickenFarmV3/vb-modules/vb-base/src/main/java/cn/vber/base/domain/UnitInfo.java
  16. 65 0
      SERVER/ChickenFarmV3/vb-modules/vb-base/src/main/java/cn/vber/base/domain/bo/UnitInfoBo.java
  17. 75 0
      SERVER/ChickenFarmV3/vb-modules/vb-base/src/main/java/cn/vber/base/domain/vo/UnitInfoVo.java
  18. 80 0
      SERVER/ChickenFarmV3/vb-modules/vb-base/src/main/java/cn/vber/base/enums/StoreInOutStatusEnum.java
  19. 17 0
      SERVER/ChickenFarmV3/vb-modules/vb-base/src/main/java/cn/vber/base/mapper/UnitInfoMapper.java
  20. 48 0
      SERVER/ChickenFarmV3/vb-modules/vb-base/src/main/java/cn/vber/base/service/IUnitInfoService.java
  21. 137 0
      SERVER/ChickenFarmV3/vb-modules/vb-base/src/main/java/cn/vber/base/service/impl/UnitInfoServiceImpl.java
  22. 7 0
      SERVER/ChickenFarmV3/vb-modules/vb-base/src/main/resources/mapper/UnitInfoMapper.xml
  23. 6 0
      SERVER/ChickenFarmV3/vb-modules/vb-erp/pom.xml
  24. 17 1
      SERVER/ChickenFarmV3/vb-modules/vb-erp/src/main/java/cn/vber/erp/service/impl/ProjectServiceImpl.java
  25. 16 1
      SERVER/ChickenFarmV3/vb-modules/vb-erp/src/main/java/cn/vber/erp/service/impl/StoreUserServiceImpl.java
  26. 18 2
      SERVER/ChickenFarmV3/vb-modules/vb-erp/src/main/java/cn/vber/erp/service/impl/SupplierServiceImpl.java
  27. 9 0
      UI/VB.VUE/src/api/erp/_project.ts
  28. 9 0
      UI/VB.VUE/src/api/erp/_storeUser.ts
  29. 159 0
      UI/VB.VUE/src/components/modal-select/StoreInOutHeaderSelect.vue
  30. 1122 0
      UI/VB.VUE/src/views/erp/store/inOutStore/_storeOut.vue
  31. 7 0
      UI/VB.VUE/src/views/erp/store/inOutStore/storeInAudit.vue
  32. 9 0
      UI/VB.VUE/src/views/erp/store/inOutStore/storeOut.vue
  33. 9 0
      UI/VB.VUE/src/views/erp/store/inOutStore/storeOutAudit.vue

+ 9 - 0
SERVER/ChickenFarmV3/vb-common/vb-common-core/src/main/java/cn/vber/common/core/constant/CacheNames.java

@@ -108,5 +108,14 @@ public interface CacheNames {
 
     String CHICKEN_COOP = "chicken_coop_info";
     String UNIT_ID_INFO = "unit_id_info";
+    String STORE_HOUSE_ID_TO_NAME = "store_house_id_name";
+    String STORE_HOUSE_ID_TO_NUM = "store_house_id_num";
+    String ITEMS_ID_TO_UNIT_ID = "items_id_to_unit_id";
+    String ITEMS_ID_TO_SPEC = "items_id_to_spec";
+    String ITEMS_ID_TO_NAME = "items_id_to_name";
+    String STORE_USER_ID_TO_NAME = "store_user_id_to_name";
+    String PROJECT_ID_TO_NAME = "project_id_to_name";
+    String SUPPLIER_ID_TO_NAME = "supplier_id_to_name";
+
 
 }

+ 9 - 0
SERVER/ChickenFarmV3/vb-common/vb-common-core/src/main/java/cn/vber/common/core/service/ItemsService.java

@@ -0,0 +1,9 @@
+package cn.vber.common.core.service;
+
+public interface ItemsService {
+
+    public String getItemNameByItemInfoId(Long id);
+    public String getSpecByItemInfoId(Long id);
+    public String getUnitIdByItemInfoId(Long id);
+    String getUnitNameById(Long id);
+}

+ 6 - 0
SERVER/ChickenFarmV3/vb-common/vb-common-core/src/main/java/cn/vber/common/core/service/ProjectService.java

@@ -0,0 +1,6 @@
+package cn.vber.common.core.service;
+
+public interface ProjectService {
+
+    String getProjectNameById(Long id);
+}

+ 12 - 0
SERVER/ChickenFarmV3/vb-common/vb-common-core/src/main/java/cn/vber/common/core/service/StoreHouseService.java

@@ -0,0 +1,12 @@
+package cn.vber.common.core.service;
+
+import cn.vber.common.core.constant.CacheNames;
+import org.springframework.cache.annotation.Cacheable;
+
+public interface StoreHouseService {
+    String getStoreNumById(Long id);
+    String getStoreNameById(Long id);
+
+
+
+}

+ 6 - 0
SERVER/ChickenFarmV3/vb-common/vb-common-core/src/main/java/cn/vber/common/core/service/StoreUserService.java

@@ -0,0 +1,6 @@
+package cn.vber.common.core.service;
+
+public interface StoreUserService {
+
+    String getStoreUserNameById(Long id);
+}

+ 6 - 0
SERVER/ChickenFarmV3/vb-common/vb-common-core/src/main/java/cn/vber/common/core/service/SupplierService.java

@@ -0,0 +1,6 @@
+package cn.vber.common.core.service;
+
+public interface SupplierService {
+
+    String getSupplierNameById(Long id);
+}

+ 5 - 0
SERVER/ChickenFarmV3/vb-common/vb-common-translation/src/main/java/cn/vber/common/translation/constant/TransConstant.java

@@ -55,5 +55,10 @@ public interface TransConstant {
     String BATCH_SOP_ID_SOP_NAME = "batch_sop_id_to_sop_name";
     String CHICKEN_SOP_ID_SOP_NAME = "chicken_sop_id_to_sop_name";
     String UNIT_ID_TO_NAME = "unit_id_to_name";
+    String STORE_HOUSE_ID_TO_INFO = "store_house_id_to_info";
+    String ITEMS_ID_TO_INFO = "items_id_to_info";
+    String STORE_USER_ID_TO_INFO = "store_user_id_to_info";
+    String PROJECT_ID_TO_INFO = "project_id_to_info";
+    String SUPPLIER_ID_TO_INFO = "supplier_id_to_info";
 
 }

+ 27 - 0
SERVER/ChickenFarmV3/vb-common/vb-common-translation/src/main/java/cn/vber/common/translation/core/impl/ItemsTranslationImpl.java

@@ -0,0 +1,27 @@
+package cn.vber.common.translation.core.impl;
+
+import cn.vber.common.core.service.ItemsService;
+import cn.vber.common.translation.annotation.TranslationType;
+import cn.vber.common.translation.constant.TransConstant;
+import cn.vber.common.translation.core.TranslationInterface;
+import lombok.AllArgsConstructor;
+
+@AllArgsConstructor
+@TranslationType(type = TransConstant.ITEMS_ID_TO_INFO)
+public class ItemsTranslationImpl implements TranslationInterface<String> {
+    private final ItemsService itemsService;
+
+    @Override
+    public String translation(Object key, String other) {
+        if (key instanceof Long id) {
+            return switch (other) {
+                case "unitName" -> itemsService.getUnitNameById(id);
+                case "unitId" -> itemsService.getUnitIdByItemInfoId(id);
+                case "name" -> itemsService.getItemNameByItemInfoId(id);
+                case "spec" -> itemsService.getSpecByItemInfoId(id);
+                default -> itemsService.getItemNameByItemInfoId(id);
+            };
+        }
+        return null;
+    }
+}

+ 23 - 0
SERVER/ChickenFarmV3/vb-common/vb-common-translation/src/main/java/cn/vber/common/translation/core/impl/ProjectTranslationImpl.java

@@ -0,0 +1,23 @@
+package cn.vber.common.translation.core.impl;
+
+import cn.vber.common.core.service.ProjectService;
+import cn.vber.common.translation.annotation.TranslationType;
+import cn.vber.common.translation.constant.TransConstant;
+import cn.vber.common.translation.core.TranslationInterface;
+import lombok.AllArgsConstructor;
+
+@AllArgsConstructor
+@TranslationType(type = TransConstant.PROJECT_ID_TO_INFO)
+public class ProjectTranslationImpl implements TranslationInterface<String> {
+    private final ProjectService projectService;
+    @Override
+    public String translation(Object key, String other) {
+        if (key instanceof Long id) {
+            return switch (other) {
+                case "name" -> projectService.getProjectNameById(id);
+                default -> projectService.getProjectNameById(id);
+            };
+        }
+        return null;
+    }
+}

+ 31 - 0
SERVER/ChickenFarmV3/vb-common/vb-common-translation/src/main/java/cn/vber/common/translation/core/impl/StoreHouseTranslationImpl.java

@@ -0,0 +1,31 @@
+package cn.vber.common.translation.core.impl;
+
+import cn.vber.common.core.service.StoreHouseService;
+import cn.vber.common.core.service.UnitService;
+import cn.vber.common.translation.annotation.TranslationType;
+import cn.vber.common.translation.constant.TransConstant;
+import cn.vber.common.translation.core.TranslationInterface;
+import lombok.AllArgsConstructor;
+
+@AllArgsConstructor
+@TranslationType(type = TransConstant.STORE_HOUSE_ID_TO_INFO)
+public class StoreHouseTranslationImpl implements TranslationInterface<String> {
+
+    private final StoreHouseService storeHouseService;
+
+    @Override
+    public String translation(Object key, String other) {
+        if (key instanceof Long id) {
+            switch (other){
+                case "storeHouseName" -> {
+                    return storeHouseService.getStoreNameById(id);
+                }
+                case "storeHouseNum" -> {
+                    return storeHouseService.getStoreNumById(id);
+                }
+            }
+            return storeHouseService.getStoreNameById(id);
+        }
+        return null;
+    }
+}

+ 24 - 0
SERVER/ChickenFarmV3/vb-common/vb-common-translation/src/main/java/cn/vber/common/translation/core/impl/StoreUserTranslationImpl.java

@@ -0,0 +1,24 @@
+package cn.vber.common.translation.core.impl;
+
+import cn.vber.common.core.service.StoreUserService;
+import cn.vber.common.translation.annotation.TranslationType;
+import cn.vber.common.translation.constant.TransConstant;
+import cn.vber.common.translation.core.TranslationInterface;
+import lombok.AllArgsConstructor;
+
+@AllArgsConstructor
+@TranslationType(type = TransConstant.STORE_USER_ID_TO_INFO)
+public class StoreUserTranslationImpl implements TranslationInterface<String> {
+
+    private final StoreUserService storeUserService;
+    @Override
+    public String translation(Object key, String other) {
+        if (key instanceof Long id) {
+            return switch (other) {
+                case "name" -> storeUserService.getStoreUserNameById(id);
+                default -> storeUserService.getStoreUserNameById(id);
+            };
+        }
+        return null;
+    }
+}

+ 24 - 0
SERVER/ChickenFarmV3/vb-common/vb-common-translation/src/main/java/cn/vber/common/translation/core/impl/SupplierTranslationImpl.java

@@ -0,0 +1,24 @@
+package cn.vber.common.translation.core.impl;
+
+import cn.vber.common.core.service.SupplierService;
+import cn.vber.common.translation.annotation.TranslationType;
+import cn.vber.common.translation.constant.TransConstant;
+import cn.vber.common.translation.core.TranslationInterface;
+import lombok.AllArgsConstructor;
+
+@AllArgsConstructor
+@TranslationType(type = TransConstant.SUPPLIER_ID_TO_INFO)
+public class SupplierTranslationImpl  implements TranslationInterface<String> {
+
+    private final SupplierService supplierService;
+    @Override
+    public String translation(Object key, String other) {
+        if (key instanceof Long id) {
+            return switch (other) {
+                case "name" -> supplierService.getSupplierNameById(id);
+                default -> supplierService.getSupplierNameById(id);
+            };
+        }
+        return null;
+    }
+}

+ 6 - 1
SERVER/ChickenFarmV3/vb-common/vb-common-translation/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

@@ -18,4 +18,9 @@ cn.vber.common.translation.core.impl.BatchSopNameTranslationImpl
 cn.vber.common.translation.core.impl.ChickenInfoTranslationImpl
 cn.vber.common.translation.core.impl.ChickenSopNameTranslationImpl
 cn.vber.common.translation.core.impl.DeviceTranslationImpl
-cn.vber.common.translation.core.impl.UnitTranslationImpl
+cn.vber.common.translation.core.impl.UnitTranslationImpl
+cn.vber.common.translation.core.impl.StoreHouseTranslationImpl
+cn.vber.common.translation.core.impl.ItemsTranslationImpl
+cn.vber.common.translation.core.impl.SupplierTranslationImpl
+cn.vber.common.translation.core.impl.StoreUserTranslationImpl
+cn.vber.common.translation.core.impl.ProjectTranslationImpl

+ 112 - 0
SERVER/ChickenFarmV3/vb-modules/vb-base/src/main/java/cn/vber/base/controller/UnitInfoController.java

@@ -0,0 +1,112 @@
+package cn.vber.base.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.vber.common.core.domain.R;
+import cn.vber.common.core.validate.AddGroup;
+import cn.vber.common.core.validate.EditGroup;
+import cn.vber.common.excel.utils.ExcelUtil;
+import cn.vber.common.idempotent.annotation.RepeatSubmit;
+import cn.vber.common.log.annotation.Log;
+import cn.vber.common.log.enums.BusinessType;
+import cn.vber.common.mybatis.core.page.PageQuery;
+import cn.vber.common.mybatis.core.page.TableDataInfo;
+import cn.vber.common.web.core.BaseController;
+import cn.vber.base.domain.bo.UnitInfoBo;
+import cn.vber.base.domain.vo.UnitInfoVo;
+import cn.vber.base.service.IUnitInfoService;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import lombok.RequiredArgsConstructor;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 计量单位管理
+ *
+ * @author IwbY
+ * @date 2025-10-23
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/base/unitInfo")
+public class UnitInfoController extends BaseController {
+
+    private final IUnitInfoService unitInfoService;
+
+    /**
+     * 查询计量单位管理列表
+     */
+    @SaCheckPermission("base:unitInfo")
+    @GetMapping("/list")
+    public TableDataInfo<UnitInfoVo> list(UnitInfoBo bo, PageQuery pageQuery) {
+        return unitInfoService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 获取计量单位管理详细信息
+     */
+    @GetMapping("/options")
+    public List<UnitInfoVo> listOptions() {
+        return unitInfoService.queryList(new UnitInfoBo());
+    }
+
+    /**
+     * 导出计量单位管理列表
+     */
+    @SaCheckPermission("base:unitInfo:export")
+    @Log(title = "计量单位管理", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(UnitInfoBo bo, HttpServletResponse response) {
+        List<UnitInfoVo> list = unitInfoService.queryList(bo);
+        ExcelUtil.exportExcel(list, "计量单位管理", UnitInfoVo.class, response);
+    }
+
+    /**
+     * 获取计量单位管理详细信息
+     *
+     * @param id 主键
+     */
+    @SaCheckPermission("base:unitInfo:query")
+    @GetMapping("/{id}")
+    public R<UnitInfoVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) {
+        return R.ok(unitInfoService.queryById(id));
+    }
+
+    /**
+     * 新增计量单位管理
+     */
+    @SaCheckPermission("base:unitInfo:add")
+    @Log(title = "计量单位管理", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping()
+    public R<Void> add(@Validated(AddGroup.class) @RequestBody UnitInfoBo bo) {
+        return toAjax(unitInfoService.insertByBo(bo));
+    }
+
+    /**
+     * 修改计量单位管理
+     */
+    @SaCheckPermission("base:unitInfo:edit")
+    @Log(title = "计量单位管理", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping()
+    public R<Void> edit(@Validated(EditGroup.class) @RequestBody UnitInfoBo bo) {
+        return toAjax(unitInfoService.updateByBo(bo));
+    }
+
+    /**
+     * 删除计量单位管理
+     *
+     * @param ids 主键串
+     */
+    @SaCheckPermission("base:unitInfo:remove")
+    @Log(title = "计量单位管理", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public R<Void> remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) {
+        return toAjax(unitInfoService.deleteWithValidByIds(List.of(ids), true));
+    }
+}

+ 69 - 0
SERVER/ChickenFarmV3/vb-modules/vb-base/src/main/java/cn/vber/base/domain/UnitInfo.java

@@ -0,0 +1,69 @@
+package cn.vber.base.domain;
+
+import cn.vber.common.mybatis.core.domain.BaseEntity;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serial;
+
+/**
+ * 计量单位管理对象 erp_unit_info
+ *
+ * @author IwbY
+ * @date 2025-10-23
+ */
+        @Data
+        @EqualsAndHashCode(callSuper = true)
+        @TableName("erp_unit_info")
+        public class UnitInfo extends BaseEntity {
+
+        @Serial
+        private static final long serialVersionUID = 1L;
+
+                /**
+                 * 
+                 */
+            @TableId(value = "id")
+        private Long id;
+
+                /**
+                 * 单位名称
+                 */
+        private String name;
+
+                /**
+                 * 符号
+                 */
+        private String unitSymbol;
+
+                /**
+                 * 是否国际标准(0:否 1:是)
+                 */
+        private Integer golbalFlag;
+
+                /**
+                 * 与国际标准的转换率
+                 */
+        private Double conversionRatio;
+
+                /**
+                 * 单位类型
+                 */
+        private Integer unitType;
+
+                /**
+                 * 备注
+                 */
+        private String remark;
+
+                /**
+                 * 删除标志(0:未删除, 1:已删除)
+                 */
+            @TableLogic
+        private String delFlag;
+
+
+        }

+ 65 - 0
SERVER/ChickenFarmV3/vb-modules/vb-base/src/main/java/cn/vber/base/domain/bo/UnitInfoBo.java

@@ -0,0 +1,65 @@
+package cn.vber.base.domain.bo;
+
+import cn.vber.common.core.validate.AddGroup;
+import cn.vber.common.core.validate.EditGroup;
+import cn.vber.common.mybatis.core.domain.BaseEntity;
+import cn.vber.base.domain.UnitInfo;
+import io.github.linpeilie.annotations.AutoMapper;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 计量单位管理业务对象 erp_unit_info
+ *
+ * @author IwbY
+ * @date 2025-10-23
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = UnitInfo.class,reverseConvertGenerate =false)
+public class UnitInfoBo extends BaseEntity {
+
+    /**
+     *
+     */
+    @NotNull(message = "不能为空", groups = {EditGroup.class})
+    private Long id;
+
+    /**
+     * 单位名称
+     */
+    @NotBlank(message = "单位名称不能为空", groups = {AddGroup.class, EditGroup.class})
+    private String name;
+
+    /**
+     * 符号
+     */
+    @NotBlank(message = "符号不能为空", groups = {AddGroup.class, EditGroup.class})
+    private String unitSymbol;
+
+    /**
+     * 是否国际标准(0:否 1:是)
+     */
+    @NotNull(message = "是否国际标准(0:否 1:是)不能为空", groups = {AddGroup.class, EditGroup.class})
+    private Integer golbalFlag;
+
+    /**
+     * 与国际标准的转换率
+     */
+    private Double conversionRatio;
+
+    /**
+     * 单位类型
+     */
+    @NotNull(message = "单位类型不能为空", groups = {AddGroup.class, EditGroup.class})
+    private Integer unitType;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+
+}

+ 75 - 0
SERVER/ChickenFarmV3/vb-modules/vb-base/src/main/java/cn/vber/base/domain/vo/UnitInfoVo.java

@@ -0,0 +1,75 @@
+package cn.vber.base.domain.vo;
+
+import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
+import cn.idev.excel.annotation.ExcelProperty;
+import cn.vber.common.excel.annotation.ExcelDictFormat;
+import cn.vber.common.excel.convert.ExcelDictConvert;
+import cn.vber.base.domain.UnitInfo;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+
+/**
+ * 计量单位管理视图对象 erp_unit_info
+ *
+ * @author IwbY
+ * @date 2025-10-23
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = UnitInfo.class)
+
+public class UnitInfoVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+            /**
+             * 
+             */
+            @ExcelProperty(value = "")
+        private Long id;
+
+            /**
+             * 单位名称
+             */
+            @ExcelProperty(value = "单位名称")
+        private String name;
+
+            /**
+             * 符号
+             */
+            @ExcelProperty(value = "符号")
+        private String unitSymbol;
+
+            /**
+             * 是否国际标准(0:否 1:是)
+             */
+            @ExcelProperty(value = "是否国际标准", converter = ExcelDictConvert.class)
+            @ExcelDictFormat(dictType = "sys_yes_no")
+        private Integer golbalFlag;
+
+            /**
+             * 与国际标准的转换率
+             */
+            @ExcelProperty(value = "与国际标准的转换率")
+        private Double conversionRatio;
+
+            /**
+             * 单位类型
+             */
+            @ExcelProperty(value = "单位类型", converter = ExcelDictConvert.class)
+            @ExcelDictFormat(dictType = "unit_type")
+        private Integer unitType;
+
+            /**
+             * 备注
+             */
+            @ExcelProperty(value = "备注")
+        private String remark;
+
+
+}

+ 80 - 0
SERVER/ChickenFarmV3/vb-modules/vb-base/src/main/java/cn/vber/base/enums/StoreInOutStatusEnum.java

@@ -0,0 +1,80 @@
+package cn.vber.base.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 批次状态枚举
+ */
+@Getter
+@AllArgsConstructor
+public enum StoreInOutStatusEnum {
+    /**
+     * 新建状态
+     */
+    WAIT_AUDIT(1, "待审核"),
+    /**
+     * 通过状态
+     */
+    PASSED(2, "通过"),
+    /**
+     * 未通过状态
+     */
+    UNPASSED(3, "未通过"),
+    /**
+     * 作废状态
+     */
+    REVOKE(4, "作废");
+
+    private final Integer code;
+    private final String description;
+
+    /**
+     * 根据code获取枚举对象
+     *
+     * @param code 状态码
+     * @return 对应的枚举对象,未找到返回null
+     */
+    public static StoreInOutStatusEnum fromCode(Integer code) {
+        if (code == null) {
+            return null;
+        }
+        for (StoreInOutStatusEnum status : StoreInOutStatusEnum.values()) {
+            if (status.getCode().equals(code)) {
+                return status;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 判断是否为绑定状态
+     * @param code 状态编码
+     * @return 是否为绑定状态
+     */
+    public static boolean isWaitAudit(Integer code) {
+        return WAIT_AUDIT.getCode().equals(code);
+    }
+
+    public static boolean isPASSED(Integer code) {
+        return PASSED.getCode().equals(code);
+    }
+
+    public static boolean isUNPASSED(Integer code) {
+        return UNPASSED.getCode().equals(code);
+    }
+
+    public static boolean isREVOKE(Integer code) {
+        return REVOKE.getCode().equals(code);
+    }
+
+
+
+    @Override
+    public String toString() {
+        return "StoreInOutStatusEnum{" +
+                "code=" + code +
+                ", description='" + description + '\'' +
+                '}';
+    }
+}

+ 17 - 0
SERVER/ChickenFarmV3/vb-modules/vb-base/src/main/java/cn/vber/base/mapper/UnitInfoMapper.java

@@ -0,0 +1,17 @@
+package cn.vber.base.mapper;
+
+import cn.vber.common.mybatis.core.mapper.BaseMapperPlus;
+import cn.vber.base.domain.UnitInfo;
+import cn.vber.base.domain.vo.UnitInfoVo;
+import org.springframework.stereotype.Repository;
+
+/**
+ * 计量单位管理Mapper接口
+ *
+ * @author IwbY
+ * @date 2025-10-23
+ */
+@Repository
+public interface UnitInfoMapper extends BaseMapperPlus<UnitInfo, UnitInfoVo> {
+
+}

+ 48 - 0
SERVER/ChickenFarmV3/vb-modules/vb-base/src/main/java/cn/vber/base/service/IUnitInfoService.java

@@ -0,0 +1,48 @@
+package cn.vber.base.service;
+
+import cn.vber.common.mybatis.core.page.PageQuery;
+import cn.vber.common.mybatis.core.page.TableDataInfo;
+import cn.vber.base.domain.bo.UnitInfoBo;
+import cn.vber.base.domain.vo.UnitInfoVo;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 计量单位管理Service接口
+ *
+ * @author IwbY
+ * @date 2025-10-23
+ */
+public interface IUnitInfoService {
+
+    /**
+     * 查询计量单位管理
+     */
+        UnitInfoVo queryById(Long id);
+
+        /**
+         * 查询计量单位管理列表
+         */
+        TableDataInfo<UnitInfoVo> queryPageList(UnitInfoBo bo, PageQuery pageQuery);
+
+    /**
+     * 查询计量单位管理列表
+     */
+    List<UnitInfoVo> queryList(UnitInfoBo bo);
+
+    /**
+     * 新增计量单位管理
+     */
+    Boolean insertByBo(UnitInfoBo bo);
+
+    /**
+     * 修改计量单位管理
+     */
+    Boolean updateByBo(UnitInfoBo bo);
+
+    /**
+     * 校验并批量删除计量单位管理信息
+     */
+    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
+}

+ 137 - 0
SERVER/ChickenFarmV3/vb-modules/vb-base/src/main/java/cn/vber/base/service/impl/UnitInfoServiceImpl.java

@@ -0,0 +1,137 @@
+package cn.vber.base.service.impl;
+
+import cn.vber.common.core.constant.CacheNames;
+import cn.vber.common.core.exception.ServiceException;
+import cn.vber.common.core.service.UnitService;
+import cn.vber.common.core.utils.MapstructUtils;
+import cn.vber.common.core.utils.StringUtils;
+import cn.vber.common.mybatis.core.page.PageQuery;
+import cn.vber.common.mybatis.core.page.TableDataInfo;
+import cn.vber.base.domain.UnitInfo;
+import cn.vber.base.domain.bo.UnitInfoBo;
+import cn.vber.base.domain.vo.UnitInfoVo;
+import cn.vber.base.mapper.UnitInfoMapper;
+import cn.vber.base.service.IUnitInfoService;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import lombok.RequiredArgsConstructor;
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.stereotype.Service;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 计量单位管理Service业务层处理
+ *
+ * @author IwbY
+ * @date 2025-10-23
+ */
+@RequiredArgsConstructor
+@Service
+public class UnitInfoServiceImpl implements IUnitInfoService , UnitService {
+
+    private final UnitInfoMapper baseMapper;
+
+    /**
+     * 查询计量单位管理
+     */
+    @Override
+    public UnitInfoVo queryById(Long id) {
+        return baseMapper.selectVoById(id);
+    }
+
+    /**
+     * 查询计量单位管理列表
+     */
+    @Override
+    public TableDataInfo<UnitInfoVo> queryPageList(UnitInfoBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<UnitInfo> lqw = buildQueryWrapper(bo);
+        Page<UnitInfoVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+        return TableDataInfo.build(result);
+    }
+
+    /**
+     * 查询计量单位管理列表
+     */
+    @Override
+    public List<UnitInfoVo> queryList(UnitInfoBo bo) {
+        LambdaQueryWrapper<UnitInfo> lqw = buildQueryWrapper(bo);
+        return baseMapper.selectVoList(lqw);
+    }
+
+    private LambdaQueryWrapper<UnitInfo> buildQueryWrapper(UnitInfoBo bo) {
+        Map<String, Object> params = bo.getParams();
+        LambdaQueryWrapper<UnitInfo> lqw = Wrappers.lambdaQuery();
+                    lqw.like(StringUtils.isNotBlank(bo.getName()), UnitInfo::getName, bo.getName());
+                    lqw.eq(StringUtils.isNotBlank(bo.getUnitSymbol()), UnitInfo::getUnitSymbol, bo.getUnitSymbol());
+                    lqw.eq(bo.getGolbalFlag() != null, UnitInfo::getGolbalFlag, bo.getGolbalFlag());
+                    lqw.eq(bo.getConversionRatio() != null, UnitInfo::getConversionRatio, bo.getConversionRatio());
+                    lqw.eq(bo.getUnitType() != null, UnitInfo::getUnitType, bo.getUnitType());
+        return lqw;
+    }
+
+    /**
+     * 新增计量单位管理
+     */
+    @Override
+    @CacheEvict(value = CacheNames.UNIT_ID_INFO, key = "#bo.id")
+    public Boolean insertByBo(UnitInfoBo bo) {
+        UnitInfo add = MapstructUtils.convert(bo, UnitInfo. class);
+        validEntityBeforeSave(add);
+        boolean flag = baseMapper.insert(add) > 0;
+        if (flag) {
+            bo.setId(add.getId());
+        }
+        return flag;
+    }
+
+    /**
+     * 修改计量单位管理
+     */
+    @Override
+    @CacheEvict(cacheNames = CacheNames.UNIT_ID_INFO, key = "#bo.id")
+    public Boolean updateByBo(UnitInfoBo bo) {
+        UnitInfo update = MapstructUtils.convert(bo, UnitInfo. class);
+        validEntityBeforeSave(update);
+        return baseMapper.updateById(update) > 0;
+    }
+
+    /**
+     * 保存前的数据校验
+     */
+    private void validEntityBeforeSave(UnitInfo entity) {
+        //TODO 做一些数据校验,如唯一约束
+        Long id = entity.getId();
+        LambdaQueryWrapper<UnitInfo> lqw = new LambdaQueryWrapper<UnitInfo>().eq(UnitInfo::getName, entity.getName());
+
+        if(id != null){
+            lqw.ne(UnitInfo::getId, id);
+        }
+        UnitInfo s = baseMapper.selectOne(lqw);
+        if(s != null){
+            throw new ServiceException("相同的名称已存在!");
+        }
+    }
+
+    /**
+     * 批量删除计量单位管理
+     */
+    @Override
+    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
+        if (isValid) {
+            //TODO 做一些业务上的校验,判断是否需要校验
+        }
+        return baseMapper.deleteByIds(ids) > 0;
+    }
+
+    @Override
+    @Cacheable(cacheNames = CacheNames.UNIT_ID_INFO, key = "#id")
+    public String getUnitNameById(Long id) {
+        UnitInfoVo unitInfoVo = baseMapper.selectVoById(id);
+        return unitInfoVo.getName();
+    }
+}

+ 7 - 0
SERVER/ChickenFarmV3/vb-modules/vb-base/src/main/resources/mapper/UnitInfoMapper.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="cn.vber.base.mapper.UnitInfoMapper">
+
+</mapper>

+ 6 - 0
SERVER/ChickenFarmV3/vb-modules/vb-erp/pom.xml

@@ -96,6 +96,12 @@
             <groupId>com.vap</groupId>
             <artifactId>vb-common-sse</artifactId>
         </dependency>
+
+        <dependency>
+            <groupId>com.vap</groupId>
+            <artifactId>vb-base</artifactId>
+        </dependency>
+
     </dependencies>
 
 </project>

+ 17 - 1
SERVER/ChickenFarmV3/vb-modules/vb-erp/src/main/java/cn/vber/erp/service/impl/ProjectServiceImpl.java

@@ -1,15 +1,20 @@
 package cn.vber.erp.service.impl;
 
+import cn.vber.common.core.constant.CacheNames;
 import cn.vber.common.core.exception.ServiceException;
+import cn.vber.common.core.service.ProjectService;
 import cn.vber.common.core.utils.MapstructUtils;
 import cn.vber.common.core.utils.StringUtils;
     import cn.vber.common.mybatis.core.page.TableDataInfo;
     import cn.vber.common.mybatis.core.page.PageQuery;
 import cn.vber.erp.domain.StoreHouse;
+import cn.vber.erp.domain.Supplier;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import lombok.RequiredArgsConstructor;
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.cache.annotation.Cacheable;
 import org.springframework.stereotype.Service;
 import cn.vber.erp.domain.bo.ProjectBo;
 import cn.vber.erp.domain.vo.ProjectVo;
@@ -29,7 +34,7 @@ import java.util.Collection;
  */
 @RequiredArgsConstructor
 @Service
-public class ProjectServiceImpl implements IProjectService {
+public class ProjectServiceImpl implements IProjectService , ProjectService {
 
     private final ProjectMapper baseMapper;
 
@@ -89,6 +94,7 @@ public class ProjectServiceImpl implements IProjectService {
      * 修改特殊统计项目(如XX型新品种研发)
      */
     @Override
+    @CacheEvict(cacheNames = CacheNames.PROJECT_ID_TO_NAME, key = "#bo.id")
     public Boolean updateByBo(ProjectBo bo) {
         Project update = MapstructUtils.convert(bo, Project. class);
         validEntityBeforeSave(update);
@@ -122,4 +128,14 @@ public class ProjectServiceImpl implements IProjectService {
         }
         return baseMapper.deleteByIds(ids) > 0;
     }
+
+    @Override
+    @Cacheable(cacheNames = CacheNames.PROJECT_ID_TO_NAME, key = "#id")
+    public String getProjectNameById(Long id) {
+        Project project = baseMapper.selectById(id);
+        if (project != null) {
+            return project.getProjectName();
+        }
+        return "";
+    }
 }

+ 16 - 1
SERVER/ChickenFarmV3/vb-modules/vb-erp/src/main/java/cn/vber/erp/service/impl/StoreUserServiceImpl.java

@@ -1,6 +1,8 @@
 package cn.vber.erp.service.impl;
 
+import cn.vber.common.core.constant.CacheNames;
 import cn.vber.common.core.exception.ServiceException;
+import cn.vber.common.core.service.StoreUserService;
 import cn.vber.common.core.utils.MapstructUtils;
 import cn.vber.common.core.utils.StringUtils;
     import cn.vber.common.mybatis.core.page.TableDataInfo;
@@ -10,6 +12,8 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import lombok.RequiredArgsConstructor;
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.cache.annotation.Cacheable;
 import org.springframework.stereotype.Service;
 import cn.vber.erp.domain.bo.StoreUserBo;
 import cn.vber.erp.domain.vo.StoreUserVo;
@@ -29,7 +33,7 @@ import java.util.Collection;
  */
 @RequiredArgsConstructor
 @Service
-public class StoreUserServiceImpl implements IStoreUserService {
+public class StoreUserServiceImpl implements IStoreUserService, StoreUserService {
 
     private final StoreUserMapper baseMapper;
 
@@ -89,6 +93,7 @@ public class StoreUserServiceImpl implements IStoreUserService {
      * 修改用户管理
      */
     @Override
+    @CacheEvict(cacheNames = CacheNames.STORE_HOUSE_ID_TO_NAME, key = "#bo.id")
     public Boolean updateByBo(StoreUserBo bo) {
         StoreUser update = MapstructUtils.convert(bo, StoreUser. class);
         validEntityBeforeSave(update);
@@ -122,4 +127,14 @@ public class StoreUserServiceImpl implements IStoreUserService {
         }
         return baseMapper.deleteByIds(ids) > 0;
     }
+
+    @Override
+    @Cacheable(cacheNames = CacheNames.STORE_HOUSE_ID_TO_NAME, key = "#id")
+    public String getStoreUserNameById(Long id) {
+        StoreUser storeUser = baseMapper.selectById(id);
+        if (storeUser != null) {
+            return storeUser.getStoreUserName();
+        }
+        return "";
+    }
 }

+ 18 - 2
SERVER/ChickenFarmV3/vb-modules/vb-erp/src/main/java/cn/vber/erp/service/impl/SupplierServiceImpl.java

@@ -1,14 +1,19 @@
 package cn.vber.erp.service.impl;
 
+import cn.vber.common.core.constant.CacheNames;
 import cn.vber.common.core.exception.ServiceException;
+import cn.vber.common.core.service.SupplierService;
 import cn.vber.common.core.utils.MapstructUtils;
 import cn.vber.common.core.utils.StringUtils;
     import cn.vber.common.mybatis.core.page.TableDataInfo;
     import cn.vber.common.mybatis.core.page.PageQuery;
-    import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import cn.vber.erp.domain.StoreUser;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import lombok.RequiredArgsConstructor;
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.cache.annotation.Cacheable;
 import org.springframework.stereotype.Service;
 import cn.vber.erp.domain.bo.SupplierBo;
 import cn.vber.erp.domain.vo.SupplierVo;
@@ -28,7 +33,7 @@ import java.util.Collection;
  */
 @RequiredArgsConstructor
 @Service
-public class SupplierServiceImpl implements ISupplierService {
+public class SupplierServiceImpl implements ISupplierService, SupplierService {
 
     private final SupplierMapper baseMapper;
 
@@ -92,6 +97,7 @@ public class SupplierServiceImpl implements ISupplierService {
      * 修改供应商管理
      */
     @Override
+    @CacheEvict(cacheNames = CacheNames.SUPPLIER_ID_TO_NAME, key = "#bo.id")
     public Boolean updateByBo(SupplierBo bo) {
         Supplier update = MapstructUtils.convert(bo, Supplier. class);
         validEntityBeforeSave(update);
@@ -125,4 +131,14 @@ public class SupplierServiceImpl implements ISupplierService {
         }
         return baseMapper.deleteByIds(ids) > 0;
     }
+
+    @Override
+    @Cacheable(cacheNames = CacheNames.SUPPLIER_ID_TO_NAME, key = "#id")
+    public String getSupplierNameById(Long id) {
+        Supplier supplier = baseMapper.selectById(id);
+        if (supplier != null) {
+            return supplier.getSupplierName();
+        }
+        return "";
+    }
 }

+ 9 - 0
UI/VB.VUE/src/api/erp/_project.ts

@@ -13,6 +13,15 @@ class projectApi {
 		})
 	}
 
+	// 查询特殊统计项目(如XX型新品种研发)选项列表
+	listOptions = (query: any) => {
+		return Rs.get({
+			url: "/erp/project/options",
+			params: query,
+			loading: false
+		})
+	}
+
 	// 查询特殊统计项目(如XX型新品种研发)详细
 	get = (id: string) => {
 		return Rs.get({

+ 9 - 0
UI/VB.VUE/src/api/erp/_storeUser.ts

@@ -13,6 +13,15 @@ class storeUserApi {
 		})
 	}
 
+	// 查询用户管理选项列表
+	listOptions = (query: any) => {
+		return Rs.get({
+			url: "/erp/storeUser/options",
+			params: query,
+			loading: false
+		})
+	}
+
 	// 查询用户管理详细
 	get = (id: string) => {
 		return Rs.get({

+ 159 - 0
UI/VB.VUE/src/components/modal-select/StoreInOutHeaderSelect.vue

@@ -0,0 +1,159 @@
+<script setup lang="ts">
+import apis from "@a"
+
+const props = withDefaults(
+	defineProps<{
+		modelValue?: any[] | any
+		modalConfig?: any
+		multiple?: boolean
+		showTag?: boolean
+		optionSelectFun?: (v: any) => any // 自定义选项数据转换
+		convertDataFun?: (v: any) => any
+		searchFormItems?: any[]
+		saveAutoClose?: boolean
+		inOutStore?: number
+	}>(),
+	{
+		showTag: true,
+		multiple: false,
+		saveAutoClose: true,
+		inOutStore: 0
+	}
+)
+const emits = defineEmits<{
+	(e: "update:modelValue", v: any[]): void
+	(e: "confirm", v: any[]): void
+}>()
+const selectRef = ref()
+
+const selectIds = ref(props.modelValue)
+
+// 定义基础列
+const baseColumns = [
+	{ field: "id", name: "ID", width: 100, isSort: true, visible: false, tooltip: true },
+	{
+		field: "receiptNum",
+		name: "单据编号",
+		visible: true,
+		isSort: false,
+		width: 200,
+		tooltip: true
+	},
+	{
+		field: "receiptDate",
+		name: "单据日期",
+		visible: true,
+		isSort: false,
+		width: 200,
+		tooltip: true
+	},
+	{
+		field: "businessType",
+		name: "业务类型",
+		visible: true,
+		isSort: false,
+		width: 200,
+		tooltip: true
+	},
+	{
+		field: "storeHouseName",
+		name: "仓库名称",
+		visible: true,
+		isSort: false,
+		width: 200,
+		tooltip: true
+	},
+	{ field: "storeType", name: "出/入库类别", visible: true, isSort: false, width: 135 },
+	{ field: "status", name: "状态", visible: true, isSort: false, width: 100 }
+] as any[]
+
+// 根据inOutStore属性添加额外列
+const additionalColumns =
+	props.inOutStore == 1
+		? [
+				{
+					field: "projectName",
+					name: "项目名称",
+					visible: true,
+					isSort: false,
+					width: 100,
+					tooltip: true
+				},
+				{
+					field: "storeUserName",
+					name: "出库对象",
+					visible: true,
+					isSort: false,
+					width: 100,
+					tooltip: true
+				}
+			]
+		: [
+				{
+					field: "supplierName",
+					name: "供应商",
+					visible: true,
+					isSort: false,
+					width: 100,
+					tooltip: true
+				}
+			]
+
+const tableOpts = reactive({
+	columns: [...baseColumns, ...additionalColumns],
+	queryParams: {
+		receiptNum: undefined
+	},
+	searchFormItems:
+		props.searchFormItems ||
+		([
+			{
+				field: "receiptNum",
+				label: "单据编号",
+				class: "w-100",
+				required: false,
+				placeholder: "请输入单据编号",
+				listeners: {
+					keyup: (e: KeyboardEvent) => {
+						if (e.code == "Enter") {
+							selectRef.value.handleQuery()
+						}
+					}
+				},
+				span: 6
+			}
+		] as any),
+	customBtns: [],
+	tableListFun: props.inOutStore == 0 ? apis.erp.storeInApi.list : apis.erp.storeOutApi.list
+})
+const convertRowDataFun = (row: any) => {
+	return props.convertDataFun ? props.convertDataFun(row) : row
+}
+function open() {
+	selectRef.value.open()
+}
+function close() {
+	selectRef.value.close()
+}
+
+defineExpose({
+	open,
+	close
+})
+</script>
+<template>
+	<ModalSelect
+		ref="selectRef"
+		:table-opts="tableOpts"
+		modalTitle="选择入库单"
+		v-model="selectIds"
+		:modal-config="modalConfig"
+		:multiple="multiple"
+		:show-tag="showTag"
+		:tag-id="'id'"
+		:tag-name="'receiptNum'"
+		:convert-data-fun="convertRowDataFun"
+		:save-auto-close="saveAutoClose"
+		@update:modelValue="emits('update:modelValue', $event)"
+		@confirm="emits('confirm', $event)"></ModalSelect>
+</template>

+ 1122 - 0
UI/VB.VUE/src/views/erp/store/inOutStore/_storeOut.vue

@@ -0,0 +1,1122 @@
+<script setup lang="ts" name="StoreOut">
+import apis from "@a"
+import dayjs from "dayjs"
+import common from "./_common"
+import type { ToolBtn } from "@@@/table/models"
+import { Plus } from "@element-plus/icons-vue"
+import StoreInOutHeaderSelect from "@/components/modal-select/StoreInOutHeaderSelect.vue"
+
+const props = withDefaults(
+	defineProps<{
+		isAudit: boolean
+		actions: ("update" | "revoke" | "audit" | "reject" | "detail" | "reSubmit")[]
+		customBtns?: ToolBtn[]
+	}>(),
+	{
+		isAudit: false,
+		customBtns: () => {
+			return []
+		}
+	}
+)
+const tableRef = ref()
+const modalRef = ref()
+const detailModalRef = ref()
+const storeInOutHeaderSelectRef = ref()
+const isEdit = ref(false)
+const isReSubmit = ref(false)
+const detailForm = ref<any>({})
+const form = ref<any>({})
+const queryParams = ref<any>({
+	receiptNum: undefined,
+	dateRangeReceiptDate: undefined,
+	outBusinessType: undefined,
+	storeHouseId: undefined,
+	outStoreType: undefined,
+	projectId: undefined,
+	storeUserId: undefined,
+	dept: undefined,
+	preparedUser: undefined,
+	auditUser: undefined,
+	dateRangeAuditDate: undefined,
+	status: props.isAudit ? 1 : undefined
+})
+
+const opts = reactive({
+	columns: [
+		{ field: "id", name: "", width: 100, isSort: true, visible: false, tooltip: true },
+		{
+			field: "receiptNum",
+			name: "单据编号",
+			visible: true,
+			isSort: false,
+			width: "auto",
+			tooltip: true
+		},
+		{ field: "receiptDate", name: "单据日期", visible: true, isSort: false, width: 185 },
+		{ field: "businessType", name: "业务类型", visible: true, isSort: false, width: 100 },
+		{
+			field: "storeHouseName",
+			name: "仓库",
+			visible: true,
+			isSort: false,
+			width: "auto",
+			tooltip: true
+		},
+		{
+			field: "storeType",
+			name: "出库种类",
+			visible: true,
+			isSort: false,
+			width: 100
+		},
+		{
+			field: "projectName",
+			name: "项目",
+			visible: true,
+			isSort: false,
+			width: "auto",
+			tooltip: true
+		},
+		{
+			field: "storeUserName",
+			name: "出库对象",
+			visible: true,
+			isSort: false,
+			width: "auto",
+			tooltip: true
+		},
+		//{ field: "dept", name: "部门", visible: true, isSort: false, width: "auto", tooltip: true },
+		{
+			field: "preparedUserName",
+			name: "制单人",
+			visible: true,
+			isSort: false,
+			width: "auto",
+			tooltip: true
+		},
+		{
+			field: "auditUserName",
+			name: "审核人",
+			visible: true,
+			isSort: false,
+			width: "auto",
+			tooltip: true
+		},
+		{ field: "auditDate", name: "审核日期", visible: true, isSort: false, width: 185 },
+		{
+			field: "status",
+			name: "状态",
+			visible: true,
+			isSort: false,
+			width: 100
+		},
+		//{ field: "remark", name: "备注", visible: true, isSort: false, tooltip: true },
+		{ field: "actions", name: `操作`, width: 150 }
+	] as any[],
+	queryParams: {
+		receiptNum: undefined,
+		dateRangeReceiptDate: undefined,
+		outBusinessType: undefined,
+		storeHouseId: undefined,
+		outStoreType: undefined,
+		projectId: undefined,
+		storeUserId: undefined,
+		dept: undefined,
+		preparedUser: undefined,
+		auditUser: undefined,
+		dateRangeAuditDate: undefined,
+		status: props.isAudit ? 1 : undefined
+	},
+	searchFormItems: [
+		{
+			field: "receiptNum",
+			label: "单据编号",
+			class: "w-100",
+			required: false,
+			placeholder: "请输入单据编号",
+			component: "I",
+			listeners: {
+				keyup: (e: KeyboardEvent) => {
+					if (e.code == "Enter") {
+						handleQuery()
+					}
+				}
+			}
+		},
+		{
+			field: "dateRangeReceiptDate",
+			label: "单据日期",
+			class: "w-100",
+			required: false,
+			component: "D",
+			placeholder: "请选择单据日期",
+			props: {
+				type: "daterange",
+				valueFormat: "YYYY-MM-DD",
+				rangeSeparator: "-",
+				startPlaceholder: "开始日期",
+				endPlaceholder: "结束日期"
+			},
+			listeners: {
+				change: (v: any) => {
+					queryParams.value.dateRangeReceiptDate = v
+					handleQuery()
+				}
+			},
+			span: 5
+		},
+		{
+			field: "outBusinessType",
+			label: "业务类型",
+			class: "w-100",
+			required: false,
+			component: "Dict",
+			props: {
+				placeholder: "请选择业务类型",
+				dictType: "out_business_type",
+				valueIsNumber: 1,
+				type: "select"
+			},
+			listeners: {
+				change: () => {
+					handleQuery()
+				}
+			}
+		},
+		{
+			field: "storeHouseId",
+			label: "仓库",
+			class: "w-100",
+			required: false,
+			placeholder: "请输入",
+			component: "VS",
+			props: {
+				data: () => common.storeHouseOptions,
+				type: "select",
+				valueIsNumber: 1
+			},
+			listeners: {
+				keyup: (e: KeyboardEvent) => {
+					if (e.code == "Enter") {
+						handleQuery()
+					}
+				}
+			}
+		},
+		{
+			field: "outStoreType",
+			label: "出库种类",
+			class: "w-100",
+			required: false,
+			component: "Dict",
+			props: {
+				placeholder: "请选择出库种类",
+				dictType: "out_store_type",
+				valueIsNumber: 1,
+				type: "select"
+			},
+			listeners: {
+				change: () => {
+					handleQuery()
+				}
+			}
+		},
+		{
+			field: "projectId",
+			label: "项目",
+			class: "w-100",
+			required: false,
+			placeholder: "请输入项目",
+			component: "VS",
+			props: {
+				data: () => common.projectOptions,
+				type: "select",
+				valueIsNumber: 1
+			},
+			listeners: {
+				keyup: (e: KeyboardEvent) => {
+					if (e.code == "Enter") {
+						handleQuery()
+					}
+				}
+			}
+		},
+		{
+			field: "storeUserId",
+			label: "出库对象",
+			class: "w-100",
+			required: false,
+			placeholder: "请输入出库对象",
+			component: "VS",
+			props: {
+				data: () => common.storeUserOptions,
+				type: "select",
+				valueIsNumber: 1
+			},
+			listeners: {
+				keyup: (e: KeyboardEvent) => {
+					if (e.code == "Enter") {
+						handleQuery()
+					}
+				}
+			}
+		},
+		{
+			field: "status",
+			label: "状态",
+			class: "w-100",
+			required: false,
+			component: "Dict",
+			props: {
+				placeholder: "请选择状态",
+				dictType: "store_in_out_status",
+				valueIsNumber: 1,
+				type: "select"
+			},
+			listeners: {
+				change: () => {
+					handleQuery()
+				}
+			}
+		}
+	] as any,
+	permission: "erp:storeOut",
+	handleBtns: [],
+	handleFuns: {
+		handleCreate,
+		handleUpdate: () => {
+			const row = tableRef.value.getSelected()
+			handleUpdate(row)
+		},
+		handleDelete: () => {
+			const rows = tableRef.value.getSelecteds()
+			handleDelete(rows)
+		}
+	},
+	customBtns: [],
+	tableListFun: apis.erp.storeOutApi.list,
+	getEntityFun: apis.erp.storeOutApi.get,
+	deleteEntityFun: apis.erp.storeOutApi.del,
+	exportUrl: apis.erp.storeOutApi.exportUrl,
+	exportName: "StoreOut",
+	modalTitle: "出库信息管理",
+	formItems: [
+		{
+			field: "receiptNum",
+			label: "单据编号",
+			class: "w-100",
+			span: 8,
+			required: true,
+			placeholder: "请输入单据编号",
+			component: "I"
+		},
+		{
+			field: "receiptDate",
+			label: "单据日期",
+			class: "w-100",
+			span: 8,
+			required: true,
+			component: "D",
+			props: {
+				placeholder: "请选择单据日期",
+				type: "date",
+				valueFormat: "YYYY-MM-DD"
+			}
+		},
+		{
+			field: "businessType",
+			label: "业务类型",
+			span: 8,
+			class: "w-100",
+			required: true,
+			component: "Dict",
+			props: {
+				placeholder: "请选择出库业务类型",
+				dictType: "out_business_type",
+				type: "select",
+				valueIsNumber: 1
+			}
+		},
+		{
+			field: "storeHouseId",
+			label: "仓库",
+			span: 8,
+			class: "w-100",
+			required: true,
+			placeholder: "请输入",
+			component: "VS",
+			props: {
+				data: () => common.storeHouseOptions,
+				type: "select",
+				valueIsNumber: 1
+			}
+		},
+		{
+			field: "storeType",
+			label: "出库种类",
+			class: "w-100",
+			span: 8,
+			required: true,
+			component: "Dict",
+			props: {
+				placeholder: "请选择出库种类",
+				dictType: "out_store_type",
+				type: "select",
+				valueIsNumber: 1
+			}
+		},
+		{
+			field: "projectId",
+			label: "项目",
+			class: "w-100",
+			span: 8,
+			required: true,
+			placeholder: "请输入项目",
+			component: "VS",
+			props: {
+				data: () => common.projectOptions,
+				type: "select",
+				valueIsNumber: 1
+			}
+		},
+		{
+			field: "storeUserId",
+			label: "出库对象",
+			class: "w-100",
+			span: 8,
+			required: true,
+			placeholder: "请输入出库对象",
+			component: "VS",
+			props: {
+				data: () => common.storeUserOptions,
+				type: "select",
+				valueIsNumber: 1
+			}
+		},
+		// {
+		// 	field: "dept",
+		// 	label: "部门",
+		// 	span: 12,
+		// 	class: "w-100",
+		// 	required: false,
+		// 	placeholder: "请输入部门",
+		// 	component: "I"
+		// },
+		// {
+		// 	field: "preparedUser",
+		// 	label: "制单人",
+		// 	class: "w-100",
+		// 	required: false,
+		// 	placeholder: "请输入制单人",
+		// 	component: "VS",
+		// 	props: {
+		// 		data: () => common.storeMangageListOptions,
+		// 		type: "select",
+		// 		valueIsNumber: 1
+		// 	}
+		// },
+		// {
+		// 	field: "auditUser",
+		// 	label: "审核人",
+		// 	class: "w-100",
+		// 	required: false,
+		// 	placeholder: "请输入审核人",
+		// 	component: "VS",
+		// 	props: {
+		// 		data: () => common.storeMangageListOptions,
+		// 		type: "select",
+		// 		valueIsNumber: 1
+		// 	}
+		// },
+		// {
+		// 	field: "auditDate",
+		// 	label: "审核日期",
+		// 	class: "w-100",
+		// 	required: false,
+		// 	component: "D",
+		// 	props: {
+		// 		placeholder: "请选择审核日期",
+		// 		type: "date",
+		// 		valueFormat: "YYYY-MM-DD"
+		// 	}
+		// },
+		// {
+		// 	field: "status",
+		// 	label: "状态",
+		// 	class: "w-100",
+		// 	required: true,
+		// 	component: "Dict",
+		// 	props: {
+		// 		dictType: "store_in_out_status",
+		// 		type: "radio",
+		// 		valueIsNumber: 1
+		// 	}
+		// },
+		{
+			field: "itemInfo",
+			label: "出库详情",
+			class: "w-100",
+			required: true,
+			placeholder: "请输入出库详情",
+			component: "slot",
+			rules: [
+				{
+					validator: (rule: any, value: any, callback: any) => {
+						if (!value || value.length === 0) {
+							callback(new Error("请至少添加一条出库明细"))
+						} else {
+							const valid = value.every(
+								(item: any) =>
+									item.itemName && item.unitId && item.quantity && item.taxInclusivePrice
+							)
+							if (!valid) {
+								callback(new Error("请完善所有出库明细项"))
+							} else {
+								callback()
+							}
+						}
+					},
+					trigger: "blur"
+				}
+			]
+		}
+	] as any,
+	resetForm: () => {
+		form.value = {
+			id: undefined,
+			receiptNum: undefined,
+			receiptDate: undefined,
+			businessType: undefined,
+			storeHouseId: undefined,
+			storeType: undefined,
+			projectId: undefined,
+			storeUserId: undefined,
+			// dept: undefined,
+			// preparedUser: undefined,
+			// auditUser: undefined,
+			// auditDate: undefined,
+			status: undefined,
+			// remark: undefined,
+			itemInfo: []
+		}
+	},
+	labelWidth: "120px"
+})
+
+/** 查询按钮操作 */
+function handleQuery(query?: any) {
+	query = query || tableRef.value?.getQueryParams() || queryParams.value
+	addDateRange(query, query.dateRangeAuditDate, "AuditDate")
+	addDateRange(query, query.dateRangeCreateTime)
+	addDateRange(query, query.dateRangeUpdateTime, "UpdateTime")
+	tableRef.value?.query(query)
+}
+
+/** 重置按钮操作 */
+function resetQuery(query?: any) {
+	query = query || tableRef.value?.getQueryParams() || queryParams.value
+	query.dateRangeAuditDate = [] as any
+	addDateRange(query, query.dateRangeAuditDate, "AuditDate")
+	query.dateRangeCreateTime = [] as any
+	addDateRange(query, query.dateRangeCreateTime)
+	query.dateRangeUpdateTime = [] as any
+	addDateRange(query, query.dateRangeUpdateTime, "UpdateTime")
+	//
+}
+function handleCreate() {
+	isEdit.value = false
+	tableRef.value.defaultHandleFuns.handleCreate()
+}
+/** 修改按钮操作 */
+function handleUpdate(row: any) {
+	isEdit.value = true
+	isReSubmit.value = false
+	if (row.status === 2 || row.status === 4) {
+		message.msgWarning("已审核通过或者作废的出库单不能修改")
+		return
+	}
+	tableRef.value.defaultHandleFuns.handleUpdate("", row)
+}
+
+/** 删除按钮操作 */
+function handleDelete(rows: any[]) {
+	tableRef.value.defaultHandleFuns.handleDelete("", rows)
+}
+
+// 入库物品明细相关变量和方法
+const itemLoading = ref(false) // 物品搜索加载状态
+
+// 查询物品列表用于autocomplete
+const queryItems = (queryString: string, cb: (results: any[]) => void) => {
+	if (!queryString) {
+		cb([])
+		return
+	}
+
+	itemLoading.value = true
+	apis.erp.itemInfoApi
+		.autoComplete(queryString)
+		.then((res: any) => {
+			itemLoading.value = false
+			const results = res.data.map((item: any) => {
+				return {
+					value: item.itemName,
+					...item
+				}
+			})
+			cb(results)
+		})
+		.catch(() => {
+			itemLoading.value = false
+			cb([])
+		})
+}
+// 当选择物品时更新相关信息
+const handleItemSelect = (item: any, index: number) => {
+	if (form.value.itemInfo && form.value.itemInfo[index]) {
+		form.value.itemInfo[index].itemId = item.id
+		form.value.itemInfo[index].itemName = item.itemName
+		form.value.itemInfo[index].specModel = item.specModel
+		form.value.itemInfo[index].unitId = item.unitId
+		// 查找单位名称
+		const unit = common.unitInfoOptions.find((u: any) => u.value === item.unitId)
+		if (unit) {
+			form.value.itemInfo[index].unitName = unit.label
+		}
+	}
+}
+/** 审核按钮操作 */
+function handleAudit(row: any) {
+	// 实现出库审核逻辑
+	console.log("审核出库单", row)
+	if (row.status !== 1) {
+		message.msgWarning("只有待审核的出库单才能审核!")
+		return
+	}
+	// 这里应该调用审核API
+	message
+		.confirm("是否确认审核当前单据?", "确认审核", {
+			confirmButtonText: "确定",
+			cancelButtonText: "取消",
+			type: "warning"
+		})
+		.then(() => {
+			apis.erp.storeOutApi.audit(row.id).then(() => {
+				handleQuery()
+			})
+		})
+}
+
+/** 拒绝出库按钮操作 */
+function handleReject(row: any) {
+	// 实现拒绝逻辑
+	if (row.status !== 1) {
+		message.msgWarning("只有待审核的出库单才能拒绝!")
+		return
+	}
+	message
+		.confirm("是否确认拒绝当前单据?", "确认拒绝", {
+			confirmButtonText: "确定",
+			cancelButtonText: "取消",
+			type: "warning"
+		})
+		.then(() => {
+			apis.erp.storeOutApi.reject(row.id).then(() => {
+				message.msgSuccess("拒绝成功")
+				handleQuery()
+			})
+		})
+}
+
+/** 再次编辑提交按钮操作 */
+function handleResubmit(row: any) {
+	isEdit.value = true
+	isReSubmit.value = true
+
+	tableRef.value.defaultHandleFuns.handleUpdate("", row)
+}
+
+function handleRevoke(row: any) {
+	// 实现撤销逻辑
+	console.log("撤销出库单", row)
+	// 这里应该调用撤销API
+	message
+		.confirm("是否确认撤销当前单据?", "确认撤销", {
+			confirmButtonText: "确定",
+			cancelButtonText: "取消",
+			type: "warning"
+		})
+		.then(() => {
+			apis.erp.storeOutApi.revoke(row.id).then(() => {
+				handleQuery()
+			})
+		})
+}
+
+/** 提交按钮 */
+function submitForm() {
+	console.log("提交出库单", form.value)
+	if (isReSubmit.value) {
+		// 设置状态为1(待审核)
+		form.value.status = 1
+	}
+	apis.erp.storeOutApi.addOrUpdate(form.value).then(() => {
+		handleQuery()
+	})
+}
+function handleStoreInOutHeaderSelect() {
+	storeInOutHeaderSelectRef.value.open(1) // 1表示出库
+}
+function handleStoreInOutHeaderSelectConfirm(ids: any[]) {
+	console.log("选择出库单", ids)
+	//form.value.receiptNum = ids[0].receiptNum
+	if (ids[0] && ids[0].id) {
+		apis.erp.storeOutApi.get(ids[0].id).then((res: any) => {
+			form.value = res.data
+			form.value.id = undefined
+			form.value.itemInfo.forEach((item: any) => {
+				item.id = undefined
+			})
+		})
+	}
+}
+
+/** 查看详情按钮操作 */
+function handleDetail(row: any) {
+	// 获取完整的出库单详情数据
+	apis.erp.storeOutApi.get(row.id).then((res: any) => {
+		detailForm.value = res.data
+		// 打开详情模态框
+		detailModalRef.value.show()
+	})
+}
+
+// 出库明细相关函数
+function addItemInfo() {
+	if (!form.value.itemInfo) {
+		form.value.itemInfo = []
+	}
+	form.value.itemInfo.push({
+		id: undefined,
+		itemId: undefined,
+		itemName: undefined,
+		specModel: undefined,
+		unitId: undefined,
+		unitName: undefined,
+		quantity: undefined,
+		taxRate: undefined,
+		taxInclusivePrice: undefined
+		// remark: undefined
+	})
+}
+function removeItemInfo(index: number) {
+	form.value.itemInfo.splice(index, 1)
+}
+// 计算含税金额
+const calculateTaxInclusiveAmount = (index: number) => {
+	if (form.value.itemInfo && form.value.itemInfo[index]) {
+		const item = form.value.itemInfo[index]
+		if (item.quantity && item.taxInclusivePrice) {
+			item.taxInclusiveAmount = item.quantity * item.taxInclusivePrice
+		} else {
+			item.taxInclusiveAmount = undefined
+		}
+	}
+}
+</script>
+<template>
+	<div class="app-container">
+		<VbDataTable
+			ref="tableRef"
+			keyField="id"
+			:columns="opts.columns"
+			:handle-perm="opts.permission"
+			:handle-btns="opts.handleBtns"
+			:handle-funs="opts.handleFuns"
+			:search-form-items="opts.searchFormItems"
+			:custom-btns="opts.customBtns"
+			:remote-fun="opts.tableListFun"
+			:get-entity-fun="opts.getEntityFun"
+			:delete-entity-fun="opts.deleteEntityFun"
+			:export-url="opts.exportUrl"
+			:export-name="opts.exportName"
+			:modal="modalRef"
+			:reset-form-fun="opts.resetForm"
+			v-model:form-data="form"
+			v-model:query-params="queryParams"
+			:check-multiple="true"
+			:reset-search-form-fun="resetQuery"
+			:custom-search-fun="handleQuery">
+			<template #receiptDate="{ row }">
+				{{ dayjs(row.receiptDate).format("YYYY-MM-DD") }}
+			</template>
+			<template #businessType="{ row }">
+				<DictTag type="out_business_type" :value-is-number="1" :value="row.businessType"></DictTag>
+			</template>
+			<template #storeType="{ row }">
+				<DictTag type="out_store_type" :value-is-number="1" :value="row.storeType"></DictTag>
+			</template>
+			<template #auditDate="{ row }">
+				{{ dayjs(row.auditDate).isValid() ? dayjs(row.auditDate).format("YYYY-MM-DD") : "-" }}
+			</template>
+			<template #status="{ row }">
+				<DictTag type="store_in_out_status" :value-is-number="1" :value="row.status"></DictTag>
+			</template>
+			<template #actions="{ row }">
+				<vb-tooltip
+					content="修改"
+					placement="top"
+					v-if="!props.isAudit && actions.includes('update')">
+					<el-button
+						link
+						type="primary"
+						@click="handleUpdate(row)"
+						v-hasPermission="'erp:storeOut:edit'">
+						<template #icon>
+							<VbIcon icon-name="notepad-edit" icon-type="duotone" class="fs-3"></VbIcon>
+						</template>
+					</el-button>
+				</vb-tooltip>
+				<vb-tooltip
+					content="重新提交审核"
+					placement="top"
+					v-if="!props.isAudit && actions.includes('reSubmit') && row.status === 3">
+					<el-button
+						link
+						type="primary"
+						@click="handleResubmit(row)"
+						v-hasPermission="'erp:storeOut:edit'">
+						<template #icon>
+							<VbIcon icon-name="refresh-circle" icon-type="duotone" class="fs-3"></VbIcon>
+						</template>
+					</el-button>
+				</vb-tooltip>
+				<vb-tooltip
+					content="作废"
+					placement="top"
+					v-if="
+						!props.isAudit && actions.includes('revoke') && (row.status === 1 || row.status === 3)
+					">
+					<el-button
+						link
+						type="primary"
+						@click="handleRevoke(row)"
+						v-hasPermission="'erp:storeOut:remove'">
+						<template #icon>
+							<VbIcon icon-name="close-circle" icon-type="duotone" class="fs-3"></VbIcon>
+						</template>
+					</el-button>
+				</vb-tooltip>
+				<vb-tooltip
+					content="审核"
+					placement="top"
+					v-if="props.isAudit && actions.includes('audit')">
+					<el-button
+						link
+						type="primary"
+						@click="handleAudit(row)"
+						v-hasPermission="'erp:storeOut:audit'">
+						<template #icon>
+							<VbIcon icon-name="check-circle" icon-type="duotone" class="fs-3"></VbIcon>
+						</template>
+					</el-button>
+				</vb-tooltip>
+				<vb-tooltip
+					content="拒绝出库"
+					placement="top"
+					v-if="props.isAudit && actions.includes('reject')">
+					<el-button
+						link
+						type="primary"
+						@click="handleReject(row)"
+						v-hasPermission="'erp:storeOut:reject'">
+						<template #icon>
+							<VbIcon icon-name="close-circle" icon-type="duotone" class="fs-3"></VbIcon>
+						</template>
+					</el-button>
+				</vb-tooltip>
+				<vb-tooltip content="查看详情" placement="top" v-if="actions.includes('detail')">
+					<el-button
+						link
+						type="primary"
+						@click="handleDetail(row)"
+						v-hasPermission="'erp:storeOut:query'">
+						<template #icon>
+							<VbIcon icon-name="eye" icon-type="duotone" class="fs-3"></VbIcon>
+						</template>
+					</el-button>
+				</vb-tooltip>
+			</template>
+		</VbDataTable>
+
+		<VbModal
+			ref="modalRef"
+			v-model:form-data="form"
+			:title="isEdit ? '修改' : '新增'"
+			:confirm-btn-text="isReSubmit ? '重新提交审核' : '提交'"
+			modalDialogStyle="max-width:1200px;"
+			:label-width="opts.labelWidth"
+			append-to-body
+			@confirm="submitForm">
+			<template #title>
+				<div class="modal-title-container">
+					<span class="modal-title-text">{{ isEdit ? "修改" : "新增" }}出库信息</span>
+					<el-button
+						v-if="!isEdit"
+						type="primary"
+						size="small"
+						@click="handleStoreInOutHeaderSelect"
+						class="p-5 template-btn">
+						<VbIcon icon-name="plus-circle" icon-type="solid" class="fs-3"></VbIcon>
+						选择出库单模板
+					</el-button>
+				</div>
+			</template>
+			<template #itemInfo_form>
+				<div class="item-info-table">
+					<el-button type="primary" size="small" @click="addItemInfo" class="mb-2">
+						<el-icon><Plus /></el-icon>
+						添加物品
+					</el-button>
+
+					<el-table :data="form.itemInfo" border style="width: 100%">
+						<el-table-column type="index" label="#" width="50"></el-table-column>
+
+						<el-table-column label="物品名称" width="200">
+							<template #default="{ row, $index }">
+								<el-autocomplete
+									v-model="row.itemName"
+									:fetch-suggestions="queryItems"
+									:loading="itemLoading"
+									placeholder="请输入物品名称"
+									@select="(item) => handleItemSelect(item, $index)"
+									clearable
+									style="width: 100%"
+									:rules="[{ required: true, message: '物品名称不能为空', trigger: 'blur' }]">
+									<template #default="{ item }">
+										<div>{{ item.itemName }}</div>
+										<div class="text-muted">{{ item.specModel }}</div>
+									</template>
+								</el-autocomplete>
+							</template>
+						</el-table-column>
+
+						<el-table-column prop="specModel" label="规格型号" width="150"></el-table-column>
+
+						<el-table-column label="计量单位" width="120">
+							<template #default="{ row }">
+								<!-- <el-select
+									v-model="row.unitId"
+									placeholder="请选择单位"
+									disabled
+									:rules="[{ required: true, message: '计量单位不能为空', trigger: 'blur' }]">
+									<el-option
+										v-for="item in common.unitInfoOptions"
+										:key="item.value"
+										:label="item.label"
+										:value="item.value"></el-option>
+								</el-select> -->
+
+								<!-- <vb-select
+									v-if="row.isAdd"
+									v-model="row.unitId"
+									:data="common.unitInfoOptions"
+									:value-is-number="false"
+									placeholder="请选择单位"></vb-select> -->
+								<span>{{ row.unitName }}</span>
+							</template>
+						</el-table-column>
+
+						<el-table-column label="实收数量" width="120">
+							<template #default="{ row, $index }">
+								<el-input-number
+									v-model="row.quantity"
+									@change="calculateTaxInclusiveAmount($index)"
+									:min="0"
+									controls-position="right"
+									style="width: 100%"
+									:rules="[
+										{ required: true, message: '实收数量不能为空', trigger: 'blur' },
+										{ type: 'number', min: 0.01, message: '实收数量必须大于0', trigger: 'blur' }
+									]"></el-input-number>
+							</template>
+						</el-table-column>
+
+						<el-table-column label="税率%" width="100">
+							<template #default="{ row, $index }">
+								<el-input-number
+									v-model="row.taxRate"
+									@change="calculateTaxInclusiveAmount($index)"
+									:min="0"
+									:max="100"
+									:step="0.1"
+									controls-position="right"
+									style="width: 100%"></el-input-number>
+							</template>
+						</el-table-column>
+
+						<el-table-column label="含税单价" width="120">
+							<template #default="{ row, $index }">
+								<el-input-number
+									v-model="row.taxInclusivePrice"
+									@change="calculateTaxInclusiveAmount($index)"
+									:min="0"
+									:step="0.01"
+									controls-position="right"
+									style="width: 100%"
+									:rules="[
+										{ required: true, message: '含税单价不能为空', trigger: 'blur' },
+										{ type: 'number', min: 0.01, message: '含税单价必须大于0', trigger: 'blur' }
+									]"></el-input-number>
+							</template>
+						</el-table-column>
+
+						<el-table-column
+							label="含税金额"
+							prop="taxInclusiveAmount"
+							width="120"></el-table-column>
+
+						<el-table-column label="操作" width="80">
+							<template #default="{ $index }">
+								<el-button type="danger" size="small" @click="removeItemInfo($index)">
+									删除
+								</el-button>
+							</template>
+						</el-table-column>
+					</el-table>
+				</div>
+			</template>
+		</VbModal>
+
+		<!-- 详情查看模态框 -->
+		<VbModal
+			v-model:modal="detailModalRef"
+			title="出库单详情"
+			modalDialogStyle="max-width:1200px;"
+			:label-width="opts.labelWidth"
+			append-to-body
+			:show-confirm-btn="false">
+			<template #title>
+				<div class="modal-title-container">
+					<span class="modal-title-text">出库单详情</span>
+				</div>
+			</template>
+			<template #body>
+				<div class="header-info">
+					<div class="header-item">
+						<span class="header-label">单据编号:</span>
+						<span class="header-value">{{ detailForm.receiptNum }}</span>
+					</div>
+					<div class="header-item">
+						<span class="header-label">单据日期:</span>
+						<span class="header-value">
+							{{ dayjs(detailForm.receiptDate).format("YYYY-MM-DD") }}
+						</span>
+					</div>
+					<div class="header-item">
+						<span class="header-label">业务类型:</span>
+						<span class="header-value">
+							<DictTag
+								type="out_business_type"
+								:value-is-number="1"
+								:value="detailForm.businessType"></DictTag>
+						</span>
+					</div>
+					<div class="header-item">
+						<span class="header-label">出库种类:</span>
+						<span class="header-value">
+							<DictTag
+								type="out_store_type"
+								:value-is-number="1"
+								:value="detailForm.storeType"></DictTag>
+						</span>
+					</div>
+					<div class="header-item">
+						<span class="header-label">出库单状态:</span>
+						<span class="header-value">
+							<DictTag
+								type="store_in_out_status"
+								:value-is-number="1"
+								:value="detailForm.status"></DictTag>
+						</span>
+					</div>
+					<div class="header-item">
+						<span class="header-label">项目:</span>
+						<span class="header-value">{{ detailForm.projectName }}</span>
+					</div>
+					<div class="header-item">
+						<span class="header-label">出库对象:</span>
+						<span class="header-value">{{ detailForm.storeUserName }}</span>
+					</div>
+					<div class="header-item">
+						<span class="header-label">仓库:</span>
+						<span class="header-value">{{ detailForm.storeHouseName }}</span>
+					</div>
+				</div>
+				<div class="item-info-table">
+					<el-table :data="detailForm.itemInfo" border style="width: 100%">
+						<el-table-column type="index" label="#" width="50"></el-table-column>
+						<el-table-column prop="itemName" label="物品名称" width="200"></el-table-column>
+						<el-table-column prop="specModel" label="规格型号" width="250"></el-table-column>
+						<el-table-column prop="unitName" label="计量单位" width="150"></el-table-column>
+						<el-table-column prop="quantity" label="实发数量" width="150"></el-table-column>
+						<el-table-column prop="taxRate" label="税率%" width="120"></el-table-column>
+						<el-table-column
+							prop="taxInclusivePrice"
+							label="含税单价"
+							width="150"></el-table-column>
+						<!-- <el-table-column prop="remark" label="备注" width="200"></el-table-column> -->
+					</el-table>
+				</div>
+			</template>
+		</VbModal>
+
+		<!-- 引用出库单选择模态框 -->
+		<StoreInOutHeaderSelect
+			ref="storeInOutHeaderSelectRef"
+			:inOutStore="1"
+			@confirm="handleStoreInOutHeaderSelectConfirm"></StoreInOutHeaderSelect>
+	</div>
+</template>
+<style scoped>
+.modal-title-container {
+	display: flex;
+	justify-content: space-between;
+	align-items: center;
+	width: 100%;
+}
+
+.modal-title-text {
+	font-size: 18px;
+	font-weight: bold;
+}
+
+.header-info {
+	display: grid;
+	grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
+	gap: 16px;
+	margin-bottom: 20px;
+	padding: 16px;
+	background-color: #f5f5f5;
+	border-radius: 4px;
+}
+
+.header-item {
+	display: flex;
+}
+
+.header-label {
+	font-weight: bold;
+	margin-right: 8px;
+	white-space: nowrap;
+}
+
+.header-value {
+	flex: 1;
+}
+
+.item-info-table {
+	margin-top: 20px;
+}
+</style>

+ 7 - 0
UI/VB.VUE/src/views/erp/store/inOutStore/storeInAudit.vue

@@ -0,0 +1,7 @@
+<script setup lang="ts">
+import StoreIn from "./_storeIn.vue"
+const actions = ["audit", "reject", "detail"]
+</script>
+<template>
+	<StoreIn :isAudit="true" :actions="actions as any"></StoreIn>
+</template>

+ 9 - 0
UI/VB.VUE/src/views/erp/store/inOutStore/storeOut.vue

@@ -0,0 +1,9 @@
+<template>
+	<div class="app-container">
+		<StoreOut :is-audit="false" :actions="['update', 'revoke', 'detail']" />
+	</div>
+</template>
+
+<script setup name="StoreOutPage">
+import StoreOut from './_storeOut.vue'
+</script>

+ 9 - 0
UI/VB.VUE/src/views/erp/store/inOutStore/storeOutAudit.vue

@@ -0,0 +1,9 @@
+<template>
+	<div class="app-container">
+		<StoreOut :is-audit="true" :actions="['audit', 'reject', 'detail']" />
+	</div>
+</template>
+
+<script setup name="StoreOutAuditPage">
+import StoreOut from './_storeOut.vue'
+</script>