647556386
2026-01-14 868c2b329f1afb52289212d37a4bf41990334065
重拣,代码优化
已添加1个文件
已修改10个文件
907 ■■■■■ 文件已修改
项目代码/WIDESEA_WMSClient/src/extension/outbound/extend/outOrderDetail.vue 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/router/viewGird.js 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/views/outbound/recheckOutPicking.vue 758 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_CheckService/ReCheckOrderService.cs 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_Common/StockEnum/StockStatusEmun.cs 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_DTO/CalcOut/OutboundCompleteRequestDTO.cs 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_IOutboundService/IOutboundService.cs 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_InboundService/InboundService.cs 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_InboundService/TakeStockOrderService.cs 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundService.cs 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Outbound/OutboundController.cs 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/outbound/extend/outOrderDetail.vue
@@ -17,10 +17,18 @@
              <el-link
                type="primary"
                size="small"
                v-if="isBatch === 0"
                v-if="isBatch === 0 && mainBusinessType != '30'"
                style="float: right; height: 20px"
                @click="handleOpenPicking"
                >拣选</el-link
              >
              <el-link
                type="primary"
                size="small"
                v-if="mainBusinessType === '30'"
                style="float: right; height: 20px"
                @click="handleOpenRecheckPicking"
                >重检拣选</el-link
              >
              <el-link
                type="primary"
