From 4476740c214edb7ab667c48fcab00488fbdd9879 Mon Sep 17 00:00:00 2001
From: pan <antony1029@163.com>
Date: 星期六, 15 十一月 2025 09:03:54 +0800
Subject: [PATCH] 提交

---
 项目代码/WIDESEA_WMSClient/src/views/outbound/PickingConfirm.vue |  777 ++++++++++++++++++++++++-----------------------------------
 1 files changed, 318 insertions(+), 459 deletions(-)

diff --git "a/\351\241\271\347\233\256\344\273\243\347\240\201/WIDESEA_WMSClient/src/views/outbound/PickingConfirm.vue" "b/\351\241\271\347\233\256\344\273\243\347\240\201/WIDESEA_WMSClient/src/views/outbound/PickingConfirm.vue"
index ef26541..d9f3d25 100644
--- "a/\351\241\271\347\233\256\344\273\243\347\240\201/WIDESEA_WMSClient/src/views/outbound/PickingConfirm.vue"
+++ "b/\351\241\271\347\233\256\344\273\243\347\240\201/WIDESEA_WMSClient/src/views/outbound/PickingConfirm.vue"
@@ -3,516 +3,375 @@
     <div class="page-header">
       <el-page-header @back="goBack">
         <template #content>
-          <span class="title">鍑哄簱鎷i�夌‘璁� - {{ orderInfo.orderNo }}</span>
+          <span class="title">鍑哄簱鎷i�夌‘璁� - {{ this.$route.query.orderNo }}</span>
         </template>
       </el-page-header>
     </div>
-
-    <el-row :gutter="20" class="main-content">
-      <el-col :span="8">
+    
+    <div class="content-layout">
+      <!-- 宸︿晶锛氭壂鐮佸尯鍩� -->
+      <div class="left-section">
         <div class="scan-section">
-          <el-card header="鎵爜鍖哄煙">
-            <el-form label-width="100px" size="small">
-              <el-form-item label="鎵樼洏鏉$爜">
-                <el-input 
-                  v-model="scanForm.palletCode" 
-                  placeholder="鎵弿鎴栬緭鍏ユ墭鐩樻潯鐮�"
-                  @keyup.enter="handlePalletScan"
-                  clearable
-                >
-                  <template #append>
-                    <el-button @click="handlePalletScan">纭</el-button>
-                  </template>
-                </el-input>
-              </el-form-item>
+          <el-alert
+            title="璇蜂娇鐢ㄦ壂鐮佹灙鎵弿鎵樼洏鐮佸拰鐗╂枡鏉$爜锛屾壂鐮佹灙甯﹀洖杞﹀姛鑳斤紝鎵畬鐗╂枡鏉$爜鑷姩纭"
+            type="info"
+            :closable="false"
+            class="scan-alert"
+          />
 
-              <el-form-item label="鐗╂枡鏉$爜">
-                <el-input 
-                  v-model="scanForm.barcode" 
-                  placeholder="鎵弿鎴栬緭鍏ョ墿鏂欐潯鐮�"
-                  @keyup.enter="handleBarcodeScan"
-                  :disabled="!currentPallet"
-                  clearable
-                >
-                  <template #append>
-                    <el-button @click="handleBarcodeScan" :disabled="!currentPallet">纭</el-button>
-                  </template>
-                </el-input>
-              </el-form-item>
+          <el-form :model="scanForm" label-width="100px" class="scan-form">
+            <el-form-item label="鎵樼洏鐮�" required>
+              <el-input
+                ref="palletInput"
+                v-model="scanForm.palletCode"
+                placeholder="璇锋壂鎻忔墭鐩樼爜"
+                @keyup.enter="handlePalletScan"
+                @blur="loadPalletSummary"
+                clearable
+              />
+            </el-form-item>
 
