From 88022fc18a3dddb2e1181eaf08e0e155cdb3094b Mon Sep 17 00:00:00 2001
From: zhangwencui <1064582902@qq.com>
Date: 星期二, 26 五月 2026 13:39:53 +0800
Subject: [PATCH] 库存管理完善

---
 src/pages/inventoryManagement/stockManagement/Record.vue |  567 +++++++++++++++++++++++++++++++++++--------------------
 1 files changed, 359 insertions(+), 208 deletions(-)

diff --git a/src/pages/inventoryManagement/stockManagement/Record.vue b/src/pages/inventoryManagement/stockManagement/Record.vue
index e4542a8..e5d5ca2 100644
--- a/src/pages/inventoryManagement/stockManagement/Record.vue
+++ b/src/pages/inventoryManagement/stockManagement/Record.vue
@@ -3,33 +3,38 @@
     <view class="search-section">
       <view class="search-bar">
         <view class="search-input">
-          <up-input
-            class="search-text"
-            placeholder="璇疯緭鍏ヤ骇鍝佸ぇ绫�"
-            v-model="searchForm.productName"
-            @confirm="handleQuery"
-            clearable
-          />
+          <up-input class="search-text"
+                    placeholder="璇疯緭鍏ヤ骇鍝佸ぇ绫�"
+                    v-model="searchForm.productName"
+                    @confirm="handleQuery"
+                    clearable />
         </view>
-        <view class="filter-button" @click="handleQuery">
-          <up-icon name="search" size="24" color="#999"></up-icon>
+        <view class="filter-button"
+              @click="handleQuery">
+          <up-icon name="search"
+                   size="24"
+                   color="#999"></up-icon>
         </view>
       </view>
     </view>
-
-    <scroll-view scroll-y class="ledger-list" v-if="tableData.length > 0" @scrolltolower="loadMore">
-      <view v-for="item in tableData" :key="item.id" class="ledger-item">
+    <scroll-view scroll-y
+                 class="ledger-list"
+                 v-if="tableData.length > 0"
+                 @scrolltolower="loadMore">
+      <view v-for="item in tableData"
+            :key="item.id"
+            class="ledger-item">
         <view class="item-header">
           <view class="item-left">
             <view class="document-icon">
-              <up-icon name="file-text" size="16" color="#ffffff"></up-icon>
+              <up-icon name="file-text"
+                       size="16"
+                       color="#ffffff"></up-icon>
             </view>
             <text class="item-id">{{ item.productName }}</text>
           </view>
         </view>
-        
         <up-divider></up-divider>
-
         <view class="item-details">
           <view class="detail-row">
             <text class="detail-label">瑙勬牸鍨嬪彿</text>
@@ -39,11 +44,20 @@
             <text class="detail-label">鍗曚綅</text>
             <text class="detail-value">{{ item.unit }}</text>
           </view>
-          <view class="detail-row">
+          <view class="detail-row"
+                @click="handleShowBatch(item.batchNo)">
             <text class="detail-label">鎵瑰彿</text>
-            <text class="detail-value">{{ item.batchNo }}</text>
+            <view class="detail-value batch-no-wrapper">
+              <text class="batch-no-text"
+                    :class="{ 'clickable': isBatchClickable(item.batchNo) }">
+                {{ formatBatchNo(item.batchNo) }}
+              </text>
+              <up-icon v-if="isBatchClickable(item.batchNo)"
+                       name="arrow-right"
+                       size="14"
+                       color="#2979ff"></up-icon>
+            </view>
           </view>
-          
           <view class="quantity-section">
             <view class="quantity-box qualified">
               <text class="q-label">鍚堟牸搴撳瓨</text>
@@ -54,7 +68,6 @@
               <text class="q-value">{{ item.unQualifiedQuantity }}</text>
             </view>
           </view>
-
           <view class="quantity-section">
             <view class="quantity-box locked">
               <text class="q-label">鍚堟牸鍐荤粨</text>
@@ -65,7 +78,6 @@
               <text class="q-value">{{ item.unQualifiedLockedQuantity }}</text>
             </view>
           </view>
-
           <view class="detail-row">
             <text class="detail-label">搴撳瓨棰勮</text>
             <text class="detail-value">{{ item.warnNum }}</text>
