|
|
<template> <div class="pda-container"> <!-- 头部栏 --> <div class="header-bar"> <div class="header-left" @click="$router.back()"> <i class="el-icon-arrow-left"></i> <span>直接领料</span> </div> <div class="header-right" @click="$router.push({ path: '/' })"> 首页 </div> </div>
<!-- 扫描框 --> <div class="search-container"> <el-input clearable class="compact-input" v-model="scanCode" placeholder="请扫描材料纸质标签" prefix-icon="el-icon-search" @keyup.enter.native="handleScan" ref="scanInput" /> <div class="mode-switch"> <el-switch class="custom-switch" v-model="isRemoveMode" active-color="#ff4949" inactive-color="#13ce66"> </el-switch> <span v-if="isRemoveMode" class="switch-text">{{ '移除' }}</span> <span v-else class="switch-text2">{{ '添加' }}</span> </div> </div>
<!-- 工单物料信息 --> <div class="work-order-list" v-if="workOrderNo && componentPartNo"> <div class="work-order-card"> <div class="card-title"> <span class="title-label">工单号:{{ workOrderNo }} 行号:{{itemNo}}</span> </div>
<!-- 物料描述单独一行 --> <div class="part-desc-row"> <span class="desc-text">物料编码:{{ componentPartNo }}</span> </div> <div class="part-desc-row"> <span class="desc-text">物料名称:{{ componentPartDesc }}</span> </div>
<div class="card-details"> <div class="detail-item"> <div class="detail-label">需求数量</div> <div class="detail-value">{{ requiredQty }}</div> </div> <div class="detail-item"> <div class="detail-label">已发数量</div> <div class="detail-value">{{ issuedQty }}</div> </div> <div class="detail-item"> <div class="detail-label">本次</div> <div class="detail-value">{{ totalScannedQty }}</div> </div> </div> </div> </div> <div class="section-title"> <div class="title-left"> <i class="el-icon-circle-check"></i> <span>出库信息确认</span> </div> </div> <!-- 标签列表 --> <div class="label-list"> <div class="list-header"> <div class="col-no">NO.</div> <div class="col-label">标签条码</div> <div class="col-batch">仓库</div> <div class="col-batch">批次号</div> <div class="col-qty">数量</div> </div>
<div v-for="(label, index) in scannedLabels" :key="label.id" class="list-item"> <div class="col-no">{{ index+1 }}</div> <div class="col-label">{{ label.labelCode }}</div> <div class="col-batch">{{label.warehouseId}}</div> <div class="col-batch">{{ label.batchNo || '-' }}</div> <div class="col-qty">{{ label.quantity }}</div> </div>
<div v-if="scannedLabels.length === 0" class="empty-labels"> <p>暂无扫描标签</p> </div> </div>
<!-- 底部操作按钮 --> <div class="bottom-actions"> <button class="action-btn secondary" @click="confirmIssue" :disabled="scannedLabels.length === 0"> 确定 </button> <!-- <button class="action-btn secondary" style="margin-left: 10px;"> 打印 </button> --> <button class="action-btn secondary" style="margin-left: 10px;" @click="clearScannedLabels"> 取消 </button> </div> </div>
</template>
<script>import { scanMaterialLabel, confirmDirectIssue,} from '@/api/production/production-issue'import moment from 'moment'
export default { data() { return { scanCode: '', isRemoveMode: false, scannedLabels: [], workOrderNo: '', componentPartNo: '', componentPartDesc: '', requiredQty: 0, issuedQty: 0, itemNo: '', } }, computed: { totalScannedQty() { return this.scannedLabels.reduce((sum, l) => sum + (l.quantity || 0), 0) }, }, methods: { formatDate(date) { return date ? moment(date).format('YYYY-MM-DD') : '' }, handleScan() { if (!this.scanCode.trim()) return if (this.isRemoveMode) { this.removeLabelByCode(this.scanCode.trim()) } else { this.validateAndAddLabel(this.scanCode.trim()) } this.scanCode = '' }, validateAndAddLabel(labelCode) { const params = { scannedLabel: labelCode, workOrderNo: this.workOrderNo, componentPartNo: this.componentPartNo, site: this.$store.state.user.site, } scanMaterialLabel(params) .then(({ data }) => { if (data && data.code === 0) { const exists = this.scannedLabels.find( (item) => item.labelCode === labelCode ) if (exists) { this.$message.warning('该标签已扫描,请勿重复扫描') return } /* if (data.labelInfo.materialCode !== this.materialCode) { this.$message.error('标签物料编码与选择的材料不匹配') return } */ this.scannedLabels.push({ id: Date.now(), labelCode, componentPartNo: data.labelInfo.materialCode, quantity: data.labelInfo.availableQty, batchNo: data.labelInfo.batchNo, warehouseId: data.labelInfo.warehouseId, locationId: data.labelInfo.locationId, }) this.$message.success('扫描成功') } else { this.$message.error(data.msg || '标签验证失败') } }) .catch(() => { this.$message.error('扫描失败') }) }, removeLabelByCode(labelCode) { const index = this.scannedLabels.findIndex( (item) => item.labelCode === labelCode ) if (index !== -1) { this.scannedLabels.splice(index, 1) this.$message.success('移除成功') } else { this.$message.warning('未找到该标签') } }, clearScannedLabels() { if (this.scannedLabels.length > 0) { this.$confirm('确定清空所有已扫描的标签吗?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning', }) .then(() => { this.scannedLabels = [] this.$router.back() this.$message.success('已清空') }) .catch(() => {}) } else { this.$router.back() } }, confirmIssue() { if (this.scannedLabels.length === 0) { this.$message.warning('请先扫描材料标签') return } console.log('1231', localStorage.getItem('userName'))
const params = { site: this.$store.state.user.site, workOrderNo: this.workOrderNo, componentPartNo: this.componentPartNo, operatorName: localStorage.getItem('userName'), itemNo: this.itemNo, selectedMaterials: this.scannedLabels.map((l, i) => ({ labelCode: l.labelCode, issueQty: l.quantity, batchNo: l.batchNo, warehouseId: l.warehouseId, locationId: l.locationId, materialCode: l.materialCode, })), } confirmDirectIssue(params) .then(({ data }) => { if (data && data.code === 0) { this.$message.success('发料成功') this.$router.back() } else { this.$message.error(data.msg || '发料失败') } }) .catch(() => { this.$message.error('发料失败') }) }, initFromRoute() { this.workOrderNo = this.$route.params.workOrderNo this.componentPartNo = this.$route.params.partNo this.componentPartDesc = this.$route.params.partDesc || '' this.requiredQty = Number(this.$route.params.requiredQty || 0) this.issuedQty = Number(this.$route.params.issuedQty || 0) this.itemNo = this.$route.params.itemNo || ''
/* if (!this.workOrderNo || !this.materialCode) { this.$message.error('参数错误') this.$router.back() } */ }, }, mounted() { this.initFromRoute() this.$nextTick(() => { if (this.$refs.scanInput) { this.$refs.scanInput.focus() } }) },}</script>
<style scoped>.pda-container { width: 100vw; height: 100vh; display: flex; flex-direction: column; background: #f5f5f5;}.header-bar { display: flex; justify-content: space-between; align-items: center; padding: 8px 16px; background: #17b3a3; color: white; height: 40px; min-height: 40px;}.header-left { display: flex; align-items: center; cursor: pointer; font-size: 16px; font-weight: 500;}.header-left i { margin-right: 8px; font-size: 18px;}.header-right { cursor: pointer; font-size: 16px; font-weight: 500;}.search-container { padding: 12px 16px; background: white; display: flex; align-items: center; gap: 12px;}.search-container .el-input { width: 240px; margin-right: 12px;}.compact-input ::v-deep .el-input__inner { height: 36px; padding: 0 12px 0 35px; font-size: 14px;}.compact-input ::v-deep .el-input__prefix { left: 10px;}.compact-input ::v-deep .el-input__suffix { right: 30px;}.mode-switch { position: relative; display: inline-block;}.custom-switch { transform: scale(1.3);}.switch-text { position: absolute; left: 25%; transform: translateX(-50%); top: 50%; transform: translateY(-50%) translateX(-50%); font-size: 12px; font-weight: 500; color: #606266; white-space: nowrap; pointer-events: none; z-index: 1; top: 53%; transform: translate(-50%, -50%); font-size: 12px; font-weight: bold; color: white; pointer-events: none; z-index: 2;}
.switch-text2 { position: absolute; left: 75%; transform: translateX(-50%); top: 50%; transform: translateY(-50%) translateX(-50%); font-size: 12px; font-weight: 500; color: #606266; white-space: nowrap; pointer-events: none; z-index: 1; top: 53%; transform: translate(-50%, -50%); font-size: 12px; font-weight: bold; color: white; pointer-events: none; z-index: 2;}/* 调整 switch 尺寸以便容纳文字 */.custom-switch ::v-deep .el-switch__core { width: 60px; height: 28px;}/* 工单列表 */.work-order-list { overflow-y: auto; padding: 12px 16px;}
/* 工单卡片 */.work-order-card { background: white; border-radius: 8px; margin-bottom: 12px; padding: 16px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); cursor: pointer; transition: all 0.2s ease; border: 2px solid transparent;}
.work-order-card:hover { box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); transform: translateY(-1px);}
.work-order-card.selected { border-color: #17b3a3; background: #f0fffe;}
.work-order-card:active { transform: translateY(0);}
/* 卡片标题 */.card-title { margin-bottom: 12px;}
.title-label { font-size: 12px; color: #666; display: block; margin-bottom: 4px;}
.title-value { font-size: 16px; font-weight: bold; color: #333; margin-left: 20px;}
.section-title { display: flex; align-items: center; justify-content: space-between; padding: 6px 8px; background: white; margin: 0 16px; margin-top: 4px; border-radius: 8px 8px 0 0; border-bottom: 2px solid #17b3a3;}
/* 物料描述行 */.part-desc-row { margin-bottom: 12px; padding: 0 4px;}
.desc-text { font-size: 12px; color: #666; line-height: 1.3; word-break: break-all;}
/* 卡片详情 */.card-details { display: flex; justify-content: space-between; align-items: flex-start; gap: 4px;}
.detail-item { flex: 1; text-align: center; min-width: 50px; max-width: 70px;}
.detail-label { font-size: 11px; color: #666; margin-bottom: 4px; line-height: 1.2; margin-left: -12px;}
.detail-value { font-size: 13px; color: #333; line-height: 1.2; margin-left: -12px;}.label-list { background: white; margin: 0 16px 12px; border-radius: 8px; overflow: hidden;}.list-header { display: flex; background: #f8f9fa; padding: 12px 8px; border-bottom: 1px solid #e0e0e0; font-size: 12px; color: #666; font-weight: 500;}.list-item { display: flex; padding: 12px 8px; border-bottom: 1px solid #f0f0f0; font-size: 12px; color: #333;}.list-item:last-child { border-bottom: none;}.col-no { width: 20px; text-align: center;}.col-label { flex: 2; text-align: center;}.col-batch { flex: 1; text-align: center;}.col-qty { width: 60px; text-align: center;}.empty-labels { padding: 40px 20px; text-align: center; color: #999;}.bottom-actions { display: flex; padding: 16px; gap: 20px; background: white; margin-top: auto;}.action-btn { flex: 1; padding: 12px; border: 1px solid #17b3a3; background: white; color: #17b3a3; border-radius: 20px; font-size: 14px; cursor: pointer; transition: all 0.2s ease;}.action-btn:hover { background: #17b3a3; color: white;}.action-btn:active { transform: scale(0.98);}@media (max-width: 360px) { .header-bar { padding: 8px 12px; } .search-container { padding: 8px 12px; } .work-order-list { padding: 8px 12px; } .work-order-card { padding: 12px; } .card-details { flex-wrap: wrap; gap: 6px; } .detail-item { flex: 0 0 48%; margin-bottom: 6px; min-width: 50px; } .label-list { margin: 0 12px 8px; }}</style>
|