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.
606 lines
16 KiB
606 lines
16 KiB
<template>
|
|
<div>
|
|
<div class="pda-container">
|
|
<!-- 头部栏 -->
|
|
<div class="header-bar">
|
|
<div class="header-left" @click="handleBack">
|
|
<i class="el-icon-arrow-left"></i>
|
|
<span>查看托盘</span>
|
|
</div>
|
|
<div class="header-right" @click="$router.push({ path: '/' })">
|
|
首页
|
|
</div>
|
|
</div>
|
|
<div class="table-body" style="max-height: 500px; overflow-y: auto;">
|
|
<div class="main-content form-section">
|
|
<!-- 栈板扫描 - rqrq -->
|
|
<div class="input-group">
|
|
<label class="input-label">栈板编码</label>
|
|
<el-input
|
|
v-model="palletCode"
|
|
placeholder="请扫描栈板编码"
|
|
class="form-input"
|
|
clearable
|
|
@keyup.enter.native="handlePalletScan"
|
|
ref="palletInput"
|
|
/>
|
|
</div>
|
|
|
|
<!-- 筛选条件 (扫描栈板后显示) - rqrq -->
|
|
<div v-if="palletScanned" class="input-group">
|
|
<label class="input-label">位置</label>
|
|
<div style="display: flex; gap: 8px;">
|
|
<el-select
|
|
v-model="selectedPosition"
|
|
placeholder="请选择位置"
|
|
style="flex: 0.75;"
|
|
>
|
|
<el-option label="ALL" value=""></el-option>
|
|
<el-option
|
|
v-for="position in positionOptions"
|
|
:key="position"
|
|
:label="position"
|
|
:value="position"
|
|
/>
|
|
</el-select>
|
|
<button
|
|
class="action-btn secondary"
|
|
style="flex: 0.25; margin: 0; white-space: nowrap;"
|
|
@click="refreshTable"
|
|
>
|
|
刷新
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 栈板明细表格 (扫描栈板后显示) - rqrq -->
|
|
<div v-if="palletScanned" class="rma-list">
|
|
<div class="list-title-row" style="display: flex; gap: 8px; align-items: center; padding: 0;">
|
|
<div class="list-title" style="flex: 1; margin: 0; display: flex; align-items: center; flex-wrap: wrap; gap: 5px;">
|
|
<button class="action-btn primary" style="margin: 0;" @click="showDetailModal" >
|
|
{{ '浏览明细' }}
|
|
</button>
|
|
<label style="margin-left: 5px;">条码数:{{detailList.length}}</label>
|
|
<!-- 预定清单按钮 - rqrq -->
|
|
<button
|
|
class="action-btn"
|
|
:class="reserveCount > 0 ? 'warning' : 'secondary'"
|
|
style="margin: 0; margin-left: 10px;"
|
|
@click="showReserveModal">
|
|
预定清单({{ reserveCount }})
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="detail-table">
|
|
<div class="table-header">
|
|
<div class="col-position">位置</div>
|
|
<div class="col-layer">层数</div>
|
|
<div class="col-serial">标签号</div>
|
|
</div>
|
|
<div
|
|
v-for="(detail, index) in detailList"
|
|
:key="index"
|
|
class="table-row"
|
|
>
|
|
<div class="col-position">{{ detail.position }}</div>
|
|
<div class="col-layer">{{ detail.layer }}</div>
|
|
<div class="col-serial">{{ detail.serialNo }}</div>
|
|
</div>
|
|
<!-- 暂无数据提示 -->
|
|
<div v-if="detailList.length === 0" class="table-row empty-row">
|
|
<div class="empty-hint">暂无栈板明细数据</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- 浏览明细弹窗 - rqrq -->
|
|
<el-dialog
|
|
:title="'栈板明细 (共'+detailList.length+'条)'"
|
|
:visible.sync="detailModalVisible"
|
|
width="90%"
|
|
:close-on-click-modal="false"
|
|
:show-close="false"
|
|
:modal="true"
|
|
:modal-append-to-body="true"
|
|
:append-to-body="true"
|
|
>
|
|
<div class="table-body" style="max-height: 400px; overflow-y: auto;">
|
|
<div class="detail-table">
|
|
<div class="table-header">
|
|
<div class="col-position">位置</div>
|
|
<div class="col-layer">层数</div>
|
|
<div class="col-serial">标签号</div>
|
|
</div>
|
|
<div
|
|
v-for="(detail, index) in detailList"
|
|
:key="index"
|
|
class="table-row"
|
|
>
|
|
<div class="col-position">{{ detail.position }}</div>
|
|
<div class="col-layer">{{ detail.layer }}</div>
|
|
<div class="col-serial">{{ detail.serialNo }}</div>
|
|
</div>
|
|
<!-- 暂无数据提示 -->
|
|
<div v-if="detailList.length === 0" class="table-row empty-row">
|
|
<div class="empty-hint">暂无栈板明细数据</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div slot="footer" class="dialog-footer">
|
|
<button class="action-btn secondary" @click="detailModalVisible=false">关闭</button>
|
|
</div>
|
|
</el-dialog>
|
|
|
|
<!-- 预定清单弹窗 - rqrq -->
|
|
<el-dialog
|
|
:title="'预定清单 (共'+reserveCount+'条)'"
|
|
:visible.sync="reserveModalVisible"
|
|
width="95%"
|
|
:close-on-click-modal="false"
|
|
:show-close="false"
|
|
:modal="true"
|
|
:modal-append-to-body="true"
|
|
:append-to-body="true"
|
|
>
|
|
<div class="table-body" style="max-height: 350px; overflow-y: auto;">
|
|
<div class="detail-table">
|
|
<div class="table-header reserve-header">
|
|
<div class="col-serial-reserve">标签号</div>
|
|
<div class="col-ref">申请单号</div>
|
|
<div class="col-ref">订单号</div>
|
|
</div>
|
|
<div
|
|
v-for="(item, index) in reservedLabels"
|
|
:key="index"
|
|
class="table-row"
|
|
>
|
|
<div class="col-serial-reserve">{{ item.serialNo || '-' }}</div>
|
|
<div class="col-ref">{{ item.reserveOrderRef1 || '-' }}</div>
|
|
<div class="col-ref">{{ item.reserveOrderRef3 || '-' }}</div>
|
|
</div>
|
|
<!-- 暂无数据提示 -->
|
|
<div v-if="reservedLabels.length === 0" class="table-row empty-row">
|
|
<div class="empty-hint">暂无预定标签</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div slot="footer" class="dialog-footer" style="display: flex; gap: 10px; justify-content: center;">
|
|
<button class="action-btn secondary" @click="reserveModalVisible=false" :disabled="cancelReserveLoading">关闭</button>
|
|
<button
|
|
class="action-btn danger"
|
|
@click="handleBatchCancelReserve"
|
|
:disabled="cancelReserveLoading || reservedLabels.length === 0 || errorStationFlag"
|
|
v-if="reservedLabels.length > 0"
|
|
>
|
|
{{ cancelReserveLoading ? '取消中...' : '一键取消预定' }}
|
|
</button>
|
|
</div>
|
|
<!-- 异常位提示:以后端 errorStationFlag 为准,不再写死 1101 - rqrq -->
|
|
<div v-if="errorStationFlag" class="reserve-warning">
|
|
<i class="el-icon-warning"></i> 栈板在立库异常位上,不允许取消预定
|
|
</div>
|
|
</el-dialog>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import {
|
|
getPalletInfoSimple,
|
|
getPalletDetails,
|
|
getPalletReservedLabels,
|
|
batchCancelReserve
|
|
} from '../../../api/haianAutoWarehouse/palletPacking'
|
|
|
|
export default {
|
|
name: 'haianPalletSearch',
|
|
data() {
|
|
return {
|
|
site: this.$store.state.user.site,
|
|
palletCode: '',
|
|
palletScanned: false,
|
|
detailModalVisible: false,
|
|
|
|
// 筛选条件 - rqrq
|
|
selectedPosition: '',
|
|
positionOptions: [],
|
|
|
|
// 栈板明细 - rqrq
|
|
detailList: [],
|
|
|
|
// 栈板库位 - rqrq
|
|
locationCode: '',
|
|
// 是否异常位:由后端 ERROR_STATIONS 判定,禁止前端写死 1101 - rqrq
|
|
errorStationFlag: false,
|
|
|
|
// 预定清单相关 - rqrq
|
|
reserveModalVisible: false,
|
|
reservedLabels: [],
|
|
reserveCount: 0,
|
|
cancelReserveLoading: false
|
|
};
|
|
},
|
|
methods: {
|
|
// 返回上一页 - rqrq
|
|
handleBack() {
|
|
// 返回菜单前清空托盘查询状态,避免keep-alive缓存导致数据残留 - rqrq
|
|
this.resetPage(false)
|
|
this.$router.back();
|
|
},
|
|
|
|
// 重置页面到初始状态 - rqrq
|
|
resetPage(needFocus = true) {
|
|
this.site = this.$store.state.user.site;
|
|
this.palletCode = '';
|
|
this.palletScanned = false;
|
|
this.selectedPosition = '';
|
|
this.positionOptions = [];
|
|
this.detailList = [];
|
|
this.detailModalVisible = false;
|
|
this.locationCode = '';
|
|
this.reserveModalVisible = false;
|
|
this.reservedLabels = [];
|
|
this.reserveCount = 0;
|
|
this.cancelReserveLoading = false;
|
|
|
|
// 聚焦到栈板输入框 - rqrq
|
|
if (needFocus) {
|
|
this.$nextTick(() => {
|
|
if (this.$refs.palletInput) {
|
|
this.$refs.palletInput.focus();
|
|
}
|
|
});
|
|
}
|
|
},
|
|
|
|
// 扫描栈板(使用无校验的简单查询)- rqrq
|
|
handlePalletScan() {
|
|
if (!this.site) {
|
|
this.$alert('当前用户缺少site,无法执行海安托盘查询', '提示', {
|
|
confirmButtonText: '确定'
|
|
});
|
|
return;
|
|
}
|
|
if (!this.palletCode.trim()) {
|
|
this.$alert('请输入栈板编码', '提示', {
|
|
confirmButtonText: '确定'
|
|
});
|
|
return;
|
|
}
|
|
|
|
getPalletInfoSimple({
|
|
site: this.site,
|
|
palletId: this.palletCode
|
|
}).then(({ data }) => {
|
|
if (data.code === 0) {
|
|
this.palletCode = data.palletId;
|
|
this.palletScanned = true;
|
|
this.positionOptions = data.positions || [];
|
|
this.locationCode = data.locationCode || '';
|
|
this.refreshTable();
|
|
this.loadReservedLabels(); // 加载预定标签数量 - rqrq
|
|
} else {
|
|
let errorMsg = data.msg || '栈板不存在';
|
|
if (errorMsg.length > 100) {
|
|
errorMsg = errorMsg.substring(0, 100) + '...';
|
|
}
|
|
this.$alert(errorMsg, '错误', {
|
|
confirmButtonText: '确定',
|
|
callback: () => {
|
|
this.palletCode = '';
|
|
this.$nextTick(() => {
|
|
if (this.$refs.palletInput) {
|
|
this.$refs.palletInput.focus();
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
}).catch(error => {
|
|
console.error('查询栈板失败:', error);
|
|
let errorMsg = error.message || '查询栈板失败';
|
|
if (errorMsg.length > 100) {
|
|
errorMsg = errorMsg.substring(0, 100) + '...';
|
|
}
|
|
this.$alert(errorMsg, '错误', {
|
|
confirmButtonText: '确定',
|
|
callback: () => {
|
|
this.palletCode = '';
|
|
this.$nextTick(() => {
|
|
if (this.$refs.palletInput) {
|
|
this.$refs.palletInput.focus();
|
|
}
|
|
});
|
|
}
|
|
});
|
|
});
|
|
},
|
|
|
|
|
|
// 刷新表格 - rqrq
|
|
refreshTable() {
|
|
getPalletDetails({
|
|
site: this.site,
|
|
palletId: this.palletCode,
|
|
position: this.selectedPosition,
|
|
layer: ''
|
|
}).then(({ data }) => {
|
|
if (data.code === 0) {
|
|
this.detailList = data.details || [];
|
|
} else {
|
|
this.detailList = [];
|
|
}
|
|
}).catch(error => {
|
|
console.error('获取栈板明细失败:', error);
|
|
this.detailList = [];
|
|
});
|
|
},
|
|
|
|
// 显示明细弹窗 - rqrq
|
|
showDetailModal() {
|
|
this.detailModalVisible = true;
|
|
},
|
|
|
|
// 显示预定清单弹窗 - rqrq
|
|
showReserveModal() {
|
|
this.loadReservedLabels();
|
|
this.reserveModalVisible = true;
|
|
},
|
|
|
|
// 加载预定标签列表 - rqrq
|
|
loadReservedLabels() {
|
|
getPalletReservedLabels({
|
|
site: this.site,
|
|
palletId: this.palletCode
|
|
}).then(({ data }) => {
|
|
if (data.code === 0 && data.data) {
|
|
this.reservedLabels = data.data.reservedLabels || [];
|
|
this.reserveCount = data.data.reserveCount || 0;
|
|
this.locationCode = data.data.locationCode || '';
|
|
this.errorStationFlag = data.data.errorStationFlag === true;
|
|
} else {
|
|
this.reservedLabels = [];
|
|
this.reserveCount = 0;
|
|
this.errorStationFlag = false;
|
|
}
|
|
}).catch(error => {
|
|
console.error('获取预定标签列表失败:', error);
|
|
this.reservedLabels = [];
|
|
this.reserveCount = 0;
|
|
this.errorStationFlag = false;
|
|
});
|
|
},
|
|
|
|
// 一键取消预定 - rqrq
|
|
handleBatchCancelReserve() {
|
|
if (this.errorStationFlag) {
|
|
this.$alert('栈板在立库异常位上,不允许取消预定', '错误', { confirmButtonText: '确定' });
|
|
return;
|
|
}
|
|
|
|
this.$confirm('确定要取消所有预定标签吗?', '提示', {
|
|
confirmButtonText: '确定',
|
|
cancelButtonText: '取消',
|
|
type: 'warning'
|
|
}).then(() => {
|
|
this.doBatchCancelReserve();
|
|
}).catch(() => {
|
|
// 用户取消
|
|
});
|
|
},
|
|
|
|
// 执行批量取消预定 - rqrq
|
|
doBatchCancelReserve() {
|
|
this.cancelReserveLoading = true;
|
|
|
|
batchCancelReserve({
|
|
site: this.site,
|
|
palletId: this.palletCode
|
|
}).then(({ data }) => {
|
|
if (data.code === 0) {
|
|
this.$message.success('取消预定成功');
|
|
this.reserveModalVisible = false;
|
|
// 重新加载预定标签列表 - rqrq
|
|
this.loadReservedLabels();
|
|
} else {
|
|
this.$alert(data.msg || '取消预定失败', '错误', { confirmButtonText: '确定' });
|
|
}
|
|
}).catch(error => {
|
|
console.error('批量取消预定失败:', error);
|
|
this.$alert('取消预定异常', '错误', { confirmButtonText: '确定' });
|
|
}).finally(() => {
|
|
this.cancelReserveLoading = false;
|
|
});
|
|
}
|
|
},
|
|
// keep-alive页面每次激活统一重置,保证海安“退出再进入”无历史残留 - rqrq
|
|
activated() {
|
|
this.resetPage(true);
|
|
},
|
|
// 离开页面时主动清理缓存状态,避免下次进入展示旧查询结果 - rqrq
|
|
beforeRouteLeave(to, from, next) {
|
|
this.resetPage(false);
|
|
next();
|
|
},
|
|
mounted() {
|
|
this.resetPage(true);
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style scoped>
|
|
/* 表格样式 */
|
|
.detail-table {
|
|
background: white;
|
|
border-radius: 6px;
|
|
overflow: hidden;
|
|
border: 1px solid #e0e0e0;
|
|
}
|
|
|
|
.table-header,
|
|
.table-row {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 8px;
|
|
border-bottom: 1px solid #e0e0e0;
|
|
}
|
|
|
|
.table-header {
|
|
background: #f5f5f5;
|
|
font-weight: bold;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.table-row {
|
|
font-size: 13px;
|
|
}
|
|
|
|
.table-row:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.col-position {
|
|
flex: 1;
|
|
text-align: center;
|
|
}
|
|
|
|
.col-layer {
|
|
flex: 1;
|
|
text-align: center;
|
|
}
|
|
|
|
.col-serial {
|
|
flex: 4;
|
|
text-align: center;
|
|
word-break: break-all;
|
|
}
|
|
|
|
/* 空数据提示 */
|
|
.empty-hint {
|
|
text-align: center;
|
|
color: #999;
|
|
padding: 20px;
|
|
background: white;
|
|
border-radius: 6px;
|
|
margin-top: 16px;
|
|
}
|
|
|
|
/* 模态框样式 */
|
|
.scan-modal-content {
|
|
padding: 10px 0;
|
|
}
|
|
|
|
.modal-form {
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.dialog-footer {
|
|
text-align: center;
|
|
}
|
|
|
|
/* 修复模态框层级问题 */
|
|
::v-deep .el-dialog__wrapper {
|
|
z-index: 2000 !important;
|
|
}
|
|
|
|
::v-deep .el-overlay {
|
|
z-index: 2000 !important;
|
|
}
|
|
|
|
/* 修复单选框样式 */
|
|
::v-deep .el-radio {
|
|
margin-right: 8px !important;
|
|
}
|
|
|
|
::v-deep .el-radio__label {
|
|
font-size: 14px;
|
|
}
|
|
|
|
/* 标题行样式 */
|
|
.list-title-row {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.list-title-row .list-title {
|
|
margin: 0;
|
|
flex: 1;
|
|
}
|
|
|
|
/* 空数据行样式 */
|
|
.empty-row {
|
|
justify-content: center;
|
|
align-items: center;
|
|
padding: 20px;
|
|
border-bottom: none;
|
|
}
|
|
|
|
.empty-row .empty-hint {
|
|
text-align: center;
|
|
color: #999;
|
|
width: 100%;
|
|
}
|
|
|
|
/* 预定清单相关样式 - rqrq */
|
|
.action-btn.warning {
|
|
background: #E6A23C;
|
|
color: white;
|
|
border: none;
|
|
}
|
|
|
|
.action-btn.warning:hover {
|
|
background: #d99429;
|
|
}
|
|
|
|
.action-btn.danger {
|
|
background: #F56C6C;
|
|
color: white;
|
|
border: none;
|
|
}
|
|
|
|
.action-btn.danger:hover:not(:disabled) {
|
|
background: #e45656;
|
|
}
|
|
|
|
.action-btn.danger:disabled {
|
|
background: #ccc;
|
|
cursor: not-allowed;
|
|
opacity: 0.6;
|
|
}
|
|
|
|
.reserve-header {
|
|
font-size: 12px;
|
|
}
|
|
|
|
.col-serial-reserve {
|
|
flex: 2;
|
|
text-align: center;
|
|
word-break: break-all;
|
|
font-size: 12px;
|
|
}
|
|
|
|
.col-ref {
|
|
flex: 1.5;
|
|
text-align: center;
|
|
word-break: break-all;
|
|
font-size: 12px;
|
|
}
|
|
|
|
.reserve-warning {
|
|
text-align: center;
|
|
color: #E6A23C;
|
|
padding: 10px;
|
|
font-size: 14px;
|
|
background: #fdf6ec;
|
|
border-radius: 4px;
|
|
margin-top: 10px;
|
|
}
|
|
|
|
.reserve-warning i {
|
|
margin-right: 5px;
|
|
}
|
|
|
|
</style>
|