Browse Source

新增MES入库申请的界面和逻辑

695593266@qq.com 6 months ago
parent
commit
c376c49aed

+ 27 - 0
api/warehouseManagement/index.js

@@ -399,3 +399,30 @@ export async function getDetailsByCode(code) {
 	}
 	return Promise.reject(new Error(res.message))
 }
+
+// 验证是否重复入库
+export async function isVerifyRepeatIsStock(data) {
+	const res = await postJ(Vue.prototype.apiUrl + `/wms/outindetailtwo/isVerifyRepeatIsStock`, data)
+	if (res.code == 0) {
+		return res.data
+	}
+	return Promise.reject(new Error(res.message))
+}
+
+// 生产入库申请驳回
+export async function notPass(data) {
+	const res = await postJ(Vue.prototype.apiUrl + `/bpm/inwarehouse/notPass`, data)
+	if (res.code == 0) {
+		return res.data
+	}
+	return Promise.reject(new Error(res.message))
+}
+
+// 根据编码批量获取物品信息
+export async function getListByNameOrModeType(data) {
+	const res = await postJ(Vue.prototype.apiUrl + `/main/category/getListByNameOrModeType`, data)
+	if (res.code == 0) {
+		return res.data
+	}
+	return Promise.reject(new Error(res.message))
+}

+ 28 - 0
api/wt/index.js

@@ -623,6 +623,19 @@ export async function getLoginUser(data) {
   return Promise.reject(new Error(res.message || res.data?.message || "获取登录用户数据失败"));
 }
 
+// 生产入库审批(申请入库)
+export async function approveTaskWithVariablesOther(data) {
+  const res = await postJ(
+    Vue.prototype.apiUrl + `/bpm/inwarehouse/assign`,
+    data,
+    true,
+  );
+  if (res.code == 0) {
+    return res.data;
+  }
+  return Promise.reject(new Error(res.message));
+}
+
 /**
  * 获取部门列表(树)
  */
@@ -667,3 +680,18 @@ export async function getUserById(id) {
   }
   return Promise.reject(new Error(res.message));
 }
+
+/**
+ * 生产入库申请单详情
+ */
+export async function getStorageDetail(id) {
+  const res = await get(
+    Vue.prototype.apiUrl + `/mes/applystorage/getById/${id}`,
+    {},
+    true,
+  );
+  if (res.code == 0) {
+    return res.data;
+  }
+  return Promise.reject(new Error(res.message));
+}

+ 2 - 1
manifest.json