@@ -82,211 +94,350 @@
       </view>
       <up-loadmore :status="loadStatus" />
     </scroll-view>
-    <view v-else-if="!loading" class="no-data">
-      <up-empty mode="data" text="鏆傛棤搴撳瓨鏁版嵁"></up-empty>
+    <view v-else-if="!loading"
+          class="no-data">
+      <up-empty mode="data"
+                text="鏆傛棤搴撳瓨鏁版嵁"></up-empty>
     </view>
+    <!-- 鎵瑰彿鍒楄〃寮圭獥 -->
+    <up-popup v-model:show="showBatchPopup"
+              @close="showBatchPopup = false"
+              mode="bottom"
+              round="20"
+              closeable>
+      <view class="batch-popup-content">
+        <view class="popup-header">
+          <text class="popup-title">鎵瑰彿璇︽儏</text>
+        </view>
+        <scroll-view scroll-y
+                     class="batch-list-scroll">
+          <view class="batch-list">
+            <view v-for="(batch, index) in currentBatchList"
+                  :key="index"
+                  class="batch-item">
+              <view class="batch-index-box">
+                <text class="batch-index">{{ index + 1 }}</text>
+              </view>
+              <text class="batch-text">{{ batch }}</text>
+            </view>
+          </view>
+        </scroll-view>
+      </view>
+    </up-popup>
   </view>
 </template>
 
 <script setup>
-import { ref, reactive, onMounted } from 'vue';
-import { getStockInventoryListPageCombined } from "@/api/inventoryManagement/stockInventory.js";
+  import { ref, reactive, onMounted } from "vue";
+  import { getStockInventoryListPageCombined } from "@/api/inventoryManagement/stockInventory.js";
 
