Jelajahi Sumber

Merge branch 'master' of http://110.41.163.243:9980/kd-aiot/aiot-app

liujt 5 bulan lalu
induk
melakukan
66975c3b38

+ 3 - 1
api/wt/index.js

@@ -632,7 +632,9 @@ export async function getLoginUser(data) {
   if (res.data && res.data.code == 0) {
     return res.data.data;
   }
-  return Promise.reject(new Error(res.message || res.data?.message || "获取登录用户数据失败"));
+  return Promise.reject(
+    new Error(res.message || res.data?.message || "获取登录用户数据失败"),
+  );
 }
 
 // 生产入库审批(申请入库)

+ 1 - 1
manifest.json

@@ -2,7 +2,7 @@
     "name" : "智慧工厂",
     "appid" : "__UNI__45B3907",
     "description" : "",
-    "versionName" : "V1.0.3.54",
+    "versionName" : "V1.0.3.62",
     "versionCode" : "100",
     "transformPx" : false,
     "h5" : {

+ 8 - 0
pages.json

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

+ 50 - 3
pages/home/wt/components/materialRequisition/taskForm.vue

@@ -1,6 +1,6 @@
 <template>
   <view class="">
-    <u-sticky offset-top="50">
+    <view class="sticky-subsection" :style="{ top: `${stickyTop}px` }">
       <view class="subsection-wrap">
         <u-subsection
           fontSize="25"
@@ -11,7 +11,11 @@
           activeColor="#157A2C"
         ></u-subsection>
       </view>
-    </u-sticky>
+    </view>
+    <view
+      class="sticky-subsection-placeholder"
+      :style="{ height: `${stickyPlaceholderHeight}px` }"
+    ></view>
 
     <view v-show="curNow === 0" class="out-info-wrap">
       <view class="out-info-title">
@@ -326,6 +330,8 @@ export default {
       wwType: null,
       outboundItemGrid: OUTBOUND_ITEM_GRID,
       packingItemGrid: PACKING_ITEM_GRID,
+      stickyPlaceholderHeight: 0,
+      stickyTop: 44,
     };
   },
   computed: {
@@ -501,11 +507,36 @@ export default {
     },
   },
   async mounted() {
+    this.initStickyTop();
     await this.loadAssetTypeMapping();
     await this.getDetailData(this.businessId);
     await this.getLoginUserData();
+    this.updateStickyPlaceholderHeight();
   },
   methods: {
+    initStickyTop() {
+      try {
+        const systemInfo = uni.getSystemInfoSync();
+        const statusBarHeight = Number(systemInfo.statusBarHeight || 0);
+        // `uni-nav-bar` 开启 statusBar 后,内容区高度默认约为 44px。
+        this.stickyTop = statusBarHeight + 44;
+      } catch (error) {
+        this.stickyTop = 50;
+      }
+    },
+    updateStickyPlaceholderHeight() {
+      this.$nextTick(() => {
+        const query = uni.createSelectorQuery().in(this);
+        query
+          .select(".sticky-subsection")
+          .boundingClientRect((rect) => {
+            if (rect && rect.height) {
+              this.stickyPlaceholderHeight = rect.height;
+            }
+          })
+          .exec();
+      });
+    },
     dash(val) {
       if (val === null || val === undefined) return "—";
       if (typeof val === "string" && val.trim() === "") return "—";
@@ -983,9 +1014,25 @@ export default {
   margin-top: 20rpx;
 }
 
+.sticky-subsection {
+  position: fixed;
+  left: 0;
+  right: 0;
+  z-index: 99;
+  width: 100%;
+  box-sizing: border-box;
+  background: #f5f7fa;
+}
+
+.sticky-subsection-placeholder {
+  width: 100%;
+}
+
 .subsection-wrap {
   background: #f5f7fa;
-  padding: 12rpx 16rpx;
+  padding: 0 16rpx 12rpx;
+  width: 100%;
+  box-sizing: border-box;
 }
 
 .pick-info-wrap {

+ 218 - 0
pages/home/wt/components/outStorageWarehouseApply/processTask.vue

@@ -0,0 +1,218 @@
+<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;
+      this.runningTasks = [];
+      this.auditForms = [];
+
+      if (!this.listData.id) {
+        this.processInstanceLoading = false;
+        return;
+      }
+
+      try {
+        const response = await getProcessInstance({
+          id: this.listData.id,
+        });
+        if (!response) {
+          uni.showToast({
+            title: "查询不到流程信息",
+            icon: "none",
+            duration: 3000,
+          });
+          this.processInstanceLoading = false;
+          return;
+        }
+        this.processInstance = response;
+        this.uniNavBarTitle = `${response.name}`;
+      } catch (err) {
+        this.processInstanceLoading = false;
+        uni.showModal({
+          title: "getProcessInstance 失败",
+          content: String((err && err.message) || err),
+          showCancel: false,
+        });
+        return;
+      }
+
+      this.processInstanceLoading = false;
+
+      try {
+        const response = await getTaskListByProcessInstanceId({
+          processInstanceId: this.listData.id,
+        });
+        if (!Array.isArray(response)) {
+          uni.showModal({
+            title: "任务列表格式异常",
+            content: JSON.stringify({ raw: response }).slice(0, 1700),
+            showCancel: false,
+          });
+          return;
+        }
+
+        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;
+          }
+        });
+
+        const userInfo = uni.getStorageSync("userInfo") || {};
+        const userId = userInfo.userId;
+
+        this.tasks.forEach((task) => {
+          if (task.result !== 1 && task.result !== 6) {
+            return;
+          }
+          if (userId == null) {
+            return;
+          }
+          if (!task.assigneeUser || task.assigneeUser.id !== userId) {
+            return;
+          }
+          if (task.taskDefinitionKey !== this.listData.taskDefinitionKey) {
+            return;
+          }
+          this.runningTasks.push({
+            ...task,
+          });
+          this.auditForms.push({
+            reason: "",
+          });
+        });
+      } catch (err) {
+        uni.showModal({
+          title: "getTaskList 或解析失败",
+          content: String((err && err.message) || err),
+          showCancel: false,
+        });
+      }
+    },
+
+    /** 处理审批通过和不通过的操作 */
+    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>

