Przeglądaj źródła

feat:
- 修改部分样式
- 实现成品上架

liuyanpeng 6 miesięcy temu
rodzic
commit
0e3c6797e0

+ 1 - 1
src/Fingerprint/FingerprintEnroll.vue

@@ -460,7 +460,7 @@ onUnmounted(() => {
   display: flex;
   flex-direction: column;
   gap: 40px;
-  flex: 1;
+  /* flex: 1; */
   justify-content: center;
 }
 

+ 107 - 102
src/agv-process/ReturnManagement.vue

@@ -55,9 +55,7 @@
         <div class="dialog-form-item">
           <v-select
             v-model="truckNo" :options="truckNos" :reduce="item => item.value" label="label"
-            placeholder="请输入料车编号搜索" :clearable="true" :filterable="true"
-            class="dialog-select"
-            @search="handleSearch"
+            placeholder="请输入料车编号搜索" :clearable="true" :filterable="true" class="dialog-select" @search="handleSearch"
           >
             <template #no-options>
               <span>{{ hasSearched ? '无该编号相关数据' : '请输入料车编号进行搜索' }}</span>
@@ -77,10 +75,7 @@
             <!-- 工装设备搜索 + 已选标签 -->
             <div class="tooling-header-row">
               <van-field
-                v-model="toolingSearchKeyword"
-                placeholder="请输入名称..."
-                left-icon="search"
-                class="tooling-input"
+                v-model="toolingSearchKeyword" placeholder="请输入名称..." left-icon="search" class="tooling-input"
                 @update:model-value="handleToolingSearch"
               />
               <div v-if="selectedToolings.length > 0" class="selected-count">
@@ -91,24 +86,19 @@
             <!-- 已选工装设备(可点击切换位置) -->
             <div v-if="selectedToolings.length > 0" class="selected-toolings">
               <div
-                v-for="id in selectedToolings"
-                :key="id"
-                class="selected-chip"
-                :class="toolingPositions[id] === 'top' ? 'chip-top' : 'chip-bottom'"
-                @click="toggleToolingPosition(id)"
+                v-for="id in selectedToolings" :key="id" class="selected-chip"
+                :class="toolingPositions[id] === 'top' ? 'chip-top' : 'chip-bottom'" @click="toggleToolingPosition(id)"
               >
                 <span class="chip-text">{{ getToolingById(id)?.no }}</span>
                 <span class="chip-pos">{{ toolingPositions[id] === 'top' ? '上' : '下' }}</span>
                 <van-icon name="cross" class="chip-close" @click.stop="removeTooling(id)" />
               </div>
             </div>
-            
+
             <!-- 工装设备列表 -->
             <div class="tooling-list">
               <div
-                v-for="tooling in toolingDevices"
-                :key="tooling.id"
-                class="tooling-item"
+                v-for="tooling in toolingDevices" :key="tooling.id" class="tooling-item"
                 :class="{ 'tooling-selected': selectedToolings.includes(tooling.id) }"
               >
                 <div class="tooling-header" @click="toggleTooling(tooling.id)">
@@ -122,14 +112,13 @@
                     </div>
                   </van-checkbox>
                 </div>
-                
+
                 <!-- 位置选择 -->
                 <transition name="expand">
                   <div v-if="selectedToolings.includes(tooling.id)" class="position-selector">
                     <div class="position-label">选择位置:</div>
                     <van-radio-group
-                      :model-value="toolingPositions[tooling.id] || 'top'"
-                      direction="horizontal"
+                      :model-value="toolingPositions[tooling.id] || 'top'" direction="horizontal"
                       @update:model-value="(val) => updatePosition(tooling.id, val)"
                     >
                       <van-radio name="top" icon-size="18px">
@@ -182,7 +171,7 @@
 <script setup>
 import { ref, onMounted, watch, computed } from 'vue';
 import { showNotify } from 'vant';
-import { getLocatorStatus, getNoInStorageTruck, generateTaskByBlankNo,findDeviceByCondition } from '../api/agv.js';
+import { getLocatorStatus, getNoInStorageTruck, generateTaskByBlankNo, findDeviceByCondition } from '../api/agv.js';
 import vSelect from 'vue-select';
 import 'vue-select/dist/vue-select.css';
 
@@ -316,12 +305,12 @@ const openStation = station => {
 const toggleTooling = toolingId => {
     const index = selectedToolings.value.indexOf(toolingId);
     if (index > -1) {
-        // 取消选择
+    // 取消选择
         selectedToolings.value.splice(index, 1);
         delete toolingPositions.value[toolingId];
         delete selectedToolingCache.value[toolingId];
     } else {
-        // 添加选择,默认位置为上层
+    // 添加选择,默认位置为上层
         selectedToolings.value.push(toolingId);
         toolingPositions.value[toolingId] = 'top';
         // 缓存设备信息
@@ -378,7 +367,7 @@ const searchToolingDevices = async () => {
             length: 100,
             positionType: 'Stereoscopic',
         };
-        
+
         const res = await findDeviceByCondition(params);
         if (res.errorCode === 0) {
             if (res.datas && res.datas.length > 0) {
@@ -418,13 +407,13 @@ const handleToolingSearch = value => {
     if (toolingSearchTimeout) {
         clearTimeout(toolingSearchTimeout);
     }
-    
+
     // 如果清空了,重新加载全部
     if (!value || value.trim() === '') {
         loadInitialToolings();
         return;
     }
-    
+
     // 500ms 防抖
     toolingSearchTimeout = setTimeout(() => {
         searchToolingDevices();
@@ -442,7 +431,7 @@ const loadInitialToolings = async () => {
             length: 100,
             positionType: 'Stereoscopic',
         };
-        
+
         const res = await findDeviceByCondition(params);
         if (res.errorCode === 0 && res.datas && res.datas.length > 0) {
             toolingDevices.value = res.datas.map(item => ({
@@ -469,11 +458,11 @@ const handleDialogClose = async action => {
         cancelTask();
         return true;
     }
-    
+
     if (action === 'confirm') {
         return await generateTask();
     }
-    
+
     return true;
 };
 
@@ -574,7 +563,7 @@ const getStations = async () => {
 // 监听料车编号变化,判断是否为接驳车
 watch(truckNo, newVal => {
     if (newVal) {
-        // 从完整数据中找到对应的料车
+    // 从完整数据中找到对应的料车
         const truck = truckData.value.find(item => item.truckNo === newVal);
         if (truck) {
             selectedTruck.value = truck;
@@ -586,7 +575,7 @@ watch(truckNo, newVal => {
             selectedToolingCache.value = {};
             toolingDevices.value = [];
             toolingSearchKeyword.value = '';
-            
+
             // 如果是接驳车,自动查询工装设备
             if (isTransferTruck.value) {
                 loadInitialToolings();
@@ -929,36 +918,37 @@ onMounted(() => {
   background: rgba(30, 144, 255, 0.3);
 }
 
-/* ========== 工装设备选择区域样式(重写) ========== */
+/* ========== 工装设备选择区域样式 ========== */
 .tooling-section {
-  margin-top: 0.875rem;
-  padding: 0.75rem;
-  background: linear-gradient(to bottom, #f8fafc, #ffffff);
-  border-radius: 8px;
-  border: 1px solid #e2e8f0;
+  margin-top: 14px;
+  padding: 12px 14px;
+  border-radius: 12px;
+  border: 1px solid rgba(4, 159, 216, 0.7);
+  background: radial-gradient(circle at top, rgba(30, 144, 255, 0.15) 0%, rgba(4, 28, 61, 0.95) 55%, rgba(4, 28, 61, 0.98) 100%);
+  box-shadow: 0 0 25px rgba(4, 159, 216, 0.35);
 }
 
 .section-title {
   display: flex;
   align-items: center;
-  gap: 0.5rem;
-  margin-bottom: 0.625rem;
-  font-size: 0.9375rem;
+  gap: 8px;
+  margin-bottom: 10px;
+  font-size: 14px;
   font-weight: 600;
-  color: #1e293b;
+  color: #e5f4ff;
 }
 
 .section-title .van-icon {
-  font-size: 1rem;
-  color: #3b82f6;
+  font-size: 18px;
+  color: #38bdf8;
 }
 
 /* 搜索行 */
 .tooling-header-row {
   display: flex;
   align-items: center;
-  gap: 0.5rem;
-  margin-bottom: 0.5rem;
+  gap: 8px;
+  margin-bottom: 8px;
 }
 
 .tooling-input {
@@ -966,15 +956,24 @@ onMounted(() => {
 }
 
 :deep(.tooling-input.van-field) {
-  padding: 4px 8px;
-  background: white;
-  border-radius: 6px;
-  border: 1px solid #d1d5db;
-  font-size: 0.875rem;
+  padding: 4px 10px;
+  background: rgba(5, 40, 80, 0.95);
+  border-radius: 8px;
+  border: 1px solid rgba(56, 189, 248, 0.6);
+  font-size: 13px;
+  color: #e5f4ff;
+}
+
+:deep(.tooling-input .van-field__control) {
+  color: #e5f4ff;
+}
+
+:deep(.tooling-input .van-field__control::placeholder) {
+  color: rgba(148, 163, 184, 0.85);
 }
 
 :deep(.tooling-input .van-field__left-icon) {
-  color: #9ca3af;
+  color: #38bdf8;
   margin-right: 4px;
 }
 
@@ -987,12 +986,12 @@ onMounted(() => {
   display: flex;
   flex-wrap: wrap;
   gap: 6px;
-  margin-bottom: 0.5rem;
+  margin-bottom: 8px;
   padding: 6px 8px;
-  background: #f1f5f9;
-  border-radius: 6px;
+  background: linear-gradient(90deg, rgba(15, 23, 42, 0.95), rgba(15, 23, 42, 0.9));
+  border-radius: 8px;
   min-height: 32px;
-  max-height: 64px;
+  max-height: 72px;
   overflow-y: auto;
 }
 
@@ -1001,31 +1000,33 @@ onMounted(() => {
   align-items: center;
   gap: 4px;
   padding: 3px 6px 3px 8px;
-  border-radius: 4px;
-  font-size: 0.75rem;
+  border-radius: 999px;
+  font-size: 12px;
   cursor: pointer;
   transition: all 0.15s ease;
   user-select: none;
 }
 
 .chip-top {
-  background: #dcfce7;
-  color: #166534;
-  border: 1px solid #86efac;
+  background: rgba(16, 185, 129, 0.15);
+  color: #bbf7d0;
+  border: 1px solid rgba(34, 197, 94, 0.8);
+  box-shadow: 0 0 10px rgba(34, 197, 94, 0.45);
 }
 
 .chip-top:hover {
-  background: #bbf7d0;
+  background: rgba(16, 185, 129, 0.25);
 }
 
 .chip-bottom {
-  background: #fef3c7;
-  color: #92400e;
-  border: 1px solid #fcd34d;
+  background: rgba(245, 158, 11, 0.16);
+  color: #fed7aa;
+  border: 1px solid rgba(245, 158, 11, 0.85);
+  box-shadow: 0 0 10px rgba(245, 158, 11, 0.4);
 }
 
 .chip-bottom:hover {
-  background: #fde68a;
+  background: rgba(245, 158, 11, 0.24);
 }
 
 .chip-text {
@@ -1033,20 +1034,20 @@ onMounted(() => {
 }
 
 .chip-pos {
-  font-size: 0.625rem;
-  padding: 1px 4px;
-  border-radius: 3px;
+  font-size: 10px;
+  padding: 1px 6px;
+  border-radius: 999px;
   font-weight: 700;
 }
 
 .chip-top .chip-pos {
   background: #22c55e;
-  color: white;
+  color: #0b1120;
 }
 
 .chip-bottom .chip-pos {
-  background: #f59e0b;
-  color: white;
+  background: #f97316;
+  color: #0b1120;
 }
 
 .chip-close {
@@ -1065,38 +1066,30 @@ onMounted(() => {
   display: flex;
   flex-direction: column;
   gap: 6px;
-  max-height: 200px;
+  max-height: 220px;
   overflow-y: auto;
   padding: 2px;
 }
 
-.tooling-list::-webkit-scrollbar {
-  width: 4px;
-}
-
-.tooling-list::-webkit-scrollbar-thumb {
-  background: #cbd5e1;
-  border-radius: 2px;
-}
-
 /* 工装设备项 */
 .tooling-item {
-  background: white;
-  border: 1px solid #e2e8f0;
-  border-radius: 6px;
+  background: rgba(15, 23, 42, 0.95);
+  border: 1px solid rgba(51, 136, 255, 0.6);
+  border-radius: 8px;
   padding: 8px 10px;
-  transition: all 0.15s ease;
+  transition: all 0.18s ease;
   cursor: pointer;
+  box-shadow: 0 4px 14px rgba(15, 23, 42, 0.8);
 }
 
 .tooling-item:hover {
-  border-color: #93c5fd;
-  background: #f8fafc;
+  border-color: #38bdf8;
+  box-shadow: 0 0 18px rgba(56, 189, 248, 0.5);
 }
 
 .tooling-item.tooling-selected {
-  background: #eff6ff;
-  border-color: #3b82f6;
+  background: radial-gradient(circle at top, rgba(56, 189, 248, 0.4) 0%, rgba(15, 23, 42, 0.98) 45%, rgba(15, 23, 42, 1) 100%);
+  border-color: #38bdf8;
 }
 
 .tooling-header {
@@ -1113,8 +1106,8 @@ onMounted(() => {
 .tooling-no {
   display: inline-block;
   padding: 2px 8px;
-  background: #3b82f6;
-  color: white;
+  background: linear-gradient(120deg, #22d3ee, #6366f1);
+  color: #0b1120;
   border-radius: 4px;
   font-size: 0.75rem;
   font-weight: 600;
@@ -1123,7 +1116,7 @@ onMounted(() => {
 .tooling-name {
   font-size: 0.8125rem;
   font-weight: 500;
-  color: #374151;
+  color: #e5e7eb;
   max-width: 180px;
   overflow: hidden;
   text-overflow: ellipsis;
@@ -1135,7 +1128,7 @@ onMounted(() => {
   margin-top: 6px;
   padding-top: 6px;
   padding-left: 24px;
-  border-top: 1px dashed #e2e8f0;
+  border-top: 1px dashed rgba(148, 163, 184, 0.6);
   display: flex;
   align-items: center;
   gap: 8px;
@@ -1144,7 +1137,7 @@ onMounted(() => {
 .position-label {
   font-size: 0.75rem;
   font-weight: 500;
-  color: #64748b;
+  color: #cbd5f5;
   white-space: nowrap;
 }
 
@@ -1161,14 +1154,14 @@ onMounted(() => {
 .position-text {
   font-size: 0.8125rem;
   font-weight: 500;
-  color: #374151;
+  color: #e5e7eb;
 }
 
 /* 选择汇总 */
 .tooling-summary {
   margin-top: 8px;
   padding-top: 8px;
-  border-top: 1px solid #e2e8f0;
+  border-top: 1px solid rgba(148, 163, 184, 0.6);
   display: flex;
   justify-content: center;
   align-items: center;
@@ -1177,16 +1170,16 @@ onMounted(() => {
 
 .summary-text {
   font-size: 0.8125rem;
-  color: #64748b;
+  color: #e5f4ff;
 }
 
 .summary-text b {
-  color: #1e293b;
+  color: #38bdf8;
   font-weight: 700;
 }
 
 .summary-divider {
-  color: #cbd5e1;
+  color: #64748b;
 }
 
 /* 无数据提示 */
@@ -1237,22 +1230,34 @@ onMounted(() => {
   max-height: 100px;
 }
 
-/* 滚动条样式 */
+@media screen and (orientation: landscape) {
+
+  :deep(.v-select .vs__dropdown-toggle,
+    .dark-select .vs__dropdown-toggle,
+    .filter-select .vs__dropdown-toggle,
+    .dialog-select .vs__dropdown-toggle) {
+    padding: 8px 12px !important;
+    min-height: 48px !important;
+  }
+}
+
+/* 工装列表滚动条 - 深色科技风 */
 .tooling-list::-webkit-scrollbar {
   width: 6px;
 }
 
 .tooling-list::-webkit-scrollbar-track {
-  background: #f3f4f6;
+  background: rgba(15, 23, 42, 0.9);
   border-radius: 3px;
 }
 
 .tooling-list::-webkit-scrollbar-thumb {
-  background: #d1d5db;
+  background: linear-gradient(180deg, #38bdf8, #6366f1);
   border-radius: 3px;
+  box-shadow: 0 0 10px rgba(56, 189, 248, 0.6);
 }
 
 .tooling-list::-webkit-scrollbar-thumb:hover {
-  background: #9ca3af;
+  background: linear-gradient(180deg, #0ea5e9, #4f46e5);
 }
 </style>

+ 34 - 0
src/api/blankBox.js

@@ -22,4 +22,38 @@ export function checkBlankBoxAndInventory() {
         url: '/api/FeedBoxResource/checkBlankBoxAndInventory',
         method: 'get',
     });
+}
+
+// 成品上架校验料箱
+export function checkFeedBoxAndProduct() {
+    return request({
+        url: '/api/FeedBoxResource/checkFeedBoxAndProduct',
+        method: 'get',
+    });
+}
+
+// 成品上架
+export function productRack(params) {
+    return request({
+        url: '/api/FeedBoxResource/productRack',
+        method: 'post',
+        data: params,
+    });
+}
+
+// 成品下架查询立体库成品
+export function findProductCanRemoved() {
+    return request({
+        url: '/api/FeedBoxResource/findProductCanRemoved',
+        method: 'get',
+    });
+}
+
+// 成品下架
+export function productRemoved(params) {
+    return request({
+        url: '/api/FeedBoxResource/productRemoved',
+        method: 'post',
+        data: params,
+    });
 }

+ 8 - 0
src/api/stockOut.js

@@ -90,6 +90,14 @@ export function cfStockOut() {
 }
 
 
+// 操作人员进入仓库拿取工装设备直接出库(申请领料)
+export function leaveCFWarehouse(inventoryId) {
+    return request({
+        url: '/api/StockOutResource/leaveCFWarehouse?inventoryId=' + inventoryId,
+        method: 'post',
+    });
+}
+
 // 领料完成离开
 export function cfStockOutLeave(params) {
     return request({

+ 13 - 0
src/assets/css/common.css

@@ -418,6 +418,19 @@
   min-height: 48px !important;
 }
 
+@media screen and (orientation: landscape) {
+  .v-select .vs__dropdown-toggle,
+.dark-select .vs__dropdown-toggle,
+.filter-select .vs__dropdown-toggle,
+.dialog-select .vs__dropdown-toggle {
+  background: rgba(13, 58, 106, 0.9) !important;
+  border: 1px solid #2a7fff !important;
+  border-radius: 8px !important;
+  padding: 1px 12px !important;
+  min-height: 0 !important;
+}
+}
+
 .v-select .vs__selected,
 .dark-select .vs__selected,
 .filter-select .vs__selected,

+ 658 - 35
src/box/FeedingArea.vue

@@ -32,8 +32,8 @@
       <div class="action-cards">
         <!-- 空料箱上架 -->
         <div class="action-card" @click="handlePlaceEmptyBin(false)">
-          <div class="card-border" />
-          <div class="card-glow" />
+          <!-- <div class="card-border" /> -->
+          <!-- <div class="card-glow" /> -->
           <div class="card-content">
             <div class="card-icon green">
               <i class="fas fa-arrow-up-from-bracket" />
@@ -48,33 +48,41 @@
 
         <!-- 请求空料箱 -->
         <div class="action-card" @click="handleRequestEmptyBin(false)">
-          <div class="card-border" />
-          <div class="card-glow" />
           <div class="card-content">
             <div class="card-icon blue">
               <i class="fas fa-box-open" />
             </div>
             <div class="card-label">请求空料箱</div>
-            <!-- <div class="card-decoration">
-              <div class="deco-line" />
-              <div class="deco-corner" />
-            </div> -->
           </div>
         </div>
 
         <!-- 请求校验 -->
         <div class="action-card" @click="handleRequestVerification(false)">
-          <div class="card-border" />
-          <div class="card-glow" />
           <div class="card-content">
             <div class="card-icon orange">
               <i class="fas fa-magnifying-glass-chart" />
             </div>
             <div class="card-label">工装/设备上架</div>
-            <!-- <div class="card-decoration">
-              <div class="deco-line" />
-              <div class="deco-corner" />
-            </div> -->
+          </div>
+        </div>
+
+        <!-- 成品上架 -->
+        <div class="action-card" @click="handleProductShelf(false)">
+          <div class="card-content">
+            <div class="card-icon purple">
+              <i class="fas fa-boxes-packing" />
+            </div>
+            <div class="card-label">成品上架</div>
+          </div>
+        </div>
+
+        <!-- 成品下架 -->
+        <div class="action-card" @click="handleProductOffShelf">
+          <div class="card-content">
+            <div class="card-icon red">
+              <i class="fas fa-dolly" />
+            </div>
+            <div class="card-label">成品下架</div>
           </div>
         </div>
       </div>
@@ -150,14 +158,233 @@
               </div>
             </template>
           </div>
+        </div>
+
+        <!-- 底部按钮 -->
+        <div class="dialog-footer">
+          <button v-if="showVerificationStatus" class="modal-btn primary-btn" @click="handleReverify">
+            <i class="fas fa-sync-alt" />
+            重新校验
+          </button>
+          <button class="modal-btn close-action-btn" @click="closeDialog">
+            关闭
+          </button>
+        </div>
+      </div>
+    </div>
+  </div>
+
+  <!-- 成品上架弹窗 -->
+  <div v-if="showProductShelfDialog" class="tech-modal-overlay" @click.self="closeProductShelfDialog">
+    <div class="tech-modal product-modal">
+      <div class="modal-header">
+        <h3>成品上架</h3>
+        <button class="close-btn" @click="closeProductShelfDialog">
+          <i class="fas fa-times" />
+        </button>
+      </div>
+
+      <div class="dialog-content">
+        <!-- 状态标签 -->
+        <div class="verification-status">
+          <span class="status-tag" :class="productShelfSuccess ? 'success' : 'danger'">
+            <i :class="productShelfSuccess ? 'fas fa-check-circle' : 'fas fa-times-circle'" />
+            {{ productShelfSuccess ? '校验通过' : '校验未通过' }}
+          </span>
+          <span v-if="productShelfError" class="status-tag danger">
+            {{ productShelfError }}
+          </span>
+        </div>
+
+        <!-- 可滚动内容区域:包含双栏 + 已选成品 -->
+        <div class="dialog-main">
+          <!-- 双栏布局 -->
+          <div class="dialog-body">
+            <!-- 左侧:料箱列表 -->
+            <div class="list-section">
+              <h3 class="section-title">
+                <i class="fas fa-boxes" />
+                料箱信息
+              </h3>
+              <div class="item-list">
+                <div v-for="bin in productShelfBinList" :key="bin.feedBoxId" class="item-card">
+                  <div class="item-header">
+                    <div class="item-info">
+                      <div class="item-id">{{ bin.feedBoxName }}</div>
+                    </div>
+                    <span class="item-tag">{{ bin.feedBoxNo }}</span>
+                  </div>
+                </div>
+                <div v-if="productShelfBinList.length === 0" class="empty-tip">
+                  暂无料箱信息
+                </div>
+              </div>
+            </div>
+
+            <!-- 右侧:根据校验状态显示不同内容 -->
+            <div class="list-section">
+              <template v-if="!productShelfSuccess">
+                <!-- 校验未通过时显示工装设备信息 -->
+                <h3 class="section-title">
+                  <i class="fas fa-tools" />
+                  工装 / 设备信息
+                </h3>
+                <div class="item-list">
+                  <div v-for="tool in productShelfToolList" :key="tool.inventoryNo" class="item-card">
+                    <div class="item-info">
+                      <div class="item-name">{{ tool.inventoryName }}</div>
+                      <div class="item-id">编号: {{ tool.inventoryNo }}</div>
+                    </div>
+                  </div>
+                  <div v-if="productShelfToolList.length === 0" class="empty-tip">
+                    暂无工装/设备信息
+                  </div>
+                </div>
+              </template>
+              <template v-else>
+                <!-- 校验通过时显示成品列表,可选择 -->
+                <h3 class="section-title">
+                  <i class="fas fa-box" />
+                  成品信息(点击选择/取消)
+                </h3>
+                <div class="item-list">
+                  <div
+                    v-for="product in productShelfProductList" :key="product.productId" class="item-card selectable"
+                    :class="{ selected: isProductSelected(product.productId) }"
+                    @click="toggleProductSelection(product)"
+                  >
+                    <div class="item-header">
+                      <div class="item-info">
+                        <div class="item-name">{{ product.productName }}</div>
+                        <div class="item-id">编号: {{ product.productNo }}</div>
+                      </div>
+                      <span v-if="isProductSelected(product.productId)" class="selected-icon">
+                        <i class="fas fa-check-circle" />
+                      </span>
+                    </div>
+                  </div>
+                  <div v-if="productShelfProductList.length === 0" class="empty-tip">
+                    暂无成品信息
+                  </div>
+                </div>
+              </template>
+            </div>
+          </div>
+
+          <!-- 已选成品区域 -->
+          <!-- <div v-if="productShelfSuccess && selectedProducts.length > 0" class="selected-section">
+            <h3 class="section-title">
+              <i class="fas fa-clipboard-check" />
+              已选成品({{ selectedProducts.length }})
+            </h3>
+            <div class="selected-list">
+              <div v-for="product in selectedProducts" :key="product.productId" class="selected-item">
+                <span class="selected-name">{{ product.productName }}</span>
+                <span class="selected-no">{{ product.productNo }}</span>
+                <button class="remove-btn" @click="removeSelectedProduct(product.productId)">
+                  <i class="fas fa-times" />
+                </button>
+              </div>
+            </div>
+          </div> -->
+        </div>
+
+        <!-- 底部按钮 -->
+        <div class="dialog-footer">
+          <button class="modal-btn primary-btn" @click="handleProductShelfReverify">
+            <i class="fas fa-sync-alt" />
+            重新校验
+          </button>
+          <button v-if="!isEmptySubmit" class="modal-btn submit-btn" @click="submitProductShelf">
+            <i class="fas fa-arrow-up-from-bracket" />
+            成品上架
+          </button>
+          <button v-else class="modal-btn submit-btn" @click="submitProductShelfEmpty">
+            <i class="fas fa-arrow-up-from-ground-water" />
+            空料箱上架
+          </button>
+        </div>
+      </div>
+    </div>
+  </div>
+
+  <!-- 成品下架弹窗 -->
+  <div v-if="showProductOffShelfDialog" class="tech-modal-overlay" @click.self="closeProductOffShelfDialog">
+    <div class="tech-modal product-modal">
+      <div class="modal-header">
+        <h3>成品下架</h3>
+        <button class="close-btn" @click="closeProductOffShelfDialog">
+          <i class="fas fa-times" />
+        </button>
+      </div>
+
+      <div class="dialog-content">
+        <!-- 可滚动内容区域:包含双栏 -->
+        <div class="dialog-main">
+          <!-- 双栏布局 -->
+          <div class="dialog-body">
+            <!-- 左侧:成品列表 -->
+            <div class="list-section">
+              <h3 class="section-title">
+                <i class="fas fa-box" />
+                成品列表(点击选择)
+              </h3>
+              <div class="item-list">
+                <div
+                  v-for="product in offShelfProductList" :key="product.id" class="item-card selectable"
+                  :class="{ selected: isOffShelfProductSelected(product.id) }"
+                  @click="toggleOffShelfProductSelection(product)"
+                >
+                  <div class="item-header">
+                    <div class="item-info">
+                      <div class="item-name">{{ product.name }}</div>
+                      <div class="item-id">编号: {{ product.no }}</div>
+                    </div>
+                    <span class="item-tag">{{ product.feedBoxName }}</span>
+                    <span v-if="isOffShelfProductSelected(product.id)" class="selected-icon">
+                      <i class="fas fa-check-circle" />
+                    </span>
+                  </div>
+                </div>
+                <div v-if="offShelfProductList.length === 0" class="empty-tip">
+                  暂无成品信息
+                </div>
+              </div>
+            </div>
+
+            <!-- 右侧:已选成品 -->
+            <div class="list-section">
+              <h3 class="section-title">
+                <i class="fas fa-clipboard-check" />
+                已选成品({{ selectedOffShelfProducts.length }})
+              </h3>
+              <div class="item-list">
+                <div v-for="product in selectedOffShelfProducts" :key="product.id" class="item-card selected-card">
+                  <div class="item-header">
+                    <div class="item-info">
+                      <div class="item-name">{{ product.name }}</div>
+                      <div class="item-id">编号: {{ product.no }}</div>
+                    </div>
+                    <span class="item-tag">{{ product.feedBoxName }}</span>
+                    <button class="remove-btn" @click="removeOffShelfProduct(product.id)">
+                      <i class="fas fa-times" />
+                    </button>
+                  </div>
+                </div>
+                <div v-if="selectedOffShelfProducts.length === 0" class="empty-tip">
+                  请从左侧选择要下架的成品
+                </div>
+              </div>
+            </div>
+          </div>
 
           <!-- 底部按钮 -->
           <div class="dialog-footer">
-            <button v-if="showVerificationStatus" class="modal-btn primary-btn" @click="handleReverify">
-              <i class="fas fa-sync-alt" />
-              重新校验
+            <button class="modal-btn submit-btn" @click="submitProductOffShelf">
+              <i class="fas fa-dolly" />
+              成品下架
             </button>
-            <button class="modal-btn close-action-btn" @click="closeDialog">
+            <button class="modal-btn close-action-btn" @click="closeProductOffShelfDialog">
               关闭
             </button>
           </div>
@@ -183,8 +410,11 @@
 
 <script setup>
 import { ref, onMounted, onUnmounted } from 'vue';
-import { showNotify, showToast } from 'vant';
-import { checkBlankBox, requestBlankBox, checkBlankBoxAndInventory } from '../api/blankBox.js';
+import { showNotify } from 'vant';
+import {
+    checkBlankBox, requestBlankBox, checkBlankBoxAndInventory, checkFeedBoxAndProduct, productRack,
+    findProductCanRemoved, productRemoved,
+} from '../api/blankBox.js';
 import { useRouter } from 'vue-router';
 
 const router = useRouter();
@@ -201,6 +431,21 @@ const binList = ref([]);
 
 const toolList = ref([]);
 
+// 成品上架相关状态
+const showProductShelfDialog = ref(false);
+const productShelfSuccess = ref(false);
+const productShelfError = ref('');
+const productShelfBinList = ref([]);
+const productShelfToolList = ref([]);
+const productShelfProductList = ref([]);
+const selectedProducts = ref([]);
+const isEmptySubmit = ref(false); // 是否为成品空料箱上架
+
+// 成品下架相关状态
+const showProductOffShelfDialog = ref(false);
+const offShelfProductList = ref([]);
+const selectedOffShelfProducts = ref([]);
+
 const goBack = () => {
     router.back();
 };
@@ -290,7 +535,7 @@ const handleRequestVerification = async isReverify => {
 };
 
 const handleReverify = () => {
-    showNotify({ type: 'primary', message: '正在重新校验....', zIndex: 9999999 });
+    showNotify({ type: 'primary', message: '正在重新校验....', duration: 1000, zIndex: 9999999 });
     if (dialogTitle.value === '上架结果') {
         handlePlaceEmptyBin();
     } else if (dialogTitle.value === '校验结果')
@@ -302,6 +547,242 @@ const closeDialog = () => {
 };
 
 
+// 处理成品上架接口数据
+const handleProductShelfDataProcessing = (res, isReverify = false) => {
+    if (res.data) {
+        isEmptySubmit.value = false;
+        productShelfBinList.value = [];
+        productShelfToolList.value = [];
+        productShelfProductList.value = [];
+        if (!isReverify) {
+            selectedProducts.value = [];
+        }
+
+        const { success, rfidFeedBoxResponseList, rfidInventoryResponseList, productResponseList, feedBoxDetailIds, blankFeedBox } = res.data;
+
+        if (!isReverify) showProductShelfDialog.value = true;
+
+        productShelfSuccess.value = success;
+        isEmptySubmit.value = blankFeedBox;
+
+        if (rfidFeedBoxResponseList && rfidFeedBoxResponseList.length > 0) {
+            productShelfBinList.value = rfidFeedBoxResponseList;
+        }
+        if (rfidInventoryResponseList && rfidInventoryResponseList.length > 0) {
+            productShelfToolList.value = rfidInventoryResponseList;
+        }
+        if (productResponseList && productResponseList.length > 0) {
+            productShelfProductList.value = productResponseList;
+
+            // 根据 feedBoxDetailIds 预选中对应成品
+            if (Array.isArray(feedBoxDetailIds) && feedBoxDetailIds.length > 0) {
+                const selectedIdSet = new Set(feedBoxDetailIds);
+                const preSelected = productResponseList.filter(p => selectedIdSet.has(p.productId));
+                if (preSelected.length > 0) {
+                    selectedProducts.value = preSelected.map(p => ({ ...p }));
+                }
+            }
+        }
+    }
+    if (res.errorCode !== 0) {
+        productShelfSuccess.value = false;
+        productShelfError.value = res.errorMessage;
+        showNotify({ type: 'danger', message: res.errorMessage, duration: 6000, zIndex: 9999999 });
+    } else {
+        productShelfError.value = '';
+    }
+    if (res.data.blankFeedBox) productShelfError.value += ',您可以进行空料想上架';
+};
+
+// 成品上架点击
+const handleProductShelf = async (isReverify = false) => {
+    loading.value = true;
+    try {
+        const res = await checkFeedBoxAndProduct();
+        handleProductShelfDataProcessing(res, isReverify);
+    } catch (error) {
+        console.error('成品上架校验失败:', error);
+        productShelfSuccess.value = false;
+        showNotify({ type: 'danger', message: '成品上架校验API调用失败' });
+    } finally {
+        loading.value = false;
+    }
+};
+
+// 重新校验成品上架
+const handleProductShelfReverify = () => {
+    showNotify({ type: 'primary', message: '正在重新校验....', duration: 1000, zIndex: 9999999 });
+    handleProductShelf(true);
+};
+
+// 关闭成品上架弹窗
+const closeProductShelfDialog = () => {
+    showProductShelfDialog.value = false;
+    selectedProducts.value = [];
+};
+
+// 检查成品是否已选中
+const isProductSelected = productId => {
+    return selectedProducts.value.some(p => p.productId === productId);
+};
+
+// 切换成品选中状态
+const toggleProductSelection = product => {
+    const index = selectedProducts.value.findIndex(p => p.productId === product.productId);
+    if (index > -1) {
+        selectedProducts.value.splice(index, 1);
+    } else {
+        selectedProducts.value.push({ ...product });
+    }
+};
+
+// 移除已选成品
+const removeSelectedProduct = productId => {
+    const index = selectedProducts.value.findIndex(p => p.productId === productId);
+    if (index > -1) {
+        selectedProducts.value.splice(index, 1);
+    }
+};
+
+// 提交成品上架
+const submitProductShelf = async () => {
+    console.log(productShelfSuccess.value);
+    if (!productShelfSuccess.value) {
+        showNotify({ type: 'danger', message: '校验未通过,请重新校验', duration: 3000, zIndex: 9999999 });
+        return;
+    }
+
+    // 校验: 是否选择了成品
+    if (selectedProducts.value.length === 0) {
+        showNotify({ type: 'danger', message: '请先选择要上架的成品', duration: 3000, zIndex: 9999999 });
+        return;
+    }
+
+    // 获取料箱ID和所选成品ID集合
+    const feedBoxId = productShelfBinList.value[0].feedBoxId;
+    const productIds = selectedProducts.value.map(p => p.productId);
+    const params = { feedBoxId, productIds };
+
+    console.log('成品上架提交数据:', params);
+
+    loading.value = true;
+    try {
+        const res = await productRack(params);
+        if (res.errorCode === 0) {
+            showNotify({ type: 'success', message: '成品上架成功', duration: 3000, zIndex: 9999999 });
+            closeProductShelfDialog();
+        } else {
+            showNotify({ type: 'danger', message: res.errorMessage, duration: 3000, zIndex: 9999999 });
+        }
+
+    } catch (error) {
+        productShelfSuccess.value = false;
+        console.error('成品上架校验失败:', error);
+        showNotify({ type: 'danger', message: '成品上架校验API调用失败' });
+    } finally {
+        loading.value = false;
+    }
+
+};
+
+// 提交成名空料箱上架
+const submitProductShelfEmpty = () => {
+    closeProductShelfDialog();
+    handlePlaceEmptyBin(false);
+};
+
+// 成品下架点击
+const handleProductOffShelf = async () => {
+    offShelfProductList.value = [];
+    selectedOffShelfProducts.value = [];
+    loading.value = true;
+    try {
+        const res = await findProductCanRemoved();
+        if (res.errorCode === 0) {
+            if (res.datas && res.datas.length > 0) {
+                offShelfProductList.value = res.datas;
+                selectedOffShelfProducts.value = [];
+                showProductOffShelfDialog.value = true;
+            } else {
+                offShelfProductList.value = [];
+            }
+        } else {
+            showNotify({ type: 'danger', message: res.errorMessage, duration: 3000, zIndex: 9999999 });
+        }
+    } catch (error) {
+        console.error('获取成品下架列表失败:', error);
+        showNotify({ type: 'danger', message: '获取成品下架列表失败' });
+    } finally {
+        loading.value = false;
+    }
+};
+
+// 关闭成品下架弹窗
+const closeProductOffShelfDialog = () => {
+    showProductOffShelfDialog.value = false;
+    selectedOffShelfProducts.value = [];
+};
+
+// 检查成品下架是否已选中
+const isOffShelfProductSelected = productId => {
+    return selectedOffShelfProducts.value.some(p => p.id === productId);
+};
+
+// 切换成品下架选中状态
+const toggleOffShelfProductSelection = product => {
+    const index = selectedOffShelfProducts.value.findIndex(p => p.id === product.id);
+    if (index > -1) {
+        selectedOffShelfProducts.value.splice(index, 1);
+    } else {
+        selectedOffShelfProducts.value.push({ ...product });
+    }
+};
+
+// 移除已选成品下架
+const removeOffShelfProduct = productId => {
+    const index = selectedOffShelfProducts.value.findIndex(p => p.id === productId);
+    if (index > -1) {
+        selectedOffShelfProducts.value.splice(index, 1);
+    }
+};
+
+// 提交成品下架
+const submitProductOffShelf = async () => {
+    // 校验是否选择了成品
+    if (selectedOffShelfProducts.value.length === 0) {
+        showNotify({ type: 'danger', message: '请先选择要下架的成品', duration: 3000, zIndex: 9999999 });
+        return;
+    }
+
+    // 获取所选成品ID集合
+    const params = selectedOffShelfProducts.value.map(p => {
+        return {
+            id: p.id,
+            feedBoxId: p.feedBoxId,
+        };
+    });
+
+    console.log('成品下架提交数据:', params);
+
+    loading.value = true;
+    try {
+        const res = await productRemoved(params);
+        if (res.errorCode === 0) {
+            showNotify({ type: 'success', message: '成品下架成功', duration: 3000, zIndex: 9999999 });
+            closeProductOffShelfDialog();
+        } else {
+            showNotify({ type: 'danger', message: res.errorMessage, duration: 6000, zIndex: 9999999 });
+        }
+    } catch (error) {
+        console.error('成品下架API调用失败:', error);
+        showNotify({ type: 'danger', message: '成品下架API调用失败' });
+    } finally {
+        loading.value = false;
+    }
+
+
+};
+
 const updateDateTime = () => {
     const now = new Date();
     const year = now.getFullYear();
@@ -524,9 +1005,9 @@ onUnmounted(() => {
   position: relative;
   z-index: 5;
   flex: 1;
-  padding: 60px 40px;
+  padding: 40px 40px;
   display: flex;
-  align-items: center;
+  align-items: flex-start;
   justify-content: center;
   overflow-y: auto;
   overflow-x: hidden;
@@ -544,7 +1025,7 @@ onUnmounted(() => {
 
 .action-card {
   position: relative;
-  height: 350px;
+  height: 300px;
   max-height: calc((100vh - 200px) / 1.5);
   cursor: pointer;
   transition: all 0.4s ease;
@@ -620,15 +1101,15 @@ onUnmounted(() => {
   flex-direction: column;
   align-items: center;
   justify-content: center;
-  gap: 40px;
+  gap: 30px;
   backdrop-filter: blur(15px);
   overflow: hidden;
   box-shadow: inset 0 0 50px rgba(0, 255, 255, 0.1), 0 15px 50px rgba(0, 0, 0, 0.6);
 }
 
 .card-icon {
-  width: 160px;
-  height: 160px;
+  width: 130px;
+  height: 130px;
   border-radius: 50%;
   display: flex;
   align-items: center;
@@ -658,6 +1139,18 @@ onUnmounted(() => {
   box-shadow: 0 0 50px rgba(255, 153, 0, 1), inset 0 0 30px rgba(255, 153, 0, 0.3);
 }
 
+.card-icon.purple {
+  background: linear-gradient(135deg, rgba(153, 51, 255, 0.3), rgba(102, 0, 204, 0.3));
+  border-color: rgba(153, 51, 255, 0.8);
+  box-shadow: 0 0 50px rgba(153, 51, 255, 1), inset 0 0 30px rgba(153, 51, 255, 0.3);
+}
+
+.card-icon.red {
+  background: linear-gradient(135deg, rgba(255, 71, 87, 0.3), rgba(220, 20, 60, 0.3));
+  border-color: rgba(255, 71, 87, 0.8);
+  box-shadow: 0 0 50px rgba(255, 71, 87, 1), inset 0 0 30px rgba(255, 71, 87, 0.3);
+}
+
 @keyframes iconPulse {
 
   0%,
@@ -825,7 +1318,7 @@ onUnmounted(() => {
   background: rgba(20, 30, 50, 0.6);
   border-radius: 10px;
   padding: 15px;
-  margin-bottom: 15px;
+  margin-bottom: 4px;
   border: 1px solid rgba(0, 153, 255, 0.3);
   transition: all 0.3s ease;
 }
@@ -982,7 +1475,7 @@ onUnmounted(() => {
   }
 
   .action-card {
-    height: 300px;
+    height: 260px;
     max-height: calc((100vh - 180px) / 1.5);
   }
 
@@ -1179,18 +1672,19 @@ onUnmounted(() => {
   border-radius: 16px;
   width: 90%;
   max-width: 800px;
-  max-height: 80vh;
+  max-height: 88vh;
   overflow: hidden;
   box-shadow: 0 0 40px rgba(4, 159, 216, 0.3);
   display: flex;
   flex-direction: column;
+  padding: 20px 28px !important;
 }
 
 .modal-header {
   display: flex;
   align-items: center;
   justify-content: space-between;
-  padding: 20px 25px;
+  padding: 14px 24px;
   border-bottom: 1px solid rgba(4, 159, 216, 0.3);
 }
 
@@ -1222,9 +1716,11 @@ onUnmounted(() => {
 }
 
 .dialog-content {
-  padding: 20px 25px;
-  overflow-y: auto;
+  padding: 14px 24px 20px;
+  overflow: hidden;
   flex: 1;
+  display: flex;
+  flex-direction: column;
 }
 
 .verification-status {
@@ -1257,6 +1753,7 @@ onUnmounted(() => {
 .dialog-body {
   display: flex;
   gap: 20px;
+  flex-shrink: 0;
 }
 
 .list-section {
@@ -1333,6 +1830,16 @@ onUnmounted(() => {
   border-radius: 12px;
 }
 
+/* 弹窗中部滚动容器:双栏 + 已选成品 */
+.dialog-main {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  gap: 16px;
+  overflow-y: auto;
+  padding-right: 4px;
+}
+
 .result-header {
   display: flex;
   flex-direction: column;
@@ -1362,9 +1869,10 @@ onUnmounted(() => {
 .dialog-footer {
   display: flex;
   gap: 15px;
-  margin-top: 20px;
-  padding-top: 20px;
+  margin-top: 16px;
+  padding-top: 16px;
   border-top: 1px solid rgba(255, 255, 255, 0.1);
+  flex-shrink: 0;
 }
 
 .modal-btn {
@@ -1401,6 +1909,113 @@ onUnmounted(() => {
   background: rgba(42, 127, 255, 0.2);
 }
 
+/* ========== 成品上架/下架相关样式 ========== */
+.product-modal {
+  max-width: 900px;
+  max-height: 88vh;
+}
+
+.item-card.selectable {
+  cursor: pointer;
+  transition: all 0.3s ease;
+}
+
+.item-card.selectable:hover {
+  background: rgba(4, 159, 216, 0.3);
+  border-color: rgba(4, 159, 216, 0.6);
+}
+
+.item-card.selected {
+  background: rgba(16, 185, 129, 0.3);
+  border-color: rgba(16, 185, 129, 0.8);
+}
+
+.item-card.selected-card {
+  background: rgba(16, 185, 129, 0.2);
+  border-color: rgba(16, 185, 129, 0.5);
+}
+
+.selected-icon {
+  color: #34d399;
+  font-size: 20px;
+}
+
+.empty-tip {
+  text-align: center;
+  color: rgba(255, 255, 255, 0.5);
+  padding: 30px 0;
+  font-size: 14px;
+}
+
+/* 已选成品区域 */
+.selected-section {
+  background: rgba(0, 0, 0, 0.2);
+  border-radius: 12px;
+  padding: 15px;
+  margin-top: 20px;
+  flex-shrink: 0;
+}
+
+.selected-list {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 10px;
+  max-height: 180px;
+  overflow-y: auto;
+}
+
+.selected-item {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+  background: rgba(16, 185, 129, 0.2);
+  border: 1px solid rgba(16, 185, 129, 0.5);
+  border-radius: 20px;
+  padding: 8px 12px;
+}
+
+.selected-name {
+  color: #34d399;
+  font-size: 14px;
+  font-weight: 500;
+}
+
+.selected-no {
+  color: rgba(255, 255, 255, 0.6);
+  font-size: 12px;
+}
+
+.remove-btn {
+  width: 22px;
+  height: 22px;
+  border-radius: 50%;
+  background: rgba(239, 68, 68, 0.3);
+  border: 1px solid rgba(239, 68, 68, 0.5);
+  color: #f87171;
+  font-size: 12px;
+  cursor: pointer;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  transition: all 0.3s;
+}
+
+.remove-btn:hover {
+  background: rgba(239, 68, 68, 0.5);
+  color: #fff;
+}
+
+/* 提交按钮样式 */
+.submit-btn {
+  background: linear-gradient(90deg, #10b981 0%, #34d399 100%);
+  border: none;
+  color: #fff;
+}
+
+.submit-btn:hover {
+  box-shadow: 0 0 20px rgba(16, 185, 129, 0.5);
+}
+
 /* 响应式 */
 @media screen and (max-width: 768px) {
   .dialog-body {
@@ -1411,5 +2026,13 @@ onUnmounted(() => {
     max-width: 95%;
     max-height: 90vh;
   }
+
+  .product-modal {
+    max-width: 95%;
+  }
+
+  .selected-list {
+    max-height: 100px;
+  }
 }
 </style>

+ 10 - 4
src/finishProduct/FinishProductIn.vue

@@ -105,10 +105,8 @@
             <label>货位选择</label>
             <v-select
               v-model="newProduct.positionId" :options="warehouseLocationOptions"
-              :reduce="item => item.positionId" label="positionName"
-              placeholder="请选择货位" :clearable="true" :filterable="true"
-              class="dark-select"
-              append-to-body
+              :reduce="item => item.positionId" label="positionName" placeholder="请选择货位" :clearable="true"
+              :filterable="true" class="dark-select" append-to-body
             >
               <template #no-options>
                 <span>无该选项数据</span>
@@ -977,6 +975,14 @@ onMounted(() => {
     letter-spacing: 6px;
     border-radius: 8px;
   }
+
+  :deep(.v-select .vs__dropdown-toggle,
+    .dark-select .vs__dropdown-toggle,
+    .filter-select .vs__dropdown-toggle,
+    .dialog-select .vs__dropdown-toggle) {
+    padding: 8px 12px !important;
+    min-height: 48px !important;
+  }
 }
 </style>
 

+ 20 - 4
src/login/UserHome.vue

@@ -29,12 +29,16 @@
               <i class="fas fa-fingerprint mr-2" />
               <span>指纹录入</span>
             </div>
+            <div class="dropdown-item" @click="goReturnManagement">
+              <i class="fas fa-box mr-2" />
+              <span>还料区管理</span>
+            </div>
             <div v-if="isOut" class="dropdown-item" @click="toggleMenu">
-              <i class="fas fa-menu mr-2" />
+              <i class="fas fa-bars-staggered" />
               <span>内侧菜单</span>
             </div>
             <div v-else class="dropdown-item" @click="toggleMenu">
-              <i class="fas fa-menu mr-2" />
+              <i class="fas fa-bars-staggered" />
               <span>外侧菜单</span>
             </div>
           </div>
@@ -149,7 +153,7 @@ const config = ref({
     header: ['名称', '图号', '类型', '操作', '时间', '操作人员'],
     data: [],
     index: true,
-    columnWidth: [50, 200, 200, 100, 100, 200, 120],
+    columnWidth: [50, 230, 230, 90, 90, 190, 110],
     align: ['center', 'center', 'center', 'center', 'center', 'center', 'center'],
     rowNum: 14,
     headerBGC: '#0c5897',
@@ -185,6 +189,8 @@ onMounted(() => {
 onUnmounted(() => {
     // 移除事件监听
     document.removeEventListener('click', handleClickOutside);
+    clearInterval(infoTimer);
+    infoTimer = null;
 });
 
 // 统计数据
@@ -227,7 +233,8 @@ const inButtons = reactive([
     { label: '还料\n离开', action: 'materialInLeave' },
     { label: '成品\n出库', action: 'finishedProductOut' },
     { label: '料箱\n上架', action: 'feedingArea' },
-    { label: '还料区\n管理', action: 'returnManagement' },
+    { label: '异常\n停泊区', action: 'abnormalAnchorageArea' },
+    // { label: '还料区\n管理', action: 'returnManagement' },
     // { label: '取料区\n管理', action: 'deliverManagement' },
 ]);
 
@@ -251,6 +258,12 @@ const goToFingerprintEnroll = () => {
     router.push('/fingerprint-enroll');
 };
 
+// 跳转到还料区管理页面
+const goReturnManagement = () => {
+    showSettings.value = false;
+    router.push('/return-management');
+};
+
 // 切换菜单
 const toggleMenu = () => {
     isOut.value = !isOut.value;
@@ -305,6 +318,9 @@ const handleAction = action => {
     case 'feedingArea':
         router.push('/feeding-area');
         break;
+    case 'abnormalAnchorageArea':
+        router.push('/abnormal-anchorage-area');
+        break;
     case 'returnManagement':
         router.push('/return-management');
         break;

+ 1 - 1
src/stock-out/OrderPicking.vue

@@ -268,7 +268,7 @@ const filterForm = reactive({
 const inventoryTypeList = ref([
     { value: 'Clamp', label: '工装' },
     { value: 'Instrument', label: '设备' },
-    { value: 'FinishProduct', label: '成品' },
+    // { value: 'FinishProduct', label: '成品' },
 ]);
 // 表格加载状态
 const loading = ref(false);

+ 24 - 3
src/stock-out/OutboundConfirm.vue

@@ -116,7 +116,7 @@
 import { useRouter } from 'vue-router';
 import { ref, computed, onMounted } from 'vue';
 import { showNotify } from 'vant';
-import { cfStockOut, createStockOut, cfStockOutLeave } from '../api/stockOut.js';
+import { cfStockOut, createStockOut, cfStockOutLeave,leaveCFWarehouse } from '../api/stockOut.js';
 
 // 图片资源
 import bgImg from '../assets/images/bj.png';
@@ -124,6 +124,9 @@ import bgImg from '../assets/images/bj.png';
 // 路由
 const router = useRouter();
 
+// 操作的id
+const operationId = ref(null);
+
 // 返回主页
 const goHome = () => {
     router.push('/home');
@@ -181,12 +184,29 @@ const isCanLeave = computed(() => {
 
 // 开始领料
 const handleStart = record => {
+    operationId.value = record.inventoryId;
     generateCFStockOut(record.stockOutPrepareLineId);
 };
 
 // 申请领料
-const handleApply = record => {
+const handleApply = async record => {
     showNotify({ type: 'success', message: '申请领料成功' });
+    try {
+        const res = await leaveCFWarehouse(record.inventoryId);
+
+        if (res.errorCode === 0) {
+            showNotify({ type: 'success', message: '申请领料成功' });
+            operationId.value = record.inventoryId;
+            getStockOutList(true);
+        } else {
+            showNotify({ type: 'danger', message: res.errorMessage });
+        }
+    } catch (error) {
+        console.error('申请领料API调用失败:', error);
+        showNotify({ type: 'danger', message: '申请领料API调用失败' });
+    } finally {
+        loading.value = false;
+    }
 };
 
 // 领料完成后离开
@@ -231,10 +251,11 @@ const getStockOutList = async (isOne = false) => {
                 } else {
                     res.datas.forEach(i => {
                         materialList.value.forEach(j => {
-                            if (i.inventoryNo === j.inventoryNo) {
+                            if (j.inventoryId === operationId.value) {
                                 j.status = 1;
                                 j.stockOutId = i.stockOutId;
                                 j.positionName = i.positionName;
+                                j.warehouseName = i.warehouseName;
                             }
                         });
                     });

+ 3 - 3
webpack.dev.js

@@ -43,17 +43,17 @@ module.exports = WebpackMerge.merge(baseConfig, {
         // },
         proxy: {
             '/api': {
-                target: 'http://192.168.1.7:10026/',
+                target: 'http://192.168.1.112:10026/',
                 ws: false,
                 changeOrigin: true,
             },
             '/authApi': {
-                target: 'http://192.168.1.7:10026/',
+                target: 'http://192.168.1.112:10026/',
                 ws: false,
                 changeOrigin: true,
             },
             '/android-device-sdk': {
-                target: 'http://192.168.1.7:10026/',
+                target: 'http://192.168.1.112:10026/',
                 ws: false,
                 changeOrigin: true,
             },