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.
 
 
 
 
 

678 lines
16 KiB

<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
v-model="searchCode"
placeholder="请扫描入库单或关联单号"
prefix-icon="el-icon-search"
@keyup.enter.native="handleSearch"
ref="searchInput"
/>
</div>
<!-- 入库单列表 -->
<div class="inbound-list">
<!-- 当没有选中入库单时,显示所有入库单 -->
<template v-if="!selectedInbound">
<div
v-for="(item, index) in qualifiedList"
:key="index"
class="inbound-card"
@click="selectInbound(item)"
>
<div class="card-title">
<span class="title-label">入库单号:{{ item.inboundNo }}</span>
</div>
<div class="card-details">
<div class="detail-item">
<div class="detail-label">标签张数</div>
<div class="detail-value">
<span class="qualified">{{ item.labelinCount }}</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.totalinLabels }}</span><span class="total">{{ item.labelCount }}</span>
</div>
</div>
<div class="detail-item">
<div class="detail-label">批次号</div>
<div class="detail-value">{{ item.batchNo }}</div>
</div>
<div class="detail-item">
<div class="detail-label">创建日期</div>
<div class="detail-value">{{ formatDate(item.inspectionDate) }}</div>
</div>
</div>
</div>
<!-- 空状态 -->
<div v-if="qualifiedList.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>
</template>
<!-- 当选中入库单时,只显示选中的入库单 -->
<div
v-else
class="inbound-card selected"
@click="deselectInbound"
>
<div class="card-title">
<span class="title-label">入库单号:{{ selectedInbound.inboundNo }}</span>
<i class="el-icon-arrow-left back-icon" title="返回列表"></i>
</div>
<div class="card-details">
<div class="detail-item">
<div class="detail-label">标签张数</div>
<div class="detail-value">
<span class="qualified">{{ selectedInbound.labelinCount }}</span><span class="total">{{ selectedInbound.totalLabels }}</span>
</div>
</div>
<div class="detail-item">
<div class="detail-label">物料总数</div>
<div class="detail-value">
<span class="qualified">{{ selectedInbound.totalinLabels }}</span><span class="total">{{ selectedInbound.labelCount }}</span>
</div>
</div>
<div class="detail-item">
<div class="detail-label">批次号</div>
<div class="detail-value">{{ selectedInbound.batchNo }}</div>
</div>
<div class="detail-item">
<div class="detail-label">创建日期</div>
<div class="detail-value">{{ formatDate(selectedInbound.inspectionDate) }}</div>
</div>
</div>
</div>
</div>
<!-- 物料详情列表(小卡片) -->
<div
class="content-area"
v-if="selectedInbound && detailList.length > 0"
>
<div
v-for="(detail, index) in detailList"
:key="index"
class="detail-card"
@click="selectDetail(detail)"
>
<!-- 第一行:关联单号(左)| 需求数量(右) -->
<div class="card-row">
<span class="field-item">
<span class="field-label">关联单号:</span>
<span class="field-value">{{ detail.relatedOrderNo }}</span>
</span>
<span class="field-item">
<span class="field-label">需求数量:</span>
<span class="field-value">{{ detail.requiredQty || 0 }}</span>
</span>
</div>
<!-- 第二行:物料编码(左)| 单位(右) -->
<div class="card-row">
<span class="field-item">
<span class="field-label">物料编码:</span>
<span class="field-value">{{ detail.partNo }}</span>
</span>
<span class="field-item">
<span class="field-label">单位:</span>
<span class="field-value">{{ detail.umName }}</span>
</span>
</div>
<!-- 第三行:物料名称(全宽) -->
<div class="card-row-full">
<span class="field-label">物料名称:</span>
<span class="field-value">{{ detail.partDesc }}</span>
</div>
<!-- 第四行:订单数量(全宽) -->
<div class="card-row-full">
<span class="field-label">订单数量:</span>
<span class="field-value">{{ detail.orderQty || 0 }}</span>
</div>
</div>
</div>
<!-- 物料详情加载状态 -->
<div v-if="selectedInbound && detailListLoading" class="loading-state">
<i class="el-icon-loading"></i>
<p>加载物料详情中...</p>
</div>
<!-- 物料详情空状态 -->
<div v-if="selectedInbound && !detailListLoading && detailList.length === 0" class="empty-state">
<i class="el-icon-document"></i>
<p>暂无物料详情</p>
</div>
</div>
</template>
<script>
import { getQualifiedInboundList, getInboundNotificationDetails } from "@/api/production/production-inbound.js";
import { getCurrentWarehouse } from '@/utils'
import moment from 'moment';
export default {
data() {
return {
searchCode: '',
qualifiedList: [],
loading: false,
selectedInbound: null,
detailList: [],
detailListLoading: false
};
},
methods: {
formatDate(date) {
return date ? moment(date).format('YYYY-MM-DD') : '';
},
// 处理搜索
handleSearch() {
if (this.searchCode.trim()) {
this.searchQualifiedList(this.searchCode.trim());
} else {
this.loadQualifiedList();
}
},
// 加载生产待入库列表
loadQualifiedList() {
const currentWarehouse = getCurrentWarehouse();
if (!currentWarehouse) {
this.$message.error('请先选择仓库');
return;
}
this.loading = true;
const params = {
warehouseId: currentWarehouse,
site:localStorage.getItem('site'),
status: '待入库',// 待入库状态
};
getQualifiedInboundList(params).then(({ data }) => {
this.loading = false;
if (data && data.code === 0) {
this.qualifiedList = data.data || [];
} else {
this.$message.error(data.msg || '获取数据失败');
}
}).catch(error => {
this.loading = false;
console.error('获取生产待入库列表失败:', error);
this.$message.error('获取数据失败');
});
},
// 搜索特定入库单
searchQualifiedList(searchCode) {
const currentWarehouse = getCurrentWarehouse();
if (!currentWarehouse) {
this.$message.error('请先选择仓库');
return;
}
this.loading = true;
const params = {
warehouseId: currentWarehouse,
searchCode: searchCode,
site:localStorage.getItem('site'),
status: '待入库'
};
getQualifiedInboundList(params).then(({ data }) => {
this.loading = false;
if (data && data.code === 0) {
if (data.data.length === 0) {
this.$message.warning('未找到匹配的入库单');
}
this.qualifiedList = data.data || [];
} else {
this.$message.error(data.msg || '查询失败');
}
}).catch(error => {
this.loading = false;
console.error('搜索失败:', error);
this.$message.error('查询失败');
});
},
// 选择入库单
selectInbound(item) {
this.selectedInbound = item;
this.loadDetailList(item);
},
// 取消选择入库单
deselectInbound() {
this.selectedInbound = null;
this.detailList = [];
},
// 加载物料详情列表(小卡片)
loadDetailList(item) {
this.detailListLoading = true;
const params = {
site: localStorage.getItem('site'),
buNo: item.buNo,
orderNo: item.inboundNo
};
getInboundNotificationDetails(params).then(({ data }) => {
this.detailListLoading = false;
if (data && data.code === 0) {
this.detailList = (data.data || []).map((detail) => ({
...detail,
// 映射后端字段到前端字段
orderNo: detail.orderNo,
orderQty: detail.orderQty,
requiredQty: detail.requiredQty,
partNo: detail.partNo,
partDesc: detail.partDesc,
umName: detail.umName,
relatedOrderNo: detail.relatedOrderNo,
relatedOrderLineNo: detail.relatedOrderLineNo
}));
} else {
this.$message.error(data.msg || '获取物料明细失败');
this.detailList = [];
}
}).catch(error => {
this.detailListLoading = false;
console.error('获取物料明细失败:', error);
this.$message.error('获取物料明细失败');
this.detailList = [];
});
},
// 选择物料详情,跳转到入库页面
selectDetail(detail) {
this.$router.push({
name: 'productionInboundStorage',
params: {
buNo: this.selectedInbound.buNo,
inboundNo: this.selectedInbound.inboundNo,
relatedOrderNo: detail.relatedOrderNo,
relatedOrderLineNo: detail.relatedOrderLineNo,
partNo: detail.partNo,
partDesc: detail.partDesc
}
});
}
},
mounted() {
// 聚焦搜索框
this.$nextTick(() => {
if (this.$refs.searchInput) {
this.$refs.searchInput.focus();
}
});
// 加载数据
this.loadQualifiedList();
}
};
</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;
}
/* 入库单列表 */
.inbound-list {
overflow-y: auto;
padding: 12px 16px;
}
/* 内容区域(物料详情列表) */
.content-area {
flex: 1;
overflow-y: auto;
padding: 8px 0 12px 0;
background: #f8f9fa;
margin: 0 16px 12px 16px;
border-radius: 0 0 8px 8px;
}
/* 入库卡片 */
.inbound-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;
}
.inbound-card:hover {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
transform: translateY(-1px);
}
.inbound-card.selected {
border-color: #17B3A3;
background: #f0fffe;
}
.inbound-card:active {
transform: translateY(0);
}
/* 物料详情卡片(小卡片) */
.detail-card {
background: white;
border-radius: 6px;
margin-bottom: 8px;
margin-left: 20px;
margin-right: 16px;
padding: 12px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
cursor: pointer;
transition: all 0.2s ease;
border: 1px solid #e8f4f3;
position: relative;
}
.detail-card::before {
content: '';
position: absolute;
left: -12px;
top: 50%;
transform: translateY(-50%);
width: 4px;
height: 20px;
background: #17B3A3;
border-radius: 2px;
}
.detail-card:hover {
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12);
transform: translateY(-1px);
border-color: #17B3A3;
}
.detail-card:active {
transform: translateY(0);
}
/* 卡片标题 */
.card-title {
margin-bottom: 12px;
position: relative;
}
.title-label {
font-size: 12px;
color: #666;
display: block;
margin-bottom: 4px;
}
.title-value {
font-size: 16px;
font-weight: bold;
color: #333;
margin-left: 16px;
}
.back-icon {
position: absolute;
right: 16px;
top: 50%;
transform: translateY(-50%);
font-size: 18px;
color: #17B3A3;
cursor: pointer;
transition: color 0.2s ease;
}
.back-icon:hover {
color: #0d8f7f;
}
/* 物料详情卡片内的字段行样式 */
.detail-card .card-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 6px;
gap: 8px;
}
.detail-card .card-row-full {
margin-bottom: 6px;
display: flex;
align-items: center;
}
.detail-card .field-item {
display: flex;
align-items: center;
flex: 1;
min-width: 0;
}
.detail-card .field-label {
font-size: 11px;
color: #666;
white-space: nowrap;
margin-right: 4px;
}
.detail-card .field-value {
font-size: 12px;
color: #333;
font-weight: 500;
word-break: break-all;
flex: 1;
}
/* 卡片详情 */
.card-details {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 4px;
}
.detail-item {
flex: 1;
text-align: center;
min-width: 60px;
max-width: 60px;
}
.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;
}
.detail-value .qualified {
color: #17B3A3;
font-weight: 500;
}
.detail-value .total {
color: #333;
font-weight: 500;
}
.detail-value .total::before {
content: '/';
color: #333;
}
/* 空状态 */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 60px 20px;
color: #999;
}
.empty-state i {
font-size: 48px;
margin-bottom: 16px;
}
.empty-state p {
font-size: 14px;
margin: 0;
}
/* 加载状态 */
.loading-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 60px 20px;
color: #17B3A3;
}
.loading-state i {
font-size: 24px;
margin-bottom: 12px;
animation: spin 1s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.loading-state p {
font-size: 14px;
margin: 0;
}
/* 响应式设计 */
@media (max-width: 360px) {
.header-bar {
padding: 8px 12px;
}
.search-container {
padding: 8px 12px;
}
.inbound-list {
padding: 8px 12px;
}
.content-area {
padding: 8px 12px;
margin: 0 12px 8px 12px;
}
.inbound-card {
padding: 12px;
}
.detail-card {
margin-left: 16px;
margin-right: 12px;
}
.card-details {
flex-wrap: wrap;
gap: 6px;
}
.detail-item {
flex: 0 0 48%;
margin-bottom: 6px;
min-width: 50px;
}
.detail-card .field-item {
flex: 0 0 45%;
min-width: 40px;
}
}
</style>