From e7dc2205d169192eea267697061efd64d89f5d63 Mon Sep 17 00:00:00 2001
From: spring <2396852758@qq.com>
Date: 星期四, 08 一月 2026 17:26:36 +0800
Subject: [PATCH] fix: 完成出库重构

---
 src/pages.json                  |    6 
 src/api/product/outbound.ts     |   32 +
 src/pages/index/index.vue       |    7 
 src/pages/outbound/material.vue |  627 +++++++++++++++++++++++++
 src/pages/outbound/index0.vue   |  350 ++++++++++++++
 src/pages/outbound/index.vue    |  476 ++++++------------
 6 files changed, 1,190 insertions(+), 308 deletions(-)

diff --git a/src/api/product/outbound.ts b/src/api/product/outbound.ts
index 30059a7..d013d4d 100644
--- a/src/api/product/outbound.ts
+++ b/src/api/product/outbound.ts
@@ -11,6 +11,38 @@
     });
   },
 
+  // 鍑哄簱鍗曟煡璇�
+  queryErpOutboundOrder(params: {
+    contractNo?: string;
+    carNo?: string;
+    current: number;
+    size: number;
+  }) {
+    return request<BaseResult<any>>({
+      url: "/app/queryErpOutboundOrder",
+      method: "POST",
+      data: params,
+    });
+  },
+
+  // 鏌ヨ鍑鸿揣鍗曟槑缁�
+  queryErpOutboundOrderDetail(params: { vbillcode: string }) {
+    return request<BaseResult<any[]>>({
+      url: "/app/queryErpOutboundOrderDetail",
+      method: "POST",
+      data: params,
+    });
+  },
+
+  // 缁炵嚎/鎷変笣浜岀淮鐮佹煡璇紙鏀寔鍑哄簱鐘舵�佸垽鏂級
+  getTagByIdAll(params: { outPutId: string | number }) {
+    return request<BaseResult<any[]>>({
+      url: "/app/getTagByIdAll",
+      method: "GET",
+      data: params,
+    });
+  },
+
   // 缁炵嚎浜岀淮鐮佹煡璇�
   getTagByIdJx(params: { outPutId: string | number }) {
     return request<BaseResult<any>>({
diff --git a/src/pages.json b/src/pages.json
index 1759892..4d61169 100644
--- a/src/pages.json
+++ b/src/pages.json
@@ -284,6 +284,12 @@
       }
     },
     {
+      "path": "pages/outbound/material",
+      "style": {
+        "navigationBarTitleText": "鐗╂枡璇︽儏"
+      }
+    },
+    {
       "path": "pages/routingInspection/index",
       "style": {
         "navigationBarTitleText": "宸℃"
diff --git a/src/pages/index/index.vue b/src/pages/index/index.vue
index 2b23c76..662e7a6 100644
--- a/src/pages/index/index.vue
+++ b/src/pages/index/index.vue
@@ -278,14 +278,19 @@
     }
   }
 
-  if (data.deviceGroupName.includes("涓�") || data.deviceGroupName.includes("缁�")) {
+  if (
+    (data.deviceGroupName && data.deviceGroupName.includes("涓�")) ||
+    (data.deviceGroupName && data.deviceGroupName.includes("缁�"))
+  ) {
     // 濡傛灉鏄笣鎴栫粸锛屾樉绀虹敓浜х鐞嗚彍鍗�
     navList[0].show = true;
   }
+  console.log("isInspector.value", isInspector.value);
   if (isInspector.value) {
     // 濡傛灉鏄贰妫�鍛橈紝鏄剧ず宸℃鑿滃崟
     navList[1].show = true;
   }
+  console.log("isStorage.value", isStorage.value);
   if (isStorage.value) {
     // 濡傛灉鏄嚭搴撹鑹诧紝鏄剧ず鍑哄簱鑿滃崟
     navList[2].show = true;
diff --git a/src/pages/outbound/index.vue b/src/pages/outbound/index.vue
index ec42661..ba578af 100644
--- a/src/pages/outbound/index.vue
+++ b/src/pages/outbound/index.vue
@@ -1,350 +1,212 @@
 <template>
-  <view class="list_box">
-    <CardTitle title="鍑哄簱" :hideAction="false">
-      <template #action>
-        <wd-button type="icon" icon="scan" color="#0D867F" @click="openScan"></wd-button>
-      </template>
-    </CardTitle>
-    <view class="list_content">
-      <view v-if="outboundList.length === 0" class="empty_tip">
-        <view class="empty_text">鏆傛棤鍑哄簱鏁版嵁</view>
-      </view>
-      <view v-for="(item, index) in outboundList" :key="index" class="outbound_item">
-        <view class="outbound_item_left">
-          <view class="outbound_item_content">
-            <view class="outbound_item_row">
-              <text class="outbound_item_label">鍚堝悓鍙凤細</text>
-              <text class="outbound_item_value">{{ item.contractNo || "-" }}</text>
-            </view>
-            <view class="outbound_item_row">
-              <text class="outbound_item_label">鐢熶骇鎵规鍙凤細</text>
-              <text class="outbound_item_value">{{ item.batchNo || "-" }}</text>
-            </view>
-            <view class="outbound_item_row">
-              <text class="outbound_item_label">瑙勬牸鍨嬪彿锛�</text>
-              <text class="outbound_item_value">{{ item.model || "-" }}</text>
-            </view>
-            <view class="outbound_item_row">
-              <text class="outbound_item_label">閲嶉噺锛�</text>
-              <text class="outbound_item_value">{{ item.weight || "-" }} kg</text>
-            </view>
-            <view class="outbound_item_row">
-              <text class="outbound_item_label">鍘傚锛�</text>
-              <text class="outbound_item_value">{{ item.clienteleName || "-" }}</text>
-            </view>
-            <view class="outbound_item_row">
-              <text class="outbound_item_label">娈甸暱锛�</text>
-              <text class="outbound_item_value">{{ item.actuallyLength || "-" }} M</text>
-            </view>
-            <view class="outbound_item_row">
-              <text class="outbound_item_label">鐢熶骇鏃ユ湡锛�</text>
-              <text class="outbound_item_value">{{ item.productionDate || "-" }}</text>
-            </view>
+  <view class="outbound-page">
+    <view class="search-section">
+      <wd-row>
+        <wd-col :span="21">
+          <wd-search
+            v-model="searchKeyword"
+            placeholder="璇疯緭鍏ヨ溅鐗屽彿"
+            placeholder-left
+            hide-cancel
+            @search="handleSearch"
+            @clear="handleClear"
+          ></wd-search>
+        </wd-col>
+        <wd-col :span="3">
+          <view class="scan_box" @click="handleSearch">
+            <wd-icon name="search" size="24px" color="#0D867F"></wd-icon>
           </view>
-        </view>
-        <view class="outbound_item_action">
-          <wd-button
-            type="icon"
-            icon="delete"
-            size="small"
-            custom-class="delete-btn"
-            @click.stop="removeOutboundItem(index)"
-          ></wd-button>
-        </view>
-      </view>
+        </wd-col>
+      </wd-row>
     </view>
-    <view v-if="outboundList.length > 0" class="outbound_footer">
-      <wd-button block @click="handleOutbound">
-        <text class="text-[#fff]">鍑哄簱</text>
-      </wd-button>
+
+    <view class="list-section">
+      <z-paging
+        ref="pagingRef"
+        v-model="shippingList"
+        :fixed="false"
+        :auto-show-back-to-top="true"
+        @query="getList"
+      >
+        <wd-card
+          v-for="(item, index) in shippingList"
+          :key="item.deliveryid || index"
+          custom-class="card_bg"
+          @click="toMaterialDetail(item)"
+        >
+          <template #title>
+            <text class="font-medium text-[#252525]">鍙戣揣鍗曞彿: {{ item.vbillcode || "-" }}</text>
+          </template>
+          <wd-row class="my-2">
+            <wd-col :span="24">
+              <view class="flex">
+                <view class="icon_box">
+                  <wd-icon name="folder" color="#0D867F"></wd-icon>
+                </view>
+                <text class="text-[#646874] mx-2">
+                  鍙戣揣鍗曟棩鏈�:
+                  <text class="text-[#252525]">{{ item.dbilldate || "-" }}</text>
+                </text>
+              </view>
+            </wd-col>
+          </wd-row>
+          <wd-row class="my-2">
+            <wd-col :span="24">
+              <view class="flex">
+                <view class="icon_box">
+                  <wd-icon name="folder" color="#0D867F"></wd-icon>
+                </view>
+                <text class="text-[#646874] mx-2">
+                  杞︾墝鍙�:
+                  <text class="text-[#252525]">{{ item.carno || "-" }}</text>
+                </text>
+              </view>
+            </wd-col>
+          </wd-row>
+          <wd-row class="my-2" v-if="item.vmemo">
+            <wd-col :span="24">
+              <view class="flex">
+                <view class="icon_box">
+                  <wd-icon name="folder" color="#0D867F"></wd-icon>
+                </view>
+                <text class="text-[#646874] mx-2">
+                  鍙戣揣鍗曞娉�:
+                  <text class="text-[#252525]">{{ item.vmemo || "-" }}</text>
+                </text>
+              </view>
+            </wd-col>
+          </wd-row>
+        </wd-card>
+      </z-paging>
     </view>
-    <Scan ref="scanRef" emitName="scanOutbound" />
+
     <wd-toast />
   </view>
 </template>
 
 <script setup lang="ts">
-import CardTitle from "@/components/card-title/index.vue";
-import Scan from "@/components/scan/index.vue";
+import { ref } from "vue";
 import { useToast } from "wot-design-uni";
-import { dayjs } from "wot-design-uni";
+import zPaging from "@/components/z-paging/z-paging.vue";
 import OutboundApi from "@/api/product/outbound";
 
-const scanRef = ref();
 const toast = useToast();
-const outboundList = ref<any[]>([]);
-const projectId = ref<string | number>("");
+const pagingRef = ref();
+const searchKeyword = ref("");
+const shippingList = ref<any[]>([]);
 
-// 鏍煎紡鍖栨椂闂�
-const formatTime = (date: Date) => {
-  return dayjs(date).format("YYYY-MM-DD HH:mm:ss");
-};
+// 鑾峰彇鍒楄〃鏁版嵁
+const getList = async (pageNo: number, pageSize: number) => {
+  const params: any = {
+    contractNo: "",
+    carNo: searchKeyword.value || "",
+    current: pageNo,
+    size: pageSize,
+  };
 
-// 鎵爜鍥炶皟
-const getScanCode = async (code: any) => {
   try {
-    // 濡傛灉 code 鏄璞′笖鏈� code 瀛楁锛屼娇鐢� code.code锛涘惁鍒欑洿鎺ヤ娇鐢� code
-    let scanCode = code.code || code;
-
-    // 濡傛灉 scanCode 鏄璞★紝灏濊瘯鑾峰彇鍏� code 瀛楁
-    if (typeof scanCode === "object" && scanCode.code) {
-      scanCode = scanCode.code;
+    const { code, data, msg } = await OutboundApi.queryErpOutboundOrder(params);
+    if (code === 200 && data) {
+      const records = data.records || [];
+      if (!records.length) {
+        pagingRef.value.complete(true);
+      } else {
+        pagingRef.value.complete(records);
+      }
+    } else {
+      toast.error(msg || "鑾峰彇鍑哄簱鍗曞垪琛ㄥけ璐�");
+      pagingRef.value.complete(true);
     }
-
-    // 濡傛灉 scanCode 鏄瓧绗︿覆锛岀洿鎺ヤ娇鐢紱濡傛灉鏄璞★紝杞负瀛楃涓�
-    if (typeof scanCode !== "string") {
-      scanCode = JSON.stringify(scanCode);
-    }
-
-    if (!scanCode) {
-      toast.error("鎵爜鍐呭涓虹┖");
-      return;
-    }
-
-    // 瑙f瀽鎵爜鏁版嵁锛岀幇鍦ㄤ簩缁寸爜鍙寘鍚玦d
-    let scanData;
-    try {
-      scanData = JSON.parse(scanCode);
-    } catch (e) {
-      toast.error("浜岀淮鐮佹牸寮忛敊璇�");
-      return;
-    }
-
-    const outPutId = scanData.id;
-
-    if (!outPutId) {
-      toast.error("浜岀淮鐮佹牸寮忛敊璇紝缂哄皯id淇℃伅");
-      return;
-    }
-
-    // 妫�鏌ユ槸鍚﹀凡瀛樺湪锛堟牴鎹甶d鍒ゆ柇锛�
-    const exists = outboundList.value.some((item) => {
-      const itemId = item.id;
-      return itemId && itemId === outPutId && itemId !== "-";
-    });
-
-    if (exists) {
-      toast.error("璇ユ潯鐮佸凡瀛樺湪锛岃鍕块噸澶嶆壂鐮�");
-      return;
-    }
-
-    // 璋冪敤鎺ュ彛鑾峰彇缁炵嚎璇︾粏淇℃伅
-    const { data: tagData } = await OutboundApi.getTagByIdJx({
-      outPutId: outPutId,
-    });
-
-    // 鎻愬彇鏁版嵁瀛楁锛堟牴鎹帴鍙h繑鍥炵殑鏁版嵁缁撴瀯锛�
-    const parsedData = {
-      id: tagData?.id || outPutId,
-      contractNo: tagData?.contractno || tagData?.contractNo || "-",
-      batchNo: tagData?.systemno || tagData?.systemNo || tagData?.batchNo || "-",
-      model: tagData?.model || "-",
-      weight: tagData?.actuallyweight || tagData?.actuallyWeight || tagData?.weight || "-",
-      clienteleName: tagData?.clientelename || tagData?.clienteleName || "-",
-      actuallyLength: tagData?.actuallylength || tagData?.actuallyLength || "-",
-      productionDate: tagData?.producttime || tagData?.productionDate || "-",
-      projectId: tagData?.projectid || tagData?.projectId || projectId.value || "",
-      // 淇濈暀鍘熷鏁版嵁
-      rawData: tagData,
-      scanCode: scanCode,
-    };
-
-    // 濡傛灉鎺ュ彛杩斿洖鐨勬暟鎹腑鏈� projectId锛屼繚瀛樺畠
-    if (parsedData.projectId && !projectId.value) {
-      projectId.value = parsedData.projectId;
-    }
-
-    // 娣诲姞鍒板垪琛�
-    const newItem = {
-      ...parsedData,
-      scanTime: formatTime(new Date()),
-    };
-
-    outboundList.value.push(newItem);
-    toast.success("鎵爜鎴愬姛");
-  } catch (error: any) {
-    console.error("鎵爜澶勭悊澶辫触:", error);
-    toast.error(error.msg || "浜岀淮鐮佸紓甯革紝璇锋洿鎹簩缁寸爜锛�");
+  } catch (error) {
+    console.error("鑾峰彇鍑哄簱鍗曞垪琛ㄥけ璐�:", error);
+    toast.error("鑾峰彇鍑哄簱鍗曞垪琛ㄥけ璐�");
+    pagingRef.value.complete(true);
   }
 };
 
-// 瑙﹀彂鎵爜
-const openScan = () => {
-  scanRef.value.triggerScan();
+// 鎼滅储澶勭悊
+const handleSearch = () => {
+  // 閲嶆柊鍔犺浇鏁版嵁
+  pagingRef.value.reload();
 };
 
-// 鍒犻櫎椤�
-const removeOutboundItem = (index: number) => {
-  const item = outboundList.value[index];
-  const itemName = item.contractNo || item.batchNo || `绗�${index + 1}椤筦;
+// 娓呯┖鎼滅储
+const handleClear = () => {
+  searchKeyword.value = "";
+  handleSearch();
+};
 
-  uni.showModal({
-    title: "纭鍒犻櫎",
-    content: `纭畾瑕佸垹闄�"${itemName}"鍚楋紵`,
-    confirmText: "鍒犻櫎",
-    cancelText: "鍙栨秷",
-    confirmColor: "#ff4444",
-    success: (res) => {
-      if (res.confirm) {
-        outboundList.value.splice(index, 1);
-        toast.success("鍒犻櫎鎴愬姛");
-      }
-    },
+// 璺宠浆鍒扮墿鏂欒鎯呴〉
+const toMaterialDetail = (item: any) => {
+  uni.navigateTo({
+    url: `/pages/outbound/material?id=${item.deliveryid}&vbillcode=${item.vbillcode}`,
   });
 };
-
-// 澶勭悊鍑哄簱
-const handleOutbound = async () => {
-  if (outboundList.value.length === 0) {
-    toast.error("鏆傛棤鍑哄簱鏁版嵁");
-    return;
-  }
-
-  // 鏋勫缓璇锋眰鏁版嵁
-  const requestData = outboundList.value.map((item) => ({
-    outPutId: item.id,
-    projectId: item.projectId,
-  }));
-
-  try {
-    uni.showLoading({
-      title: "鍑哄簱涓�...",
-      mask: true,
-    });
-    console.log("requestData", requestData);
-    const { code, msg } = await OutboundApi.finishedOutbound(requestData);
-
-    uni.hideLoading();
-
-    if (code === 200) {
-      toast.success("鍑哄簱鎴愬姛");
-      // 娓呯┖鍒楄〃
-      outboundList.value = [];
-    } else {
-      toast.error(msg || "鍑哄簱澶辫触");
-    }
-  } catch (error: any) {
-    uni.hideLoading();
-    console.error("鍑哄簱澶辫触:", error);
-  }
-};
-
-// 纭繚鍏堢Щ闄ゅ啀娣诲姞鐩戝惉
-const setupScanListener = () => {
-  uni.$off("scanOutbound", getScanCode); // 鍏堢Щ闄ゆ棫鐨�
-  uni.$on("scanOutbound", getScanCode); // 鍐嶆坊鍔犳柊鐨�
-};
-
-onMounted(() => {
-  setupScanListener();
-});
-
-onUnmounted(() => {
-  uni.$off("scanOutbound", getScanCode);
-});
 </script>
 
 <style lang="scss" scoped>
-.list_box {
-  height: calc(100vh - 100px);
+.outbound-page {
+  min-height: 100vh;
   background: #f3f9f8;
-  display: flex;
-  flex-direction: column;
+  padding: 10rpx;
 }
 
-.list_content {
-  flex: 1;
-  overflow-y: auto;
-  padding-bottom: 120rpx;
+.search-section {
+  background: #fff;
+  padding: 10rpx;
+  margin-bottom: 10rpx;
+}
+
+.scan_box {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 38px;
+  height: 38px;
+  padding: 6px;
+  background: #fff;
+}
+
+.list-section {
+  padding: 0 4px;
+  height: calc(100vh - 150rpx);
+}
+
+.card_bg {
+  box-shadow: 0px 0px 12px 0px rgba(0, 0, 0, 0.05);
+  padding-bottom: 10px;
+  margin-bottom: 10px;
+  margin-left: 6px;
+  margin-right: 6px;
+}
+
+.shipping-card {
+  margin: 0 5px;
+}
+
+.icon_box {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 20px;
+  height: 20px;
+  background: #e7f4ec99;
+  border-radius: 50%;
 }
 
 .empty_tip {
   display: flex;
-  flex-direction: column;
-  align-items: center;
   justify-content: center;
-  padding: 100rpx 0;
-  color: #999;
-
-  .empty_text {
-    font-size: 32rpx;
-    margin-bottom: 20rpx;
-  }
-}
-
-.outbound_item {
-  display: flex;
   align-items: center;
-  justify-content: space-between;
-  padding: 30rpx;
-  margin: 20rpx;
-  background: #fff;
-  border-radius: 16rpx;
-  box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
-
-  .outbound_item_left {
-    display: flex;
-    align-items: center;
-    flex: 1;
-  }
-
-  .outbound_item_content {
-    flex: 1;
-  }
-
-  .outbound_item_row {
-    display: flex;
-    align-items: center;
-    margin-bottom: 12rpx;
-    font-size: 28rpx;
-
-    &:last-child {
-      margin-bottom: 0;
-    }
-  }
-
-  .outbound_item_label {
-    color: #666;
-    min-width: 140rpx;
-    flex-shrink: 0;
-  }
-
-  .outbound_item_value {
-    color: #333;
-    flex: 1;
-    word-break: break-all;
-  }
-
-  .outbound_item_action {
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    margin-left: 20rpx;
-    flex-shrink: 0;
-  }
+  padding: 40px 0;
 }
 
-:deep(.delete-btn) {
-  .wd-button__content {
-    color: #ff4444 !important;
-  }
-
-  &:active {
-    opacity: 0.7;
-  }
+.empty_text {
+  color: #999;
+  font-size: 14px;
 }
 
-:deep(.wd-button__content) {
-  color: #0d867f;
-}
-
-.outbound_footer {
-  position: fixed;
-  bottom: 0;
-  left: 0;
-  right: 0;
-  padding: 20rpx 30rpx;
-  padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
-  z-index: 100;
+:deep(.wd-search__block) {
+  border-radius: unset;
 }
 </style>
diff --git a/src/pages/outbound/index0.vue b/src/pages/outbound/index0.vue
new file mode 100644
index 0000000..88beb06
--- /dev/null
+++ b/src/pages/outbound/index0.vue
@@ -0,0 +1,350 @@
+<template>
+  <view class="list_box">
+    <CardTitle title="鍑哄簱" :hideAction="false">
+      <template #action>
+        <wd-button type="icon" icon="scan" color="#0D867F" @click="openScan"></wd-button>
+      </template>
+    </CardTitle>
+    <view class="list_content">
+      <view v-if="outboundList.length === 0" class="empty_tip">
+        <view class="empty_text">鏆傛棤鍑哄簱鏁版嵁</view>
+      </view>
+      <view v-for="(item, index) in outboundList" :key="index" class="outbound_item">
+        <view class="outbound_item_left">
+          <view class="outbound_item_content">
+            <view class="outbound_item_row">
+              <text class="outbound_item_label">鍚堝悓鍙凤細</text>
+              <text class="outbound_item_value">{{ item.contractNo || "-" }}</text>
+            </view>
+            <view class="outbound_item_row">
+              <text class="outbound_item_label">鐢熶骇鎵规鍙凤細</text>
+              <text class="outbound_item_value">{{ item.batchNo || "-" }}</text>
+            </view>
+            <view class="outbound_item_row">
+              <text class="outbound_item_label">瑙勬牸鍨嬪彿锛�</text>
+              <text class="outbound_item_value">{{ item.model || "-" }}</text>
+            </view>
+            <view class="outbound_item_row">
+              <text class="outbound_item_label">閲嶉噺锛�</text>
+              <text class="outbound_item_value">{{ item.weight || "-" }} kg</text>
+            </view>
+            <view class="outbound_item_row">
+              <text class="outbound_item_label">鍘傚锛�</text>
+              <text class="outbound_item_value">{{ item.clienteleName || "-" }}</text>
+            </view>
+            <view class="outbound_item_row">
+              <text class="outbound_item_label">娈甸暱锛�</text>
+              <text class="outbound_item_value">{{ item.actuallyLength || "-" }} M</text>
+            </view>
+            <view class="outbound_item_row">
+              <text class="outbound_item_label">鐢熶骇鏃ユ湡锛�</text>
+              <text class="outbound_item_value">{{ item.productionDate || "-" }}</text>
+            </view>
+          </view>
+        </view>
+        <view class="outbound_item_action">
+          <wd-button
+            type="icon"
+            icon="delete"
+            size="small"
+            custom-class="delete-btn"
+            @click.stop="removeOutboundItem(index)"
+          ></wd-button>
+        </view>
+      </view>
+    </view>
+    <view v-if="outboundList.length > 0" class="outbound_footer">
+      <wd-button block @click="handleOutbound">
+        <text class="text-[#fff]">鍑哄簱</text>
+      </wd-button>
+    </view>
+    <Scan ref="scanRef" emitName="scanOutbound" />
+    <wd-toast />
+  </view>
+</template>
+
+<script setup lang="ts">
+import CardTitle from "@/components/card-title/index.vue";
+import Scan from "@/components/scan/index.vue";
+import { useToast } from "wot-design-uni";
+import { dayjs } from "wot-design-uni";
+import OutboundApi from "@/api/product/outbound";
+
+const scanRef = ref();
+const toast = useToast();
+const outboundList = ref<any[]>([]);
+const projectId = ref<string | number>("");
+
+// 鏍煎紡鍖栨椂闂�
+const formatTime = (date: Date) => {
+  return dayjs(date).format("YYYY-MM-DD HH:mm:ss");
+};
+
+// 鎵爜鍥炶皟
+const getScanCode = async (code: any) => {
+  try {
+    // 濡傛灉 code 鏄璞′笖鏈� code 瀛楁锛屼娇鐢� code.code锛涘惁鍒欑洿鎺ヤ娇鐢� code
+    let scanCode = code.code || code;
+
+    // 濡傛灉 scanCode 鏄璞★紝灏濊瘯鑾峰彇鍏� code 瀛楁
+    if (typeof scanCode === "object" && scanCode.code) {
+      scanCode = scanCode.code;
+    }
+
+    // 濡傛灉 scanCode 鏄瓧绗︿覆锛岀洿鎺ヤ娇鐢紱濡傛灉鏄璞★紝杞负瀛楃涓�
+    if (typeof scanCode !== "string") {
+      scanCode = JSON.stringify(scanCode);
+    }
+
+    if (!scanCode) {
+      toast.error("鎵爜鍐呭涓虹┖");
+      return;
+    }
+
+    // 瑙f瀽鎵爜鏁版嵁锛岀幇鍦ㄤ簩缁寸爜鍙寘鍚玦d
+    let scanData;
+    try {
+      scanData = JSON.parse(scanCode);
+    } catch (e) {
+      toast.error("浜岀淮鐮佹牸寮忛敊璇�");
+      return;
+    }
+
+    const outPutId = scanData.id;
+
+    if (!outPutId) {
+      toast.error("浜岀淮鐮佹牸寮忛敊璇紝缂哄皯id淇℃伅");
+      return;
+    }
+
+    // 妫�鏌ユ槸鍚﹀凡瀛樺湪锛堟牴鎹甶d鍒ゆ柇锛�
+    const exists = outboundList.value.some((item) => {
+      const itemId = item.id;
+      return itemId && itemId === outPutId && itemId !== "-";
+    });
+
+    if (exists) {
+      toast.error("璇ユ潯鐮佸凡瀛樺湪锛岃鍕块噸澶嶆壂鐮�");
+      return;
+    }
+
+    // 璋冪敤鎺ュ彛鑾峰彇缁炵嚎璇︾粏淇℃伅
+    const { data: tagData } = await OutboundApi.getTagByIdJx({
+      outPutId: outPutId,
+    });
+
+    // 鎻愬彇鏁版嵁瀛楁锛堟牴鎹帴鍙h繑鍥炵殑鏁版嵁缁撴瀯锛�
+    const parsedData = {
+      id: tagData?.id || outPutId,
+      contractNo: tagData?.contractno || tagData?.contractNo || "-",
+      batchNo: tagData?.systemno || tagData?.systemNo || tagData?.batchNo || "-",
+      model: tagData?.model || "-",
+      weight: tagData?.actuallyweight || tagData?.actuallyWeight || tagData?.weight || "-",
+      clienteleName: tagData?.clientelename || tagData?.clienteleName || "-",
+      actuallyLength: tagData?.actuallylength || tagData?.actuallyLength || "-",
+      productionDate: tagData?.producttime || tagData?.productionDate || "-",
+      projectId: tagData?.projectid || tagData?.projectId || projectId.value || "",
+      // 淇濈暀鍘熷鏁版嵁
+      rawData: tagData,
+      scanCode: scanCode,
+    };
+
+    // 濡傛灉鎺ュ彛杩斿洖鐨勬暟鎹腑鏈� projectId锛屼繚瀛樺畠
+    if (parsedData.projectId && !projectId.value) {
+      projectId.value = parsedData.projectId;
+    }
+
+    // 娣诲姞鍒板垪琛�
+    const newItem = {
+      ...parsedData,
+      scanTime: formatTime(new Date()),
+    };
+
+    outboundList.value.push(newItem);
+    toast.success("鎵爜鎴愬姛");
+  } catch (error: any) {
+    console.error("鎵爜澶勭悊澶辫触:", error);
+    toast.error(error.msg || "浜岀淮鐮佸紓甯革紝璇锋洿鎹簩缁寸爜锛�");
+  }
+};
+
+// 瑙﹀彂鎵爜
+const openScan = () => {
+  scanRef.value.triggerScan();
+};
+
+// 鍒犻櫎椤�
+const removeOutboundItem = (index: number) => {
+  const item = outboundList.value[index];
+  const itemName = item.contractNo || item.batchNo || `绗�${index + 1}椤筦;
+
+  uni.showModal({
+    title: "纭鍒犻櫎",
+    content: `纭畾瑕佸垹闄�"${itemName}"鍚楋紵`,
+    confirmText: "鍒犻櫎",
+    cancelText: "鍙栨秷",
+    confirmColor: "#ff4444",
+    success: (res) => {
+      if (res.confirm) {
+        outboundList.value.splice(index, 1);
+        toast.success("鍒犻櫎鎴愬姛");
+      }
+    },
+  });
+};
+
+// 澶勭悊鍑哄簱
+const handleOutbound = async () => {
+  if (outboundList.value.length === 0) {
+    toast.error("鏆傛棤鍑哄簱鏁版嵁");
+    return;
+  }
+
+  // 鏋勫缓璇锋眰鏁版嵁
+  const requestData = outboundList.value.map((item) => ({
+    outPutId: item.id,
+    projectId: item.projectId,
+  }));
+
+  try {
+    uni.showLoading({
+      title: "鍑哄簱涓�...",
+      mask: true,
+    });
+    console.log("requestData", requestData);
+    const { code, msg } = await OutboundApi.finishedOutbound(requestData);
+
+    uni.hideLoading();
+
+    if (code === 200) {
+      toast.success("鍑哄簱鎴愬姛");
+      // 娓呯┖鍒楄〃
+      outboundList.value = [];
+    } else {
+      toast.error(msg || "鍑哄簱澶辫触");
+    }
+  } catch (error: any) {
+    uni.hideLoading();
+    console.error("鍑哄簱澶辫触:", error);
+  }
+};
+
+// 纭繚鍏堢Щ闄ゅ啀娣诲姞鐩戝惉
+const setupScanListener = () => {
+  uni.$off("scanOutbound", getScanCode); // 鍏堢Щ闄ゆ棫鐨�
+  uni.$on("scanOutbound", getScanCode); // 鍐嶆坊鍔犳柊鐨�
+};
+
+onMounted(() => {
+  setupScanListener();
+});
+
+onUnmounted(() => {
+  uni.$off("scanOutbound", getScanCode);
+});
+</script>
+
+<style lang="scss" scoped>
+.list_box {
+  height: calc(100vh - 100px);
+  background: #f3f9f8;
+  display: flex;
+  flex-direction: column;
+}
+
+.list_content {
+  flex: 1;
+  overflow-y: auto;
+  padding-bottom: 120rpx;
+}
+
+.empty_tip {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  padding: 100rpx 0;
+  color: #999;
+
+  .empty_text {
+    font-size: 32rpx;
+    margin-bottom: 20rpx;
+  }
+}
+
+.outbound_item {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 30rpx;
+  margin: 5rpx;
+  background: #fff;
+  border-radius: 16rpx;
+  box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
+
+  .outbound_item_left {
+    display: flex;
+    align-items: center;
+    flex: 1;
+  }
+
+  .outbound_item_content {
+    flex: 1;
+  }
+
+  .outbound_item_row {
+    display: flex;
+    align-items: center;
+    margin-bottom: 12rpx;
+    font-size: 28rpx;
+
+    &:last-child {
+      margin-bottom: 0;
+    }
+  }
+
+  .outbound_item_label {
+    color: #666;
+    min-width: 140rpx;
+    flex-shrink: 0;
+  }
+
+  .outbound_item_value {
+    color: #333;
+    flex: 1;
+    word-break: break-all;
+  }
+
+  .outbound_item_action {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    margin-left: 20rpx;
+    flex-shrink: 0;
+  }
+}
+
+:deep(.delete-btn) {
+  .wd-button__content {
+    color: #ff4444 !important;
+  }
+
+  &:active {
+    opacity: 0.7;
+  }
+}
+
+:deep(.wd-button__content) {
+  color: #0d867f;
+}
+
+.outbound_footer {
+  position: fixed;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  padding: 20rpx 30rpx;
+  padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
+  z-index: 100;
+}
+</style>
diff --git a/src/pages/outbound/material.vue b/src/pages/outbound/material.vue
new file mode 100644
index 0000000..477cbd4
--- /dev/null
+++ b/src/pages/outbound/material.vue
@@ -0,0 +1,627 @@
+<template>
+  <view class="material-page">
+    <view class="material-header">
+      <CardTitle title="鐗╂枡璇︽儏" :hideAction="false">
+        <template #action>
+          <wd-button
+            type="icon"
+            icon="scan"
+            color="#0d867f"
+            @click="openScan"
+            style="color: #0d867f"
+          ></wd-button>
+        </template>
+      </CardTitle>
+
+      <!-- 鐗╂枡淇℃伅 -->
+      <view class="material-info">
+        <wd-card
+          v-for="(item, index) in materialList"
+          :key="item.materialcode || index"
+          custom-class="info-card"
+        >
+          <wd-row class="info-row">
+            <wd-col :span="24">
+              <view class="flex">
+                <view class="icon_box">
+                  <wd-icon name="folder" color="#0D867F"></wd-icon>
+                </view>
+                <text class="text-[#646874] mx-2">
+                  鐗╂枡鍚嶇О:
+                  <text class="text-[#252525]">{{ item.materialname || "-" }}</text>
+                </text>
+              </view>
+            </wd-col>
+          </wd-row>
+          <wd-row class="info-row">
+            <wd-col :span="24">
+              <view class="flex">
+                <view class="icon_box">
+                  <wd-icon name="folder" color="#0D867F"></wd-icon>
+                </view>
+                <text class="text-[#646874] mx-2">
+                  鐗╂枡瑙勬牸:
+                  <text class="text-[#252525]">{{ item.materialspec || "-" }}</text>
+                </text>
+              </view>
+            </wd-col>
+          </wd-row>
+          <wd-row class="info-row">
+            <wd-col :span="24">
+              <view class="flex">
+                <view class="icon_box">
+                  <wd-icon name="folder" color="#0D867F"></wd-icon>
+                </view>
+                <text class="text-[#646874] mx-2">
+                  宸插彂璐ф暟閲�:
+                  <text class="text-[#252525]">{{ item.shippedQuantity || 0 }}</text>
+                </text>
+              </view>
+            </wd-col>
+          </wd-row>
+        </wd-card>
+      </view>
+    </view>
+
+    <!-- 璐х墿鍒楄〃 -->
+    <view class="list_content">
+      <view v-if="goodsList.length === 0" class="empty_tip">
+        <view class="empty_text">鏆傛棤璐х墿鏁版嵁</view>
+      </view>
+      <view v-for="(item, index) in goodsList" :key="index" class="outbound_item">
+        <view class="outbound_item_left">
+          <view class="outbound_item_content">
+            <view class="outbound_item_row">
+              <text class="outbound_item_label">鍚堝悓鍙凤細</text>
+              <text class="outbound_item_value">{{ item.contractNo || "-" }}</text>
+            </view>
+            <view class="outbound_item_row">
+              <text class="outbound_item_label">鐢熶骇鎵规鍙凤細</text>
+              <text class="outbound_item_value">{{ item.monofilamentNumber || "-" }}</text>
+            </view>
+            <view class="outbound_item_row">
+              <text class="outbound_item_label">瑙勬牸鍨嬪彿锛�</text>
+              <text class="outbound_item_value">{{ item.model || "-" }}</text>
+            </view>
+            <view class="outbound_item_row">
+              <text class="outbound_item_label">閲嶉噺锛�</text>
+              <text class="outbound_item_value">{{ item.weight || "-" }} kg</text>
+            </view>
+            <view class="outbound_item_row">
+              <text class="outbound_item_label">鍘傚锛�</text>
+              <text class="outbound_item_value">{{ item.clienteleName || "-" }}</text>
+            </view>
+            <view class="outbound_item_row">
+              <text class="outbound_item_label">娈甸暱锛�</text>
+              <text class="outbound_item_value">{{ item.actuallyLength || "-" }} M</text>
+            </view>
+            <view class="outbound_item_row">
+              <text class="outbound_item_label">鐢熶骇鏃ユ湡锛�</text>
+              <text class="outbound_item_value">{{ item.productionDate || "-" }}</text>
+            </view>
+          </view>
+        </view>
+        <view class="outbound_item_action">
+          <wd-button
+            type="icon"
+            icon="delete"
+            size="small"
+            custom-class="delete-btn"
+            @click.stop="removeGoodsItem(index)"
+          ></wd-button>
+        </view>
+      </view>
+    </view>
+
+    <!-- 搴曢儴鎸夐挳 -->
+    <view v-if="goodsList.length > 1" class="outbound_footer">
+      <wd-button block @click="handleOutbound" style="background: #0d867f">
+        <text class="text-[#fff]">鍑哄簱</text>
+      </wd-button>
+    </view>
+
+    <Scan ref="scanRef" emitName="scanMaterial" />
+    <wd-toast />
+  </view>
+</template>
+
+<script setup lang="ts">
+import { ref, onMounted, onUnmounted } from "vue";
+import { onLoad } from "@dcloudio/uni-app";
+// @ts-ignore
+import CardTitle from "@/components/card-title/index.vue";
+// @ts-ignore
+import Scan from "@/components/scan/index.vue";
+import { useToast, dayjs } from "wot-design-uni";
+import OutboundApi from "@/api/product/outbound";
+
+const toast = useToast();
+const scanRef = ref();
+const materialId = ref<string>("");
+const vbillcode = ref<string>("");
+
+// 鐗╂枡淇℃伅鍒楄〃
+const materialList = ref<any[]>([]);
+
+// 璐х墿鍒楄〃
+const goodsList = ref<any[]>([]);
+
+// 鏍煎紡鍖栨椂闂�
+const formatTime = (date: Date) => {
+  return dayjs(date).format("YYYY-MM-DD HH:mm:ss");
+};
+
+// 鑾峰彇鐗╂枡璇︽儏
+const getMaterialDetail = async () => {
+  if (!vbillcode.value) {
+    toast.error("鍙戣揣鍗曞彿涓嶈兘涓虹┖");
+    return;
+  }
+
+  try {
+    uni.showLoading({
+      title: "鍔犺浇涓�...",
+      mask: true,
+    });
+
+    const { code, data, msg } = await OutboundApi.queryErpOutboundOrderDetail({
+      vbillcode: vbillcode.value,
+    });
+
+    uni.hideLoading();
+
+    if (code === 200 && data) {
+      // 灏嗘帴鍙h繑鍥炵殑鏁版嵁鏄犲皠鍒� materialList
+      materialList.value = data.map((item: any) => ({
+        materialcode: item.materialcode,
+        materialname: item.materialname || "-",
+        materialspec: item.materialspec || "-",
+        shippedQuantity: item.nnum || 0,
+        cdeliveryid: item.cdeliveryid,
+        cdeliverybid: item.cdeliverybid,
+        vsrccode: item.vsrccode,
+      }));
+    } else {
+      toast.error(msg || "鑾峰彇鐗╂枡璇︽儏澶辫触");
+      materialList.value = [];
+    }
+  } catch (error: any) {
+    uni.hideLoading();
+    console.error("鑾峰彇鐗╂枡璇︽儏澶辫触:", error);
+    toast.error(error.msg || "鑾峰彇鐗╂枡璇︽儏澶辫触");
+    materialList.value = [];
+  }
+};
+
+// 鐩存帴鎵爜
+const openScan = () => {
+  scanRef.value?.triggerScan();
+};
+
+// 鎵爜鍥炶皟
+const getScanCode = async (code: any) => {
+  try {
+    // 濡傛灉 code 鏄璞′笖鏈� code 瀛楁锛屼娇鐢� code.code锛涘惁鍒欑洿鎺ヤ娇鐢� code
+    let scanCode = code.code || code;
+
+    // 濡傛灉 scanCode 鏄璞★紝灏濊瘯鑾峰彇鍏� code 瀛楁
+    if (typeof scanCode === "object" && scanCode.code) {
+      scanCode = scanCode.code;
+    }
+
+    // 濡傛灉 scanCode 鏄瓧绗︿覆锛岀洿鎺ヤ娇鐢紱濡傛灉鏄璞★紝杞负瀛楃涓�
+    if (typeof scanCode !== "string") {
+      scanCode = JSON.stringify(scanCode);
+    }
+
+    if (!scanCode) {
+      toast.error("鎵爜鍐呭涓虹┖");
+      return;
+    }
+
+    // 瑙f瀽鎵爜鏁版嵁锛岀幇鍦ㄤ簩缁寸爜鍙寘鍚玦d
+    let scanData;
+    try {
+      scanData = JSON.parse(scanCode);
+    } catch (e) {
+      toast.error("浜岀淮鐮佹牸寮忛敊璇�");
+      return;
+    }
+
+    const outPutId = scanData.id;
+
+    if (!outPutId) {
+      toast.error("浜岀淮鐮佹牸寮忛敊璇紝缂哄皯id淇℃伅");
+      return;
+    }
+
+    // 妫�鏌ユ槸鍚﹀凡瀛樺湪锛堟牴鎹甶d鍒ゆ柇锛�
+    const exists = goodsList.value.some((item) => {
+      const itemId = item.id;
+      return itemId && itemId === outPutId && itemId !== "-";
+    });
+
+    if (exists) {
+      toast.error("璇ユ潯鐮佸凡瀛樺湪锛岃鍕块噸澶嶆壂鐮�");
+      return;
+    }
+
+    // 璋冪敤鎺ュ彛鑾峰彇缁炵嚎/鎷変笣璇︾粏淇℃伅锛堝惈鍑哄簱鐘舵�侊級
+    const { data } = await OutboundApi.getTagByIdAll({
+      outPutId: outPutId,
+    });
+
+    const list = data || [];
+    if (!list.length) {
+      toast.error("鏈煡璇㈠埌鏉$爜淇℃伅");
+      return;
+    }
+
+    const tagData = list[0];
+
+    // 宸插嚭搴撴牎楠�
+    if (tagData?.state === "宸插嚭搴�") {
+      toast.error("璇ユ潯鐮佸凡鍑哄簱锛屾棤娉曢噸澶嶅嚭搴�");
+      return;
+    }
+
+    // 鎻愬彇鏁版嵁瀛楁锛堟牴鎹帴鍙h繑鍥炵殑鏁版嵁缁撴瀯锛�
+    const parsedData = {
+      id: tagData?.id || outPutId,
+      contractNo: tagData?.contractno || tagData?.contractNo || "-",
+      monofilamentNumber:
+        tagData?.monofilamentnumber ||
+        tagData?.monofilamentNumber ||
+        tagData?.systemno ||
+        tagData?.systemNo ||
+        "-",
+      model: tagData?.model || "-",
+      weight: tagData?.actuallylength || tagData?.actuallyLength || tagData?.weight || "-",
+      clienteleName: tagData?.clientelename || tagData?.clienteleName || "-",
+      actuallyLength: tagData?.actuallylength || tagData?.actuallyLength || "-",
+      productionDate: tagData?.producttime || tagData?.productionDate || "-",
+      type: tagData?.type || "",
+      devicemodel: tagData?.devicemodel || "",
+      state: tagData?.state || "",
+      projectId: tagData?.projectid || tagData?.projectId || "",
+      productuser: tagData?.productuser || "",
+      // 淇濈暀鍘熷鏁版嵁
+      rawData: tagData,
+      scanCode: scanCode,
+    };
+
+    // 娣诲姞鍒板垪琛�
+    const newItem = {
+      ...parsedData,
+      scanTime: formatTime(new Date()),
+    };
+
+    goodsList.value.push(newItem);
+    toast.success("鎵爜鎴愬姛");
+  } catch (error: any) {
+    console.error("鎵爜澶勭悊澶辫触:", error);
+    toast.error(error.msg || "浜岀淮鐮佸紓甯革紝璇锋洿鎹簩缁寸爜锛�");
+  }
+};
+
+// 鍒犻櫎璐х墿椤�
+const removeGoodsItem = (index: number) => {
+  const item = goodsList.value[index];
+  const itemName = item.contractNo || item.monofilamentNumber || `绗�${index + 1}椤筦;
+
+  uni.showModal({
+    title: "纭鍒犻櫎",
+    content: `纭畾瑕佸垹闄�"${itemName}"鍚楋紵`,
+    confirmText: "鍒犻櫎",
+    cancelText: "鍙栨秷",
+    confirmColor: "#ff4444",
+    success: (res) => {
+      if (res.confirm) {
+        goodsList.value.splice(index, 1);
+        toast.success("鍒犻櫎鎴愬姛");
+      }
+    },
+  });
+};
+
+// 缁熻鐗╂枡鏁伴噺鍜岀被鍨嬭鏍�
+const getMaterialStatistics = () => {
+  const statistics: Record<string, { type: string; model: string; count: number }> = {};
+
+  goodsList.value.forEach((item) => {
+    const type = item.type || "鏈煡绫诲瀷";
+    const model = item.model || "鏈煡瑙勬牸";
+    const key = `${type}_${model}`;
+
+    if (!statistics[key]) {
+      statistics[key] = {
+        type,
+        model,
+        count: 0,
+      };
+    }
+    statistics[key].count++;
+  });
+
+  return Object.values(statistics);
+};
+
+// 澶勭悊鍑哄簱
+const handleOutbound = async () => {
+  if (goodsList.value.length === 0) {
+    toast.error("鏆傛棤璐х墿鏁版嵁");
+    return;
+  }
+
+  // 缁熻鐗╂枡鏁伴噺鍜岀被鍨嬭鏍�
+  const statistics = getMaterialStatistics();
+
+  // 鏋勫缓缁熻淇℃伅鏂囨湰
+  let statisticsText = "鍑哄簱鐗╂枡缁熻锛歕n\n";
+  statistics.forEach((item, index) => {
+    statisticsText += `${index + 1}. ${item.type} - ${item.model}锛�${item.count}浠禱n`;
+  });
+  statisticsText += `\n鎬昏锛�${goodsList.value.length}浠禱n\n纭鍑哄簱鍚楋紵`;
+
+  // 鏄剧ず纭寮规
+  uni.showModal({
+    title: "纭鍑哄簱",
+    content: statisticsText,
+    confirmText: "纭",
+    cancelText: "鍙栨秷",
+    confirmColor: "#0d867f",
+    success: async (res) => {
+      if (res.confirm) {
+        // 鏋勫缓璇锋眰鏁版嵁
+        const requestData = goodsList.value.map((item) => ({
+          outPutId: item.id,
+          projectId: item.projectId || "",
+        }));
+
+        try {
+          uni.showLoading({
+            title: "鍑哄簱涓�...",
+            mask: true,
+          });
+
+          console.log("requestData", requestData);
+          const { code, msg } = await OutboundApi.finishedOutbound(requestData);
+
+          uni.hideLoading();
+
+          if (code === 200) {
+            toast.success("鍑哄簱鎴愬姛");
+            // 娓呯┖鍒楄〃
+            goodsList.value = [];
+            // 杩斿洖涓婁竴椤垫垨鍒锋柊鐗╂枡璇︽儏
+            setTimeout(() => {
+              uni.navigateBack();
+            }, 1500);
+          } else {
+            toast.error(msg || "鍑哄簱澶辫触");
+          }
+        } catch (error: any) {
+          uni.hideLoading();
+          console.error("鍑哄簱澶辫触:", error);
+          toast.error(error.msg || "鍑哄簱澶辫触");
+        }
+      }
+    },
+  });
+};
+
+// 璁剧疆鎵爜鐩戝惉
+const setupScanListener = () => {
+  uni.$off("scanMaterial", getScanCode);
+  uni.$on("scanMaterial", getScanCode);
+};
+
+onLoad((options: any) => {
+  materialId.value = options.id || "";
+  vbillcode.value = options.vbillcode || "";
+  getMaterialDetail();
+});
+
+onMounted(() => {
+  setupScanListener();
+});
+
+onUnmounted(() => {
+  uni.$off("scanMaterial", getScanCode);
+});
+</script>
+
+<style lang="scss" scoped>
+.material-page {
+  min-height: 100vh;
+  background: #f3f9f8;
+  padding-bottom: 200rpx;
+  display: flex;
+  flex-direction: column;
+}
+
+.material-header {
+  position: sticky;
+  top: 0;
+  z-index: 10;
+  background: #f3f9f8;
+  padding-bottom: 10rpx;
+}
+
+.material-info {
+  padding: 20rpx;
+}
+
+.info-card {
+  box-shadow: 0px 0px 12px 0px rgba(0, 0, 0, 0.05);
+  margin-bottom: 20rpx;
+  margin-left: 6px;
+  margin-right: 6px;
+  padding: 10px 18px;
+  background: #f3f9f8;
+
+  &:last-child {
+    margin-bottom: 0;
+  }
+}
+
+.info-row {
+  margin-bottom: 20rpx;
+
+  &:last-child {
+    margin-bottom: 0;
+  }
+}
+
+.icon_box {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 20px;
+  height: 20px;
+  background: #e7f4ec99;
+  border-radius: 50%;
+}
+
+.list_content {
+  flex: 1;
+  overflow-y: auto;
+  padding-bottom: 120rpx;
+}
+
+.empty_tip {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  padding: 100rpx 0;
+  color: #999;
+
+  .empty_text {
+    font-size: 32rpx;
+    margin-bottom: 20rpx;
+  }
+}
+
+.outbound_item {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 30rpx;
+  margin: 30rpx;
+  background: #fff;
+  border-radius: 16rpx;
+  box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
+
+  .outbound_item_left {
+    display: flex;
+    align-items: center;
+    flex: 1;
+  }
+
+  .outbound_item_content {
+    flex: 1;
+  }
+
+  .outbound_item_row {
+    display: flex;
+    align-items: center;
+    margin-bottom: 12rpx;
+    font-size: 28rpx;
+
+    &:last-child {
+      margin-bottom: 0;
+    }
+  }
+
+  .outbound_item_label {
+    color: #666;
+    min-width: 140rpx;
+    flex-shrink: 0;
+  }
+
+  .outbound_item_value {
+    color: #333;
+    flex: 1;
+    word-break: break-all;
+  }
+
+  .outbound_item_action {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    margin-left: 20rpx;
+    flex-shrink: 0;
+  }
+}
+
+:deep(.delete-btn) {
+  .wd-button__content {
+    color: #ff4444 !important;
+  }
+
+  &:active {
+    opacity: 0.7;
+  }
+}
+
+.outbound_footer {
+  position: fixed;
+  bottom: 20rpx;
+  left: 0;
+  right: 0;
+  padding: 0 30rpx;
+  padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
+  z-index: 100;
+  background: transparent;
+}
+
+.shipping-btn-wrapper {
+  position: fixed;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  padding: 20rpx 30rpx;
+  padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
+  background: #fff;
+  z-index: 100;
+  box-shadow: 0 -2rpx 8rpx rgba(0, 0, 0, 0.05);
+}
+
+.shipping-dialog {
+  background: #fff;
+  border-radius: 24rpx 24rpx 0 0;
+  padding-bottom: env(safe-area-inset-bottom);
+}
+
+.dialog-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 32rpx;
+  border-bottom: 1px solid #e6e6e6;
+}
+
+.dialog-title {
+  font-size: 32rpx;
+  font-weight: 500;
+  color: #333;
+}
+
+.close-icon {
+  font-size: 40rpx;
+  color: #999;
+  padding: 8rpx;
+}
+
+.dialog-content {
+  padding: 32rpx;
+}
+
+.scan-tip {
+  text-align: center;
+  color: #666;
+  font-size: 28rpx;
+  padding: 40rpx 0;
+}
+</style>

--
Gitblit v1.9.3