-              <el-form-item label="鎷i�夋暟閲�">
-                <el-input-number 
-                  v-model="scanForm.quantity" 
-                  :min="1" 
-                  :max="maxPickQuantity"
-                  :disabled="!currentLockInfo"
-                />
-              </el-form-item>
+            <el-form-item label="鐗╂枡鏉$爜" required>
+              <el-input
+                ref="materialInput"
+                v-model="scanForm.materialBarcode"
+                placeholder="璇锋壂鎻忕墿鏂欐潯鐮�"
+                :disabled="!scanForm.palletCode"
+                @keyup.enter="handleMaterialScan"
+                clearable
+              />
+            </el-form-item>
+          </el-form>
 
-              <!-- 鐗╂枡淇℃伅鏄剧ず -->
-              <el-form-item label="鐗╂枡缂栫爜" v-if="currentMaterialInfo">
-                <el-input v-model="currentMaterialInfo.materielCode" readonly />
-              </el-form-item>
-              <el-form-item label="鐗╂枡鍚嶇О" v-if="currentMaterialInfo">
-                <el-input v-model="currentMaterialInfo.materielName" readonly />
-              </el-form-item>
-            </el-form>
-
-            <div class="current-info" v-if="currentPallet">
-              <p>褰撳墠鎵樼洏: {{ currentPallet.palletCode }}</p>
-              <p>璐т綅: {{ currentPallet.locationCode }}</p>
-              <p>鐘舵��: {{ currentPallet.statusText }}</p>
-            </div>
-          </el-card>
+          <!-- 鎵樼洏鎷h揣缁熻 -->
+          <div v-if="palletSummary" class="pallet-summary">
+            <el-card header="鎵樼洏鎷h揣缁熻">
+              <el-descriptions :column="3" border>
+                <el-descriptions-item label="鎵樼洏鍙�">
+                  {{ scanForm.palletCode }}
+                </el-descriptions-item>
+                <el-descriptions-item label="鏈嫞璐ф潯鏁�">
+                  <el-text type="warning">{{ palletSummary.unpickedCount }}</el-text>
+                </el-descriptions-item>
+                <el-descriptions-item label="鏈嫞璐ф�绘暟">
+                  <el-text type="danger">{{ palletSummary.unpickedTotal }}</el-text>
+                </el-descriptions-item>
+              </el-descriptions>
+            </el-card>
+          </div>
 
           <div class="action-buttons">
-            <el-button 
-              type="warning" 
-              @click="handleBackToStock" 
-              :disabled="!currentPallet"
-              style="margin-bottom: 10px;"
-            >
-              鍥炲簱
+            <el-button type="primary" @click="handleConfirm" :loading="confirmLoading">
+              鎵嬪姩纭
             </el-button>
-            <el-button 
-              type="success" 
-              @click="handleDirectOutbound" 
-              :disabled="!currentPallet"
-              style="margin-bottom: 10px;"
-            >
-              鐩存帴鍑哄簱
-            </el-button>
-            <el-button 
-              type="primary" 
-              @click="handleOpenSplit" 
-              :disabled="!currentLockInfo"
-            >
-              鎷嗗寘
-            </el-button>
+            <el-button @click="handleReset">閲嶇疆</el-button>
+            <el-button @click="$emit('close')">鍙栨秷</el-button>
           </div>
         </div>
-      </el-col>
+      </div>
 
-      <el-col :span="16">
-        <el-card header="鎷i�夌粨鏋�">
-          <div class="summary-info">
-            <el-alert
-              :title="`鏈嫞璐�: ${unpickedCount} 鏉�, ${unpickedQuantity} 涓猔"
-              type="warning"
-              :closable="false"
-            />
-          </div>
-
+      <!-- 鍙充晶锛氬嚭搴撹鎯呭垪琛� -->
+      <div class="right-section">
+        <el-card class="outbound-details-card" header="鍑哄簱璇︽儏">
           <vol-table
