|
|
|
@ -21,39 +21,38 @@ |
|
|
|
ref="searchInput" |
|
|
|
/> |
|
|
|
</div> |
|
|
|
<!-- 工单信息卡片列表 --> |
|
|
|
<div class="work-order-list" v-if="outboundList.length > 0"> |
|
|
|
<!-- 工单信息卡片列表(对齐申请单退料样式/逻辑) --> |
|
|
|
<div class="work-order-list" v-if="displayOutboundList.length > 0 "> |
|
|
|
<div |
|
|
|
v-for="(workOrder, index) in outboundList" |
|
|
|
:key="index" |
|
|
|
class="work-order-card" |
|
|
|
v-for="workOrder in displayOutboundList" |
|
|
|
:key="workOrder.orderNo || workOrder.soorderNo" |
|
|
|
:class="['work-order-card', { selected: selectedWorkOrder && isSameWorkOrder(selectedWorkOrder, workOrder) }]" |
|
|
|
@click="selectWorkOrder(workOrder)" |
|
|
|
> |
|
|
|
<div class="card-title"> |
|
|
|
<span class="title-label">工单号:{{ workOrder.orderNo }}</span> |
|
|
|
|
|
|
|
<span class="title-label">申请单号:{{ currentNotifyNo }}</span> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- 工单号单独一行 --> |
|
|
|
<div class="part-desc-row"> |
|
|
|
<span class="title-value">{{ workOrder.partNo }}</span> |
|
|
|
<span class="desc-text">工单号:{{ workOrder.orderNo || workOrder.soorderNo }}</span> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- 物料描述单独一行 --> |
|
|
|
<div class="part-desc-row"> |
|
|
|
<span class="desc-text">{{ workOrder.partDesc }}</span> |
|
|
|
<span class="desc-text">物料号:{{ workOrder.fgpartNo }}</span> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div class="card-details"> |
|
|
|
<div class="detail-item"> |
|
|
|
<div class="detail-label">计划数量</div> |
|
|
|
<div class="detail-value">{{ workOrder.lotSize }}</div> |
|
|
|
<div class="detail-label">需求日期</div> |
|
|
|
<div class="detail-value">{{ workOrder.needDate }}</div> |
|
|
|
</div> |
|
|
|
<div class="detail-item"> |
|
|
|
<div class="detail-label">状态</div> |
|
|
|
<div class="detail-value">{{ workOrder.status }}</div> |
|
|
|
<div class="detail-value">{{ workOrder.status || '待发料' }}</div> |
|
|
|
</div> |
|
|
|
<div class="detail-item"> |
|
|
|
<div class="detail-label">单位</div> |
|
|
|
<div class="detail-value">{{ workOrder.uom || "个" }}</div> |
|
|
|
<div class="detail-label">行号</div> |
|
|
|
<div class="detail-value">{{ workOrder.itemNo }}</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
@ -67,29 +66,26 @@ |
|
|
|
v-for="(material, index) in materialList" |
|
|
|
:key="index" |
|
|
|
class="material-card" |
|
|
|
@click="openIssueList(material)" |
|
|
|
@click="goToPickingPage(material)" |
|
|
|
> |
|
|
|
<div class="card-title"> |
|
|
|
<span class="title-label" |
|
|
|
>物料编码:{{ material.componentPartNo }} 行号:{{ |
|
|
|
material.lineNo || index + 1 |
|
|
|
}}</span |
|
|
|
> |
|
|
|
>物料编码:{{ material.partNo }} </span> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- 物料描述单独一行 --> |
|
|
|
<div class="part-desc-row"> |
|
|
|
<span class="desc-text">{{ material.componentPartDesc }}</span> |
|
|
|
<span class="desc-text">{{ material.partDesc || material.componentPartDesc }}</span> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div class="card-details"> |
|
|
|
<div class="detail-item"> |
|
|
|
<div class="detail-label">需求数量</div> |
|
|
|
<div class="detail-value">{{ material.qtyRequired }}</div> |
|
|
|
<div class="detail-value">{{ material.requestQty }}</div> |
|
|
|
</div> |
|
|
|
<div class="detail-item"> |
|
|
|
<div class="detail-label">已发数量</div> |
|
|
|
<div class="detail-value">{{ material.qtyIssued || 0 }}</div> |
|
|
|
<div class="detail-label">申请物料行号</div> |
|
|
|
<div class="detail-value">{{ material.bomItemNo }}</div> |
|
|
|
</div> |
|
|
|
<div class="detail-item"> |
|
|
|
<div class="detail-label">单位</div> |
|
|
|
@ -99,58 +95,18 @@ |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- 出库单列表 --> |
|
|
|
<div class="content-area"> |
|
|
|
<div |
|
|
|
v-for="(item, index) in outboundList" |
|
|
|
:key="index" |
|
|
|
class="outbound-card" |
|
|
|
@click="goToPickingPage(item)" |
|
|
|
> |
|
|
|
<div class="card-title"> |
|
|
|
<span class="title-label">出库单号</span> |
|
|
|
<span class="title-value">{{ item.notifyNo }}</span> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div class="card-details"> |
|
|
|
<div class="detail-item"> |
|
|
|
<div class="detail-label">关联单号</div> |
|
|
|
<div class="detail-value">{{ item.workOrderNo }}</div> |
|
|
|
</div> |
|
|
|
<div class="detail-item"> |
|
|
|
<div class="detail-label">标签张数</div> |
|
|
|
<div class="detail-value"> |
|
|
|
<span class="qualified">{{ item.pickedLabels }}</span><span class="total">{{ item.totalLabels }}</span> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<div class="detail-item"> |
|
|
|
<div class="detail-label">物料总数</div> |
|
|
|
<div class="detail-value"> |
|
|
|
<span class="qualified">{{ item.requestQty }}</span><span class="total">{{ item.remainQty }}</span> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- 空状态 --> |
|
|
|
<div v-if="outboundList.length === 0 && !loading" class="empty-state"> |
|
|
|
<i class="el-icon-box"></i> |
|
|
|
<p>暂无待领料出库单</p> |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- 加载状态 --> |
|
|
|
<div v-if="loading" class="loading-state"> |
|
|
|
<i class="el-icon-loading"></i> |
|
|
|
<p>加载中...</p> |
|
|
|
</div> |
|
|
|
<!-- 加载状态 --> |
|
|
|
<div v-if="loading" class="loading-state"> |
|
|
|
<i class="el-icon-loading"></i> |
|
|
|
<p>加载中...</p> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</template> |
|
|
|
|
|
|
|
<script> |
|
|
|
import { |
|
|
|
getIssureNotifyByNo, |
|
|
|
getIssueNotifyHeaderInfo |
|
|
|
getIssueNotifyHeaderInfo, |
|
|
|
getRequestMaterials |
|
|
|
} from '@/api/production/production-issue' |
|
|
|
import { getCurrentWarehouse } from '@/utils' |
|
|
|
import moment from 'moment'; |
|
|
|
@ -159,74 +115,61 @@ export default { |
|
|
|
data() { |
|
|
|
return { |
|
|
|
searchCode: '', |
|
|
|
currentNotifyNo: '', |
|
|
|
outboundList: [], |
|
|
|
loading: false |
|
|
|
loading: false, |
|
|
|
selectedWorkOrder: null, |
|
|
|
materialList:[], |
|
|
|
showOnlySelected: false |
|
|
|
}; |
|
|
|
}, |
|
|
|
computed: { |
|
|
|
displayOutboundList() { |
|
|
|
if (this.showOnlySelected && this.selectedWorkOrder) { |
|
|
|
return [this.selectedWorkOrder] |
|
|
|
} |
|
|
|
return this.outboundList |
|
|
|
} |
|
|
|
}, |
|
|
|
methods: { |
|
|
|
formatDate(date) { |
|
|
|
return date ? moment(date).format('YYYY-MM-DD') : ''; |
|
|
|
}, |
|
|
|
// 处理搜索 |
|
|
|
// 处理搜索(扫描领料申请单号) |
|
|
|
handleSearch() { |
|
|
|
if (this.searchCode.trim()) { |
|
|
|
this.searchOutboundList(this.searchCode.trim()); |
|
|
|
} else { |
|
|
|
this.loadOutboundList(); |
|
|
|
const code = this.searchCode.trim() |
|
|
|
if (!code) { |
|
|
|
this.$message.warning('请扫描领料申请单号') |
|
|
|
return |
|
|
|
} |
|
|
|
this.searchOutboundList(code) |
|
|
|
}, |
|
|
|
|
|
|
|
// 加载生产出库单列表 |
|
|
|
// 按申请单查询工单列表 |
|
|
|
loadOutboundList() { |
|
|
|
/* const currentWarehouse = getCurrentWarehouse(); |
|
|
|
if (!currentWarehouse) { |
|
|
|
this.$message.error('请先选择仓库'); |
|
|
|
return; |
|
|
|
} */ |
|
|
|
|
|
|
|
this.loading = true; |
|
|
|
const params = { |
|
|
|
site: localStorage.getItem('site'), |
|
|
|
status: '待出库', |
|
|
|
} |
|
|
|
console.log('params', params); |
|
|
|
|
|
|
|
getIssueNotifyHeaderInfo(params).then(({ data }) => { |
|
|
|
this.loading = false; |
|
|
|
if (data && data.code === 0) { |
|
|
|
this.outboundList = data.list || []; |
|
|
|
} else { |
|
|
|
this.$message.error(data.msg || '获取数据失败'); |
|
|
|
} |
|
|
|
}).catch(error => { |
|
|
|
this.loading = false; |
|
|
|
console.error('获取生产出库单列表失败:', error); |
|
|
|
this.$message.error('获取数据失败'); |
|
|
|
}); |
|
|
|
// 保留空实现,当前页面以扫描查询为主 |
|
|
|
this.outboundList = [] |
|
|
|
}, |
|
|
|
|
|
|
|
// 搜索特定出库单 |
|
|
|
// 搜索申请单对应的工单列表 |
|
|
|
searchOutboundList(searchCode) { |
|
|
|
/* const currentWarehouse = getCurrentWarehouse(); |
|
|
|
if (!currentWarehouse) { |
|
|
|
this.$message.error('请先选择仓库'); |
|
|
|
return; |
|
|
|
} */ |
|
|
|
|
|
|
|
this.loading = true; |
|
|
|
this.selectedWorkOrder = null |
|
|
|
this.materialList = [] |
|
|
|
const params = { |
|
|
|
notifyNo: searchCode, |
|
|
|
site: localStorage.getItem('site'), |
|
|
|
status: 'ISSUE' |
|
|
|
site: localStorage.getItem('site') |
|
|
|
}; |
|
|
|
|
|
|
|
getIssueNotifyHeaderInfo(params).then(({ data }) => { |
|
|
|
this.loading = false; |
|
|
|
if (data && data.code === 0) { |
|
|
|
if (data.list.length === 0) { |
|
|
|
this.$message.warning('未找到匹配的出库单'); |
|
|
|
const list = data.notify || [] |
|
|
|
if (list.length === 0) { |
|
|
|
this.$message.warning('未找到该申请单的工单信息'); |
|
|
|
} |
|
|
|
this.outboundList = data.list || []; |
|
|
|
this.currentNotifyNo = searchCode |
|
|
|
this.outboundList = list; |
|
|
|
} else { |
|
|
|
this.$message.error(data.msg || '查询失败'); |
|
|
|
} |
|
|
|
@ -236,25 +179,45 @@ export default { |
|
|
|
}); |
|
|
|
}, |
|
|
|
|
|
|
|
// 选择工单 |
|
|
|
isSameWorkOrder(a, b) { |
|
|
|
if (!a || !b) return false |
|
|
|
return (a.orderNo || a.soorderNo) === (b.orderNo || b.soorderNo) |
|
|
|
}, |
|
|
|
|
|
|
|
// 选择工单(支持绿色选中与折叠) |
|
|
|
selectWorkOrder(workOrder) { |
|
|
|
this.selectedWorkOrder = workOrder; |
|
|
|
this.loadMaterialList(); |
|
|
|
if ( |
|
|
|
this.showOnlySelected && |
|
|
|
this.selectedWorkOrder && |
|
|
|
this.isSameWorkOrder(this.selectedWorkOrder, workOrder) |
|
|
|
) { |
|
|
|
// 再次点击同一条,恢复显示所有主数据 |
|
|
|
this.selectedWorkOrder = null |
|
|
|
this.materialList = [] |
|
|
|
this.showOnlySelected = false |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
// 选择并仅显示当前工单 |
|
|
|
this.selectedWorkOrder = workOrder |
|
|
|
this.showOnlySelected = true |
|
|
|
this.loadMaterialList() |
|
|
|
}, |
|
|
|
|
|
|
|
// 加载材料清单(工单BOM) |
|
|
|
// 加载申请单的物料清单 |
|
|
|
loadMaterialList() { |
|
|
|
if (!this.selectedWorkOrder) { |
|
|
|
if (!this.selectedWorkOrder || !this.currentNotifyNo) { |
|
|
|
this.materialList = []; |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
const params = { |
|
|
|
workOrderNo: this.selectedWorkOrder.orderNo, |
|
|
|
notifyNo: this.currentNotifyNo, |
|
|
|
site: localStorage.getItem('site'), |
|
|
|
itemNo: this.selectedWorkOrder.itemNo |
|
|
|
}; |
|
|
|
|
|
|
|
/* getWorkOrderMaterials(params) |
|
|
|
getRequestMaterials(params) |
|
|
|
.then(({ data }) => { |
|
|
|
if (data && data.code === 0) { |
|
|
|
this.materialList = (data.materials || []).map((item, index) => ({ |
|
|
|
@ -267,17 +230,27 @@ export default { |
|
|
|
} |
|
|
|
}) |
|
|
|
.catch((error) => { |
|
|
|
console.error('获取材料清单失败:', error); |
|
|
|
this.$message.error('获取材料清单失败'); |
|
|
|
}); */ |
|
|
|
this.materialList = []; |
|
|
|
}); |
|
|
|
}, |
|
|
|
|
|
|
|
// 跳转到领料页面 |
|
|
|
goToPickingPage(item) { |
|
|
|
console.log("选中物料:", item, this.selectedWorkOrder); |
|
|
|
|
|
|
|
this.$router.push({ |
|
|
|
name: 'productionPickingDetail', |
|
|
|
params: { |
|
|
|
query: { |
|
|
|
outboundNo: item.notifyNo, |
|
|
|
notifyInfo: { |
|
|
|
orderNo: this.selectedWorkOrder.soorderNo, |
|
|
|
componentPartNo: item.partNo, |
|
|
|
qtyToIssue: item.requestQty, |
|
|
|
lineItemNo: item.bomItemNo, |
|
|
|
releaseNo: this.selectedWorkOrder.releaseNo, |
|
|
|
sequenceNo: this.selectedWorkOrder.sequenceNo, |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
@ -350,6 +323,28 @@ export default { |
|
|
|
padding: 12px 16px; |
|
|
|
} |
|
|
|
|
|
|
|
/* 物料卡片(对齐 productionReturnPDA.vue) */ |
|
|
|
.material-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; |
|
|
|
} |
|
|
|
|
|
|
|
.material-card:hover { |
|
|
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); |
|
|
|
transform: translateY(-1px); |
|
|
|
} |
|
|
|
|
|
|
|
.material-card.selected { |
|
|
|
border-color: #17b3a3; |
|
|
|
background: #f0fffe; |
|
|
|
} |
|
|
|
|
|
|
|
/* 出库卡片 */ |
|
|
|
.outbound-card { |
|
|
|
background: white; |
|
|
|
@ -389,6 +384,19 @@ export default { |
|
|
|
margin-left: 20px; |
|
|
|
} |
|
|
|
|
|
|
|
/* 物料描述行(对齐 productionReturnPDA.vue) */ |
|
|
|
.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; |
|
|
|
|