You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
363 lines
8.9 KiB
363 lines
8.9 KiB
<template>
|
|
<div>
|
|
<div class="status-bar">
|
|
<div class="goBack" @click="handleBack"><i class="el-icon-arrow-left"></i>上一页</div>
|
|
<div class="goBack">{{ functionTitle }}</div>
|
|
<div class="network" style="color: #fff" @click="$router.push({ path: '/' })">🏠首页</div>
|
|
</div>
|
|
<div class="input-section">
|
|
<!-- PO号输入 -->
|
|
<div v-if="processFlag === 1">
|
|
<div class="input-group">
|
|
<div class="input-group">
|
|
<label>PO号</label>
|
|
<div class="input-with-scan">
|
|
<input v-model="poNo" placeholder="请输入或扫描PO号" @keyup.enter="loadMaterials" />
|
|
</div>
|
|
</div>
|
|
<div class="input-group">
|
|
<button @click="loadMaterials" class="scan-btn">确认</button>
|
|
</div>
|
|
</div>
|
|
<div class="materials-section" v-if="materialList.length">
|
|
<div class="material-list">
|
|
<div v-for="(material, index) in materialList" :key="index" class="material-item"
|
|
:class="{ selected: selectedMaterial && selectedMaterial.partNo === material.partNo }"
|
|
@click="goToDetail(material)">
|
|
<div class="material-info">
|
|
<div class="part-no">{{ material.partNo }}</div>
|
|
<div class="part-desc">{{ material.desc }}</div>
|
|
<div class="qty-info">
|
|
需求: {{ material.qty }} | 已发: {{ material.recvQty }} | 剩余: {{ material.thisRecvQty }}
|
|
</div>
|
|
</div>
|
|
<div class="material-status">
|
|
<span v-if="material.thisRecvQty > 0" class="status-pending">待发料</span>
|
|
<span v-else class="status-complete">已完成</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- 扫描标签/发料详情 -->
|
|
<div class="scan-section" v-if="processFlag === 2">
|
|
<div class="label-info" v-if="labelInfo">
|
|
<div class="info-row"><span class="label">物料编码:</span><span class="value">{{ labelInfo.partNo }}</span></div>
|
|
<div class="info-row"><span class="label">批次号:</span><span class="value">{{ labelInfo.batchNo }}</span></div>
|
|
<div class="info-row"><span class="label">可用数量:</span><span class="value">{{ labelInfo.availableQty }}</span>
|
|
</div>
|
|
</div>
|
|
<div class="qty-input" v-if="labelInfo">
|
|
<div class="input-group">
|
|
<label>发料数量</label>
|
|
<input v-model="issueQty" type="number"
|
|
:max="Math.min(selectedMaterial.thisRecvQty, labelInfo.availableQty)" placeholder="请输入发料数量" />
|
|
</div>
|
|
<div class="input-group">
|
|
<label>备注</label>
|
|
<el-input type="textarea" v-model="remark" placeholder="可选" />
|
|
</div>
|
|
<button @click="confirmIssue" class="confirm-btn" :disabled="!issueQty">确认发料</button>
|
|
</div>
|
|
</div>
|
|
<!-- 消息提示 -->
|
|
<div class="message" v-if="message" :class="messageType">{{ message }}</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
import { getPoList } from '@/api/po/po.js'
|
|
export default {
|
|
name: 'DirectIssue',
|
|
props: {
|
|
functionTitle: {
|
|
type: String,
|
|
default: '',
|
|
},
|
|
},
|
|
data() {
|
|
return {
|
|
processFlag: 1, // 1=PO号输入, 2=物料列表, 3=扫描标签/发料详情
|
|
poNo: '',
|
|
materialList: [],
|
|
selectedMaterial: null,
|
|
scannedLabel: '',
|
|
labelInfo: null,
|
|
issueQty: null,
|
|
remark: '',
|
|
message: '',
|
|
messageType: 'info',
|
|
}
|
|
},
|
|
methods: {
|
|
handleBack() {
|
|
if (this.processFlag === 2) {
|
|
this.processFlag = 1
|
|
} else if (this.processFlag === 1) {
|
|
this.$emit('back')
|
|
}
|
|
},
|
|
async loadMaterials() {
|
|
if (!this.poNo) {
|
|
this.showMessage('请输入PO号', 'error')
|
|
return
|
|
}
|
|
// 调用API获取PO物料
|
|
try {
|
|
const { data } = await getPoList({
|
|
poNumber: this.poNo,
|
|
site: localStorage.getItem('site'),
|
|
})
|
|
if (data.code === 0 && data.rows && data.rows.length > 0) {
|
|
this.materialList = data.rows
|
|
} else {
|
|
this.showMessage(data.msg || '未找到PO物料', 'warning')
|
|
}
|
|
} catch (e) {
|
|
this.showMessage('网络错误', 'error')
|
|
}
|
|
},
|
|
goToDetail(material) {
|
|
if (material.thisRecvQty <= 0) {
|
|
this.showMessage('该物料已发料完成', 'warning')
|
|
return
|
|
}
|
|
this.selectedMaterial = material
|
|
this.scannedLabel = ''
|
|
this.labelInfo = {
|
|
partNo: this.selectedMaterial.partNo,
|
|
batchNo: 'BATCH001',
|
|
availableQty: 100,
|
|
}
|
|
this.issueQty = null
|
|
this.processFlag = 2
|
|
},
|
|
resetPO() {
|
|
this.poNo = ''
|
|
this.materialList = []
|
|
this.selectedMaterial = null
|
|
this.scannedLabel = ''
|
|
this.labelInfo = null
|
|
this.issueQty = null
|
|
this.processFlag = 1
|
|
},
|
|
parseMaterialLabel() {
|
|
// TODO: 调用解析标签API,获取labelInfo
|
|
this.labelInfo = {
|
|
partNo: this.selectedMaterial.partNo,
|
|
batchNo: 'BATCH001',
|
|
availableQty: 100,
|
|
}
|
|
this.issueQty = null
|
|
this.showMessage('标签解析成功', 'success')
|
|
},
|
|
confirmIssue() {
|
|
if (!this.issueQty || this.issueQty <= 0) {
|
|
this.showMessage('请输入有效的发料数量', 'error')
|
|
return
|
|
}
|
|
// TODO: 调用发料API
|
|
this.showMessage('发料成功', 'success')
|
|
// 刷新物料列表
|
|
this.loadMaterials()
|
|
this.selectedMaterial = null
|
|
this.scannedLabel = ''
|
|
this.labelInfo = null
|
|
this.issueQty = null
|
|
this.processFlag = 1
|
|
},
|
|
showMessage(text, type = 'info') {
|
|
this.message = text
|
|
this.messageType = type
|
|
setTimeout(() => {
|
|
this.message = ''
|
|
}, 2000)
|
|
},
|
|
},
|
|
}
|
|
</script>
|
|
<style scoped>
|
|
.input-section {
|
|
background: white;
|
|
border-radius: 8px;
|
|
padding: 15px;
|
|
margin-bottom: 15px;
|
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
}
|
|
.section-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 15px;
|
|
padding-bottom: 10px;
|
|
border-bottom: 1px solid #eee;
|
|
}
|
|
.reset-btn {
|
|
background: #17b3a3;
|
|
color: white;
|
|
border: none;
|
|
padding: 6px 12px;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
font-size: 12px;
|
|
}
|
|
.input-group {
|
|
margin-bottom: 15px;
|
|
}
|
|
.input-group label {
|
|
display: block;
|
|
margin-bottom: 5px;
|
|
font-weight: bold;
|
|
color: #333;
|
|
}
|
|
.input-with-scan {
|
|
display: flex;
|
|
gap: 10px;
|
|
}
|
|
.input-with-scan input {
|
|
flex: 1;
|
|
padding: 10px;
|
|
border: 1px solid #ddd;
|
|
border-radius: 4px;
|
|
font-size: 16px;
|
|
}
|
|
.scan-btn,
|
|
.confirm-btn {
|
|
background: #17b3a3;
|
|
color: white;
|
|
border: none;
|
|
padding: 10px 15px;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
font-size: 14px;
|
|
white-space: nowrap;
|
|
}
|
|
.scan-btn:hover,
|
|
.confirm-btn:hover {
|
|
background: #13998c;
|
|
}
|
|
.confirm-btn:disabled {
|
|
background: #6c757d;
|
|
cursor: not-allowed;
|
|
}
|
|
.material-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 10px;
|
|
}
|
|
.material-item {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 15px;
|
|
border: 1px solid #ddd;
|
|
border-radius: 6px;
|
|
cursor: pointer;
|
|
transition: all 0.3s;
|
|
}
|
|
.material-item.selected {
|
|
border-color: #007bff;
|
|
background-color: #e3f2fd;
|
|
}
|
|
.material-info {
|
|
flex: 1;
|
|
}
|
|
.part-no {
|
|
font-weight: bold;
|
|
font-size: 16px;
|
|
color: #333;
|
|
margin-bottom: 4px;
|
|
}
|
|
.part-desc {
|
|
color: #666;
|
|
font-size: 14px;
|
|
margin-bottom: 4px;
|
|
}
|
|
.qty-info {
|
|
font-size: 12px;
|
|
color: #666;
|
|
}
|
|
.material-status {
|
|
text-align: right;
|
|
}
|
|
.status-pending {
|
|
background: #ffc107;
|
|
color: #212529;
|
|
padding: 4px 8px;
|
|
border-radius: 12px;
|
|
font-size: 12px;
|
|
}
|
|
.status-complete {
|
|
background: #28a745;
|
|
color: white;
|
|
padding: 4px 8px;
|
|
border-radius: 12px;
|
|
font-size: 12px;
|
|
}
|
|
.label-info {
|
|
background: #f8f9fa;
|
|
border-radius: 6px;
|
|
padding: 15px;
|
|
margin-bottom: 15px;
|
|
}
|
|
.info-row {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
margin-bottom: 8px;
|
|
padding: 5px 0;
|
|
border-bottom: 1px solid #eee;
|
|
}
|
|
.info-row:last-child {
|
|
border-bottom: none;
|
|
margin-bottom: 0;
|
|
}
|
|
.info-row .label {
|
|
font-weight: bold;
|
|
color: #666;
|
|
}
|
|
.info-row .value {
|
|
color: #333;
|
|
}
|
|
.qty-input {
|
|
margin-top: 15px;
|
|
}
|
|
.qty-input input[type='number'] {
|
|
width: 100%;
|
|
padding: 10px;
|
|
border: 1px solid #ddd;
|
|
border-radius: 4px;
|
|
font-size: 16px;
|
|
}
|
|
.message {
|
|
margin-top: 10px;
|
|
padding: 8px 12px;
|
|
border-radius: 4px;
|
|
font-weight: bold;
|
|
}
|
|
.message.success {
|
|
background: #d4edda;
|
|
color: #155724;
|
|
border: 1px solid #c3e6cb;
|
|
}
|
|
.message.error {
|
|
background: #f8d7da;
|
|
color: #721c24;
|
|
border: 1px solid #f5c6cb;
|
|
}
|
|
.message.warning {
|
|
background: #fff3cd;
|
|
color: #856404;
|
|
border: 1px solid #ffeaa7;
|
|
}
|
|
.message.info {
|
|
background: #d1ecf1;
|
|
color: #0c5460;
|
|
border: 1px solid #bee5eb;
|
|
}
|
|
.confirm-btn {
|
|
width: 100%;
|
|
}
|
|
.scan-btn {
|
|
width: 100%;
|
|
}
|
|
</style>
|