@@ -403,6 +411,12 @@
        query: { orderId: this.row.id, orderNo: this.row.orderNo },
      });
    },
    handleOpenRecheckPicking() {
      this.$router.push({
        path: "/outbound/recheckOutPicking",
        query: { orderId: this.row.id, orderNo: this.row.orderNo },
      });
    },
    outbound() {
      if (this.selection.length === 0) {
        return this.$message.error("请选择单据明细");
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/router/viewGird.js
@@ -255,6 +255,12 @@
    path: '/Dt_MesReturnRecord',
    name: 'Dt_MesReturnRecord',
    component: () => import('@/views/basic/Dt_MesReturnRecord.vue')
  },
 {
  path: '/outbound/recheckOutPicking',
  name: 'recheckOutPicking',
  component: () => import('@/views/outbound/recheckOutPicking.vue'),
  meta: { title: '拣选确认', keepAlive: false }
  }
]
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/views/outbound/recheckOutPicking.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,758 @@
<template>
    <div class="picking-container" v-loading="globalLoading" element-loading-text="处理中..."
        element-loading-background="rgba(255, 255, 255, 0.8)" element-loading-spinner="el-icon-loading"
        element-loading-custom-class="custom-loading">
        <!-- é¡¶éƒ¨è®¢å•信息 -->
        <el-card class="order-info-card" shadow="never">
            <div class="order-header">
                <div class="order-title">
                    <i class="el-icon-document"></i>
                    <span class="order-label">订单号:</span>
                    <span class="order-value">{{ orderNo }}</span>
                </div>
                <div class="order-status">
                    <el-tag v-if="orderInfo" :type="getStatusType(orderInfo.orderStatus)" size="medium"
                        style="margin-left: 10px;">
                        {{ orderInfo.statusName || '进行中' }}
                    </el-tag>
                </div>
            </div>
        </el-card>
        <!-- æ‰«ç æ“ä½œåŒºåŸŸ -->
        <el-card class="scan-section-card" shadow="never">
            <div class="scan-section">
                <el-alert title="请先扫描托盘码,再扫描物料条码进行拣选" type="info" :closable="false" show-icon class="scan-alert">
                    <template #default>
                        <div>
                            <div>1. æ‰«ææ‰˜ç›˜ç  â†’ 2. æ‰«æç‰©æ–™æ¡ç è‡ªåŠ¨æ‹£é€‰</div>
                            <div style="margin-top: 8px; font-size: 13px; color: #666;">
                                <i class="el-icon-info" style="color: #409EFF;"></i>
                                æ‰«ææ¡ç åŽä¼šè‡ªåŠ¨è¯·æ±‚æ‹£é€‰æŽ¥å£ï¼Œæ— éœ€æ‰‹åŠ¨ç‚¹å‡»ç¡®è®¤
                            </div>
                        </div>
                    </template>
                </el-alert>
                <el-form :model="scanForm" :rules="scanRules" ref="scanFormRef" class="scan-form">
                    <el-row :gutter="20">
                        <el-col :span="12">
                            <el-form-item label="托盘码" prop="palletCode">
                                <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-input>
                            </el-form-item>
                        </el-col>
                        <el-col :span="12">
                            <el-form-item label="物料条码" prop="materialBarcode">
                                <el-input ref="materialInput" v-model="scanForm.materialBarcode" placeholder="请扫描物料条码"
                                    size="large" clearable :disabled="!scanForm.palletCode"
                                    @keyup.enter="handleMaterialScan">
                                    <template #prefix>
                                        <i class="el-icon-s-grid"></i>
                                    </template>
                                    <template #append>
                                        <el-button type="primary" @click="handleMaterialScan" :loading="pickLoading">
                                            æ‰«ææ‹£é€‰
                                        </el-button>
                                    </template>
                                </el-input>
                            </el-form-item>
                        </el-col>
                    </el-row>
                    <!-- æ“ä½œæŒ‰é’® -->
                    <el-row :gutter="20" style="margin-top: 10px;">
                        <el-col :span="24">
                            <div class="action-buttons">
                                <el-button type="success" size="large" @click="handleReturnToWarehouse"
                                    :disabled="!scanForm.palletCode" :loading="returnLoading">
                                    <i class="el-icon-refresh-left"></i>
                                    å›žåº“
                                </el-button>
                            </div>
                        </el-col>
                    </el-row>
                </el-form>
            </div>
        </el-card>
        <!-- æ‰«æéªŒè¯çš„æ¡ç åˆ—表 -->
        <div class="tables-section">
            <el-card class="table-card" shadow="never">
                <template #header>
                    <div class="card-header">
                        <span class="card-title">
                            <i class="el-icon-s-grid"></i>
                            æ‰«æé€šè¿‡éªŒè¯çš„æ¡ç åˆ—表
                        </span>
                        <div>
                            <el-badge :value="scannedList.length" class="badge-item" type="primary">
                                <el-button size="small" @click="clearScannedList" icon="el-icon-delete">
                                    æ¸…空列表
                                </el-button>
                            </el-badge>
                            <el-button size="small" @click="refreshScannedList" icon="el-icon-refresh" style="margin-left: 10px;">
                                åˆ·æ–°
                            </el-button>
                        </div>
                    </div>
                </template>
                <el-table :data="scannedList" height="500" stripe>
                    <el-table-column type="index" label="序号" width="60" align="center" />
                    <el-table-column prop="barcode" label="条码" width="200">
                        <template #default="scope">
                            <el-tag type="success">{{ scope.row.barcode }}</el-tag>
                        </template>
                    </el-table-column>
                    <el-table-column prop="scanTime" label="扫描时间" width="160">
                        <template #default="scope">
                            <el-text type="info">{{ scope.row.scanTime }}</el-text>
                        </template>
                    </el-table-column>
                    <el-table-column prop="status" label="状态" width="80" align="center">
                        <template #default="scope">
                            <el-tag :type="scope.row.status === 'success' ? 'success' : 'danger'" size="small">
                                {{ scope.row.status === 'success' ? '成功' : '失败' }}
                            </el-tag>
                        </template>
                    </el-table-column>
                    <el-table-column prop="message" label="消息" show-overflow-tooltip />
                    <el-table-column label="操作" width="80" align="center">
                        <template #default="scope">
                            <el-button type="text" size="small" @click="removeScannedItem(scope.row.barcode)" :disabled="scope.row.status === 'pending'">
                                åˆ é™¤
                            </el-button>
                        </template>
                    </el-table-column>
                </el-table>
                <div class="table-footer">
                    <el-descriptions :column="4" size="small">
                        <el-descriptions-item label="总条码数">
                            <el-text type="info">{{ scannedList.length }}</el-text>
                        </el-descriptions-item>
                        <el-descriptions-item label="成功数">
                            <el-text type="success">{{ successCount }}</el-text>
                        </el-descriptions-item>
                        <el-descriptions-item label="失败数">
                            <el-text type="danger">{{ failedCount }}</el-text>
                        </el-descriptions-item>
                        <el-descriptions-item label="重复数">
                            <el-text type="warning">{{ duplicateCount }}</el-text>
                        </el-descriptions-item>
                    </el-descriptions>
                </div>
            </el-card>
        </div>
        <!-- ç¡®è®¤å¯¹è¯æ¡† -->
        <el-dialog v-model="confirmDialogVisible" title="操作确认" width="400px" :before-close="handleDialogClose">
            <div class="confirm-content">
                <p>{{ confirmMessage }}</p>
            </div>
            <template #footer>
                <span class="dialog-footer">
                    <el-button @click="confirmDialogVisible = false">取消</el-button>
                    <el-button type="primary" @click="executeConfirm" :loading="executeLoading">
                        ç¡®å®š
                    </el-button>
                </span>
            </template>
        </el-dialog>
    </div>