@@ -10,12 +10,13 @@
             "proxy" : {
                 "/api" : {
                     // "target" : "http://192.168.1.110:18086/",
-                    "target" : "http://192.168.1.125:18086/",
+                    // "target" : "http://192.168.1.125:18086/",
                     // "target" : "http://192.168.1.251:18086/",
                     // "target" : "http://192.168.1.251:18086/",
                     // "target" : "http://110.41.182.105/api/",
                     // "target": "http://116.63.185.248:80/api",
                     // "target" : "http://192.168.1.251:18186/",
+                    "target" : "http://aiot.zoomwin.com.cn:51001/api",
                     "changeOrigin" : true,
                     "secure" : false,
                     "pathRewrite" : {

+ 8 - 0
pages.json

@@ -134,6 +134,14 @@
 				"navigationBarTextStyle": "white"
 			}
 		},
+		{
+			"path": "pages/home/wt/components/productionWarehousing/processTask",
+			"style": {
+				"navigationBarTitleText": "生产入库审批",
+				"navigationStyle": "custom",
+				"navigationBarTextStyle": "white"
+			}
+		},
 		{
 			"path": "pages/home/wt/components/materialRequisition/processTask",
 			"style": {

+ 189 - 0
pages/home/wt/components/productionWarehousing/processTask.vue

@@ -0,0 +1,189 @@
+<template>
+  <view class="havedone-container">
+    <uni-nav-bar
+      fixed="true"
+      statusBar="true"
+      left-icon="back"
+      :title="uniNavBarTitle"
+      background-color="#157A2C"
+      color="#fff"
+      @clickLeft="back"
+    ></uni-nav-bar>
+    <!-- <iframe src="http://aiot.zoomwin.com.cn:51001/test/a.html" style="width: 200px;height: 600px" frameborder="0"></iframe> -->
+    <view v-if="processInstance.processDefinition">
+      <taskForm
+        id="async-biz-form-component"
+        :taskId="listData.taskId"
+        :businessId="listData.businessId"
+        :id="listData.id"
+        :taskDefinitionKey="listData.taskDefinitionKey"
+        ref="bziRef"
+      ></taskForm>
+    </view>
+    <view v-for="(item, index) in runningTasks" :key="index">
+      <div v-if="processInstance.processDefinition">
+        <taskSubmit
+          id="async-sub-form-component"
+          :taskId="listData.taskId"
+          :businessId="listData.businessId"
+          :id="listData.id"
+          :taskDefinitionKey="listData.taskDefinitionKey"
+          @handleAudit="handleAudit"
+          @getTableValue="getTableValue"
+          @handleUpdateAssignee="handleUpdateAssignee(item)"
+          @handleBackList="handleBackList(item)"
+          ref="subForm"
+        >
+        </taskSubmit>
+      </div>
+    </view>
+    <u-toast ref="uToast"></u-toast>
+  </view>
+</template>
+
+<script>
+import {
+  getProcessInstance,
+  getTaskListByProcessInstanceId,
+} from "@/api/wt/index.js";
+import Vue from "vue";
+import taskForm from "./taskForm.vue";
+import taskSubmit from "./taskSubmit.vue";
+
+export default {
+  name: "processTask",
+  components: { taskForm, taskSubmit },
+  data() {
+    return {
+      uniNavBarTitle: "",
+      processInstanceLoading: false,
+      listData: {},
+      processInstance: {},
+      runningTasks: [],
+      auditForms: [],
+      activeComp: null,
+    };
+  },
+
+  onLoad(option) {
+    this.listData = option;
+    this.getDetail();
+    this.activeComp = "tab1";
+  },
+  methods: {
+    /** 获得流程实例 */
+    async getDetail() {
+      // 获得流程实例相关
+      this.processInstanceLoading = true;
+      getProcessInstance({
+        id: this.listData.id,
+      }).then(async (response) => {
+        if (!response) {
+          this.$message.error("查询不到流程信息!");
+          return;
+        }
+        // 设置流程信息
+        this.processInstance = response;
+        this.uniNavBarTitle = `${response.name}`;
+
+        this.processInstanceLoading = false;
+      });
+
+      this.runningTasks = [];
+      this.auditForms = [];
+      getTaskListByProcessInstanceId({
+        processInstanceId: this.listData.id,
+      }).then((response) => {
+        console.log(response, "response");
+        // 审批记录
+        this.tasks = [];
+        // 移除已取消的审批
+        response.forEach((task) => {
+          if (task.result !== 4) {
+            this.tasks.push(task);
+          }
+        });
+        // 排序,将未完成的排在前面,已完成的排在后面;
+        this.tasks.sort((a, b) => {
+          // 有已完成的情况,按照完成时间倒序
+          if (a.endTime && b.endTime) {
+            return b.endTime - a.endTime;
+          } else if (a.endTime) {
+            return 1;
+          } else if (b.endTime) {
+            return -1;
+            // 都是未完成,按照创建时间倒序
+          } else {
+            return b.createTime - a.createTime;
+          }
+        });
+
+        // 需要审核的记录
+        let userInfo = wx.getStorageSync("userInfo");
+        this.tasks.forEach((task) => {
+          if (task.result !== 1 && task.result !== 6) {
+            // 只有待处理才需要
+            return;
+          }
+          if (!task.assigneeUser || task.assigneeUser.id !== userInfo.userId) {
+            // 自己不是处理人
+            return;
+          }
+          if (task.taskDefinitionKey !== this.listData.taskDefinitionKey) {
+            // 不是当前流程的
+            return;
+          }
+          this.runningTasks.push({
+            ...task,
+          });
+          console.log(this.runningTasks, " this.runningTasks");
+          this.auditForms.push({
+            reason: "",
+          });
+        });
+      });
+    },
+
+    /** 处理审批通过和不通过的操作 */
+    handleAudit(data) {
+      let text = data.status === 1 ? "通过" : "不通过";
+      this.$refs.uToast.show({
+        type: "success",
+        message: `审批${data.title || text}成功!`,
+        iconUrl: "https://cdn.uviewui.com/uview/demo/toast/success.png",
+      });
+      // 获得最新详情
+      setTimeout(() => {
+        uni.navigateBack();
+      }, 1000);
+
+      // const index = this.runningTasks.indexOf(task);
+      // this.$refs['form' + index][0].validate((valid) => {
+      //   if (!valid) {
+      //     return;
+      //   }
+      //   const data = {
+      //     id: task.id,
+      //     reason: this.auditForms[index].reason
+      //   };
+      //   if (pass) {
+      //     approveTask(data).then((response) => {
+      //       this.$message.success('审批通过成功!');
+      //       this.handleClose(); // 获得最新详情
+      //     });
+      //   } else {
+      //     rejectTask(data).then((response) => {
+      //       this.$message.success('审批不通过成功!');
+      //       this.handleClose(); // 获得最新详情
+      //     });
+      //   }
+      // });
+    },
+    getTableValue(fn) {
+      fn(this.$refs.bziRef.getTableValue());
+    },
+  },
+};
+</script>
+
+<style></style>

+ 1748 - 0
pages/home/wt/components/productionWarehousing/taskForm.vue

@@ -0,0 +1,1748 @@
+<template>
+  <view class="">
+    <u-sticky offset-top="50">
+      <view class="subsection-wrap">
+        <u-subsection
+          fontSize="25"
+          mode="subsection"
+          :list="tabList"
+          :current="curNow"
+          @change="sectionChange"
+          activeColor="#157A2C"
+        ></u-subsection>
+      </view>
+    </u-sticky>
+
+    <!-- Tab 0: 入库单详情 -->
+    <view v-show="curNow === 0" class="basic_info">
+      <view class="info_header">
+        <view class="info_title">入库单详情</view>
+        <u-gap height="4" bgColor="#1890FF"></u-gap>
+      </view>
+      <view class="info_content">
+        <u-row>
+          <u-col span="4">
+            <view class="label">物品名称:</view>
+          </u-col>
+          <u-col span="8">
+            <view class="value">{{ form.categoryName || "—" }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="4">
+            <view class="label">物品编码:</view>
+          </u-col>
+          <u-col span="8">
+            <view class="value">{{ form.categoryCode || "—" }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="4">
+            <view class="label">物品分类:</view>
+          </u-col>
+          <u-col span="8">
+            <view class="value">{{ form.categoryLevelName || "—" }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="4">
+            <view class="label">包装数量:</view>
+          </u-col>
+          <u-col span="8">
+            <view class="value">{{ totalPackage || "—" }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="4">
+            <view class="label">计量数量:</view>
+          </u-col>
+          <u-col span="8">
+            <view class="value">{{ totalCount || "—" }}</view>
+          </u-col>
+        </u-row>
+      </view>
+    </view>
+
+    <!-- Tab 1: 基本信息 -->
+    <view v-show="curNow === 1" class="basic_info">
+      <view class="info_header">
+        <view class="info_title">基本信息</view>
+        <u-gap height="4" bgColor="#1890FF"></u-gap>
+      </view>
+      <view class="info_content">
+        <u-row>
+          <u-col span="4">
+            <view class="label">物品类型:</view>
+          </u-col>
+          <u-col span="8">
+            <view class="value">{{ assetTypeLabel || "—" }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="4">
+            <view class="label">入库类型:</view>
+          </u-col>
+          <u-col span="8">
+            <view class="value">{{ bizTypeLabel || "—" }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="4">
+            <view class="label">单据来源:</view>
+          </u-col>
+          <u-col span="8">
+            <view class="value">{{ formData.sourceBizNo || "—" }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="4">
+            <view class="label">入库时间:</view>
+          </u-col>
+          <u-col span="8">
+            <view class="value">{{ formData.storageTime || "—" }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="4">
+            <view class="label">入库登记人:</view>
+          </u-col>
+          <u-col span="8">
+            <view class="value">{{ formData.createUserName || "—" }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="4">
+            <view class="label">审核人:</view>
+          </u-col>
+          <u-col span="8">
+            <view class="value">{{ formData.approvalUserName || "—" }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="4">
+            <view class="label">备注:</view>
+          </u-col>
+          <u-col span="8">
+            <view class="value">{{ formData.remark || "—" }}</view>
+          </u-col>
+        </u-row>
+      </view>
+    </view>
+
+    <!-- Tab 2: 产品信息 -->
+    <view v-show="curNow === 2" class="basic_info">
+      <view class="info_header">
+        <view class="info_title">产品信息</view>
+        <u-gap height="4" bgColor="#1890FF"></u-gap>
+      </view>
+      <view v-if="!productList.length" class="empty_text"> 暂无产品信息 </view>
+      <view v-for="(row, idx) in productList" :key="idx" class="card_box">
+        <view class="card_title">产品 {{ idx + 1 }}</view>
+        <u-row>
+          <u-col span="3">
+            <view class="label">编码:</view>
+          </u-col>
+          <u-col span="9">
+            <view class="value">{{ row.categoryCode || "—" }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="3">
+            <view class="label">名称:</view>
+          </u-col>
+          <u-col span="9">
+            <view class="value">{{ row.categoryName || "—" }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="3">
+            <view class="label">牌号:</view>
+          </u-col>
+          <u-col span="9">
+            <view class="value">{{ row.brandNum || "—" }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="3">
+            <view class="label">型号:</view>
+          </u-col>
+          <u-col span="9">
+            <view class="value">{{ row.categoryModel || "—" }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="3">
+            <view class="label">规格:</view>
+          </u-col>
+          <u-col span="9">
+            <view class="value">{{ row.specification || "—" }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="3">
+            <view class="label">批次号:</view>
+          </u-col>
+          <u-col span="9">
+            <view class="value">{{ row.batchNo || "—" }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="3">
+            <view class="label">包装数量:</view>
+          </u-col>
+          <u-col span="9">
+            <view class="value">{{ row.packingQuantityDisplay || "—" }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="3">
+            <view class="label">计量数量:</view>
+          </u-col>
+          <u-col span="9">
+            <view class="value">{{ row.measureQuantityDisplay || "—" }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="3">
+            <view class="label">重量:</view>
+          </u-col>
+          <u-col span="9">
+            <view class="value">{{ row.weightDisplay || "—" }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="3">
+            <view class="label">包装规格:</view>
+          </u-col>
+          <u-col span="9">
+            <view class="value">{{ row.packingSpecification || "—" }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="3">
+            <view class="label">仓库:</view>
+          </u-col>
+          <u-col span="9">
+            <view class="value">{{ row.warehouseName || "—" }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="3">
+            <view class="label">供应商:</view>
+          </u-col>
+          <u-col span="9">
+            <view class="value">{{ row.supplierName || "—" }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="3">
+            <view class="label">供应商代号:</view>
+          </u-col>
+          <u-col span="9">
+            <view class="value">{{ row.supplierCode || "—" }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="3">
+            <view class="label">机型:</view>
+          </u-col>
+          <u-col span="9">
+            <view class="value">{{ formatField(row.modelKey) }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="3">
+            <view class="label">颜色:</view>
+          </u-col>
+          <u-col span="9">
+            <view class="value">{{ formatField(row.colorKey) }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="3">
+            <view class="label">生产要求:</view>
+          </u-col>
+          <u-col span="9">
+            <view class="value">{{ row.productionRequirements || "—" }}</view>
+          </u-col>
+        </u-row>
+      </view>
+    </view>
+
+    <!-- Tab 3: 包装明细 -->
+    <view v-show="curNow === 3" class="basic_info">
+      <view class="info_header">
+        <view class="info_title">包装明细</view>
+        <u-gap height="4" bgColor="#1890FF"></u-gap>
+      </view>
+      <view v-if="!packingList.length" class="empty_text"> 暂无包装明细 </view>
+      <view v-for="(row, idx) in packingList" :key="idx" class="card_box">
+        <view class="card_title">包装 {{ idx + 1 }}</view>
+        <u-row>
+          <u-col span="3">
+            <view class="label">编码:</view>
+          </u-col>
+          <u-col span="9">
+            <view class="value">{{ row.categoryCode || "—" }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="3">
+            <view class="label">名称:</view>
+          </u-col>
+          <u-col span="9">
+            <view class="value">{{ row.categoryName || "—" }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="3">
+            <view class="label">批次号:</view>
+          </u-col>
+          <u-col span="9">
+            <view class="value">{{ row.batchNo || "—" }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="3">
+            <view class="label">包装编码:</view>
+          </u-col>
+          <u-col span="9">
+            <view class="value">{{ row.packageNo || "—" }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="3">
+            <view class="label">包装数量:</view>
+          </u-col>
+          <u-col span="9">
+            <view class="value">{{ row.packingQuantityDisplay || "—" }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="3">
+            <view class="label">计量数量:</view>
+          </u-col>
+          <u-col span="9">
+            <view class="value">{{ row.measureQuantityDisplay || "—" }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="3">
+            <view class="label">重量:</view>
+          </u-col>
+          <u-col span="9">
+            <view class="value">{{ row.weightDisplay || "—" }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="3">
+            <view class="label">仓库:</view>
+          </u-col>
+          <u-col span="9">
+            <view class="value">{{ row.warehouseName || "—" }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="3">
+            <view class="label">物料代号:</view>
+          </u-col>
+          <u-col span="9">
+            <view class="value">{{ row.materielDesignation || "—" }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="3">
+            <view class="label">客户代号:</view>
+          </u-col>
+          <u-col span="9">
+            <view class="value">{{ row.clientCode || "—" }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="3">
+            <view class="label">发货条码:</view>
+          </u-col>
+          <u-col span="9">
+            <view class="value">{{ row.barcodes || "—" }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="3">
+            <view class="label">刻码:</view>
+          </u-col>
+          <u-col span="9">
+            <view class="value">{{ row.engrave || "—" }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="3">
+            <view class="label">供应商:</view>
+          </u-col>
+          <u-col span="9">
+            <view class="value">{{ row.supplierName || "—" }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="3">
+            <view class="label">供应商代号:</view>
+          </u-col>
+          <u-col span="9">
+            <view class="value">{{ row.supplierCode || "—" }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="3">
+            <view class="label">机型:</view>
+          </u-col>
+          <u-col span="9">
+            <view class="value">{{ formatField(row.modelKey) }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="3">
+            <view class="label">颜色:</view>
+          </u-col>
+          <u-col span="9">
+            <view class="value">{{ formatField(row.colorKey) }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="3">
+            <view class="label">质检结果:</view>
+          </u-col>
+          <u-col span="9">
+            <view class="value">{{ row.resultLabel || "—" }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="3">
+            <view class="label">质检状态:</view>
+          </u-col>
+          <u-col span="9">
+            <view class="value">{{ row.statusLabel || "—" }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="3">
+            <view class="label">生产日期:</view>
+          </u-col>
+          <u-col span="9">
+            <view class="value">{{ row.productionDate || "—" }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="3">
+            <view class="label">采购日期:</view>
+          </u-col>
+          <u-col span="9">
+            <view class="value">{{ row.purchaseDate || "—" }}</view>
+          </u-col>
+        </u-row>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script>
+import {
+  getStorageDetail,
+  allCategoryLevel,
+  getLoginUser,
+} from "@/api/wt/index.js";
+import {
+  getListByNameOrModeType,
+  getCategoryPackageDisposition,
+  getCode,
+  getAssetNum,
+  isVerifyRepeatIsStock,
+} from "@/api/warehouseManagement/index.js";
+
+const INPUT_SCENE_STATE = [
+  { code: 1, label: "生产入库" },
+  { code: 2, label: "采购入库" },
+  { code: 3, label: "退货入库" },
+  { code: 4, label: "委外入库" },
+  { code: 5, label: "受托入库" },
+  { code: 6, label: "调拨入库" },
+  { code: 7, label: "委外退货入库" },
+  { code: 8, label: "委外采购入库" },
+  { code: 9, label: "仓库委外入库" },
+  { code: 10, label: "采购退货入库" },
+  { code: 99, label: "其他入库" },
+];
+
+const QUALITY_RESULTS = {
+  0: "无",
+  1: "合格",
+  2: "不合格",
+  3: "让步接收",
+};
+
+const QUALITY_STATUS = {
+  0: "未质检",
+  1: "待检",
+  2: "已质检",
+};
+
+export default {
+  props: {
+    businessId: {
+      default: "",
+    },
+    taskDefinitionKey: {
+      default: "",
+    },
+  },
+  data() {
+    return {
+      form: {},
+      formData: {
+        type: 1,
+        bizType: "",
+        sourceBizNo: "",
+        storageTime: "",
+        createUserId: "",
+        createUserName: "",
+        approvalUserId: "",
+        approvalUserName: "",
+        remark: "",
+        extInfo: {
+          assetType: [],
+          assetTypeName: "",
+          deptCode: "",
+          deptName: "",
+          createUserName: "",
+          createUserId: "",
+          deliveryPhone: "",
+          documentSource: "",
+          supplierName: "",
+        },
+        outInDetailList: [],
+      },
+      tabList: ["入库单详情", "基本信息", "产品信息", "包装明细"],
+      curNow: 0,
+      productList: [],
+      packingList: [],
+      assetTypeMapping: {},
+      isDetail: false,
+      totalPackage: "",
+      totalCount: "",
+    };
+  },
+  computed: {
+    bizType() {
+      return this.taskDefinitionKey === "wmsBizType4" ? 4 : 1;
+    },
+    bizTypeLabel() {
+      const item = INPUT_SCENE_STATE.find(
+        (s) => s.code === Number(this.formData.bizType),
+      );
+      return item ? item.label : "";
+    },
+    assetTypeLabel() {
+      const types = this.formData.extInfo.assetType;
+      if (Array.isArray(types)) {
+        return types
+          .map((id) => this.assetTypeMapping[id] || "")
+          .filter(Boolean)
+          .join("、");
+      }
+      if (typeof types === "string" && types) {
+        return types
+          .split(",")
+          .map((id) => this.assetTypeMapping[id.trim()] || "")
+          .filter(Boolean)
+          .join("、");
+      }
+      return "";
+    },
+  },
+  async mounted() {
+    await this.loadAssetTypeMapping();
+    await this.getDetailData(this.businessId);
+    await this.getLoginUserData();
+  },
+  methods: {
+    sectionChange(index) {
+      this.curNow = index;
+    },
+
+    formatField(val) {
+      if (Array.isArray(val)) return val.join(",") || "—";
+      return val || "—";
+    },
+
+    async loadAssetTypeMapping() {
+      try {
+        const data = await allCategoryLevel();
+        const list = Array.isArray(data) ? data : data?.data || [];
+        const obj = {};
+        list.forEach((item) => {
+          obj[item.id] = item.name;
+        });
+        this.assetTypeMapping = obj;
+      } catch (e) {
+        console.warn("加载物品分类失败", e);
+      }
+    },
+
+    async getDetailData(id) {
+      try {
+        const data = await getStorageDetail(id);
+        this.form = data;
+
+        this.isDetail = data.detailList?.[0]?.isAllPackageData == 1;
+
+        this.totalPackage = this.isDetail
+          ? (data.detailList[0].packingCount || "") +
+            (data.detailList[0]?.packingUnit || "")
+          : data.detailList.length + (data.detailList[0]?.packingUnit || "");
+
+        this.totalCount =
+          (data.totalCount || "") + (data.detailList?.[0]?.measuringUnit || "");
+
+        this.form.detailList = (data.detailList || []).map((item) => ({
+          ...item,
+          productName: item.categoryName,
+          productCode: item.categoryCode,
+        }));
+
+        let categoryLevelTopIds = data.detailList.map(
+          (item) => item.rootCategoryLevelId,
+        );
+        let filterCategoryLevelTopIds = [
+          ...new Set(categoryLevelTopIds.filter(Boolean)),
+        ];
+        data.categoryLevelTopId = filterCategoryLevelTopIds.join(",");
+
+        if (this.taskDefinitionKey === "wmsBizType4") {
+          this.form.totalCount = data.detailList.reduce(
+            (sum, item) => sum + (item.quantity || 0),
+            0,
+          );
+        }
+
+        await this.initFormData();
+        await this.initProducts();
+      } catch (e) {
+        console.warn("获取入库单详情失败", e);
+      }
+    },
+
+    async initFormData() {
+      this.formData.type = 1;
+      this.formData.bizType = this.bizType;
+      this.formData.sourceBizNo = this.form.code || "";
+
+      const categoryLevelTopIds = (this.form.detailList || [])
+        .map((item) => item.rootCategoryLevelId)
+        .filter(Boolean);
+      this.formData.extInfo.assetType = [...new Set(categoryLevelTopIds)];
+
+      this.formData.extInfo.documentSource = this.form.code || "";
+      this.formData.extInfo.supplierName = this.form.supplierName || "";
+
+      this.getNowFormatDate();
+    },
+
+    getNowFormatDate() {
+      const now = new Date();
+      const pad = (n) => String(n).padStart(2, "0");
+      this.formData.storageTime = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`;
+    },
+
+    async getLoginUserData() {
+      try {
+        const data = await getLoginUser();
+        this.formData.createUserId =
+          this.form.createUserId || data.userId || "";
+        this.formData.createUserName =
+          this.form.createUserName || data.name || "";
+        this.formData.approvalUserId =
+          this.form.approvalUserId || data.userId || "";
+        this.formData.approvalUserName =
+          this.form.approvalUserName || data.name || "";
+        this.formData.extInfo.createUserId =
+          this.form.createUserId || data.userId || "";
+        this.formData.extInfo.createUserName =
+          this.form.createUserName || data.name || "";
+        this.formData.extInfo.deliveryPhone = data.phone || "";
+        this.formData.extInfo.deptName =
+          this.form.deptName || data.deptName || "";
+      } catch (e) {
+        console.warn("获取登录用户数据失败", e);
+      }
+    },
+
+    async initProducts() {
+      const detailList = this.form.detailList || [];
+      if (!detailList.length) return;
+
+      const codeList = detailList
+        .map((item) => item.productCode || item.categoryCode)
+        .filter(Boolean);
+      if (!codeList.length) {
+        this.buildFromDetailList(detailList);
+        return;
+      }
+
+      try {
+        const categoryList = await getListByNameOrModeType({ codeList });
+        if (!categoryList || !categoryList.length) {
+          this.buildFromDetailList(detailList);
+          return;
+        }
+
+        const assetTypes = [
+          ...new Set(
+            categoryList
+              .map((item) => item.categoryLevelPathIdParent)
+              .filter(Boolean),
+          ),
+        ];
+        if (assetTypes.length) {
+          this.formData.extInfo.assetType = assetTypes;
+        }
+
+        let batchNo = "";
+        try {
+          batchNo = await getCode("lot_number_code");
+        } catch (e) {
+          console.warn("获取批次号失败", e);
+        }
+
+        let packingSpecs = [];
+        try {
+          packingSpecs = await getCategoryPackageDisposition({
+            categoryIds: categoryList.map((item) => item.id),
+          });
+        } catch (e) {
+          console.warn("获取包装规格失败", e);
+        }
+
+        const isMoreProduct = categoryList.length > 1;
+
+        this.productList = categoryList.map((item, index) => {
+          const detail =
+            detailList.find(
+              (d) => item.code === (d.productCode || d.categoryCode),
+            ) ||
+            detailList[index] ||
+            {};
+
+          const weightUnit = item.weightUnit || "";
+
+          const itemPackingSpecs = (packingSpecs || [])
+            .filter((s) => s.categoryId === item.id)
+            .sort((a, b) => a.sort - b.sort);
+          const packingSpecLabel = itemPackingSpecs
+            .map((s) =>
+              s.sort > 0
+                ? `${s.packageCell}${s.packageUnit}/${s.conversionUnit}`
+                : null,
+            )
+            .filter(Boolean)
+            .join(", ");
+
+          let packingQuantity,
+            measureQuantity,
+            packUnit,
+            measureUnit,
+            warehouseId,
+            warehouseName,
+            warehouseIds,
+            warehouseNames,
+            modelKey,
+            colorKey,
+            packingUnitId;
+
+          if (this.isDetail) {
+            // isAllPackageData==1: 对齐PC端 saleProductList 路径
+            packUnit =
+              detail.packingUnit || item.packingUnit || "";
+            measureUnit =
+              detail.measuringUnit || item.measuringUnit || "";
+            packingQuantity =
+              detail.packingCount || detail.quantity || 0;
+            measureQuantity = detail.quantity || 0;
+            warehouseId = detail.warehouseId || "";
+            warehouseName = detail.warehouseName || "";
+            warehouseIds = detail.warehouseId
+              ? [detail.warehouseId]
+              : [];
+            warehouseNames = detail.warehouseName
+              ? [detail.warehouseName]
+              : [];
+            modelKey = detail.modelKey
+              ? detail.modelKey.split(",")
+              : "";
+            colorKey = detail.colorKey
+              ? detail.colorKey.split(",")
+              : "";
+            packingUnitId =
+              detail.purchaseUnitId || detail.saleUnitId || "";
+          } else {
+            // isAllPackageData!=1: 对齐PC端 detailList 路径
+            packUnit = item.packingUnit || "";
+            measureUnit = item.measuringUnit || "";
+            const matchDetails = detailList.filter(
+              (d) =>
+                (d.productCode || d.categoryCode) === item.code,
+            );
+            packingQuantity = isMoreProduct
+              ? matchDetails.length
+              : detailList.length;
+            measureQuantity = isMoreProduct
+              ? matchDetails.reduce(
+                  (sum, d) => sum + (d.quantity || 0),
+                  0,
+                )
+              : this.form.totalCount || 0;
+            if (isMoreProduct) {
+              warehouseId =
+                matchDetails[0]?.warehouseId || "";
+              warehouseName =
+                matchDetails[0]?.warehouseName || "";
+              warehouseIds = matchDetails.map(
+                (d) => d.warehouseId,
+              );
+              warehouseNames = matchDetails.map(
+                (d) => d.warehouseName,
+              );
+            } else {
+              warehouseId = this.form.warehouseId || "";
+              warehouseName = this.form.warehouseName || "";
+              warehouseIds = Array(detailList.length).fill(
+                this.form.warehouseId || "",
+              );
+              warehouseNames = Array(detailList.length).fill(
+                this.form.warehouseName || "",
+              );
+            }
+            if (this.bizType == 1) {
+              modelKey = this.form.modelKey
+                ? this.form.modelKey.split(",")
+                : "";
+              colorKey = this.form.colorKey
+                ? this.form.colorKey.split(",")
+                : "";
+            } else {
+              modelKey = item.modelKey
+                ? item.modelKey.split(",")
+                : "";
+              colorKey = item.colorKey
+                ? item.colorKey.split(",")
+                : "";
+            }
+            packingUnitId = "";
+          }
+
+          let weight = detail.weight || 0;
+          if (
+            (detail.sendTotalWeight || detail.receiveTotalWeight) &&
+            item.weightUnit === item.measuringUnit
+          ) {
+            weight =
+              detail.sendTotalWeight || detail.receiveTotalWeight;
+          }
+          const singleWeight = detail.singleWeight || 0;
+          if (!weight && singleWeight) {
+            weight = singleWeight * measureQuantity;
+          }
+
+          return {
+            index,
+            categoryId: item.id,
+            categoryName: item.name,
+            categoryCode: item.code,
+            categoryModel: item.modelType || "",
+            specification: item.specification || "",
+            brandNum: item.brandNum || "",
+            batchNo: this.isDetail
+              ? detail.batchNo || batchNo
+              : [1, 3, 6].includes(this.bizType)
+                ? detailList[0]?.batchNo || batchNo
+                : batchNo,
+            packingQuantity,
+            packingUnit: packUnit,
+            packingQuantityDisplay:
+              packingQuantity !== 0
+                ? `${packingQuantity}${packUnit}`
+                : "",
+            measureQuantity,
+            measureUnit: measureUnit,
+            measuringUnit: measureUnit,
+            measureQuantityDisplay:
+              measureQuantity !== 0
+                ? `${measureQuantity}${measureUnit}`
+                : "",
+            measureType: item.measureType,
+            weight,
+            weightUnit,
+            weightDisplay:
+              weight !== 0 ? `${weight}${weightUnit}` : "",
+            singleWeight,
+            warehouseId,
+            warehouseName,
+            warehouseIds,
+            warehouseNames,
+            supplierName:
+              detail.supplierName || item.supplierName || "",
+            supplierCode:
+              detail.supplierCode || item.supplierCode || "",
+            supplierId:
+              detail.supplierId || this.form.supplierId || "",
+            productionRequirements:
+              detail.productionRequirements || "",
+            packingSpecification:
+              item.packingSpecification ||
+              packingSpecLabel ||
+              "",
+            packingSpecificationOption: itemPackingSpecs,
+            packingUnitId,
+            modelKey,
+            colorKey,
+            netWeight:
+              item.netWeight > -1 ? item.netWeight : 0,
+            isUnpack: item.isUnpack,
+            qualityControl: detail.qualityControl,
+            extField: item.extField || {},
+            isSave: true,
+            outInDetailRecordRequestList: [],
+          };
+        });
+
+        await this.buildPackingList(batchNo);
+        this.formData.outInDetailList = this.productList;
+      } catch (e) {
+        console.warn("初始化产品数据失败", e);
+        this.buildFromDetailList(detailList);
+      }
+    },
+
+    buildFromDetailList(detailList) {
+      this.productList = detailList.map((item, index) => {
+        const packUnit = item.packingUnit || "";
+        const measureUnit = item.measuringUnit || "";
+        const pQty = item.packingCount || item.quantity || 0;
+        const mQty = item.quantity || item.measureQuantity || 0;
+        return {
+          index,
+          categoryId: item.categoryId || item.id || "",
+          categoryName: item.categoryName || "",
+          categoryCode: item.categoryCode || "",
+          categoryModel: item.modelType || "",
+          specification: item.specification || "",
+          brandNum: item.brandNum || "",
+          batchNo: item.batchNo || "",
+          packingQuantity: pQty,
+          packingUnit: packUnit,
+          packingQuantityDisplay: pQty ? `${pQty}${packUnit}` : "",
+          measureQuantity: mQty,
+          measureUnit: measureUnit,
+          measuringUnit: measureUnit,
+          measureQuantityDisplay: mQty ? `${mQty}${measureUnit}` : "",
+          weight: item.weight || 0,
+          weightUnit: item.weightUnit || "",
+          weightDisplay: item.weight
+            ? `${item.weight}${item.weightUnit || ""}`
+            : "",
+          singleWeight: item.singleWeight || 0,
+          warehouseId: item.warehouseId || "",
+          warehouseName: item.warehouseName || "",
+          warehouseIds: item.warehouseId ? [item.warehouseId] : [],
+          warehouseNames: item.warehouseName ? [item.warehouseName] : [],
+          supplierName: item.supplierName || "",
+          supplierCode: item.supplierCode || "",
+          supplierId: item.supplierId || "",
+          productionRequirements: item.productionRequirements || "",
+          packingSpecification: item.packingSpecification || "",
+          isSave: true,
+          outInDetailRecordRequestList: [],
+        };
+      });
+
+      this.productList.forEach((product) => {
+        product.outInDetailRecordRequestList = [
+          this.buildDefaultPackingEntry(product),
+        ];
+      });
+
+      this.packingList = this.productList.flatMap(
+        (p) => p.outInDetailRecordRequestList,
+      );
+      this.formData.outInDetailList = this.productList;
+    },
+
+    buildDefaultPackingEntry(product) {
+      const detail =
+        (this.form.detailList || []).find(
+          (d) => (d.productCode || d.categoryCode) === product.categoryCode,
+        ) || {};
+      return {
+        packageNo: "",
+        categoryCode: product.categoryCode,
+        categoryName: product.categoryName,
+        batchNo: product.batchNo,
+        packingQuantity: product.packingQuantity,
+        packingUnit: product.packingUnit,
+        packingQuantityDisplay: product.packingQuantityDisplay,
+        measureQuantity: product.measureQuantity,
+        measureUnit: product.measureUnit,
+        measureQuantityDisplay: product.measureQuantityDisplay,
+        weight: product.weight,
+        weightUnit: product.weightUnit,
+        weightDisplay: product.weightDisplay,
+        warehouseId: product.warehouseId,
+        warehouseName: product.warehouseName,
+        materielDesignation: "",
+        clientCode: "",
+        barcodes: "",
+        engrave: "",
+        supplierName: product.supplierName,
+        supplierCode: product.supplierCode,
+        result: 0,
+        status: 0,
+        resultLabel: QUALITY_RESULTS[0],
+        statusLabel: QUALITY_STATUS[0],
+        productionDate: "",
+        purchaseDate: "",
+        modelKey: product.modelKey || "",
+        colorKey: product.colorKey || "",
+        workOrderId: detail.workOrderId || "",
+        taskId: detail.taskId || "",
+      };
+    },
+
+    _getNowDateStr() {
+      const now = new Date();
+      const pad = (n) => String(n).padStart(2, "0");
+      return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`;
+    },
+
+    _isNonSplitUnit(unit) {
+      if (!unit) return false;
+      const u = unit.toUpperCase();
+      return ["KG", "G", "L", "ML"].includes(u) || unit === "立方";
+    },
+
+    async buildPackingList(batchNo) {
+      const detailList = this.form.detailList || [];
+      const isMoreProduct = this.productList.length > 1;
+
+      let productionDate = "";
+      let purchaseDate = "";
+      if (this.bizType == 1) {
+        productionDate = this._getNowDateStr();
+      }
+
+      if (this.isDetail) {
+        // isAllPackageData == 1: 对齐PC端 saleProductList → batchSave → generateWrappers 路径
+        // 从 productList 生成包装明细
+        try {
+          for (const product of this.productList) {
+            if (
+              !product.packingQuantity ||
+              product.packingQuantity <= 0
+            )
+              continue;
+
+            const packingSpecOption =
+              product.packingSpecificationOption || [];
+            const packingBoolen = this._isNonSplitUnit(
+              product.packingUnit,
+            );
+            const measureBoolen = this._isNonSplitUnit(
+              product.measureUnit,
+            );
+
+            let packingNum = Number(product.packingQuantity);
+            let filterArr = [];
+
+            if (packingBoolen) {
+              if (
+                !product.isUnpack &&
+                product.packingUnit === "立方"
+              ) {
+                packingNum = 1;
+              } else {
+                filterArr = packingSpecOption.filter(
+                  (s) =>
+                    s.packageUnit === product.packingUnit &&
+                    s.packageUnit !== s.conversionUnit,
+                );
+                if (filterArr.length) {
+                  packingNum = Math.ceil(
+                    product.packingQuantity /
+                      filterArr[0].packageCell,
+                  );
+                }
+              }
+            } else {
+              if (product.isUnpack) {
+                if (measureBoolen) {
+                  let splitIndex =
+                    packingSpecOption.findIndex(
+                      (s) =>
+                        s.conversionUnit ===
+                          product.packingUnit &&
+                        s.packageUnit !== s.conversionUnit,
+                    );
+                  let num = product.packingQuantity;
+                  for (; splitIndex > 1; splitIndex--) {
+                    num =
+                      num *
+                      packingSpecOption[splitIndex]
+                        .packageCell;
+                  }
+                  packingNum = Math.ceil(num);
+                } else {
+                  if (product.packingUnitId) {
+                    let splitIndex =
+                      packingSpecOption.findIndex(
+                        (s) => s.id == product.packingUnitId,
+                      );
+                    if (splitIndex == 0) {
+                      packingNum = Math.ceil(
+                        product.measureQuantity /
+                          (packingSpecOption[1]?.packageCell ||
+                            1),
+                      );
+                    } else if (splitIndex == 1) {
+                      packingNum = product.packingQuantity;
+                    } else {
+                      for (
+                        ;
+                        splitIndex > 1;
+                        splitIndex--
+                      ) {
+                        packingNum = Math.ceil(
+                          product.packingQuantity *
+                            packingSpecOption[splitIndex]
+                              .packageCell,
+                        );
+                      }
+                    }
+                  } else if (
+                    packingSpecOption[1]?.packageCell
+                  ) {
+                    packingNum = Math.ceil(
+                      product.measureQuantity /
+                        packingSpecOption[1].packageCell,
+                    );
+                    filterArr = packingSpecOption.filter(
+                      (s) =>
+                        s.packageUnit ===
+                          product.packingUnit &&
+                        s.packageUnit !== s.conversionUnit,
+                    );
+                  }
+                }
+              } else {
+                if (
+                  product.measureUnit === product.packingUnit
+                ) {
+                  packingNum = 0;
+                } else {
+                  packingNum = Number(
+                    product.packingQuantity,
+                  );
+                }
+              }
+            }
+
+            if (packingNum <= 0) packingNum = 1;
+
+            let packageNos = [];
+            try {
+              const assetNumRes = await getAssetNum([
+                {
+                  assetCode:
+                    product.categoryCode + product.index,
+                  batchNum: product.batchNo,
+                  num: packingNum,
+                },
+              ]);
+              packageNos = assetNumRes?.data || [];
+            } catch (e) {
+              console.warn("获取包装编码失败", e);
+            }
+
+            const qualityStatus =
+              product.qualityControl == 1 ? 0 : 2;
+            const packingEntries = [];
+
+            for (let i = 0; i < packageNos.length; i++) {
+              let measureQuantity = 1;
+              let packingQuantity = 1;
+
+              if (packingBoolen) {
+                const cell =
+                  filterArr[0]?.packageCell || 1;
+                measureQuantity =
+                  Number(product.packingQuantity) >
+                  cell * (i + 1)
+                    ? cell
+                    : Number(product.packingQuantity) -
+                      cell * i;
+              } else {
+                if (
+                  product.weightUnit ===
+                    product.measureUnit &&
+                  product.weight
+                ) {
+                  measureQuantity =
+                    Math.trunc(
+                      (product.weight /
+                        packageNos.length) *
+                        10000,
+                    ) / 10000;
+                } else if (measureBoolen) {
+                  let splitIndex =
+                    packingSpecOption.findIndex(
+                      (s) =>
+                        s.conversionUnit ===
+                          product.packingUnit &&
+                        s.packageUnit !== s.conversionUnit,
+                    );
+                  measureQuantity = 1;
+                  for (; splitIndex > 0; splitIndex--) {
+                    measureQuantity =
+                      measureQuantity *
+                      packingSpecOption[splitIndex]
+                        .packageCell;
+                  }
+                } else {
+                  measureQuantity =
+                    packingSpecOption[1]?.packageCell || 1;
+                }
+              }
+
+              let clientCode = "";
+              if (this.bizType == 2) {
+                clientCode =
+                  product.customerMark || "";
+              } else {
+                clientCode =
+                  product.extInfo?.clientCode || "";
+              }
+
+              let packingUnit = product.packingUnit;
+              if (product.isUnpack) {
+                if (packingBoolen) {
+                  packingUnit =
+                    packingSpecOption[1]
+                      ?.conversionUnit ||
+                    product.packingUnit;
+                } else if (measureBoolen) {
+                  packingUnit =
+                    (
+                      packingSpecOption[1] ||
+                      packingSpecOption[0]
+                    )?.conversionUnit ||
+                    product.packingUnit;
+                } else {
+                  packingUnit =
+                    packingSpecOption[1]
+                      ?.conversionUnit ||
+                    product.packingUnit;
+                }
+              }
+
+              let measureUnit = product.isUnpack
+                ? measureBoolen
+                  ? (
+                      packingSpecOption[1] ||
+                      packingSpecOption[0]
+                    )?.packageUnit ||
+                    product.measureUnit
+                  : product.measureUnit
+                : product.measureUnit;
+
+              let weight = 0;
+              if (
+                product.weightUnit === product.measureUnit
+              ) {
+                weight = measureQuantity
+                  ? Number(measureQuantity)
+                  : 0;
+              } else if (
+                product.singleWeight &&
+                measureQuantity
+              ) {
+                weight =
+                  Number(measureQuantity) *
+                  Number(product.singleWeight);
+              }
+
+              packingEntries.push({
+                index: product.index + "-" + i,
+                warehouseId: product.warehouseId || "",
+                warehouseName:
+                  product.warehouseName || "",
+                categoryName:
+                  product.categoryName || "",
+                categoryCode:
+                  product.categoryCode || "",
+                categoryModel:
+                  product.categoryModel || "",
+                specification:
+                  product.specification || "",
+                supplierCode:
+                  product.supplierCode || "",
+                supplierName:
+                  product.supplierName || "",
+                brandNum: product.brandNum || "",
+                parentIndex: product.index,
+                batchNo: product.batchNo || batchNo,
+                packageNo:
+                  packageNos[i]?.onlyCode || "",
+                packingQuantity: packingQuantity,
+                modelKey: product.modelKey || "",
+                colorKey: product.colorKey || "",
+                packingUnit: packingUnit,
+                measureQuantity: measureQuantity,
+                measureUnit: measureUnit,
+                weight: weight,
+                packingSpecificationOption:
+                  product.packingSpecificationOption ||
+                  [],
+                weightUnit:
+                  product.weightUnit || "",
+                netWeight: product.netWeight || 0,
+                barcodes: "",
+                clientCode: clientCode,
+                materielDesignation:
+                  product.extInfo?.materielCode ||
+                  "",
+                engrave: "",
+                isUnpack: product.isUnpack,
+                productionDate: productionDate,
+                purchaseDate: "",
+                result: 1,
+                qualityControl:
+                  product.qualityControl,
+                status: qualityStatus,
+                resultLabel:
+                  QUALITY_RESULTS[1] || "合格",
+                statusLabel:
+                  QUALITY_STATUS[qualityStatus] ||
+                  "未质检",
+                workOrderId: "",
+                taskId: "",
+              });
+            }
+
+            // 处理尾包余数
+            if (
+              product.weightUnit !== product.measureUnit &&
+              packingSpecOption[1]?.packageCell
+            ) {
+              const remainder =
+                product.measureQuantity %
+                packingSpecOption[1].packageCell;
+              if (
+                remainder > 0 &&
+                packingEntries.length > 0
+              ) {
+                const lastEntry =
+                  packingEntries[
+                    packingEntries.length - 1
+                  ];
+                lastEntry.measureQuantity = remainder;
+                if (product.singleWeight) {
+                  lastEntry.weight =
+                    product.singleWeight * remainder;
+                }
+              }
+            }
+
+            product.outInDetailRecordRequestList =
+              packingEntries;
+            this.packingList.push(...packingEntries);
+          }
+        } catch (e) {
+          console.warn("生成包装明细失败", e);
+        }
+      } else {
+        // isAllPackageData != 1: 对齐PC端 detailList 路径
+        // detailList 每条记录即是一个包装, 直接映射
+        const product0 = this.productList[0] || {};
+
+        let getAssetNumPr = isMoreProduct
+          ? detailList.map((val) => ({
+              assetCode: val.productCode,
+              batchNum: val.productBrand,
+              num: 1,
+            }))
+          : [
+              {
+                assetCode:
+                  (product0.categoryCode || "") +
+                  (product0.index ?? 0),
+                batchNum: product0.batchNo || "",
+                num: detailList.length,
+              },
+            ];
+
+        let packingCodeList = [];
+        try {
+          const assetNumRes = await getAssetNum(
+            getAssetNumPr,
+          );
+          packingCodeList = assetNumRes?.data || [];
+        } catch (e) {
+          console.warn("获取包装编码失败", e);
+        }
+
+        this.packingList = detailList.map(
+          (item, index) => {
+            const matchProduct = isMoreProduct
+              ? this.productList.find(
+                  (val) =>
+                    val.categoryCode ===
+                    item.productCode,
+                ) || product0
+              : product0;
+
+            let clientCode = "";
+            if (this.bizType == 2) {
+              clientCode = item.customerMark || "";
+            } else {
+              clientCode =
+                item.clientCode ||
+                item.extInfo?.clientCode ||
+                "";
+            }
+
+            return {
+              index:
+                (isMoreProduct
+                  ? matchProduct.index
+                  : product0.index) +
+                "-" +
+                index,
+              warehouseId:
+                item.warehouseId ||
+                (product0.warehouseIds || [])[index] ||
+                "",
+              warehouseName:
+                item.warehouseName ||
+                (product0.warehouseNames || [])[
+                  index
+                ] ||
+                "",
+              categoryName:
+                item.productName ||
+                product0.categoryName ||
+                "",
+              categoryCode:
+                item.productCode ||
+                product0.categoryCode ||
+                "",
+              categoryModel:
+                item.modelType ||
+                product0.categoryModel ||
+                "",
+              specification:
+                item.specification ||
+                product0.specification ||
+                "",
+              brandNum:
+                item.productBrand ||
+                product0.brandNum ||
+                "",
+              colorKey: (
+                item.colorKey
+                  ? item.colorKey.split(",")
+                  : ""
+              ) || product0.colorKey || "",
+              modelKey: (
+                item.modelKey
+                  ? item.modelKey.split(",")
+                  : ""
+              ) || product0.modelKey || "",
+              parentIndex: isMoreProduct
+                ? matchProduct.index
+                : product0.index,
+              batchNo: product0.batchNo || batchNo,
+              packageNo:
+                item.packageNo ||
+                item.packingCode ||
+                packingCodeList[index]?.onlyCode ||
+                "",
+              packingQuantity: 1,
+              packingUnit: item.packingUnit || "",
+              measureQuantity:
+                item.quantity ||
+                item.measureQuantity ||
+                0,
+              measureUnit:
+                item.measuringUnit ||
+                item.measureUnit ||
+                "",
+              supplierCode:
+                item.supplierCode || "",
+              supplierId: item.supplierId || "",
+              supplierName:
+                item.supplierName || "",
+              weight: item.packingWeight || 0,
+              singleWeight:
+                item.singleWeight || 0,
+              pricingWay: item.pricingWay || "",
+              weightUnit:
+                item.weightUnit ||
+                product0.weightUnit ||
+                "",
+              packingSpecificationOption:
+                isMoreProduct
+                  ? matchProduct.packingSpecificationOption ||
+                    []
+                  : product0.packingSpecificationOption ||
+                    [],
+              netWeight: product0.netWeight || 0,
+              barcodes:
+                item.barcodes ||
+                item.sendCode ||
+                "",
+              clientCode: clientCode,
+              materielDesignation:
+                item.materielDesignation ||
+                item.extInfo?.materielCode ||
+                "",
+              engrave:
+                item.engrave ||
+                item.extInfo?.engrave ||
+                "",
+              isUnpack: product0.isUnpack,
+              productionDate: productionDate,
+              purchaseDate: purchaseDate,
+              result: 1,
+              status: 2,
+              resultLabel:
+                QUALITY_RESULTS[1] || "合格",
+              statusLabel:
+                QUALITY_STATUS[2] || "已质检",
+              serialNumber:
+                item.productSequence || "",
+              workOrderId:
+                item.workOrderId || "",
+              taskId: item.taskId || "",
+            };
+          },
+        );
+
+        this.productList.forEach((product) => {
+          product.outInDetailRecordRequestList =
+            this.packingList.filter(
+              (p) =>
+                p.categoryCode ===
+                product.categoryCode,
+            );
+        });
+      }
+    },
+
+    async getReturnStorage() {
+      if (!this.productList.length) {
+        uni.showToast({ title: "暂无产品信息!", icon: "none" });
+        return null;
+      }
+
+      const detailList = this.form.detailList || [];
+
+      const _packingList = this.packingList.map((packingItem) => {
+        let _workOrderId = null;
+        let _taskId = null;
+        if ([1].includes(this.bizType)) {
+          _workOrderId = detailList[0]?.workOrderId;
+          _taskId = detailList[0]?.taskId;
+        }
+        const processed = { ...packingItem };
+        if (processed.modelKey) {
+          processed.modelKey = processed.modelKey.toString();
+        }
+        if (processed.colorKey) {
+          processed.colorKey = processed.colorKey.toString();
+        }
+        return {
+          ...processed,
+          workOrderId: _workOrderId,
+          taskId: _taskId,
+          materialDetailList: [],
+        };
+      });
+
+      const processedProductList = this.productList.map((productItem) => {
+        const processed = { ...productItem };
+        if (processed.modelKey) {
+          processed.modelKey = processed.modelKey.toString();
+        }
+        if (processed.colorKey) {
+          processed.colorKey = processed.colorKey.toString();
+        }
+        return {
+          ...processed,
+          outInDetailRecordRequestList: _packingList.filter(
+            (item) => item.parentIndex === productItem.index,
+          ),
+        };
+      });
+
+      const obj = JSON.parse(JSON.stringify(this.formData));
+      obj.type = 1;
+
+      if (Array.isArray(obj.extInfo.assetType)) {
+        obj.extInfo.assetType = obj.extInfo.assetType.join(",");
+      }
+
+      obj.extInfo.documentSource = obj.sourceBizNo;
+      obj.extInfo.deptName = this.formData.extInfo.deptName || "";
+      obj.extInfo.supplierName = this.form.supplierName || "";
+
+      const warehouseId = [];
+      const warehouseName = [];
+      processedProductList.forEach((item) => {
+        const ids = item.warehouseIds || [];
+        const names = item.warehouseNames || [];
+        ids.forEach((id, idx) => {
+          if (id && !warehouseId.includes(id)) {
+            warehouseId.push(id);
+            warehouseName.push(names[idx] || "");
+          }
+        });
+      });
+
+      obj.warehouseIds = warehouseId;
+      obj.warehouseNames = warehouseName;
+      obj.outInDetailList = processedProductList;
+      obj._packingList = _packingList;
+
+      try {
+        const isVerifyData = await isVerifyRepeatIsStock({
+          categoryCodes: this.productList.map((item) => item.categoryCode),
+          batchNos: this.productList.map((item) => item.batchNo),
+        });
+
+        if (isVerifyData?.length) {
+          return new Promise((resolve) => {
+            uni.showModal({
+              title: "提示",
+              content: `当前批次:${isVerifyData[0].batchNo},物品${isVerifyData
+                .map((item) => item.categoryName)
+                .join(",")}已有入库记录,是否继续入库!`,
+              confirmText: "是",
+              cancelText: "否",
+              success: (modalRes) => {
+                if (modalRes.confirm) {
+                  resolve(obj);
+                } else {
+                  resolve(false);
+                }
+              },
+            });
+          });
+        }
+      } catch (e) {
+        console.warn("验证重复入库失败", e);
+      }
+
+      return obj;
+    },
+
+    async getTableValue() {
+      const returnStorageData = await this.getReturnStorage();
+      return {
+        form: this.form,
+        returnStorageData: returnStorageData || {},
+      };
+    },
+  },
+};
+</script>
+
+<style lang="scss">
+.subsection-wrap {
+  background: #f5f7fa;
+  padding: 0 16rpx 12rpx;
+}
+
+.basic_info {
+  box-sizing: border-box;
+  padding: 0 20rpx;
+}
+
+.info_header {
+  margin-top: 10rpx;
+}
+
+.info_title {
+  position: relative;
+  padding: 16rpx 50rpx;
+  font-size: 36rpx;
+  color: #606266;
+}
+
+.info_title::after {
+  content: "";
+  position: absolute;
+  top: 16rpx;
+  left: 0px;
+  width: 16rpx;
+  height: 50rpx;
+  background: #1890ff;
+}
+
+.info_content {
+  .label {
+    padding: 20rpx 0;
+    text-align: right;
+    font-size: 28rpx;
+    font-weight: 700;
+    color: #6e6e6e;
+  }
+
+  .value {
+    padding: 20rpx 0;
+    font-size: 28rpx;
+    color: #606266;
+  }
+}
+
+.card_box {
+  box-sizing: border-box;
+  padding: 20rpx;
+  margin: 20rpx;
+  width: calc(100% - 40rpx);
+  border-radius: 20rpx;
+  box-shadow: 0 0 12rpx -6rpx #000;
+
+  .card_title {
+    font-size: 32rpx;
+    font-weight: 700;
+    color: #666;
+    margin-bottom: 10rpx;
+  }
+
+  .label {
+    padding: 18rpx 0;
+    text-align: right;
+    font-size: 28rpx;
+    font-weight: 700;
+    color: #6e6e6e;
+  }
+
+  .value {
+    padding: 18rpx 0;
+    font-size: 28rpx;
+    color: #606266;
+  }
+}
+
+.empty_text {
+  text-align: center;
+  color: #909399;
+  font-size: 28rpx;
+  padding: 80rpx 0;
+}
+</style>

+ 236 - 0
pages/home/wt/components/productionWarehousing/taskSubmit.vue

@@ -0,0 +1,236 @@
+<template>
+  <view class="">
+    <u--form
+      style="margin: 0 20px"
+      labelPosition="left"
+      :model="form"
+      :rules="rules"
+      ref="uForm"
+      labelWidth="140rpx"
+    >
+      <u-form-item label="审批建议" prop="reason" required>
+        <u--textarea
+          style="width: 100%"
+          height="120"
+          border="surround"
+          placeholder="请输入审批建议"
+          v-model="form.reason"
+        ></u--textarea>
+      </u-form-item>
+    </u--form>
+    <view class="btnList">
+      <u-button
+        style="width: 45%; margin-bottom: 10rpx"
+        :loading="loading"
+        type="success"
+        text="申请入库"
+        @click="storeManagerApprove"
+      >
+      </u-button>
+      <u-button
+        style="width: 45%"
+        :loading="loading"
+        type="error"
+        text="驳回"
+        @click="handleReject"
+      ></u-button>
+    </view>
+    <view class="btnConcel" v-if="taskDefinitionKey !== 'deptLeaderApprove'">
+      <u-button @click="showAction = true">更多</u-button>
+    </view>
+    <u-action-sheet
+      :actions="actionList"
+      :closeOnClickOverlay="true"
+      :closeOnClickAction="true"
+      title="更多操作"
+      :show="showAction"
+      @close="showAction = false"
+      @select="selectActionClick"
+    ></u-action-sheet>
+  </view>
+</template>
+
+<script>
+import {
+  approveTaskWithVariablesOther,
+  cancelTask,
+} from "@/api/wt/index.js";
+import { storage, notPass } from "@/api/warehouseManagement/index.js";
+
+export default {
+  name: "taskSubmit",
+  props: {
+    businessId: {
+      default: "",
+    },
+    taskId: {
+      default: "",
+    },
+    id: {
+      default: "",
+    },
+    taskDefinitionKey: {
+      default: "",
+    },
+  },
+
+  data() {
+    return {
+      showAction: false,
+      loading: false,
+      taskFormData: {},
+      actionList: [
+        {
+          name: "作废",
+          fontSize: "28",
+          color: "#ffaa7f",
+        },
+      ],
+      form: {
+        reason: "同意",
+      },
+      rules: {
+        reason: {
+          type: "string",
+          required: true,
+          message: "请输入审批建议",
+          trigger: "blur",
+        },
+      },
+    };
+  },
+  mounted() {
+    this.$refs.uForm.setRules(this.rules);
+  },
+  methods: {
+    async storeManagerApprove() {
+      try {
+        await this.$refs.uForm.validate();
+      } catch (e) {
+        return;
+      }
+
+      let res = await this.getTableValue();
+      let storageData = res.returnStorageData;
+      if (!storageData || storageData === false) {
+        return;
+      }
+
+      storageData.isSkip = 1;
+
+      try {
+        this.loading = true;
+        let storageRes = await storage(storageData);
+        await approveTaskWithVariablesOther({
+          id: this.taskId,
+          reason: this.form.reason,
+          outInId: storageRes.data[0],
+          businessId: this.businessId,
+          variables: {
+            pass: true,
+          },
+        });
+        this.$emit("handleAudit", {
+          status: 1,
+          title: "入库",
+        });
+      } catch (error) {
+        uni.showToast({ title: "保存失败", icon: "none" });
+      } finally {
+        this.loading = false;
+      }
+    },
+
+    async handleReject() {
+      try {
+        this.loading = true;
+        await notPass({
+          id: this.taskId,
+          reason: this.form.reason,
+          businessId: this.businessId,
+          variables: {
+            pass: false,
+          },
+        });
+        this.$emit("handleAudit", {
+          status: 0,
+          title: "驳回",
+        });
+      } catch (error) {
+        console.error("驳回操作失败", error);
+        uni.showToast({ title: "驳回失败", icon: "none" });
+      } finally {
+        this.loading = false;
+      }
+    },
+
+    async syncTaskFormData() {
+      try {
+        const data = await this.getTableValue();
+        this.taskFormData = data || {};
+      } catch (e) {
+        console.warn("获取 taskForm 数据失败", e);
+        this.taskFormData = {};
+      }
+      return this.taskFormData;
+    },
+
+    selectActionClick(item) {
+      if (item.name == "作废") {
+        uni.showModal({
+          title: "提示",
+          content: "是否确认作废?",
+          success: async (modalRes) => {
+            if (modalRes.confirm) {
+              if (!this.form.reason) {
+                return uni.showToast({
+                  title: "审批建议不能为空",
+                  icon: "none",
+                });
+              }
+              this.loading = true;
+              await this.syncTaskFormData();
+              cancelTask({
+                taskId: this.taskId,
+                id: this.id,
+                reason: this.form.reason,
+                businessId: this.businessId,
+                variables: {
+                  taskFormData: this.taskFormData,
+                },
+              })
+                .then(() => {
+                  this.loading = false;
+                  this.$emit("handleAudit", {
+                    title: "作废",
+                  });
+                })
+                .catch(() => {
+                  this.loading = false;
+                  uni.showToast({ title: "流程作废失败", icon: "none" });
+                });
+            }
+          },
+        });
+      }
+    },
+
+    getTableValue() {
+      return new Promise((resolve, reject) => {
+        this.$emit("getTableValue", async (data) => {
+          resolve(await data);
+        });
+      });
+    },
+  },
+};
+</script>
+
+<style scoped>
+.btnList {
+  display: flex;
+}
+.btnConcel {
+  margin-top: 20rpx;
+}
+</style>

+ 1 - 1
pages/home/wt/todo/todo.vue

@@ -29,12 +29,12 @@
               <view class="card-footer">
                 <u-button
                   type="success"
+                  @click="handleDetail(item, 'handle')"
                   v-if="
                     ['0', '2'].includes(
                       item.extensionProperty.taskHandlePlatform,
                     )
                   "
-                  @click="handleDetail(item, 'handle')"
                   >处理</u-button
                 >
                 <u-button v-else type="error">请在PC端处理</u-button>