-            :data="pickedList"
-            :columns="pickedColumns"
-            :pagination="false"
-            :height="400"
-          >
-            <template #action="{ row }">
-              <el-button type="text" @click="handleCancelPick(row)">鎾ら攢</el-button>
-            </template>
-          </vol-table>
+            ref="outboundTable"
+            :table-config="outboundTableConfig"
+            :height="300"
+          />
         </el-card>
-      </el-col>
-    </el-row>
+      </div>
+    </div>
 
-    <!-- 鎷嗗寘寮圭獥 -->
-    <vol-box
-      v-model="splitVisible"
-      title="鎷嗗寘鎿嶄綔"
-      :width="600"
-      :height="500"
-    >
-      <SplitPackageModal
-        v-if="splitVisible"
-        :lockInfo="currentLockInfo"
-        @success="handleSplitSuccess"
-        @close="splitVisible = false"
-      />
-    </vol-box>
+    <!-- 宸插垎鎷h褰曞垪琛� -->
+    <div class="picked-records">
+      <el-card header="宸插垎鎷h褰�">
+        <vol-table
+          ref="pickedTable"
+          :table-config="pickedTableConfig"
+          :height="300"
+        />
+      </el-card>
+    </div>
   </div>
 </template>
 
 <script>
-import SplitPackageModal from './SplitPackageModal.vue'
+import http from '@/api/http.js'
+import { ref, defineComponent } from "vue";
+import { ElMessage } from "element-plus";
+import { useRoute } from 'vue-router' 
 