</template>
<script>
import { stationManager } from "@/../src/uitils/stationManager";
import { ElLoading } from 'element-plus'
export default {
    name: 'OutPicking',
    data() {
        return {
            orderNo: '',
            orderInfo: null,
            scanForm: {
                palletCode: '',
                materialBarcode: ''
            },
            scanRules: {
                palletCode: [
                    { required: true, message: '请扫描托盘码', trigger: 'blur' }
                ]
            },
            // æ‰«æé€šè¿‡çš„æ¡ç åˆ—表
            scannedList: [],
            confirmDialogVisible: false,
            confirmMessage: '',
            currentAction: null,
            executeLoading: false,
            globalLoading: false,
            loadingInstance: null,
            pickLoading: false,
            returnLoading: false
        }
    },
    computed: {
        // è®¡ç®—成功数量
        successCount() {
            return this.scannedList.filter(item => item.status === 'success').length;
        },
        // è®¡ç®—失败数量
        failedCount() {
            return this.scannedList.filter(item => item.status === 'failed').length;
        },
        // è®¡ç®—重复扫描次数(不包括当前列表中已经存在的)
        duplicateCount() {
            const barcodes = this.scannedList.map(item => item.barcode);
            const uniqueBarcodes = [...new Set(barcodes)];
            return barcodes.length - uniqueBarcodes.length;
        }
    },
    mounted() {
        this.initPage()
    },
    methods: {
        initPage() {
            // ä»Žè·¯ç”±å‚数获取订单号
            this.orderNo = this.$route.query.orderNo || ''
            if (!this.orderNo) {
                this.$message.error('订单号不能为空')
                this.$router.back()
                return
            }
            // åŠ è½½è®¢å•ä¿¡æ¯
            this.loadOrderInfo()
            // è‡ªåŠ¨èšç„¦åˆ°æ‰˜ç›˜ç è¾“å…¥æ¡†
            this.$nextTick(() => {
                if (this.$refs.palletInput) {
                    this.$refs.palletInput.focus()
                }
            })
        },
        async loadOrderInfo() {
            try {
                this.showFullScreenLoading()
                const response = await this.http.get(`/api/Outbound/GetOrderInfo?orderNo=${this.orderNo}`)
                if (response.status) {
                    this.orderInfo = response.data
                } else {
                    this.$message.error(response.message || '加载订单信息失败')
                }
            }
            catch (error) {
                this.$message.error('加载订单信息失败')
            } finally {
                this.hideFullScreenLoading()
            }
        },
        async handlePalletScan() {
            if (this.scanForm.palletCode) {
                this.scanForm.materialBarcode = ''
            } else {
                this.$message.warning('请输入托盘码')
            }
        },
        async handleMaterialScan() {
            if (!this.scanForm.palletCode) {
                this.$message.warning('请先扫描托盘码')
                this.$refs.palletInput.focus()
                return
            }
            if (!this.scanForm.materialBarcode) {
                this.$message.warning('请扫描物料条码')
                return
            }
            // 1. æ£€æŸ¥æ˜¯å¦é‡å¤æ‰«æ
            const isDuplicate = this.scannedList.some(item => item.barcode === this.scanForm.materialBarcode);
            if (isDuplicate) {
                this.$message.warning(`条码 ${this.scanForm.materialBarcode} å·²æ‰«æè¿‡ï¼Œä¸èƒ½é‡å¤æ‰«æ`)
                this.scanForm.materialBarcode = ''
                this.$nextTick(() => {
                    if (this.$refs.materialInput) {
                        this.$refs.materialInput.focus()
                    }
                })
                return
            }
            this.pickLoading = true
            // 2. å…ˆæ·»åŠ åˆ°åˆ—è¡¨ï¼ŒçŠ¶æ€ä¸ºpending(处理中)
            const pendingItem = {
                barcode: this.scanForm.materialBarcode,
                materielCode: '',
                materielName: '',
                batchNo: '',
                quantity: 1,
                unit: '',
                scanTime: this.formatTime(new Date()),
                status: 'pending',
                message: '处理中...'
            };
            this.scannedList.unshift(pendingItem); // æ·»åŠ åˆ°åˆ—è¡¨é¡¶éƒ¨
            try {
                const response = await this.http.post('/api/Outbound/RecheckPicking', {
                    orderNo: this.orderNo,
                    barCode: this.scanForm.materialBarcode
                })
                // 3. æ›´æ–°åˆ—表中的状态
                const index = this.scannedList.findIndex(item => item.barcode === this.scanForm.materialBarcode);
                if (index !== -1) {
                    if (response.status) {
                        // æˆåŠŸ
                        this.scannedList[index] = {
                            ...this.scannedList[index],
                            ...response.data, // å‡è®¾åŽç«¯è¿”回了物料信息
                            status: 'success',
                            message: '拣选成功'
                        };
                        this.$message.success('拣选成功')
                    } else {
                        // å¤±è´¥
                        this.scannedList[index] = {
                            ...this.scannedList[index],
                            status: 'failed',
                            message: response.message || '拣选失败'
                        };
                        this.$message.error(response.message || '拣选失败')
                    }
                }
            } catch (error) {
                // æ›´æ–°å¤±è´¥çŠ¶æ€
                const index = this.scannedList.findIndex(item => item.barcode === this.scanForm.materialBarcode);
                if (index !== -1) {
                    this.scannedList[index] = {
                        ...this.scannedList[index],
                        status: 'failed',
                        message: '请求失败'
                    };
                }
                this.$message.error('拣选失败')
            } finally {
                // æ¸…空条码框
                this.scanForm.materialBarcode = ''
                this.pickLoading = false
                // é‡æ–°èšç„¦åˆ°ç‰©æ–™æ¡ç è¾“入框,方便继续扫描
                this.$nextTick(() => {
                    if (this.$refs.materialInput) {
                        this.$refs.materialInput.focus()
                    }
                })
            }
        },
        handleReturnToWarehouse() {
            if (!this.scanForm.palletCode) {
                this.$message.warning('请先扫描托盘码')
                return
            }
            this.confirmMessage = `确定要将托盘 ${this.scanForm.palletCode} å›žåº“吗?`
            this.currentAction = 'returnToWarehouse'
            this.confirmDialogVisible = true
        },
        async executeConfirm() {
            if (this.currentAction === 'returnToWarehouse') {
                await this.executeReturnToWarehouse()
            }
        },
        async executeReturnToWarehouse() {
            this.executeLoading = true
            this.returnLoading = true
            try {
                const response = await this.http.post('/api/Outbound/ReturnToWarehouse', {
                    orderNo: this.orderNo,
                    palletCode: this.scanForm.palletCode,
                    station: stationManager.getStation()
                })
                if (response.status) {
                    this.$message.success('回库成功')
                    this.confirmDialogVisible = false
                    this.resetForm()
                } else {
                    this.$message.error(response.message || '回库失败')
                }
            } catch (error) {
                this.$message.error('回库失败')
            } finally {
                this.executeLoading = false
                this.returnLoading = false
            }
        },
        handleDialogClose() {
            if (!this.executeLoading) {
                this.confirmDialogVisible = false
            }
        },
        // åˆ·æ–°æ‰«æåˆ—表(主要是为了重新获取后端状态)
        refreshScannedList() {
            // å¯ä»¥åœ¨è¿™é‡Œé‡æ–°éªŒè¯åˆ—表中条码的状态
            this.$message.info('列表已刷新')
        },
        // æ¸…空扫描列表
        clearScannedList() {
            this.$confirm('确定要清空扫描列表吗?', '提示', {
                type: 'warning'
            }).then(() => {
                this.scannedList = []
                this.$message.success('列表已清空')
            }).catch(() => {
                // ç”¨æˆ·å–消
            })
        },
        // åˆ é™¤å•个扫描记录
        removeScannedItem(barcode) {
            this.scannedList = this.scannedList.filter(item => item.barcode !== barcode)
            this.$message.success('已删除')
        },
        resetForm() {
            this.scanForm.palletCode = ''
            this.scanForm.materialBarcode = ''
            this.$nextTick(() => {
                if (this.$refs.palletInput) {
                    this.$refs.palletInput.focus()
                }
            })
        },
        // æ ¼å¼åŒ–æ—¶é—´
        formatTime(date) {
            const year = date.getFullYear()
            const month = String(date.getMonth() + 1).padStart(2, '0')
            const day = String(date.getDate()).padStart(2, '0')
            const hours = String(date.getHours()).padStart(2, '0')
            const minutes = String(date.getMinutes()).padStart(2, '0')
            const seconds = String(date.getSeconds()).padStart(2, '0')
            return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
        },
        getStatusType(status) {
            const statusMap = {
                0: 'info',    // å¾…处理
                1: 'warning', // è¿›è¡Œä¸­
                20: 'primary', // æ‹£é€‰ä¸­
                30: 'success', // å·²å®Œæˆ
                40: 'danger'   // å¼‚常
            }
            return statusMap[status] || 'info'
        },
        // æ˜¾ç¤ºå…¨å±é®ç½©å±‚
        showFullScreenLoading() {
            if (this.loadingInstance) {
                this.loadingInstance.close()
            }
            this.loadingInstance = ElLoading.service({
                lock: true,
                text: '处理中...',
                background: 'rgba(0, 0, 0, 0.7)',
                customClass: 'custom-full-loading'
            })
        },
        // éšè—å…¨å±é®ç½©å±‚
        hideFullScreenLoading() {
            if (this.loadingInstance) {
                this.loadingInstance.close()
                this.loadingInstance = null
            }
        }
    }
}
</script>
<style scoped>
.picking-container {
    padding: 20px;
    background-color: #f5f5f5;
    min-height: 100vh;
    position: relative;
    overflow: hidden;
}
/* è®¢å•信息卡片 */
.order-info-card {
    margin-bottom: 20px;
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    color: white;
}
.order-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
}
.order-title {
    display: flex;
    align-items: center;
    font-size: 18px;
    font-weight: bold;
}
.order-title i {
    margin-right: 10px;
    font-size: 24px;
}
.order-label {
    margin-left: 10px;
    opacity: 0.9;
}
.order-value {
    font-size: 20px;
    font-weight: bold;
    letter-spacing: 1px;
}
/* æ‰«ç åŒºåŸŸ */
.scan-section-card {
    margin-bottom: 20px;
}
.scan-section {
    padding: 10px 0;
}
.scan-alert {
    margin-bottom: 20px;
}
.scan-form {
    margin-top: 20px;
}
.action-buttons {
    display: flex;
    gap: 8px;
    justify-content: center;
}
.action-buttons .el-button {
    flex: 0 0 auto;
    width: 150px;
    height: 40px;
    font-weight: bold;
    border-radius: 6px;
}
/* è¡¨æ ¼åŒºåŸŸ */
.tables-section {
    margin-top: 20px;
}
.table-card {
    height: 600px;
    display: flex;
    flex-direction: column;
}
.card-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
}
.card-title {
    display: flex;
    align-items: center;
    font-weight: bold;
    font-size: 16px;
}
.card-title i {
    margin-right: 8px;
    color: #409EFF;
}
.badge-item {
    margin-left: 10px;
}
.table-footer {
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px solid #ebeef5;
}
/* è¡¨æ ¼è¡Œæ ·å¼ */
.el-table tbody tr:hover>td {
    background-color: #f0f9ff !important;
}
.el-table tbody tr.current-row>td {
    background-color: #e1f3ff !important;
}
/* å¯¹è¯æ¡†æ ·å¼ */
.confirm-content {
    padding: 20px 0;
    text-align: center;
}
.confirm-content p {
    font-size: 16px;
    color: #606266;
}
.dialog-footer {
    text-align: center;
}
/* æè¿°åˆ—表样式 */
::v-deep .el-descriptions__label {
    font-weight: bold;
    color: #909399;
}
::v-deep .el-descriptions__content {
    color: #606266;
}
/* Element Plus Loading é®ç½©å±‚样式修复 */
::v-deep .custom-loading {
    background-color: rgba(0, 0, 0, 0.7) !important;
    z-index: 9999 !important;
}
::v-deep .custom-loading .el-loading-mask {
    background-color: rgba(0, 0, 0, 0.7) !important;
}
::v-deep .custom-loading .el-loading-spinner {
    z-index: 10000 !important;
}
::v-deep .custom-loading .el-loading-text {
    color: #ffffff !important;
    font-weight: bold !important;
    font-size: 16px !important;
}
/* å…¨å±Loading自定义样式 */
::v-deep .custom-full-loading {
    z-index: 999999 !important;
}
::v-deep .custom-full-loading .el-loading-mask {
    z-index: 999999 !important;
}
::v-deep .custom-full-loading .el-loading-spinner {
    z-index: 1000000 !important;
}
::v-deep .custom-full-loading .el-loading-text {
    color: #ffffff !important;
    font-weight: bold !important;
    font-size: 18px !important;
}
/* ç¡®ä¿å¯¹è¯æ¡†ä¸ä¼šé®ç›–loading */
::v-deep .el-dialog {
    z-index: 2000 !important;
}
::v-deep .el-dialog__wrapper {
    z-index: 2000 !important;
}
/* ç¡®ä¿å®¹å™¨ç›¸å¯¹å®šä½ */
.picking-container {
    position: relative !important;
    min-height: 100vh;
}
/* è¾“入框附加按钮样式 */
::v-deep .el-input-group__append {
    background-color: #409EFF !important;
    border-color: #409EFF !important;
}
::v-deep .el-input-group__append .el-button {
    color: white !important;
    background-color: transparent !important;
    border: none !important;
}
/* è¡¨æ ¼æ ·å¼è°ƒæ•´ */
::v-deep .el-table th {
    background-color: #fafafa;
    font-weight: 600;
    color: #303133;
}
::v-deep .el-table .el-text {
    font-weight: 500;
}
/* æ ‡ç­¾æ ·å¼å¢žå¼º */
::v-deep .el-tag--small {
    font-weight: 500;
}
/* æç¤ºä¿¡æ¯æ ·å¼å¢žå¼º */
.scan-alert ::v-deep .el-alert__content {
    width: 100%;
}
.scan-alert ::v-deep .el-alert__description {
    margin-top: 8px;
}
/* Element UI ç»„件样式覆盖 */
::v-deep .el-card__header {
    padding: 18px 20px;
    border-bottom: 1px solid #ebeef5;
}
::v-deep .el-input__inner {
    border-radius: 6px;
}
::v-deep .el-button--primary {
    background: linear-gradient(135deg, #409EFF 0%, #3a8ee6 100%);
    border: none;
}
::v-deep .el-button--success {
    background: linear-gradient(135deg, #67C23A 0%, #5daf34 100%);
    border: none;
}
/* å›¾æ ‡æ ·å¼ */
.el-icon-document,
.el-icon-box,
.el-icon-s-grid,
.el-icon-refresh-left,
.el-icon-time,
.el-icon-circle-check {
    font-size: 18px;
}
</style>
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_CheckService/ReCheckOrderService.cs
@@ -116,10 +116,10 @@
                {
                    isupdate = true;    
                }
                if (model.Result != 0 && recheckOrder.SignSeq != model.SignSeq)
                if (model.Result == 1 && recheckOrder.SignSeq != model.SignSeq)
                {
                    var stockDetailIds = BaseDal.Db.Queryable<Dt_StockInfoDetail>()
                    .Where(x => x.MaterielCode == model.MaterielCode && x.BatchNo == model.BatchNo && x.ValidDate != null)
                    .Where(x => x.MaterielCode == model.MaterielCode && x.BatchNo == model.BatchNo && x.ValidDate != null && x.Status == (int)StockStatusEmun.手动冻结)
                    .Select(x => x.Id)
                    .ToList();
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_Common/StockEnum/StockStatusEmun.cs
@@ -113,6 +113,9 @@
        [Description("手动解锁")]
        æ‰‹åŠ¨è§£é” = 66,
        [Description("重检中")]
        é‡æ£€ä¸­ = 77,
        [Description("过期")]
        è¿‡æœŸ =98,
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_DTO/CalcOut/OutboundCompleteRequestDTO.cs
@@ -58,4 +58,22 @@
        /// </summary>
        public string Operator { get; set; }
    }
    /// <summary>
    /// é‡æ£€æ‹£é€‰
    /// </summary>
    public class RecheckPickingDTO
    {
        /// <summary>
        /// å‡ºåº“单编号
        /// </summary>
        [Required(ErrorMessage = "出库单编号不能为空")]
        public string orderNo { get; set; }
        /// <summary>
        /// æ¡ç 
        /// </summary>
        [Required(ErrorMessage = "条码不能为空")]
        public string barCode { get; set; }
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_IOutboundService/IOutboundService.cs
@@ -61,5 +61,12 @@
        public void PerformFullOutboundOperation(Dt_StockInfoDetail stockDetail, Dt_StockInfo stockInfo,
            decimal actualOutboundQuantity, OutboundCompleteRequestDTO request, decimal beforeQuantity, int taskNum);
        /// <summary>
        /// é‡æ£€æ‹£é€‰
        /// </summary>
        /// <param name="barCode"></param>
        /// <param name="orderNo"></param>
        /// <returns></returns>
        WebResponseContent RecheckPicking(RecheckPickingDTO pickingDTO);
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_InboundService/InboundService.cs
@@ -779,16 +779,19 @@
                {
                    return content.Error($"该托盘组盘仓库为{stockInfo.Details.FirstOrDefault()?.WarehouseCode}与当前仓库{palletDto.WarehouseType}不一致,不允许组盘");
                }
                Dt_StockInfoDetail stockInfoDetail = _stockInfoRepository.Db.Queryable<Dt_StockInfoDetail>().Where(x => x.Barcode == palletDto.Barcode && x.Status == StockStatusEmun.手动冻结.ObjToInt()).First();
                Dt_StockInfoDetail stockInfoDetail = _stockInfoRepository.Db.Queryable<Dt_StockInfoDetail>().Where(x => x.Barcode == palletDto.Barcode && x.Status == StockStatusEmun.重检中.ObjToInt()).First();
                if(stockInfoDetail == null)
                {
                    return content.Error("未找到库存中重检冻结的条码");
                }
                Dt_ReCheckOrder reCheckOrder = _stockInfoRepository.Db.Queryable<Dt_ReCheckOrder>().Where(x => x.MaterielCode == stockInfoDetail.MaterielCode && x.BatchNo == stockInfoDetail.BatchNo && x.Result == 0).First();
                if(reCheckOrder != null)
                Dt_ReCheckOrder reCheckOrder = _stockInfoRepository.Db.Queryable<Dt_ReCheckOrder>().Where(x => x.MaterielCode == stockInfoDetail.MaterielCode && x.BatchNo == stockInfoDetail.BatchNo && x.OrderNo == stockInfoDetail.OrderNo && (x.Result == 1 || x.Result == 2 )).First();
                if(reCheckOrder == null)
                {
                    return content.Error($"该重检条码的批次在重检单中未拿到重检结果,请检测重检单{reCheckOrder.OrderNo}状态");
                }
                int newStatus = reCheckOrder.Result == 1 ? StockStatusEmun.入库完成.ObjToInt(): StockStatusEmun.手动冻结.ObjToInt();
                stockInfo.Details.Add(new Dt_StockInfoDetail
                {
                    StockId = stockInfo == null ? 0 : stockInfo.Id,
@@ -803,7 +806,7 @@
                    BarcodeQty = stockInfoDetail.BarcodeQty,
                    BarcodeUnit = stockInfoDetail.BarcodeUnit,
                    FactoryArea = stockInfoDetail.FactoryArea,
                    Status = stockInfoDetail.Status,
                    Status = newStatus,
                    OrderNo = stockInfoDetail.OrderNo,
                    BusinessType = "30",
                    ValidDate = stockInfoDetail.ValidDate
@@ -819,6 +822,13 @@
                _stockService.StockInfoService.AddMaterielGroup(stockInfo);
                _unitOfWorkManage.CommitTran();
                Dt_StockInfo oldStockInfo = _stockInfoRepository.Db.Queryable<Dt_StockInfo>().Where(x => x.Id == stockInfoDetail.StockId).Includes(x=>x.Details).First();
                if (oldStockInfo.Details.Count <= 0)
                {
                    oldStockInfo.PalletType = (int)PalletTypeEnum.Empty;
                }
                _stockInfoRepository.UpdateData(oldStockInfo);
                Dt_StockInfo? NewstockInfo = await _stockInfoRepository.Db.Queryable<Dt_StockInfo>().Includes(x => x.Details).Where(x => x.PalletCode == palletDto.PalletCode).FirstAsync();
                return WebResponseContent.Instance.OK(data: NewstockInfo.Details.OrderByDescending(x => x.Id));
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_InboundService/TakeStockOrderService.cs
@@ -260,6 +260,11 @@
                    PalletType = stock.PalletType,
                    WarehouseId = stock.WarehouseId
                };
                if (stock.Details.Count <= 0)
                {
                    stock.PalletType = (int)PalletTypeEnum.Empty;
                    newTask.TaskType = TaskTypeEnum.InEmpty.ObjToInt();
                }
                _unitOfWorkManage.BeginTran();
                _stockInfoRepository.UpdateData(stock);
                _takeStockOrder.UpdateData(takeStockOrder);
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundService.cs
@@ -28,6 +28,7 @@
using WIDESEA_IRecordService;
using WIDESEA_IStockService;
using WIDESEA_Model.Models;
using WIDESEA_Model.Models.Check;
using static HslCommunication.Profinet.Knx.KnxCode;
namespace WIDESEA_OutboundService
@@ -176,7 +177,7 @@
                    foreach (var detail in materielCalc.Details)
                    {
                        if (remainingToLock <= 0) break;
                        decimal maxLockableQty = detail.OrderQuantity - detail.OverOutQuantity - detail.LockQuantity;
                        decimal maxLockableQty = detail.OrderQuantity - detail.OverOutQuantity;
                        if (maxLockableQty <= 0) continue;
                        decimal currentLockQty = Math.Min(remainingToLock, maxLockableQty);
                        detail.LockQuantity += currentLockQty;
@@ -599,7 +600,7 @@
            List<int> stockIds = stockDetailList.GroupBy(x => x.StockId).Select(x => x.Key).ToList();
            List<Dt_StockInfo> stockInfos = _stockInfoRepository.QueryData(x =>
                stockIds.Contains(x.Id) && (x.StockStatus == StockStatusEmun.入库完成.ObjToInt() || x.StockStatus == StockStatusEmun.出库锁定.ObjToInt())
                stockIds.Contains(x.Id) && (x.StockStatus == StockStatusEmun.入库完成.ObjToInt())
                && !string.IsNullOrEmpty(x.LocationCode) && locationCodes.Contains(x.LocationCode));
            foreach (var stockInfo in stockInfos)
@@ -2032,6 +2033,67 @@
            }
        }
        public WebResponseContent RecheckPicking(RecheckPickingDTO pickingDTO)
        {
            try
            {
                Dt_ReCheckOrder reCheckOrder = _outboundRepository.Db.Queryable<Dt_ReCheckOrder>().Where(x => x.OrderNo == pickingDTO.orderNo && x.Result == 0).First();
                if(reCheckOrder == null)
                {
                    return WebResponseContent.Instance.Error($"未找到该待重拣的单据{pickingDTO.orderNo}");
                }
                Dt_StockInfoDetail stockInfoDetail = _stockDetailRepository.QueryFirst(x=>x.Barcode == pickingDTO.barCode && x.Status == StockStatusEmun.手动冻结.ObjToInt());
                if(stockInfoDetail == null)
                {
                    return WebResponseContent.Instance.Error($"未在库存中找到该冻结/隔离条码 {pickingDTO.barCode}");
                }
                if (stockInfoDetail.MaterielCode != reCheckOrder.MaterielCode || stockInfoDetail.BatchNo != reCheckOrder.BatchNo)
                {
                    return WebResponseContent.Instance.Error("该条码的物料编码和批次和该重检单不符");
                }
                stockInfoDetail.OrderNo = pickingDTO.orderNo;
                stockInfoDetail.Status = StockStatusEmun.重检中.ObjToInt();
                var currentRemark = _outboundRepository.Db.Queryable<Dt_OutboundOrder>()
                .Where(x => x.OrderNo == pickingDTO.orderNo)
                .Select(x => x.Remark)
                .First();
                string newRemark;
                if (string.IsNullOrWhiteSpace(currentRemark))
                {
                    newRemark = pickingDTO.barCode;
                }
                else
                {
                    var existingCodes = currentRemark.Split(',', StringSplitOptions.RemoveEmptyEntries)
                        .Select(s => s.Trim())
                        .ToList();
                    if (!existingCodes.Contains(pickingDTO.barCode))
                    {
                        existingCodes.Add(pickingDTO.barCode);
                        newRemark = string.Join(",", existingCodes);
                    }
                    else
                    {
                        newRemark = currentRemark;
                    }
                }
                _outboundRepository.Db.Updateable<Dt_OutboundOrder>()
                    .SetColumns(x => x.Remark == newRemark)
                    .SetColumns(x=>x.OrderStatus == (int)OutOrderStatusEnum.出库完成)
                    .Where(x => x.OrderNo == pickingDTO.orderNo)
                    .ExecuteCommand();
                _stockDetailRepository.UpdateData(stockInfoDetail);
                return WebResponseContent.Instance.OK();
            }
            catch(Exception ex)
            {
                return WebResponseContent.Instance.Error(ex.Message);
            }
        }
        #endregion
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Outbound/OutboundController.cs
@@ -114,5 +114,11 @@
        {
            return await _outboundService.ReturnToWarehouse(returnToWarehouse.palletCode, returnToWarehouse.orderNo, returnToWarehouse.station);
        }
        [HttpPost, Route("RecheckPicking"), AllowAnonymous]
        public WebResponseContent RecheckPicking([FromBody] RecheckPickingDTO pickingDTO)
        {
            return _outboundService.RecheckPicking(pickingDTO);
        }
    }
}