| | |
| | | <template> |
| | | <div class="picking-container"> |
| | | <div class="picking-container" v-loading="globalLoading" element-loading-text="å¤çä¸..." |
| | | element-loading-background="rgba(255, 255, 255, 0.8)"> |
| | | <!-- é¡¶é¨è®¢åä¿¡æ¯ --> |
| | | <el-card class="order-info-card" shadow="never"> |
| | | <div class="order-header"> |
| | |
| | | <el-row :gutter="20"> |
| | | <el-col :span="8"> |
| | | <el-form-item label="æçç " prop="palletCode"> |
| | | <el-input ref="palletInput" v-model="scanForm.palletCode" placeholder="è¯·æ«ææçç " |
| | | size="large" clearable @keyup.enter="handlePalletScan"> |
| | | <el-input ref="palletInput" v-model="scanForm.palletCode" placeholder="è¯·æ«ææçç " size="large" |
| | | clearable @keyup.enter="handlePalletScan"> |
| | | <template #prefix> |
| | | <i class="el-icon-box"></i> |
| | | </template> |
| | |
| | | </el-tag> |
| | | </div> |
| | | <div class="stat-item"> |
| | | <el-tag type="success" size="medium" effect="dark"> |
| | | <el-tag :type="hasWholeOut() ? 'success' : 'warning'" size="medium" effect="dark"> |
| | | <i class="el-icon-box"></i> |
| | | æ¯å¦æ´åºï¼{{ hasWholeOut() ? 'æ¯' : 'å¦' }} |
| | | </el-tag> |
| | |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- æ´åºç¡®è®¤å¯¹è¯æ¡ --> |
| | | <el-dialog v-model="wholeOutDialogVisible" title="æ´åºæä½ç¡®è®¤" width="500px" :before-close="handleWholeOutDialogClose" |
| | | custom-class="whole-out-dialog" style="margin-right: 0px;"> |
| | | <div class="whole-out-content" v-if="wholeOutInfo"> |
| | | <!-- è¦åæç¤º --> |
| | | <el-alert title="该æçå
å«éè¦æ´åºçç©æ" type="warning" :closable="false" show-icon class="whole-out-alert"> |
| | | <template #default> |
| | | <div>æ´åºæä½å°ä¸æ¬¡æ§æ£éè¯¥ç©æçææåºåï¼è¯·ç¡®è®¤ä¿¡æ¯æ è¯¯åæ§è¡</div> |
| | | </template> |
| | | </el-alert> |
| | | |
| | | <!-- æçä¿¡æ¯ --> |
| | | <div class="info-section"> |
| | | <h4 class="section-title"> |
| | | <i class="el-icon-box"></i> |
| | | æçä¿¡æ¯ |
| | | </h4> |
| | | <div class="info-grid"> |
| | | <div class="info-item"> |
| | | <label>æçç ï¼</label> |
| | | <span class="info-value">{{ wholeOutInfo.palletCode }}</span> |
| | | </div> |
| | | <div class="info-item"> |
| | | <label>åºä½ï¼</label> |
| | | <span class="info-value">{{ wholeOutInfo.locationCode || 'æªæå®' }}</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- æ´åºç©æè¯¦æ
--> |
| | | <div class="info-section"> |
| | | <h4 class="section-title"> |
| | | <i class="el-icon-s-grid"></i> |
| | | æ´åºç©æè¯¦æ
|
| | | </h4> |
| | | <div class="info-grid"> |
| | | <div class="info-item"> |
| | | <label>ç©æç¼ç ï¼</label> |
| | | <span class="info-value">{{ wholeOutInfo.materielCode }}</span> |
| | | </div> |
| | | <div class="info-item"> |
| | | <label>ç©æåç§°ï¼</label> |
| | | <span class="info-value">{{ wholeOutInfo.materielName }}</span> |
| | | </div> |
| | | <div class="info-item"> |
| | | <label>æ¹æ¬¡å·ï¼</label> |
| | | <span class="info-value">{{ wholeOutInfo.batchNo }}</span> |
| | | </div> |
| | | <div class="info-item"> |
| | | <label>æ´åºæ°éï¼</label> |
| | | <span class="info-value highlight">{{ wholeOutInfo.assignQuantity }} {{ wholeOutInfo.unit |
| | | }}</span> |
| | | </div> |
| | | <div class="info-item"> |
| | | <label>å½ååºåï¼</label> |
| | | <span class="info-value">{{ wholeOutInfo.currentStock || wholeOutInfo.originalQuantity }} {{ |
| | | wholeOutInfo.unit }}</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- æä½æç¤º --> |
| | | <div class="operation-tip"> |
| | | <i class="el-icon-info"></i> |
| | | <span>确认æ§è¡æ´åºæä½åï¼æ¤æä½å°ä¸æ¬¡æ§æ£éè¯¥ç©æçææåºåã</span> |
| | | </div> |
| | | </div> |
| | | <template #footer> |
| | | <span class="dialog-footer"> |
| | | <el-button @click="wholeOutDialogVisible = false" size="medium">åæ¶</el-button> |
| | | <el-button type="warning" @click="executeWholeOut" :loading="executeLoading" size="medium"> |
| | | <i class="el-icon-check"></i> |
| | | 确认æ´åº |
| | | </el-button> |
| | | </span> |
| | | </template> |
| | | </el-dialog> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | |
| | | import printView from "@/extension/outbound/extend/printView.vue" |
| | | import { stationManager, STATION_STORAGE_KEY } from "@/../src/uitils/stationManager"; |
| | | export default { |
| | | components: { printView }, |
| | | name: 'OutPicking', |
| | |
| | | confirmMessage: '', |
| | | currentAction: null, |
| | | executeLoading: false, |
| | | matMixed: true |
| | | matMixed: true, |
| | | wholeOutDialogVisible: false, |
| | | wholeOutInfo: null, |
| | | globalLoading: false |
| | | } |
| | | }, |
| | | computed: { |
| | |
| | | }) |
| | | }, |
| | | |
| | | loadPalletData() { |
| | | async loadPalletData() { |
| | | if (!this.scanForm.palletCode) { |
| | | this.unpickedData = [] |
| | | return |
| | | } |
| | | |
| | | try { |
| | | this.loadUnpickedData(); |
| | | this.loadPickedData(); |
| | | this.globalLoading = true |
| | | await Promise.all([ |
| | | this.loadUnpickedData(), |
| | | this.loadPickedData() |
| | | ]); |
| | | |
| | | } catch (error) { |
| | | console.error('å è½½æçæ°æ®å¤±è´¥:', error) |
| | | this.unpickedData = [] |
| | | } finally { |
| | | this.globalLoading = false |
| | | } |
| | | }, |
| | | loadUnpickedData() { |
| | | try { |
| | | return new Promise((resolve, reject) => { |
| | | this.http.post(`/api/Outbound/QueryPickingTasks?orderNo=${this.orderNo}&palletCode=${this.scanForm.palletCode}`, {}).then(response => { |
| | | if (response.status) { |
| | | if (response.data.outStockLockInfos.length > 0) { |
| | | this.unpickedData = response.data.outStockLockInfos; |
| | | this.matMixed = response.data.isMatMixed; |
| | | this.calculateUnpickedStats() |
| | | |
| | | // æ£æ¥æ¯å¦éè¦æ´åºç¡®è®¤ |
| | | this.$nextTick(() => { |
| | | if (this.hasWholeOut()) { |
| | | this.showWholeOutConfirm() |
| | | } |
| | | }) |
| | | // èªå¨èç¦å°ç©ææ¡ç è¾å
¥æ¡ |
| | | this.$nextTick(() => { |
| | | if (this.$refs.materialInput) { |
| | |
| | | this.$message.warning('该æçæ æªæ£éä»»å¡') |
| | | this.unpickedData = [] |
| | | } |
| | | resolve() |
| | | } else { |
| | | this.$message.error(response.message || 'è·åæçæ°æ®å¤±è´¥') |
| | | this.unpickedData = [] |
| | | reject(response.message || 'è·åæçæ°æ®å¤±è´¥') |
| | | } |
| | | } |
| | | ) |
| | | |
| | | } catch (error) { |
| | | console.error('å è½½æªæ£éæ°æ®å¤±è´¥:', error) |
| | | } |
| | | }).catch(error => { |
| | | console.error('å è½½æªæ£éæ°æ®å¤±è´¥:', error) |
| | | reject(error) |
| | | }) |
| | | }) |
| | | }, |
| | | |
| | | loadPickedData() { |
| | | try { |
| | | return new Promise((resolve, reject) => { |
| | | this.http.post(`/api/Outbound/QueryPickedList?orderNo=${this.orderNo}&palletCode=${this.scanForm.palletCode}`, {}).then(response => { |
| | | if (response.status) { |
| | | if (response.data.length > 0) { |
| | |
| | | } else { |
| | | this.pickedData = [] |
| | | } |
| | | resolve() |
| | | } else { |
| | | this.$message.error(response.message || 'è·åæçæ°æ®å¤±è´¥') |
| | | this.pickedData = [] |
| | | reject(response.message || 'è·åæçæ°æ®å¤±è´¥') |
| | | } |
| | | } |
| | | ) |
| | | |
| | | } catch (error) { |
| | | console.error('å 载已æ£éæ°æ®å¤±è´¥:', error) |
| | | } |
| | | }).catch(error => { |
| | | console.error('å 载已æ£éæ°æ®å¤±è´¥:', error) |
| | | reject(error) |
| | | }) |
| | | }) |
| | | }, |
| | | |
| | | // è®¡ç®æªæ£é |
| | |
| | | if (this.scanForm.palletCode) { |
| | | // this.$message.success(`æçç : ${this.scanForm.palletCode}`) |
| | | this.loadPalletData() |
| | | |
| | | |
| | | } |
| | | }, |
| | | |
| | |
| | | } |
| | | |
| | | this.confirmLoading = true |
| | | this.globalLoading = true |
| | | |
| | | try { |
| | | this.http.post('/api/Outbound/CompleteOutboundWithBarcode', { |
| | |
| | | this.$message.error('æ£é确认失败') |
| | | } finally { |
| | | this.confirmLoading = false |
| | | this.globalLoading = false |
| | | } |
| | | }, |
| | | |
| | |
| | | |
| | | executeConfirm() { |
| | | this.executeLoading = true |
| | | this.globalLoading = true |
| | | |
| | | try { |
| | | let apiUrl = '' |
| | | let params = { |
| | | orderNo: this.orderNo, |
| | | palletCode: this.scanForm.palletCode |
| | | palletCode: this.scanForm.palletCode, |
| | | station: stationManager.getStation() |
| | | } |
| | | |
| | | console.log(params); |
| | | if (this.currentAction === 'emptyBox') { |
| | | apiUrl = '/api/Outbound/EmptyBox' |
| | | } else if (this.currentAction === 'returnToWarehouse') { |
| | |
| | | this.$message.error('æä½å¤±è´¥') |
| | | } finally { |
| | | this.executeLoading = false |
| | | this.globalLoading = false |
| | | } |
| | | }, |
| | | |
| | | handleDialogClose() { |
| | | if (!this.executeLoading) { |
| | | this.confirmDialogVisible = false |
| | | } |
| | | }, |
| | | |
| | | // æ´åºç¡®è®¤ç¸å
³æ¹æ³ |
| | | showWholeOutConfirm() { |
| | | // è·åéè¦æ´åºçç©æä¿¡æ¯ |
| | | const wholeOutItem = this.unpickedData.find(item => item.assignQuantity === item.originalQuantity); |
| | | console.log('wholeOutItem:', wholeOutItem); |
| | | if (wholeOutItem) { |
| | | this.wholeOutInfo = { |
| | | palletCode: this.scanForm.palletCode, |
| | | locationCode: wholeOutItem.locationCode, |
| | | materielCode: wholeOutItem.materielCode, |
| | | materielName: wholeOutItem.materielName, |
| | | batchNo: wholeOutItem.batchNo, |
| | | assignQuantity: wholeOutItem.assignQuantity, |
| | | currentStock: wholeOutItem.currentStock, |
| | | originalQuantity: wholeOutItem.originalQuantity, |
| | | unit: wholeOutItem.unit |
| | | }; |
| | | this.wholeOutDialogVisible = true; |
| | | } |
| | | }, |
| | | |
| | | handleWholeOutDialogClose() { |
| | | if (!this.executeLoading) { |
| | | this.wholeOutDialogVisible = false; |
| | | this.wholeOutInfo = null; |
| | | } |
| | | }, |
| | | |
| | | executeWholeOut() { |
| | | if (!this.wholeOutInfo) { |
| | | this.$message.error('æ´åºä¿¡æ¯æ æ'); |
| | | return; |
| | | } |
| | | |
| | | this.executeLoading = true; |
| | | this.globalLoading = true; |
| | | |
| | | try { |
| | | // è°ç¨æ´åºæ¥å£ï¼è¿é使ç¨ç©æç¼ç ä½ä¸ºæ¡ç |
| | | this.http.post('/api/Outbound/CompleteOutboundWithPallet', { |
| | | orderNo: this.orderNo, |
| | | palletCode: this.scanForm.palletCode, |
| | | operator: this.getUserName() |
| | | }).then(response => { |
| | | if (response.status) { |
| | | this.$message.success('æ´åºæä½æå'); |
| | | this.wholeOutDialogVisible = false; |
| | | this.wholeOutInfo = null; |
| | | this.loadPalletData(); |
| | | } else { |
| | | this.$message.error(response.message || 'æ´åºæä½å¤±è´¥'); |
| | | } |
| | | }); |
| | | } catch (error) { |
| | | console.error('æ´åºæä½å¤±è´¥:', error); |
| | | this.$message.error('æ´åºæä½å¤±è´¥'); |
| | | } finally { |
| | | this.executeLoading = false; |
| | | this.globalLoading = false; |
| | | } |
| | | }, |
| | | |
| | |
| | | }, |
| | | |
| | | refreshPickedTable() { |
| | | this.loadPickedData() |
| | | this.loadPickedData().catch(error => { |
| | | console.error('å·æ°å·²æ£éå表失败:', error) |
| | | }) |
| | | }, |
| | | |
| | | resetMaterialBarcode() { |
| | |
| | | |
| | | // æ£æ¥æ¯å¦å
嫿´åº |
| | | hasWholeOut() { |
| | | return this.unpickedData.some(item => item.assignQuantity === item.originalQuantity) && this.matMixed; |
| | | console.log('æ£æ¥æ´åºç¶æ:', this.unpickedData.some(item => item.assignQuantity === item.originalQuantity)); |
| | | return this.unpickedData.some(item => item.assignQuantity === item.originalQuantity) && !this.matMixed; |
| | | }, |
| | | |
| | | // 计ç®å©ä½åºå |
| | |
| | | } |
| | | </script> |
| | | |
| | | <style> |
| | | .el-dialog__header { |
| | | margin: 0; |
| | | } |
| | | </style> |
| | | |
| | | <style scoped> |
| | | .picking-container { |
| | | padding: 20px; |
| | | background-color: #f5f5f5; |
| | | min-height: 100vh; |
| | | position: relative; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | /* 订åä¿¡æ¯å¡ç */ |
| | |
| | | height: 18px; |
| | | line-height: 16px; |
| | | } |
| | | |
| | | /* æ´åºç¡®è®¤å¼¹çªæ ·å¼ */ |
| | | ::v-deep .whole-out-dialog { |
| | | border-radius: 8px; |
| | | } |
| | | |
| | | ::v-deep .whole-out-dialog .el-dialog__header { |
| | | background: linear-gradient(135deg, #E6A23C 0%, #d9971a 100%); |
| | | color: white; |
| | | padding: 15px 20px; |
| | | border-radius: 8px 8px 0 0; |
| | | } |
| | | |
| | | ::v-deep .whole-out-dialog .el-dialog__title { |
| | | color: white; |
| | | font-weight: bold; |
| | | font-size: 16px; |
| | | } |
| | | |
| | | ::v-deep .whole-out-dialog .el-dialog__headerbtn .el-dialog__close { |
| | | color: white; |
| | | font-size: 18px; |
| | | } |
| | | |
| | | .whole-out-content { |
| | | padding: 0 10px; |
| | | } |
| | | |
| | | .whole-out-alert { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .whole-out-alert ::v-deep .el-alert__description { |
| | | margin-top: 5px; |
| | | font-size: 14px; |
| | | color: #666; |
| | | } |
| | | |
| | | .info-section { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .section-title { |
| | | color: #303133; |
| | | font-size: 15px; |
| | | font-weight: bold; |
| | | margin-bottom: 10px; |
| | | padding-bottom: 5px; |
| | | border-bottom: 2px solid #E6A23C; |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .section-title i { |
| | | margin-right: 8px; |
| | | color: #E6A23C; |
| | | font-size: 16px; |
| | | } |
| | | |
| | | .info-grid { |
| | | display: grid; |
| | | grid-template-columns: 1fr 1fr; |
| | | gap: 12px; |
| | | } |
| | | |
| | | .info-item { |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 8px 0; |
| | | border-bottom: 1px solid #f0f0f0; |
| | | } |
| | | |
| | | .info-item label { |
| | | font-weight: 500; |
| | | color: #606266; |
| | | min-width: 80px; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .info-item .info-value { |
| | | color: #303133; |
| | | font-size: 14px; |
| | | flex: 1; |
| | | } |
| | | |
| | | .info-item .info-value.highlight { |
| | | color: #E6A23C; |
| | | font-weight: bold; |
| | | font-size: 15px; |
| | | } |
| | | |
| | | .operation-tip { |
| | | background-color: #fff7e6; |
| | | border: 1px solid #ffd591; |
| | | border-radius: 6px; |
| | | padding: 12px 15px; |
| | | margin-top: 20px; |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .operation-tip i { |
| | | color: #E6A23C; |
| | | margin-right: 8px; |
| | | font-size: 16px; |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .operation-tip span { |
| | | color: #606266; |
| | | font-size: 14px; |
| | | line-height: 1.5; |
| | | } |
| | | |
| | | /* ååºå¼è°æ´ */ |
| | | @media (max-width: 768px) { |
| | | .info-grid { |
| | | grid-template-columns: 1fr; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .info-item { |
| | | padding: 6px 0; |
| | | } |
| | | |
| | | .info-item label { |
| | | min-width: 70px; |
| | | font-size: 13px; |
| | | } |
| | | |
| | | .info-item .info-value { |
| | | font-size: 13px; |
| | | } |
| | | } |
| | | |
| | | /* Element Plus Loading é®ç½©å±æ ·å¼ä¿®å¤ */ |
| | | ::v-deep .el-loading-mask { |
| | | background-color: rgba(255, 255, 255, 0.8); |
| | | z-index: 2000; |
| | | } |
| | | |
| | | ::v-deep .el-loading-spinner { |
| | | z-index: 2001; |
| | | } |
| | | |
| | | ::v-deep .el-loading-text { |
| | | color: #409EFF; |
| | | font-weight: bold; |
| | | font-size: 14px; |
| | | } |
| | | </style> |