-export default {
-  components: { SplitPackageModal },
+export default defineComponent({
+  name: 'PickingConfirm',
+  components: {
+  
+  },
+  props: {
+    orderNo: {
+      type: String,
+      required: true
+    }
+  },
+  emits: ['confirm', 'close'],
   data() {
     return {
-      orderInfo: {},
       scanForm: {
         palletCode: '',
-        barcode: '',
-        quantity: 1
+        materialBarcode: ''
       },
-      currentPallet: null,
-      currentLockInfo: null,
-      currentMaterialInfo: null, // 鏂板锛氬綋鍓嶇墿鏂欎俊鎭�
-      pickedList: [],
-      pickedColumns: [
-        { field: 'barcode', title: '鐗╂枡鏉$爜', width: 150 },
-        { field: 'materielCode', title: '鐗╂枡缂栫爜', width: 120 },
-        { field: 'materielName', title: '鐗╂枡鍚嶇О', width: 150 },
-        { field: 'pickQuantity', title: '鎷i�夋暟閲�', width: 100 },
-        { field: 'palletCode', title: '鎵樼洏缂栧彿', width: 120 },
-        { field: 'pickTime', title: '鎷i�夋椂闂�', width: 160 },
-        { field: 'operator', title: '鎿嶄綔浜�', width: 100 },
-        { field: 'action', title: '鎿嶄綔', width: 80, slot: true }
-      ],
-      splitVisible: false,
-      maxPickQuantity: 0,
-      allLockInfos: [] // 鏂板锛氫繚瀛樻墍鏈夐攣瀹氫俊鎭紝鐢ㄤ簬鏉$爜鍖归厤
-    }
-  },
-  computed: {
-    unpickedCount() {
-      return this.orderInfo.unpickedCount || 0
-    },
-    unpickedQuantity() {
-      return this.orderInfo.unpickedQuantity || 0
-    }
-  },
-  methods: {
-    goBack() {
-      this.$router.back()
-    },
-
-    async loadOrderInfo() {
-      const orderId = this.$route.query.orderId
-      if (!orderId) return
-
-      try {
-        const result = await this.http.get(`api/OutboundOrder/GetById?id=${orderId}`)
-        if (result.status) {
-          this.orderInfo = result.data
-        }
-      } catch (error) {
-        this.$message.error('鍔犺浇鍑哄簱鍗曚俊鎭け璐�')
-      }
-    },
-
-    async handlePalletScan() {
-      if (!this.scanForm.palletCode) {
-        this.$message.warning('璇疯緭鍏ユ墭鐩樻潯鐮�')
-        return
-      }
-
-      try {
-        const result = await this.http.get(
-          `api/OutboundPicking/GetPalletOutboundStatus?palletCode=${this.scanForm.palletCode}`
-        )
-        if (result.status) {
-          this.currentPallet = result.data
-          await this.loadPalletLockInfo()
-          this.$message.success(`鎵樼洏 ${this.scanForm.palletCode} 璇嗗埆鎴愬姛`)
-        } else {
-          this.$message.error(result.message)
-        }
-      } catch (error) {
-        this.$message.error('鎵樼洏璇嗗埆澶辫触')
-      }
-    },
-
-    async loadPalletLockInfo() {
-      if (!this.currentPallet) return
-
-      try {
-        const result = await this.http.get(
-          `api/OutboundPicking/GetPalletLockInfos?palletCode=${this.currentPallet.palletCode}`
-        )
-        if (result.status) {
-          this.allLockInfos = result.data
-          // 榛樿閫夋嫨绗竴涓攣瀹氫俊鎭�
-          if (this.allLockInfos.length > 0) {
-            this.currentLockInfo = this.allLockInfos[0]
-            this.currentMaterialInfo = {
-              materielCode: this.currentLockInfo.materielCode,
-              materielName: this.currentLockInfo.materielName
-            }
-            this.maxPickQuantity = this.currentLockInfo.assignQuantity - this.currentLockInfo.pickedQty
-          }
-        }
-      } catch (error) {
-        console.error('鍔犺浇閿佸畾淇℃伅澶辫触:', error)
-      }
-    },
-
-    // 鏍规嵁鏉$爜鏌ユ壘瀵瑰簲鐨勯攣瀹氫俊鎭拰鐗╂枡淇℃伅
-    findLockInfoByBarcode(barcode) {
-      if (!this.allLockInfos || this.allLockInfos.length === 0) {
-        return null
-      }
-      
-      // 棣栧厛绮剧‘鍖归厤褰撳墠鏉$爜
-      let lockInfo = this.allLockInfos.find(x => x.currentBarcode === barcode)
-      if (lockInfo) {
-        return lockInfo
-      }
-      
-      // 濡傛灉娌℃湁绮剧‘鍖归厤锛屾煡鎵捐鏉$爜瀵瑰簲鐨勭墿鏂欐槸鍚﹀湪閿佸畾淇℃伅涓�
-      // 杩欓噷闇�瑕佽皟鐢ㄥ悗绔帴鍙i獙璇佹潯鐮佸搴旂殑鐗╂枡
-      return null
-    },
-
-    async handleBarcodeScan() {
-      if (!this.scanForm.barcode) {
-        this.$message.warning('璇疯緭鍏ョ墿鏂欐潯鐮�')
-        return
-      }
-
-      if (!this.currentPallet) {
-        this.$message.warning('璇峰厛鎵弿鎵樼洏鏉$爜')
-        return
-      }
-
-      if (this.scanForm.quantity <= 0) {
-        this.$message.warning('璇疯緭鍏ユ湁鏁堢殑鎷i�夋暟閲�')
-        return
-      }
-
-      try {
-        // 楠岃瘉鏉$爜骞惰幏鍙栫墿鏂欎俊鎭�
-        const materialInfo = await this.validateBarcode(this.scanForm.barcode)
-        if (!materialInfo) {
-          this.$message.error('鏃犳晥鐨勭墿鏂欐潯鐮�')
-          return
-        }
-
-        // 鏌ユ壘瀵瑰簲鐨勯攣瀹氫俊鎭�
-        const targetLockInfo = this.findLockInfoByBarcodeAndMaterial(this.scanForm.barcode, materialInfo.materielCode)
-        if (!targetLockInfo) {
-          this.$message.error('璇ョ墿鏂欐潯鐮佷笉鍦ㄥ綋鍓嶆墭鐩樼殑閿佸畾淇℃伅涓�')
-          return
-        }
-
-        // 妫�鏌ユ嫞閫夋暟閲�
-        const availableQuantity = targetLockInfo.assignQuantity - targetLockInfo.pickedQty
-        if (this.scanForm.quantity > availableQuantity) {
-          this.$message.error(`鎷i�夋暟閲忚秴杩囧彲鐢ㄦ暟閲忥紝鍓╀綑鍙嫞閫夛細${availableQuantity}`)
-          return
-        }
-
-        // 鍑嗗璇锋眰鏁版嵁
-        const request = {
-          orderDetailId: targetLockInfo.orderDetailId,
-          barcode: this.scanForm.barcode,
-          materielCode: materialInfo.materielCode, // 浼犻�掔墿鏂欑紪鐮�
-          pickQuantity: this.scanForm.quantity,
-          locationCode: this.currentPallet.locationCode,
-          palletCode: this.currentPallet.palletCode,
-          stockId: targetLockInfo.stockId,
-          outStockLockInfoId: targetLockInfo.id // 浼犻�掗攣瀹氫俊鎭疘D
-        }
-
-        const result = await this.http.post('api/OutboundPicking/ConfirmPicking', request)
-        if (result.status) {
-          this.$message.success('鎷i�夌‘璁ゆ垚鍔�')
-          
-          // 閲嶇疆琛ㄥ崟
-          this.scanForm.barcode = ''
-          this.scanForm.quantity = 1
-          this.currentMaterialInfo = null
-          
-          // 鍒锋柊鏁版嵁
-          this.loadOrderInfo()
-          this.loadPickedHistory()
-          this.loadPalletLockInfo()
-        } else {
-          this.$message.error(result.message)
-        }
-      } catch (error) {
-        this.$message.error('鎷i�夌‘璁ゅけ璐�: ' + (error.message || '鏈煡閿欒'))
-      }
-    },
-
-    // 鏍规嵁鏉$爜鍜岀墿鏂欑紪鐮佹煡鎵鹃攣瀹氫俊鎭�
-    findLockInfoByBarcodeAndMaterial(barcode, materielCode) {
-      if (!this.allLockInfos || this.allLockInfos.length === 0) {
-        return null
-      }
-      
-      // 棣栧厛灏濊瘯绮剧‘鍖归厤鏉$爜
-      let lockInfo = this.allLockInfos.find(x => 
-        x.currentBarcode === barcode && x.materielCode === materielCode
-      )
-      
-      if (lockInfo) {
-        return lockInfo
-      }
-      
-      // 濡傛灉绮剧‘鍖归厤澶辫触锛屽彧鍖归厤鐗╂枡缂栫爜锛堝厑璁镐粠鍚屼竴鐗╂枡鐨勪笉鍚屾潯鐮佹嫞閫夛級
-      lockInfo = this.allLockInfos.find(x => 
-        x.materielCode === materielCode && 
-        (x.assignQuantity - x.pickedQty) > 0
-      )
-      
-      return lockInfo
-    },
-
-    // 楠岃瘉鏉$爜骞惰幏鍙栫墿鏂欎俊鎭�
-    async validateBarcode(barcode) {
-      try {
-        const result = await this.http.get(`api/OutboundPicking/ValidateBarcode?barcode=${barcode}`)
-        if (result.status) {
-          return result.data
-        } else {
-          this.$message.error(result.message)
-          return null
-        }
-      } catch (error) {
-        this.$message.error('鏉$爜楠岃瘉澶辫触')
-        return null
-      }
-    },
-
-    async handleBackToStock() {
-      if (!this.currentPallet) return
-
-      try {
-        await this.$confirm(`纭畾灏嗘墭鐩� ${this.currentPallet.palletCode} 鍥炲簱鍚楋紵`, '鎻愮ず', {
-          type: 'warning'
-        })
-
-        const result = await this.http.post('api/BackToStock/GenerateBackToStockTask', {
-          palletCode: this.currentPallet.palletCode,
-          currentLocation: '鎷i�変綅',
-          operator: '褰撳墠鐢ㄦ埛'
-        })
-
-        if (result.status) {
-          this.$message.success('鍥炲簱浠诲姟宸茬敓鎴�')
-          this.resetCurrentPallet()
-        }
-      } catch (error) {
-        // 鐢ㄦ埛鍙栨秷
-      }
-    },
-
-    async handleDirectOutbound() {
-      if (!this.currentPallet) return
-
-      try {
-        await this.$confirm(`纭畾灏嗘墭鐩� ${this.currentPallet.palletCode} 鐩存帴鍑哄簱鍚楋紵`, '鎻愮ず', {
-          type: 'warning'
-        })
-
-        const result = await this.http.post('api/OutboundPicking/DirectOutbound', {
-          palletCode: this.currentPallet.palletCode
-        })
-
-        if (result.status) {
-          this.$message.success('鐩存帴鍑哄簱鎴愬姛')
-          this.resetCurrentPallet()
-          this.loadOrderInfo()
-        }
-      } catch (error) {
-        // 鐢ㄦ埛鍙栨秷
-      }
-    },
-
-    handleOpenSplit() {
-      if (!this.currentLockInfo) {
-        this.$message.warning('璇峰厛閫夋嫨閿佸畾淇℃伅')
-        return
-      }
-      this.splitVisible = true
-    },
-
-    handleSplitSuccess() {
-      this.$message.success('鎷嗗寘鎴愬姛')
-      this.loadPalletLockInfo()
-    },
-
-    resetCurrentPallet() {
-      this.currentPallet = null
-      this.currentLockInfo = null
-      this.currentMaterialInfo = null
-      this.allLockInfos = []
-      this.scanForm.palletCode = ''
-    },
-
-    async loadPickedHistory() {
-      const orderId = this.$route.query.orderId
-      if (!orderId) return
-
-      try {
-        const result = await this.http.get(`api/OutboundPicking/GetPickingHistory?orderId=${orderId}`)
-        if (result.status) {
-          this.pickedList = result.data
-        }
-      } catch (error) {
-        console.error('鍔犺浇鎷i�夊巻鍙插け璐�:', error)
-      }
-    },
-
-    async handleCancelPick(row) {
-      try {
-        await this.$confirm('纭畾鎾ら攢杩欐潯鎷i�夎褰曞悧锛�', '鎻愮ず', { type: 'warning' })
-        
-        const result = await this.http.post('api/OutboundPicking/CancelPicking', {
-          pickingHistoryId: row.id
-        })
-
-        if (result.status) {
-          this.$message.success('鎾ら攢鎴愬姛')
-          this.loadPickedHistory()
-          this.loadOrderInfo()
-        }
-      } catch (error) {
-        // 鐢ㄦ埛鍙栨秷
-      }
+      palletSummary: null,
+      confirmLoading: false,
+      pickedTableConfig: {
+        url: '/api/outbound/getPickingRecords',
+        query: { orderNo: this.orderNo },
+        columns: [
+          { prop: 'TaskNo', label: '浠诲姟鍙�', width: 150 },
+          { prop: 'Barcode', label: '鐗╂枡鏉$爜', width: 150 },
+          { prop: 'MaterielName', label: '鐗╂枡鍚嶇О', width: 150 },
+          { prop: 'PickQuantity', label: '鎷h揣鏁伴噺', width: 100 },
+          { prop: 'LocationCode', label: '璐т綅', width: 120 },
+          { prop: 'CreateTime', label: '鎷h揣鏃堕棿', width: 180 }
+        ]
+      },
+      // 鍑哄簱璇︽儏琛ㄦ牸閰嶇疆
+      outboundTableConfig: {
+        url: '/api/outbound/getOutboundDetails',
+        query: { orderNo: this.orderNo },
+        columns: [
+          { prop: 'OrderNo', label: '鍑哄簱鍗曞彿', width: 150 },
+          { prop: 'MaterialCode', label: '鐗╂枡缂栧彿', width: 120 },
+          { prop: 'MaterialBarcode', label: '鐗╂枡鏉$爜', width: 150 },
+          { prop: 'BatchNo', label: '鎵规鍙�', width: 120 },
+          { prop: 'AssignQuantity', label: '鍒嗛厤鍑哄簱閲�', width: 100 },
+          { prop: 'PalletCode', label: '鎵樼洏缂栧彿', width: 120 },
+          { prop: 'Unit', label: '鍗曚綅', width: 80 }
+        ]
+      },
+      orderInfo: {orderNo:''}
     }
   },
   mounted() {
-    this.loadOrderInfo()
-    this.loadPickedHistory()
+    this.loadOrderInfo();
+    this.$nextTick(() => {
+      if (this.$refs.palletInput) {
+        this.$refs.palletInput.focus()
+      }
+    })
+  },
+  methods: {
+    loadOrderInfo() {
+      const orderId = this.$route.query.orderId
+      if (!orderId) return
+
+      try {
+        this.http.get(`/api/OutboundOrder/GetById?id=${orderId}`).then(response => {debugger;
+          if (response.status) {
+            this.orderInfo = response.data
+              
+          }
+        })
+      } catch (error) {
+        ElMessage.error('鍔犺浇鍑哄簱鍗曚俊鎭け璐�')
+      }
+    },
+     goBack() {
+      this.$router.back()
+    },
+    async handlePalletScan() {
+      if (this.scanForm.palletCode) {
+        ElMessage.success(`宸叉壂鎻忔墭鐩�: ${this.scanForm.palletCode}`)
+        await this.loadPalletSummary()
+
+        this.$nextTick(() => {
+          if (this.$refs.materialInput) {
+            this.$refs.materialInput.focus()
+          }
+        })
+      }
+    },
+    async handleMaterialScan() {
+      if (!this.scanForm.palletCode) {
+        ElMessage.warning('璇峰厛鎵弿鎵樼洏鐮�')
+        this.$refs.palletInput.focus()
+        return
+      }
+
+      if (!this.scanForm.materialBarcode) {
+        ElMessage.warning('璇锋壂鎻忕墿鏂欐潯鐮�')
+        return
+      }
+
+      await this.executePickingConfirm()
+    },
+    async loadPalletSummary() {
+      if (!this.scanForm.palletCode) {
+        this.palletSummary = null
+        return
+      }
+
+      try {
+        const result = await http.get('/api/outbound/getPalletPickingSummary', {
+          params: {
+            orderNo: this.orderNo,
+            palletCode: this.scanForm.palletCode
+          }
+        })
+
+        if (result.success) {
+          // 澶勭悊缁熻淇℃伅
+          const summary = result.data
+          const assigned = summary.find(x => x.Status === '宸插垎閰�') || { TotalAssignQty: 0, TotalPickedQty: 0 }
+          const picked = summary.find(x => x.Status === '宸叉嫞閫�') || { TotalPickedQty: 0 }
+
+          this.palletSummary = {
+            unpickedCount: assigned.TotalAssignQty > 0 ? 1 : 0, // 绠�鍖栬绠�
+            unpickedTotal: assigned.TotalAssignQty - assigned.TotalPickedQty
+          }
+        }
+      } catch (error) {
+        console.error('鍔犺浇鎵樼洏缁熻澶辫触:', error)
+      }
+    },
+    async handleConfirm() {
+      if (!this.scanForm.palletCode || !this.scanForm.materialBarcode) {
+        ElMessage.warning('璇峰~鍐欏畬鏁寸殑鎵爜淇℃伅')
+        return
+      }
+
+      await this.executePickingConfirm()
+    },
+    async executePickingConfirm() {
+      this.confirmLoading = true
+
+      try {
+        // 鍏堟壘鍒板搴旂殑鍑哄簱閿佸畾淇℃伅
+        const lockInfoResult = await this.http.get('/api/outbound/getOutStockLockInfo', {
+          params: {
+            orderNo: this.orderNo,
+            palletCode: this.scanForm.palletCode,
+            materialBarcode: this.scanForm.materialBarcode
+          }
+        })
+
+        if (!lockInfoResult.success || !lockInfoResult.data || lockInfoResult.data.length === 0) {
+          ElMessage.error('鏈壘鍒板搴旂殑鍑哄簱閿佸畾淇℃伅')
+          return
+        }
+
+        const lockInfo = lockInfoResult.data[0]
+
+        const request = {
+          outStockLockId: lockInfo.Id,
+          taskNo: `TASK_${Date.now()}`,
+          palletCode: this.scanForm.palletCode,
+          materialBarcode: this.scanForm.materialBarcode,
+          locationCode: lockInfo.LocationCode
+        }
+
+        const result = await this.http.post('/api/outbound/pickingConfirm', request)
+
+        if (result.success) {
+          ElMessage.success('鍒嗘嫞纭鎴愬姛')
+          this.handleReset()
+          this.$emit('confirm')
+
+          // 鍒锋柊琛ㄦ牸
+          if (this.$refs.pickedTable) {
+            this.$refs.pickedTable.refresh()
+          }
+          
+          // 鍒锋柊鍑哄簱璇︽儏琛ㄦ牸
+          if (this.$refs.outboundTable) {
+            this.$refs.outboundTable.refresh()
+          }
+
+          // 閲嶆柊鍔犺浇鎵樼洏缁熻
+          await this.loadPalletSummary()
+        } else {
+          ElMessage.error(result.ElMessage)
+        }
+      } catch (error) {
+        ElMessage.error('鍒嗘嫞纭澶辫触')
+      } finally {
+        this.confirmLoading = false
+      }
+    },
+    handleReset() {
+      this.scanForm.materialBarcode = ''
+      this.$nextTick(() => {
+        if (this.$refs.materialInput) {
+          this.$refs.materialInput.focus()
+        }
+      })
+    }
   }
-}
+})
 </script>
 
 <style scoped>
 .picking-confirm {
-  padding: 20px;
+  display: flex;
+  flex-direction: column;
+  height: 70vh;
 }
 