-const props = defineProps({
-  productId: {
-    type: Number,
-    required: true
-  }
-});
-
-const tableData = ref([]);
-const loading = ref(false);
-const loadStatus = ref('loadmore');
-const page = reactive({ current: 1, size: 10 });
-const total = ref(0);
-const searchForm = reactive({ 
-  productName: '',
-  topParentProductId: props.productId
-});
-
-const handleQuery = () => {
-  page.current = 1;
-  tableData.value = [];
-  getList();
-};
-
-const getList = () => {
-  if (loading.value) return;
-  loading.value = true;
-  loadStatus.value = 'loading';
-  
-  getStockInventoryListPageCombined({ 
-    ...searchForm, 
-    current: page.current,
-    size: page.size
-  }).then(res => {
-    loading.value = false;
-    const records = res.data.records || [];
-    tableData.value = page.current === 1 ? records : [...tableData.value, ...records];
-    total.value = res.data.total;
-    loadStatus.value = tableData.value.length >= total.value ? 'nomore' : 'loadmore';
-  }).catch(() => {
-    loading.value = false;
-    loadStatus.value = 'loadmore';
+  const props = defineProps({
+    productId: {
+      type: Number,
+      required: true,
+    },
   });
-};
 
-const loadMore = () => {
-  if (loadStatus.value === 'loadmore') {
-    page.current++;
+  const tableData = ref([]);
+  const loading = ref(false);
+  const loadStatus = ref("loadmore");
+  const page = reactive({ current: 1, size: 10 });
+  const total = ref(0);
+  const searchForm = reactive({
+    productName: "",
+    topParentProductId: props.productId,
+  });
+
+  const showBatchPopup = ref(false);
+  const currentBatchList = ref([]);
+
+  const handleQuery = () => {
+    page.current = 1;
+    tableData.value = [];
     getList();
-  }
-};
+  };
 
-onMounted(() => {
-  getList();
-});
+  const getList = () => {
+    if (loading.value) return;
+    loading.value = true;
+    loadStatus.value = "loading";
+
+    getStockInventoryListPageCombined({
+      ...searchForm,
+      current: page.current,
+      size: page.size,
+    })
+      .then(res => {
+        loading.value = false;
+        const records = res.data.records || [];
+        tableData.value =
+          page.current === 1 ? records : [...tableData.value, ...records];
+        total.value = res.data.total;
+        loadStatus.value =
+          tableData.value.length >= total.value ? "nomore" : "loadmore";
+      })
+      .catch(() => {
+        loading.value = false;
+        loadStatus.value = "loadmore";
+      });
+  };
+
+  const loadMore = () => {
+    if (loadStatus.value === "loadmore") {
+      page.current++;
+      getList();
+    }
+  };
+
+  const handleShowBatch = batchNo => {
+    if (!batchNo) return;
+    // 鏀寔閫楀彿銆佺┖鏍兼垨鎹㈣鍒嗛殧
+    currentBatchList.value = batchNo
+      .split(/[,锛孿s\n]+/)
+      .filter(item => item.trim() !== "");
+    if (currentBatchList.value.length > 0) {
+      showBatchPopup.value = true;
+    }
+  };
+
+  const formatBatchNo = batchNo => {
+    if (!batchNo) return "-";
+    if (batchNo.length > 25) {
+      return batchNo.substring(0, 25) + "...";
+    }
+    return batchNo;
+  };
+
+  const isBatchClickable = batchNo => {
+    if (!batchNo) return false;
+    return batchNo.length > 25 || batchNo.includes(",") || batchNo.includes("锛�");
+  };
+
+  onMounted(() => {
+    getList();
+  });
 </script>
 
 <style scoped lang="scss">
-.record-container {
-  height: 100%;
-  display: flex;
-  flex-direction: column;
-  background-color: #f5f7fa;
-}
-
-.search-section {
-  padding: 20rpx;
-  background-color: #ffffff;
-  position: sticky;
-  top: 0;
-  z-index: 10;
-}
-
-.search-bar {
-  display: flex;
-  align-items: center;
-  background-color: #f2f2f2;
-  border-radius: 40rpx;
-  padding: 0 30rpx;
-  height: 80rpx;
-}
-
-.search-input {
-  flex: 1;
-}
-
-.search-text {
-  font-size: 28rpx;
-}
-
-.filter-button {
-  padding-left: 20rpx;
-}
-
-.ledger-list {
-  flex: 1;
-  padding: 20rpx;
-  box-sizing: border-box;
-}
-
-.ledger-item {
-  background-color: #ffffff;
-  border-radius: 16rpx;
-  padding: 30rpx;
-  margin-bottom: 20rpx;
-  box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
-}
-
-.item-header {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  margin-bottom: 20rpx;
-}
-
-.item-left {
-  display: flex;
-  align-items: center;
-}
-
-.document-icon {
-  width: 40rpx;
-  height: 40rpx;
-  background: linear-gradient(135deg, #2979ff, #1565c0);
-  border-radius: 8rpx;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  margin-right: 16rpx;
-}
-
-.item-id {
-  font-size: 30rpx;
-  font-weight: bold;
-  color: #303133;
-}
-
-.item-details {
-  .detail-row {
-    display: flex;
-    justify-content: space-between;
-    margin-bottom: 16rpx;
-    font-size: 26rpx;
-
-    .detail-label {
-      color: #909399;
-    }
-
-    .detail-value {
-      color: #303133;
-      font-weight: 500;
-    }
-  }
-}
-
-.quantity-section {
-  display: flex;
-  gap: 20rpx;
-  margin: 20rpx 0;
-  
-  .quantity-box {
-    flex: 1;
-    padding: 16rpx;
-    border-radius: 8rpx;
+  .record-container {
+    height: 100%;
     display: flex;
     flex-direction: column;
+    background-color: #f5f7fa;
+  }
+
+  .search-section {
+    padding: 20rpx;
+    background-color: #ffffff;
+    position: sticky;
+    top: 0;
+    z-index: 10;
+  }
+
+  .search-bar {
+    display: flex;
     align-items: center;
-    
-    .q-label {
-      font-size: 22rpx;
-      margin-bottom: 8rpx;
-    }
-    
-    .q-value {
-      font-size: 32rpx;
-      font-weight: bold;
-    }
-    
-    &.qualified {
-      background-color: #ecf5ff;
-      color: #409eff;
-    }
-    
-    &.unqualified {
-      background-color: #fef0f0;
-      color: #f56c6c;
-    }
-    
-    &.locked {
-      background-color: #f4f4f5;
-      color: #909399;
+    background-color: #f2f2f2;
+    border-radius: 40rpx;
+    padding: 0 30rpx;
+    height: 80rpx;
+  }
+
+  .search-input {
+    flex: 1;
+  }
+
+  .search-text {
+    font-size: 28rpx;
+  }
+
+  .filter-button {
+    padding-left: 20rpx;
+  }
+
+  .ledger-list {
+    padding: 20rpx;
+    box-sizing: border-box;
+    overflow: auto;
+  }
+
+  .ledger-item {
+    background-color: #ffffff;
+    border-radius: 16rpx;
+    padding: 30rpx;
+    margin-bottom: 20rpx;
+    box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
+  }
+
+  .item-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 20rpx;
+  }
+
+  .item-left {
+    display: flex;
+    align-items: center;
+  }
+
+  .document-icon {
+    width: 40rpx;
+    height: 40rpx;
+    background: linear-gradient(135deg, #2979ff, #1565c0);
+    border-radius: 8rpx;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    margin-right: 16rpx;
+  }
+
+  .item-id {
+    font-size: 30rpx;
+    font-weight: bold;
+    color: #303133;
+  }
+
+  .item-details {
+    .detail-row {
+      display: flex;
+      justify-content: space-between;
+      margin-bottom: 16rpx;
+      font-size: 26rpx;
+
+      .detail-label {
+        color: #909399;
+      }
+
+      .detail-value {
+        color: #303133;
+        font-weight: 500;
+      }
+
+      .batch-no-wrapper {
+        display: flex;
+        align-items: center;
+        max-width: 70%;
+
+        .batch-no-text {
+          overflow: hidden;
+          text-overflow: ellipsis;
+          white-space: nowrap;
+
+          &.clickable {
+            color: #2979ff;
+            text-decoration: underline;
+          }
+        }
+      }
     }
   }
-}
 
-.no-data {
-  padding-top: 200rpx;
-}
+  .batch-popup-content {
+    background-color: #fff;
+    padding: 30rpx;
+    max-height: 70vh;
+    display: flex;
+    flex-direction: column;
+
+    .popup-header {
+      padding-bottom: 30rpx;
+      border-bottom: 1rpx solid #ebeef5;
+      margin-bottom: 20rpx;
+      text-align: center;
+
+      .popup-title {
+        font-size: 32rpx;
+        font-weight: bold;
+        color: #303133;
+      }
+    }
+
+    .batch-list-scroll {
+      flex: 1;
+      overflow: hidden;
+    }
+
+    .batch-list {
+      padding: 10rpx 0;
+
+      .batch-item {
+        display: flex;
+        align-items: center;
+        padding: 24rpx 0;
+        border-bottom: 1rpx solid #f2f6fc;
+
+        &:last-child {
+          border-bottom: none;
+        }
+
+        .batch-index-box {
+          width: 40rpx;
+          height: 40rpx;
+          background-color: #f0f2f5;
+          border-radius: 50%;
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          margin-right: 20rpx;
+
+          .batch-index {
+            font-size: 20rpx;
+            color: #909399;
+          }
+        }
+
+        .batch-text {
+          font-size: 28rpx;
+          color: #303133;
+          flex: 1;
+          word-break: break-all;
+        }
+      }
+    }
+  }
+
+  .quantity-section {
+    display: flex;
+    gap: 20rpx;
+    margin: 20rpx 0;
+
+    .quantity-box {
+      flex: 1;
+      padding: 16rpx;
+      border-radius: 8rpx;
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+
+      .q-label {
+        font-size: 22rpx;
+        margin-bottom: 8rpx;
+      }
+
+      .q-value {
+        font-size: 32rpx;
+        font-weight: bold;
+      }
+
+      &.qualified {
+        background-color: #ecf5ff;
+        color: #409eff;
+      }
+
+      &.unqualified {
+        background-color: #fef0f0;
+        color: #f56c6c;
+      }
+
+      &.locked {
+        background-color: #f4f4f5;
+        color: #909399;
+      }
+    }
+  }
+
+  .no-data {
+    padding-top: 200rpx;
+  }
 </style>

--
Gitblit v1.9.3