+ 1686 - 0
pages/home/wt/components/outStorageWarehouseApply/taskForm.vue

@@ -0,0 +1,1686 @@
+<template>
+  <view class="">
+    <view class="sticky-subsection" :style="{ top: `${stickyTop}px` }">
+      <view class="subsection-wrap">
+        <u-subsection
+          fontSize="25"
+          mode="subsection"
+          :list="tabList"
+          :current="curNow"
+          @change="sectionChange"
+          activeColor="#157A2C"
+        ></u-subsection>
+      </view>
+    </view>
+    <view
+      class="sticky-subsection-placeholder"
+      :style="{ height: `${stickyPlaceholderHeight}px` }"
+    ></view>
+
+    <!-- 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">{{
+              formatValueWithUnit(
+                row.packingQuantityDisplay,
+                row.packingQuantity,
+                row.packingUnit,
+              )
+            }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="3">
+            <view class="label">计量数量:</view>
+          </u-col>
+          <u-col span="9">
+            <view class="value">{{
+              formatValueWithUnit(
+                row.measureQuantityDisplay,
+                row.measureQuantity,
+                row.measureUnit,
+              )
+            }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="3">
+            <view class="label">重量:</view>
+          </u-col>
+          <u-col span="9">
+            <view class="value">{{
+              formatValueWithUnit(row.weightDisplay, row.weight, row.weightUnit)
+            }}</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">{{
+              formatValueWithUnit(
+                row.packingQuantityDisplay,
+                row.packingQuantity,
+                row.packingUnit,
+              )
+            }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="3">
+            <view class="label">计量数量:</view>
+          </u-col>
+          <u-col span="9">
+            <view class="value">{{
+              formatValueWithUnit(
+                row.measureQuantityDisplay,
+                row.measureQuantity,
+                row.measureUnit,
+              )
+            }}</view>
+          </u-col>
+        </u-row>
+        <u-row>
+          <u-col span="3">
+            <view class="label">重量:</view>
+          </u-col>
+          <u-col span="9">
+            <view class="value">{{
+              formatValueWithUnit(row.weightDisplay, row.weight, row.weightUnit)
+            }}</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: "",
+      stickyPlaceholderHeight: 0,
+      stickyTop: 44,
+    };
+  },
+  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() {
+    this.initStickyTop();
+    await this.loadAssetTypeMapping();
+    await this.getDetailData(this.businessId);
+    await this.getLoginUserData();
+    this.updateStickyPlaceholderHeight();
+  },
+  methods: {
+    padNumber(n) {
+      return n < 10 ? `0${n}` : String(n);
+    },
+    arrayIncludes(list, value) {
+      return Array.isArray(list) && list.indexOf(value) !== -1;
+    },
+    initStickyTop() {
+      try {
+        const systemInfo = uni.getSystemInfoSync();
+        const statusBarHeight = Number(systemInfo.statusBarHeight || 0);
+        // `uni-nav-bar` 在开启 statusBar 后,内容区默认高度约为 44px。
+        this.stickyTop = statusBarHeight + 44;
+      } catch (error) {
+        this.stickyTop = 50;
+      }
+    },
+    updateStickyPlaceholderHeight() {
+      this.$nextTick(() => {
+        const query = uni.createSelectorQuery().in(this);
+        query
+          .select(".sticky-subsection")
+          .boundingClientRect((rect) => {
+            if (rect && rect.height) {
+              this.stickyPlaceholderHeight = rect.height;
+            }
+          })
+          .exec();
+      });
+    },
+    sectionChange(index) {
+      this.curNow = index;
+    },
+
+    formatField(val) {
+      if (Array.isArray(val)) return val.join(",") || "—";
+      return val || "—";
+    },
+
+    formatValueWithUnit(displayValue, value, unit) {
+      if (displayValue || displayValue === 0 || displayValue === "0") {
+        return String(displayValue);
+      }
+      if (value === null || value === undefined || value === "") {
+        return "—";
+      }
+      return `${value}${unit || ""}`;
+    },
+
+    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) => this.padNumber(n);
+      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
+              : this.arrayIncludes([1, 3, 6], 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);
+        console.log("this.productList", this.productList);
+        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.reduce((list, product) => {
+        return list.concat(product.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) => this.padNumber(n);
+      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 this.arrayIncludes(["KG", "G", "L", "ML"], 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,
+                packingQuantityDisplay: this.formatValueWithUnit(
+                  "",
+                  packingQuantity,
+                  packingUnit,
+                ),
+                measureQuantity: measureQuantity,
+                measureUnit: measureUnit,
+                measureQuantityDisplay: this.formatValueWithUnit(
+                  "",
+                  measureQuantity,
+                  measureUnit,
+                ),
+                weight: weight,
+                weightDisplay: this.formatValueWithUnit(
+                  "",
+                  weight,
+                  product.weightUnit || "",
+                ),
+                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;
+                lastEntry.measureQuantityDisplay = this.formatValueWithUnit(
+                  "",
+                  remainder,
+                  lastEntry.measureUnit,
+                );
+                if (product.singleWeight) {
+                  lastEntry.weight = product.singleWeight * remainder;
+                  lastEntry.weightDisplay = this.formatValueWithUnit(
+                    "",
+                    lastEntry.weight,
+                    lastEntry.weightUnit,
+                  );
+                }
+              }
+            }
+
+            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 ? val.productBrand : val.batchNo,
+              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 || "",
+            packingQuantityDisplay: this.formatValueWithUnit(
+              "",
+              1,
+              item.packingUnit || "",
+            ),
+            measureQuantity: item.quantity || item.measureQuantity || 0,
+            measureUnit: item.measuringUnit || item.measureUnit || "",
+            measureQuantityDisplay: this.formatValueWithUnit(
+              "",
+              item.quantity || item.measureQuantity || 0,
+              item.measuringUnit || item.measureUnit || "",
+            ),
+            supplierCode: item.supplierCode || "",
+            supplierId: item.supplierId || "",
+            supplierName: item.supplierName || "",
+            weight: item.packingWeight || 0,
+            weightDisplay: this.formatValueWithUnit(
+              "",
+              item.packingWeight || 0,
+              item.weightUnit || product0.weightUnit || "",
+            ),
+            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 (this.arrayIncludes([1], 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.indexOf(id) === -1) {
+            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">
+.sticky-subsection {
+  position: fixed;
+  left: 0;
+  right: 0;
+  z-index: 99;
+  width: 100%;
+  box-sizing: border-box;
+  background: #f5f7fa;
+}
+
+.sticky-subsection-placeholder {
+  width: 100%;
+}
+
+.subsection-wrap {
+  background: #f5f7fa;
+  padding: 0 16rpx 12rpx;
+  width: 100%;
+  box-sizing: border-box;
+}
+
+.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/outStorageWarehouseApply/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>

+ 57 - 28
pages/home/wt/components/productionWarehousing/processTask.vue

@@ -66,82 +66,111 @@ export default {
   },
 
   onLoad(option) {
-    this.listData = option;
+    this.listData = option || {};
     this.getDetail();
     this.activeComp = "tab1";
   },
+
   methods: {
     /** 获得流程实例 */
     async getDetail() {
-      // 获得流程实例相关
       this.processInstanceLoading = true;
-      getProcessInstance({
-        id: this.listData.id,
-      }).then(async (response) => {
+      this.runningTasks = [];
+      this.auditForms = [];
+
+      if (!this.listData.id) {
+        this.processInstanceLoading = false;
+        return;
+      }
+
+      try {
+        const response = await getProcessInstance({
+          id: this.listData.id,
+        });
         if (!response) {
-          this.$message.error("查询不到流程信息!");
+          uni.showToast({
+            title: "查询不到流程信息",
+            icon: "none",
+            duration: 3000,
+          });
+          this.processInstanceLoading = false;
           return;
         }
-        // 设置流程信息
         this.processInstance = response;
         this.uniNavBarTitle = `${response.name}`;
-
+      } catch (err) {
         this.processInstanceLoading = false;
-      });
+        uni.showModal({
+          title: "getProcessInstance 失败",
+          content: String((err && err.message) || err),
+          showCancel: false,
+        });
+        return;
+      }
+
+      this.processInstanceLoading = false;
+
+      try {
+        const response = await getTaskListByProcessInstanceId({
+          processInstanceId: this.listData.id,
+        });
+        if (!Array.isArray(response)) {
+          uni.showModal({
+            title: "任务列表格式异常",
+            content: JSON.stringify({ raw: response }).slice(0, 1700),
+            showCancel: false,
+          });
+          return;
+        }
 
-      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");
+        const userInfo = uni.getStorageSync("userInfo") || {};
+        const userId = userInfo.userId;
+
         this.tasks.forEach((task) => {
           if (task.result !== 1 && task.result !== 6) {
-            // 只有待处理才需要
             return;
           }
-          if (!task.assigneeUser || task.assigneeUser.id !== userInfo.userId) {
-            // 自己不是处理人
+          if (userId == null) {
+            return;
+          }
+          if (!task.assigneeUser || task.assigneeUser.id !== userId) {
             return;
           }
           if (task.taskDefinitionKey !== this.listData.taskDefinitionKey) {
-            // 不是当前流程的
             return;
           }
           this.runningTasks.push({
             ...task,
           });
-          console.log(this.runningTasks, " this.runningTasks");
           this.auditForms.push({
             reason: "",
           });
         });
-      });
+      } catch (err) {
+        uni.showModal({
+          title: "getTaskList 或解析失败",
+          content: String((err && err.message) || err),
+          showCancel: false,
+        });
+      }
     },
 
     /** 处理审批通过和不通过的操作 */

+ 309 - 378
pages/home/wt/components/productionWarehousing/taskForm.vue

@@ -1,6 +1,6 @@
 <template>
   <view class="">
-    <u-sticky offset-top="50">
+    <view class="sticky-subsection" :style="{ top: `${stickyTop}px` }">
       <view class="subsection-wrap">
         <u-subsection
           fontSize="25"
@@ -11,7 +11,11 @@
           activeColor="#157A2C"
         ></u-subsection>
       </view>
-    </u-sticky>
+    </view>
+    <view
+      class="sticky-subsection-placeholder"
+      :style="{ height: `${stickyPlaceholderHeight}px` }"
+    ></view>
 
     <!-- Tab 0: 入库单详情 -->
     <view v-show="curNow === 0" class="basic_info">
@@ -191,7 +195,13 @@
             <view class="label">包装数量:</view>
           </u-col>
           <u-col span="9">
-            <view class="value">{{ row.packingQuantityDisplay || "—" }}</view>
+            <view class="value">{{
+              formatValueWithUnit(
+                row.packingQuantityDisplay,
+                row.packingQuantity,
+                row.packingUnit,
+              )
+            }}</view>
           </u-col>
         </u-row>
         <u-row>
@@ -199,7 +209,13 @@
             <view class="label">计量数量:</view>
           </u-col>
           <u-col span="9">
-            <view class="value">{{ row.measureQuantityDisplay || "—" }}</view>
+            <view class="value">{{
+              formatValueWithUnit(
+                row.measureQuantityDisplay,
+                row.measureQuantity,
+                row.measureUnit,
+              )
+            }}</view>
           </u-col>
         </u-row>
         <u-row>
@@ -207,7 +223,9 @@
             <view class="label">重量:</view>
           </u-col>
           <u-col span="9">
-            <view class="value">{{ row.weightDisplay || "—" }}</view>
+            <view class="value">{{
+              formatValueWithUnit(row.weightDisplay, row.weight, row.weightUnit)
+            }}</view>
           </u-col>
         </u-row>
         <u-row>
@@ -315,7 +333,13 @@
             <view class="label">包装数量:</view>
           </u-col>
           <u-col span="9">
-            <view class="value">{{ row.packingQuantityDisplay || "—" }}</view>
+            <view class="value">{{
+              formatValueWithUnit(
+                row.packingQuantityDisplay,
+                row.packingQuantity,
+                row.packingUnit,
+              )
+            }}</view>
           </u-col>
         </u-row>
         <u-row>
@@ -323,7 +347,13 @@
             <view class="label">计量数量:</view>
           </u-col>
           <u-col span="9">
-            <view class="value">{{ row.measureQuantityDisplay || "—" }}</view>
+            <view class="value">{{
+              formatValueWithUnit(
+                row.measureQuantityDisplay,
+                row.measureQuantity,
+                row.measureUnit,
+              )
+            }}</view>
           </u-col>
         </u-row>
         <u-row>
@@ -331,7 +361,9 @@
             <view class="label">重量:</view>
           </u-col>
           <u-col span="9">
-            <view class="value">{{ row.weightDisplay || "—" }}</view>
+            <view class="value">{{
+              formatValueWithUnit(row.weightDisplay, row.weight, row.weightUnit)
+            }}</view>
           </u-col>
         </u-row>
         <u-row>
@@ -527,6 +559,8 @@ export default {
       isDetail: false,
       totalPackage: "",
       totalCount: "",
+      stickyPlaceholderHeight: 0,
+      stickyTop: 44,
     };
   },
   computed: {
@@ -558,11 +592,36 @@ export default {
     },
   },
   async mounted() {
+    this.initStickyTop();
     await this.loadAssetTypeMapping();
     await this.getDetailData(this.businessId);
     await this.getLoginUserData();
+    this.updateStickyPlaceholderHeight();
   },
   methods: {
+    initStickyTop() {
+      try {
+        const systemInfo = uni.getSystemInfoSync();
+        const statusBarHeight = Number(systemInfo.statusBarHeight || 0);
+        // `uni-nav-bar` 在开启 statusBar 后,内容区默认高度约为 44px。
+        this.stickyTop = statusBarHeight + 44;
+      } catch (error) {
+        this.stickyTop = 50;
+      }
+    },
+    updateStickyPlaceholderHeight() {
+      this.$nextTick(() => {
+        const query = uni.createSelectorQuery().in(this);
+        query
+          .select(".sticky-subsection")
+          .boundingClientRect((rect) => {
+            if (rect && rect.height) {
+              this.stickyPlaceholderHeight = rect.height;
+            }
+          })
+          .exec();
+      });
+    },
     sectionChange(index) {
       this.curNow = index;
     },
@@ -572,6 +631,16 @@ export default {
       return val || "—";
     },
 
+    formatValueWithUnit(displayValue, value, unit) {
+      if (displayValue || displayValue === 0 || displayValue === "0") {
+        return String(displayValue);
+      }
+      if (value === null || value === undefined || value === "") {
+        return "—";
+      }
+      return `${value}${unit || ""}`;
+    },
+
     async loadAssetTypeMapping() {
       try {
         const data = await allCategoryLevel();
@@ -758,57 +827,35 @@ export default {
 
           if (this.isDetail) {
             // isAllPackageData==1: 对齐PC端 saleProductList 路径
-            packUnit =
-              detail.packingUnit || item.packingUnit || "";
-            measureUnit =
-              detail.measuringUnit || item.measuringUnit || "";
-            packingQuantity =
-              detail.packingCount || detail.quantity || 0;
+            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 || "";
+            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,
+              (d) => (d.productCode || d.categoryCode) === item.code,
             );
             packingQuantity = isMoreProduct
               ? matchDetails.length
               : detailList.length;
             measureQuantity = isMoreProduct
-              ? matchDetails.reduce(
-                  (sum, d) => sum + (d.quantity || 0),
-                  0,
-                )
+              ? 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,
-              );
+              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 || "";
@@ -827,12 +874,8 @@ export default {
                 ? this.form.colorKey.split(",")
                 : "";
             } else {
-              modelKey = item.modelKey
-                ? item.modelKey.split(",")
-                : "";
-              colorKey = item.colorKey
-                ? item.colorKey.split(",")
-                : "";
+              modelKey = item.modelKey ? item.modelKey.split(",") : "";
+              colorKey = item.colorKey ? item.colorKey.split(",") : "";
             }
             packingUnitId = "";
           }
@@ -842,8 +885,7 @@ export default {
             (detail.sendTotalWeight || detail.receiveTotalWeight) &&
             item.weightUnit === item.measuringUnit
           ) {
-            weight =
-              detail.sendTotalWeight || detail.receiveTotalWeight;
+            weight = detail.sendTotalWeight || detail.receiveTotalWeight;
           }
           const singleWeight = detail.singleWeight || 0;
           if (!weight && singleWeight) {
@@ -866,44 +908,32 @@ export default {
             packingQuantity,
             packingUnit: packUnit,
             packingQuantityDisplay:
-              packingQuantity !== 0
-                ? `${packingQuantity}${packUnit}`
-                : "",
+              packingQuantity !== 0 ? `${packingQuantity}${packUnit}` : "",
             measureQuantity,
             measureUnit: measureUnit,
             measuringUnit: measureUnit,
             measureQuantityDisplay:
-              measureQuantity !== 0
-                ? `${measureQuantity}${measureUnit}`
-                : "",
+              measureQuantity !== 0 ? `${measureQuantity}${measureUnit}` : "",
             measureType: item.measureType,
             weight,
             weightUnit,
-            weightDisplay:
-              weight !== 0 ? `${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 || "",
+            supplierName: detail.supplierName || item.supplierName || "",
+            supplierCode: detail.supplierCode || item.supplierCode || "",
+            supplierId: detail.supplierId || this.form.supplierId || "",
+            productionRequirements: detail.productionRequirements || "",
             packingSpecification:
-              item.packingSpecification ||
-              packingSpecLabel ||
-              "",
+              item.packingSpecification || packingSpecLabel || "",
             packingSpecificationOption: itemPackingSpecs,
             packingUnitId,
             modelKey,
             colorKey,
-            netWeight:
-              item.netWeight > -1 ? item.netWeight : 0,
+            netWeight: item.netWeight > -1 ? item.netWeight : 0,
             isUnpack: item.isUnpack,
             qualityControl: detail.qualityControl,
             extField: item.extField || {},
@@ -1041,29 +1071,18 @@ export default {
         // 从 productList 生成包装明细
         try {
           for (const product of this.productList) {
-            if (
-              !product.packingQuantity ||
-              product.packingQuantity <= 0
-            )
+            if (!product.packingQuantity || product.packingQuantity <= 0)
               continue;
 
-            const packingSpecOption =
-              product.packingSpecificationOption || [];
-            const packingBoolen = this._isNonSplitUnit(
-              product.packingUnit,
-            );
-            const measureBoolen = this._isNonSplitUnit(
-              product.measureUnit,
-            );
+            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 === "立方"
-              ) {
+              if (!product.isUnpack && product.packingUnit === "立方") {
                 packingNum = 1;
               } else {
                 filterArr = packingSpecOption.filter(
@@ -1073,80 +1092,60 @@ export default {
                 );
                 if (filterArr.length) {
                   packingNum = Math.ceil(
-                    product.packingQuantity /
-                      filterArr[0].packageCell,
+                    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 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;
+                    num = num * packingSpecOption[splitIndex].packageCell;
                   }
                   packingNum = Math.ceil(num);
                 } else {
                   if (product.packingUnitId) {
-                    let splitIndex =
-                      packingSpecOption.findIndex(
-                        (s) => s.id == product.packingUnitId,
-                      );
+                    let splitIndex = packingSpecOption.findIndex(
+                      (s) => s.id == product.packingUnitId,
+                    );
                     if (splitIndex == 0) {
                       packingNum = Math.ceil(
                         product.measureQuantity /
-                          (packingSpecOption[1]?.packageCell ||
-                            1),
+                          (packingSpecOption[1]?.packageCell || 1),
                       );
                     } else if (splitIndex == 1) {
                       packingNum = product.packingQuantity;
                     } else {
-                      for (
-                        ;
-                        splitIndex > 1;
-                        splitIndex--
-                      ) {
+                      for (; splitIndex > 1; splitIndex--) {
                         packingNum = Math.ceil(
                           product.packingQuantity *
-                            packingSpecOption[splitIndex]
-                              .packageCell,
+                            packingSpecOption[splitIndex].packageCell,
                         );
                       }
                     }
-                  } else if (
-                    packingSpecOption[1]?.packageCell
-                  ) {
+                  } else if (packingSpecOption[1]?.packageCell) {
                     packingNum = Math.ceil(
                       product.measureQuantity /
                         packingSpecOption[1].packageCell,
                     );
                     filterArr = packingSpecOption.filter(
                       (s) =>
-                        s.packageUnit ===
-                          product.packingUnit &&
+                        s.packageUnit === product.packingUnit &&
                         s.packageUnit !== s.conversionUnit,
                     );
                   }
                 }
               } else {
-                if (
-                  product.measureUnit === product.packingUnit
-                ) {
+                if (product.measureUnit === product.packingUnit) {
                   packingNum = 0;
                 } else {
-                  packingNum = Number(
-                    product.packingQuantity,
-                  );
+                  packingNum = Number(product.packingQuantity);
                 }
               }
             }
@@ -1157,8 +1156,7 @@ export default {
             try {
               const assetNumRes = await getAssetNum([
                 {
-                  assetCode:
-                    product.categoryCode + product.index,
+                  assetCode: product.categoryCode + product.index,
                   batchNum: product.batchNo,
                   num: packingNum,
                 },
@@ -1168,8 +1166,7 @@ export default {
               console.warn("获取包装编码失败", e);
             }
 
-            const qualityStatus =
-              product.qualityControl == 1 ? 0 : 2;
+            const qualityStatus = product.qualityControl == 1 ? 0 : 2;
             const packingEntries = [];
 
             for (let i = 0; i < packageNos.length; i++) {
@@ -1177,157 +1174,124 @@ export default {
               let packingQuantity = 1;
 
               if (packingBoolen) {
-                const cell =
-                  filterArr[0]?.packageCell || 1;
+                const cell = filterArr[0]?.packageCell || 1;
                 measureQuantity =
-                  Number(product.packingQuantity) >
-                  cell * (i + 1)
+                  Number(product.packingQuantity) > cell * (i + 1)
                     ? cell
-                    : Number(product.packingQuantity) -
-                      cell * i;
+                    : Number(product.packingQuantity) - cell * i;
               } else {
                 if (
-                  product.weightUnit ===
-                    product.measureUnit &&
+                  product.weightUnit === product.measureUnit &&
                   product.weight
                 ) {
                   measureQuantity =
-                    Math.trunc(
-                      (product.weight /
-                        packageNos.length) *
-                        10000,
-                    ) / 10000;
+                    Math.trunc((product.weight / packageNos.length) * 10000) /
+                    10000;
                 } else if (measureBoolen) {
-                  let splitIndex =
-                    packingSpecOption.findIndex(
-                      (s) =>
-                        s.conversionUnit ===
-                          product.packingUnit &&
-                        s.packageUnit !== s.conversionUnit,
-                    );
+                  let splitIndex = packingSpecOption.findIndex(
+                    (s) =>
+                      s.conversionUnit === product.packingUnit &&
+                      s.packageUnit !== s.conversionUnit,
+                  );
                   measureQuantity = 1;
                   for (; splitIndex > 0; splitIndex--) {
                     measureQuantity =
                       measureQuantity *
-                      packingSpecOption[splitIndex]
-                        .packageCell;
+                      packingSpecOption[splitIndex].packageCell;
                   }
                 } else {
-                  measureQuantity =
-                    packingSpecOption[1]?.packageCell || 1;
+                  measureQuantity = packingSpecOption[1]?.packageCell || 1;
                 }
               }
 
               let clientCode = "";
               if (this.bizType == 2) {
-                clientCode =
-                  product.customerMark || "";
+                clientCode = product.customerMark || "";
               } else {
-                clientCode =
-                  product.extInfo?.clientCode || "";
+                clientCode = product.extInfo?.clientCode || "";
               }
 
               let packingUnit = product.packingUnit;
               if (product.isUnpack) {
                 if (packingBoolen) {
                   packingUnit =
-                    packingSpecOption[1]
-                      ?.conversionUnit ||
-                    product.packingUnit;
+                    packingSpecOption[1]?.conversionUnit || product.packingUnit;
                 } else if (measureBoolen) {
                   packingUnit =
-                    (
-                      packingSpecOption[1] ||
-                      packingSpecOption[0]
-                    )?.conversionUnit ||
-                    product.packingUnit;
+                    (packingSpecOption[1] || packingSpecOption[0])
+                      ?.conversionUnit || product.packingUnit;
                 } else {
                   packingUnit =
-                    packingSpecOption[1]
-                      ?.conversionUnit ||
-                    product.packingUnit;
+                    packingSpecOption[1]?.conversionUnit || product.packingUnit;
                 }
               }
 
               let measureUnit = product.isUnpack
                 ? measureBoolen
-                  ? (
-                      packingSpecOption[1] ||
-                      packingSpecOption[0]
-                    )?.packageUnit ||
-                    product.measureUnit
+                  ? (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);
+              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 || "",
+                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 || "",
+                packageNo: packageNos[i]?.onlyCode || "",
                 packingQuantity: packingQuantity,
+                packingQuantityDisplay: this.formatValueWithUnit(
+                  "",
+                  packingQuantity,
+                  packingUnit,
+                ),
                 modelKey: product.modelKey || "",
                 colorKey: product.colorKey || "",
                 packingUnit: packingUnit,
                 measureQuantity: measureQuantity,
                 measureUnit: measureUnit,
+                measureQuantityDisplay: this.formatValueWithUnit(
+                  "",
+                  measureQuantity,
+                  measureUnit,
+                ),
                 weight: weight,
-                packingSpecificationOption:
-                  product.packingSpecificationOption ||
-                  [],
-                weightUnit:
+                weightDisplay: this.formatValueWithUnit(
+                  "",
+                  weight,
                   product.weightUnit || "",
+                ),
+                packingSpecificationOption:
+                  product.packingSpecificationOption || [],
+                weightUnit: product.weightUnit || "",
                 netWeight: product.netWeight || 0,
                 barcodes: "",
                 clientCode: clientCode,
-                materielDesignation:
-                  product.extInfo?.materielCode ||
-                  "",
+                materielDesignation: product.extInfo?.materielCode || "",
                 engrave: "",
                 isUnpack: product.isUnpack,
                 productionDate: productionDate,
                 purchaseDate: "",
                 result: 1,
-                qualityControl:
-                  product.qualityControl,
+                qualityControl: product.qualityControl,
                 status: qualityStatus,
-                resultLabel:
-                  QUALITY_RESULTS[1] || "合格",
-                statusLabel:
-                  QUALITY_STATUS[qualityStatus] ||
-                  "未质检",
+                resultLabel: QUALITY_RESULTS[1] || "合格",
+                statusLabel: QUALITY_STATUS[qualityStatus] || "未质检",
                 workOrderId: "",
                 taskId: "",
               });
@@ -1339,26 +1303,27 @@ export default {
               packingSpecOption[1]?.packageCell
             ) {
               const remainder =
-                product.measureQuantity %
-                packingSpecOption[1].packageCell;
-              if (
-                remainder > 0 &&
-                packingEntries.length > 0
-              ) {
-                const lastEntry =
-                  packingEntries[
-                    packingEntries.length - 1
-                  ];
+                product.measureQuantity % packingSpecOption[1].packageCell;
+              if (remainder > 0 && packingEntries.length > 0) {
+                const lastEntry = packingEntries[packingEntries.length - 1];
                 lastEntry.measureQuantity = remainder;
+                lastEntry.measureQuantityDisplay = this.formatValueWithUnit(
+                  "",
+                  remainder,
+                  lastEntry.measureUnit,
+                );
                 if (product.singleWeight) {
-                  lastEntry.weight =
-                    product.singleWeight * remainder;
+                  lastEntry.weight = product.singleWeight * remainder;
+                  lastEntry.weightDisplay = this.formatValueWithUnit(
+                    "",
+                    lastEntry.weight,
+                    lastEntry.weightUnit,
+                  );
                 }
               }
             }
 
-            product.outInDetailRecordRequestList =
-              packingEntries;
+            product.outInDetailRecordRequestList = packingEntries;
             this.packingList.push(...packingEntries);
           }
         } catch (e) {
@@ -1372,14 +1337,13 @@ export default {
         let getAssetNumPr = isMoreProduct
           ? detailList.map((val) => ({
               assetCode: val.productCode,
-              batchNum: val.productBrand,
+              batchNum: val.productBrand ? val.productBrand : val.batchNo,
               num: 1,
             }))
           : [
               {
                 assetCode:
-                  (product0.categoryCode || "") +
-                  (product0.index ?? 0),
+                  (product0.categoryCode || "") + (product0.index ?? 0),
                 batchNum: product0.batchNo || "",
                 num: detailList.length,
               },
@@ -1387,158 +1351,109 @@ export default {
 
         let packingCodeList = [];
         try {
-          const assetNumRes = await getAssetNum(
-            getAssetNumPr,
-          );
+          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 ||
-                "";
-            }
+        this.packingList = detailList.map((item, index) => {
+          const matchProduct = isMoreProduct
+            ? this.productList.find(
+                (val) => val.categoryCode === item.productCode,
+              ) || product0
+            : product0;
 
-            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 || "",
-            };
-          },
-        );
+          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 || "",
+            packingQuantityDisplay: this.formatValueWithUnit(
+              "",
+              1,
+              item.packingUnit || "",
+            ),
+            measureQuantity: item.quantity || item.measureQuantity || 0,
+            measureUnit: item.measuringUnit || item.measureUnit || "",
+            measureQuantityDisplay: this.formatValueWithUnit(
+              "",
+              item.quantity || item.measureQuantity || 0,
+              item.measuringUnit || item.measureUnit || "",
+            ),
+            supplierCode: item.supplierCode || "",
+            supplierId: item.supplierId || "",
+            supplierName: item.supplierName || "",
+            weight: item.packingWeight || 0,
+            weightDisplay: this.formatValueWithUnit(
+              "",
+              item.packingWeight || 0,
+              item.weightUnit || product0.weightUnit || "",
+            ),
+            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,
-            );
+          product.outInDetailRecordRequestList = this.packingList.filter(
+            (p) => p.categoryCode === product.categoryCode,
+          );
         });
       }
     },
@@ -1662,9 +1577,25 @@ export default {
 </script>
 
 <style lang="scss">
+.sticky-subsection {
+  position: fixed;
+  left: 0;
+  right: 0;
+  z-index: 99;
+  width: 100%;
+  box-sizing: border-box;
+  background: #f5f7fa;
+}
+
+.sticky-subsection-placeholder {
+  width: 100%;
+}
+
 .subsection-wrap {
   background: #f5f7fa;
   padding: 0 16rpx 12rpx;
+  width: 100%;
+  box-sizing: border-box;
 }
 
 .basic_info {

+ 12 - 3
pages/pda/picking/bill/components/outInstanceBom.vue

@@ -69,11 +69,16 @@ import { tableHeader } from "./outCommon.js";
 import { queryOutWordDetail } from "@/api/pda/picking.js";
 import { typeName } from "@/pages/pda/feeding/common.js";
 export default {
-  props: ["item"],
+  props: {
+    item: {
+      type: Object,
+      default: () => ({}),
+    },
+  },
   data() {
     return {
       typeName,
-      outWorkList: null,
+      outWorkList: [],
     };
   },
 
@@ -83,10 +88,14 @@ export default {
 
   methods: {
     async getOutWorkList() {
+      if (!this.item?.pickOrderId) {
+        this.outWorkList = [];
+        return;
+      }
       await queryOutWordDetail({
         pickOrderId: this.item.pickOrderId,
       }).then((res) => {
-        this.outWorkList = res;
+        this.outWorkList = Array.isArray(res) ? res : [];
       });
     },
 

+ 25 - 8
pages/pda/picking/bill/index.vue

@@ -5,10 +5,11 @@
       statusBar="true"
       left-icon="back"
       :title="title"
-      background-color="#F7F9FA"
-      color="#000"
+      background-color="#157A2C"
+      color="#fff"
       @clickLeft="back"
     ></uni-nav-bar>
+    <!-- <view class="nav-placeholder"></view> -->
 
     <view class="list_box" v-for="(item, idx) in dataList" :key="idx">
       <view class="card_box cx-sc">
@@ -16,7 +17,7 @@
           <view class="item">
             <view class="lable rx-cc" style="color: #157a2c">领料单号</view>
             <view class="content rx-bc" style="color: #157a2c">
-              {{ item.code }}
+              {{ pickOrderCodeSafe(item) }}
             </view>
           </view>
           <view class="item">
@@ -180,7 +181,7 @@ export default {
     return {
       ids: [],
       dataList: [],
-      title: null,
+      title: "",
       status: 1,
       item: null,
     };
@@ -188,7 +189,8 @@ export default {
 
   onLoad(option) {
     this.status = option.status || 1;
-    console.log(option.id, "optionoptionoption");
+    console.log(option.status, "optionoptionoption");
+
     if (option.item) {
       this.title =
         option.status == 1 ? "领料详情" : option.status == 2 ? " 出库详情" : "";
@@ -200,6 +202,7 @@ export default {
           ? [parsed]
           : [];
     } else {
+      this.title = "领料详情";
       this.ids = [option.id];
       this.getList();
     }
@@ -264,6 +267,15 @@ export default {
       console.log(item, "itemitemitem");
       return Array.isArray(item?.detailList) ? item.detailList : [];
     },
+    pickOrderCodeSafe(item) {
+      return (
+        item?.code ||
+        item?.pickCode ||
+        item?.pickOrderCode ||
+        item?.orderCode ||
+        "--"
+      );
+    },
 
     scrolltolower() {},
 
@@ -278,15 +290,20 @@ export default {
 
 <style lang="scss" scoped>
 .content-box {
-  /* 让页面按内容自然撑开,避免内部列表被压缩 */
   min-height: 100vh;
   display: flex;
   flex-direction: column;
+  box-sizing: border-box;
+}
+
+.nav-placeholder {
+  /* 给 fixed 导航栏预留高度,避免真机上首屏第一行被遮住 */
+  height: calc(var(--status-bar-height) + 88rpx);
+  flex-shrink: 0;
 }
 
 .list_box {
-  /* 不再参与 flex 等分高度,每个单据块按内容自适应 */
-  padding: 16rpx 0;
+  padding: 16rpx 0 0;
 }
 
 .bottom-wrapper {

+ 4 - 4
pages/pda/picking/details.vue

@@ -5,8 +5,8 @@
       statusBar="true"
       left-icon="back"
       title="领料"
-      background-color="#F7F9FA"
-      color="#404446"
+      background-color="#157A2C"
+      color="#fff"
       @clickLeft="back"
     ></uni-nav-bar>
     <u-button
@@ -489,7 +489,7 @@ export default {
             this.$set(
               this.List[index][key][i],
               "warehouseId",
-              this.warehouseId
+              this.warehouseId,
             );
           });
         }
@@ -768,7 +768,7 @@ export default {
         this.$set(
           this.List[index].pickList[idx],
           "demandQuantity",
-          Number(item.availableCountBase)
+          Number(item.availableCountBase),
         );
         const idsList = [];
         item.warehouseList.forEach((it) => {

+ 39 - 31
pages/pda/workOrder/extrusionMolding/index.vue

@@ -81,12 +81,12 @@
           <text class="lable">牌号</text>
           <text>{{ info.brandNo }}</text>
         </view>
-		
+
         <view class="item_list rx-bc">
           <text class="lable">规格</text>
           <text>{{ info.specification }}</text>
         </view>
-		
+
         <view class="item_list rx-bc">
           <text class="lable">型号</text>
           <text>{{ info.model }}</text>
@@ -278,15 +278,23 @@ export default {
   },
   computed: {
     isFeedStatus() {
-      const currentStep = this.stepsList?.[this.currentStepIndex]
+      const currentStep = this.stepsList?.[this.currentStepIndex];
       // 投料状态 0未投料 1已投料 2已报工
-      return this.feedStatusOption.find(item => item.value === currentStep?.feedStatus)?.label || ''
+      return (
+        this.feedStatusOption.find(
+          (item) => item.value === currentStep?.feedStatus,
+        )?.label || ""
+      );
     },
     isPickingStatus() {
-      const currentStep = this.stepsList?.[this.currentStepIndex]
+      const currentStep = this.stepsList?.[this.currentStepIndex];
       // 领料状态 0未领料 1领料中2已出库3已驳回
-      return this.pickStatusOption.find(item => item.value === currentStep?.pickStatus)?.label || ''
-    }
+      return (
+        this.pickStatusOption.find(
+          (item) => item.value === currentStep?.pickStatus,
+        )?.label || ""
+      );
+    },
   },
   data() {
     return {
@@ -314,36 +322,36 @@ export default {
       currentStepIndex: 0, // 当前步骤索引
       pickStatusOption: [
         {
-          label: '未领料',
-          value: 0
+          label: "未领料",
+          value: 0,
         },
         {
-          label: '领料中',
-          value: 1
+          label: "领料中",
+          value: 1,
         },
         {
-          label: '已出库',
-          value: 2
+          label: "已出库",
+          value: 2,
         },
         {
-          label: '已驳回',
-          value: 3
-        }
+          label: "已驳回",
+          value: 3,
+        },
       ],
       feedStatusOption: [
         {
-          label: '未投料',
-          value: 0
+          label: "未投料",
+          value: 0,
         },
         {
-          label: '已投料',
-          value: 1
+          label: "已投料",
+          value: 1,
         },
         {
-          label: '已报工',
-          value: 2
-        }
-      ]
+          label: "已报工",
+          value: 2,
+        },
+      ],
     };
   },
 
@@ -411,8 +419,8 @@ export default {
           let id = this.currentTaskId || this.taskId;
 
           if (f.taskId == id) {
-            (this.currentType = f.type),
-              (this.existOutsource = f.existOutsource);
+            ((this.currentType = f.type),
+              (this.existOutsource = f.existOutsource));
             this.controlReportMethod = f.controlReportMethod;
             this.currentStepIndex = f.index;
           }
@@ -428,7 +436,7 @@ export default {
 
     // 点击工序列表
     selectStep(item) {
-      console.log('item', item)
+      console.log("item", item);
       this.currentTaskId = item.taskId;
       this.currentTaskName = item.taskTypeName;
       this.existOutsource = item.existOutsource;
@@ -490,7 +498,7 @@ export default {
           url = "/pages/pda/feeding/details";
           let _arr = JSON.stringify([this.info.id]);
           url += `?arr=${encodeURIComponent(
-            _arr
+            _arr,
           )}&taskId=${taskId}&taskName=${taskName}&type=${type}&feedNeedEquipment=${
             this.feedNeedEquipment
           }`;
@@ -504,7 +512,7 @@ export default {
             url = "/pages/pda/feeding/details";
             let _arr = JSON.stringify([this.info.id]);
             url += `?arr=${encodeURIComponent(
-              _arr
+              _arr,
             )}&taskId=${taskId}&taskName=${taskName}&type=${type}&feedNeedEquipment=${
               this.feedNeedEquipment
             }&taskType=${this.currentType}`;
@@ -513,7 +521,7 @@ export default {
             url = "/pages/pda/feeding/details";
             let _arr = JSON.stringify([this.info.id]);
             url += `?arr=${encodeURIComponent(
-              _arr
+              _arr,
             )}&taskId=${taskId}&taskName=${taskName}&type=${type}&feedNeedEquipment=${
               this.feedNeedEquipment
             }&taskType=${this.currentType}`;
@@ -604,7 +612,7 @@ export default {
         url = "/pages/pda/picking/details";
         let _arr = JSON.stringify([this.info.id]);
         url += `?arr=${encodeURIComponent(
-          _arr
+          _arr,
         )}&taskId=${taskId}&isOutsource=1`;
         uni.navigateTo({
           url,