-.page-header {
-  margin-bottom: 20px;
+.content-layout {
+  display: flex;
+  gap: 16px;
+  margin-bottom: 16px;
+  flex: 1;
+  min-height: 0; /* 閲嶈锛氶槻姝lex瀛愬厓绱犳孩鍑� */
 }
 
-.title {
-  font-size: 18px;
-  font-weight: bold;
+.left-section {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+}
+
+.right-section {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
 }
 
 .scan-section {
-  margin-bottom: 20px;
+  flex-shrink: 0;
+}
+
+.scan-alert {
+  margin-bottom: 16px;
+}
+
+.scan-form {
+  max-width: 500px;
+}
+
+.pallet-summary {
+  margin: 16px 0;
 }
 
 .action-buttons {
+  margin-top: 16px;
+}
+
+.outbound-details-card {
+  height: 100%;
   display: flex;
   flex-direction: column;
-  gap: 10px;
 }
 
-.action-buttons .el-button {
-  width: 100%;
+.outbound-details-card :deep(.el-card__body) {
+  flex: 1;
+  padding: 0;
 }
 
-.current-info {
-  margin-top: 15px;
-  padding: 10px;
-  background-color: #f5f7fa;
-  border-radius: 4px;
+.picked-records {
+  flex-shrink: 0;
+  height: 300px;
 }
 
-.current-info p {
-  margin: 5px 0;
-  font-size: 14px;
-}
-
-.summary-info {
-  margin-bottom: 15px;
+.picked-records :deep(.el-card__body) {
+  padding: 0;
 }
 </style>
\ No newline at end of file

--
Gitblit v1.9.3