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 |  659 +++++++++++++++++++++++++++++++----------------------------
 1 files changed, 341 insertions(+), 318 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 899297b..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,352 +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>
+            <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>
 
-            <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,
-      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
-    }
-  },
-  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.post(`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
-          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 && result.data.length > 0) {
-          this.currentLockInfo = result.data[0]
-          this.maxPickQuantity = this.currentLockInfo.assignQuantity - this.currentLockInfo.pickedQty
-        }
-      } catch (error) {
-        console.error('鍔犺浇閿佸畾淇℃伅澶辫触:', error)
-      }
-    },
-
-    async handleBarcodeScan() {
-      // 瀹炵幇鎵爜纭閫昏緫
-      if (!this.scanForm.barcode) {
-        this.$message.warning('璇疯緭鍏ョ墿鏂欐潯鐮�')
-        return
-      }
-
-      try {
-        const request = {
-          barcode: this.scanForm.barcode,
-          quantity: this.scanForm.quantity,
-          palletCode: this.currentPallet.palletCode,
-          orderId: this.orderInfo.id
-        }
-
-        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.loadPickedHistory()
-          this.loadOrderInfo()
-        } else {
-          this.$message.error(result.message)
-        }
-      } catch (error) {
-        this.$message.error('鎷i�夌‘璁ゅけ璐�')
-      }
-    },
-
-    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�変綅'
-        })
-
-        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.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 {
+  display: flex;
+  flex-direction: column;
+  height: 70vh;
 }
-</script>
\ No newline at end of file
+
+.content-layout {
+  display: flex;
+  gap: 16px;
+  margin-bottom: 16px;
+  flex: 1;
+  min-height: 0; /* 閲嶈锛氶槻姝lex瀛愬厓绱犳孩鍑� */
+}
+
+.left-section {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+}
+
+.right-section {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+}
+
+.scan-section {
+  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;
+}
+
+.outbound-details-card :deep(.el-card__body) {
+  flex: 1;
+  padding: 0;
+}
+
+.picked-records {
+  flex-shrink: 0;
+  height: 300px;
+}
+
+.picked-records :deep(.el-card__body) {
+  padding: 0;
+}
+</style>
\ No newline at end of file

--
Gitblit v1.9.3