|
|
<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 v-model="scanCode" placeholder="请扫描标签条码" prefix-icon="el-icon-search" @keyup.enter.native="handleScan" ref="scanInput" clearable /> </div>
<!-- 标签信息卡片 --> <div class="label-info-card" v-if="currentLabel.labelCode"> <div class="info-row"> <span class="info-label">标签条码</span> <span class="info-value">{{ currentLabel.labelCode || ''}}</span> </div>
<div class="info-row"> <span class="info-label">标签类型</span> <span class="info-value">{{ currentLabel.labelType || '' }}</span> </div>
<div class="info-row"> <span class="info-label">标签张数</span> <span class="info-value">{{ currentLabel.labelQty || ''}}</span> </div>
<div class="info-row"> <span class="info-label">标签数量</span> <span class="info-value">{{ currentLabel.qtyOnHand || ''}}</span> </div>
<div class="info-row"> <span class="info-label">所在仓库</span> <span class="info-value">{{ currentLabel.warehouseName || ''}}</span> </div>
<div class="info-row"> <span class="info-label">所在库位</span> <span class="info-value">{{ currentLabel.locationId || ''}}</span> </div>
<div class="info-row"> <span class="info-label">标签状态</span> <span class="info-value" :class="currentLabel.status === '冻结' ? 'status-frozen' : 'status-normal'">{{ currentLabel.status || ''}}</span> </div> </div>
<!-- 操作按钮 --> <div class="action-buttons" v-if="currentLabel.labelCode"> <button class="action-btn split-btn" @click="showSplitDialog"> 拆分 </button> <button class="action-btn merge-btn" @click="showMergeDialog"> 合并 </button> </div>
<!-- 拆分对话框 --> <div v-if="splitDialogVisible" class="dialog-overlay"> <div class="dialog-modal"> <div class="dialog-header"> <span class="dialog-title">标签的拆分</span> </div>
<div class="dialog-body"> <div class="split-input-section"> <el-input v-model="splitQuantity" placeholder="请输入拆分数量" type="number" class="split-input inlineNumber numInput"/> </div> </div>
<div class="dialog-footer"> <button class="btn-split" @click="confirmSplit" :disabled="!splitQuantity || splitQuantity <= 0"> 拆分 </button> <button class="btn-cancel" @click="closeSplitDialog"> 取消 </button> </div> </div> </div>
<!-- 合并对话框 --> <div v-if="mergeDialogVisible" class="dialog-overlay"> <div class="dialog-modal"> <div class="dialog-header"> <span class="dialog-title">标签的合并</span> </div>
<div class="dialog-body"> <div class="merge-input-section"> <el-input v-model="mergeTargetCode" placeholder="请扫描合并标签" prefix-icon="el-icon-search" class="merge-input" ref="mergeInput" /> </div> </div>
<div class="dialog-footer"> <button class="btn-merge" @click="confirmMerge" :disabled="!mergeTargetCode.trim()"> 合并 </button> <button class="btn-cancel" @click="closeMergeDialog"> 取消 </button> </div> </div> </div> </div></template>
<script>import { getStockInfoByLabelCode, splitLabel, mergeLabel, getUserDefaultPrinter } from "@/api/label-split-merge/label-split-merge.js";import { getCurrentWarehouse } from '@/utils'import getLodop from '@/utils/LodopFuncs.js';import labelPrintTemplates from '@/mixins/labelPrintTemplates.js';
export default { mixins: [labelPrintTemplates], data() { return { scanCode: '', currentLabel: {}, splitDialogVisible: false, mergeDialogVisible: false, splitQuantity: '', mergeTargetCode: '', mergeTargetLabel: {} }; }, methods: { // 处理扫描
handleScan() { if (!this.scanCode.trim()) { return; }
this.getStockInfo(this.scanCode.trim()); this.scanCode = ''; },
// 获取库存信息
getStockInfo(labelCode) { const params = { labelCode: labelCode, site: localStorage.getItem('site'), warehouseId: getCurrentWarehouse() };
getStockInfoByLabelCode(params).then(({ data }) => { if (data && data.code === 0) { this.currentLabel = data.data; // this.$message.success('获取标签信息成功');
} else { this.$message.error(data.msg || '未找到该标签的库存信息'); this.currentLabel = {}; } }).catch(error => { console.error('获取库存信息失败:', error); this.$message.error('获取库存信息失败'); this.currentLabel = {}; }); },
// 显示拆分对话框
showSplitDialog() { if (this.currentLabel.qtyOnHand <= 1) { this.$message.warning('标签数量必须大于1才能拆分'); return; } this.splitDialogVisible = true; this.splitQuantity = ''; },
// 关闭拆分对话框
closeSplitDialog() { this.splitDialogVisible = false; this.splitQuantity = ''; },
// 确认拆分
confirmSplit() { const splitQty = parseFloat(this.splitQuantity); const currentQty = parseFloat(this.currentLabel.qtyOnHand); if (!splitQty || splitQty <= 0) { this.$message.warning('请输入有效的拆分数量'); return; } if (splitQty >= currentQty) { this.$message.warning('拆分数量必须小于当前数量'); return; } const params = { site: localStorage.getItem('site'), buNo: this.currentLabel.buNo, warehouseId: getCurrentWarehouse(), originalLabelCode: this.currentLabel.labelCode, wdr: this.currentLabel.wdr, partNo: this.currentLabel.partNo, batchNo: this.currentLabel.batchNo, locationId: this.currentLabel.locationId, originalQuantity: currentQty, splitQuantity: splitQty, labelTypeTb: this.currentLabel.labelTypeTb, labelType: this.currentLabel.labelType, freezeFlag: this.currentLabel.freezeFlag, manufactureDate: this.currentLabel.productionDate, expiredDate: this.currentLabel.expiryDate, orderref1: this.currentLabel.orderref1, orderref2: this.currentLabel.orderref2, orderref3: this.currentLabel.orderref3, status: this.currentLabel.status, statusTb: this.currentLabel.statusTb }; splitLabel(params).then(async ({ data }) => { if (data && data.code === 0) { this.$message.success(`拆分成功!新标签: ${data.data.newLabelCode}`); this.closeSplitDialog();
// 自动打印标签(拆分打印两张:原标签和新标签)
const printList = data.data.printList || []; if (printList.length > 0) { await this.printLabelsWithTemplate(printList); }
// 刷新当前标签信息
this.getStockInfo(this.currentLabel.labelCode); } else { this.$message.error(data.msg || '拆分失败'); } }).catch(error => { console.error('拆分失败:', error); this.$message.error('拆分失败'); }); },
// 显示合并对话框
showMergeDialog() { this.mergeDialogVisible = true; this.mergeTargetCode = ''; this.mergeTargetLabel = {}; this.$nextTick(() => { if (this.$refs.mergeInput) { this.$refs.mergeInput.focus(); } }); },
// 关闭合并对话框
closeMergeDialog() { this.mergeDialogVisible = false; this.mergeTargetCode = ''; this.mergeTargetLabel = {}; },
// 确认合并
confirmMerge() { if (!this.mergeTargetCode.trim()) { this.$message.warning('请扫描目标标签'); return; }
if (this.mergeTargetCode.trim() === this.currentLabel.labelCode) { this.$message.warning('不能合并到自己'); return; }
// 在合并前再次验证目标标签
const params = { labelCode: this.mergeTargetCode.trim(), site: localStorage.getItem('site'), warehouseId: getCurrentWarehouse() };
getStockInfoByLabelCode(params).then(({ data }) => { if (data && data.code === 0) { const targetLabel = data.data;
// 检查是否可以合并(同物料、同批次)
if (targetLabel.partNo !== this.currentLabel.partNo) { this.$message.error('不同物料不能合并'); return; }
if (targetLabel.batchNo !== this.currentLabel.batchNo) { this.$message.error('不同批次不能合并'); return; }
// 验证通过,执行合并
const mergeParams = { site: localStorage.getItem('site'), buNo: this.currentLabel.buNo, warehouseId: getCurrentWarehouse(), targetLabelCode: targetLabel.labelCode, sourceLabelCode: this.currentLabel.labelCode, partNo: this.currentLabel.partNo, batchNo: this.currentLabel.batchNo, locationId: this.currentLabel.locationId, targetQuantity: parseFloat(targetLabel.qtyOnHand), sourceQuantity: parseFloat(this.currentLabel.qtyOnHand), status: this.currentLabel.status, statusTb: this.currentLabel.statusTb };
mergeLabel(mergeParams).then(async ({ data }) => { if (data && data.code === 0) { this.$message.success('合并成功!'); this.closeMergeDialog();
// 自动打印标签(合并只打印一张:源标签/合并后的标签)
const printList = data.data.printList || []; if (printList.length > 0) { await this.printLabelsWithTemplate(printList); }
// 清空当前标签信息,因为已经合并出库
this.currentLabel = {}; // 聚焦扫描框
this.$nextTick(() => { if (this.$refs.scanInput) { this.$refs.scanInput.focus(); } }); } else { this.$message.error(data.msg || '合并失败'); } }).catch(error => { console.error('合并失败:', error); this.$message.error('合并失败'); });
} else { this.$message.error(data.msg || '未找到目标标签的库存信息'); } }).catch(error => { console.error('获取目标标签信息失败:', error); this.$message.error('获取目标标签信息失败'); }); },
/** * 获取用户默认打印机配置 */ async fetchUserDefaultPrinter(labelNo) { try { const params = { userName: localStorage.getItem('userName'), labelNo: labelNo || '' }; const { data } = await getUserDefaultPrinter(params); if (data && data.code === 0 && data.printerName) { return { printerName: data.printerName, printerIp: data.printerIp, labelNo: data.labelNo }; } return null; } catch (error) { console.error('获取用户打印机配置失败:', error); return null; } },
/** * 使用模板打印标签 * @param {Array} printList - 打印数据列表(存储过程UspPartLabelTemplate返回) */ async printLabelsWithTemplate(printList) { try { // 1. 获取 LODOP 打印控件
const LODOP = getLodop(); if (!LODOP) { console.warn('无法连接到打印控件,跳过打印'); this.$message.warning('无法连接到打印控件,请确保已安装并启动打印服务'); return; }
// 2. 检测打印机数量
const printerCount = LODOP.GET_PRINTER_COUNT(); if (printerCount === 0) { console.warn('未检测到打印机,跳过打印'); this.$message.warning('未检测到打印机'); return; }
// 3. 获取用户配置的打印机
const firstLabel = printList[0] || {}; const printerConfig = await this.fetchUserDefaultPrinter(firstLabel.labelNo);
let printerName = null; if (printerConfig && printerConfig.printerName) { printerName = printerConfig.printerName; console.log('使用用户配置的打印机:', printerName); } else { console.warn('未找到用户打印机配置,跳过打印'); this.$message.warning('未配置用户打印机,请在系统中配置默认打印机后再打印'); return; }
// 4. 执行打印
await this.executePrintWithTemplate(LODOP, printList, printerName);
this.$message.success('标签打印任务已发送!');
} catch (error) { console.error('模板打印失败:', error); this.$message.warning('标签打印失败,请手动打印'); } },
/** * 执行模板打印 * @param {Object} LODOP - 打印控件对象 * @param {Array} printDataList - 打印数据列表 * @param {String} printerName - 用户配置的打印机名称(可选) */ async executePrintWithTemplate(LODOP, printDataList, printerName) { console.log('开始打印,标签数量:', printDataList.length, '打印机:', printerName || '默认', '标签数据:', printDataList);
// 循环打印每个标签(每个标签单独打印一次)
for (let i = 0; i < printDataList.length; i++) { const printData = printDataList[i];
// 获取标签模板编号(存储过程返回)
const labelNo = printData.labelNo;
// 每个标签单独初始化一个打印任务
LODOP.PRINT_INIT('拆合组托标签打印_' + (i + 1));
// 设置用户配置的打印机(如果有)
if (printerName) { LODOP.SET_PRINTER_INDEX(printerName); }
// 设置打印模式
LODOP.SET_PRINT_MODE("PRINT_NOCOLLATE", true);
// 根据标签模板编号调用对应的打印方法
if (labelNo === 'A001') { await this.printLabelA001(LODOP, printData, false); } else if (labelNo === 'A002') { this.printLabelA002(LODOP, printData, false); } else if (labelNo === 'A003') { this.printLabelA003(LODOP, printData, false); } else { // 默认使用 A001 模板
console.warn('未知标签模板:', labelNo, ',使用默认模板 A001'); await this.printLabelA001(LODOP, printData, false); }
// 执行打印
LODOP.PRINT(); } } },
mounted() { // 聚焦扫描框
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;}
.search-container .el-input { width: 100%;}
/* 标签信息卡片 */.label-info-card { background: white; margin: 8px 16px; padding: 16px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); flex: 1; overflow-y: auto;}
.info-row { display: flex; align-items: center; margin-bottom: 16px; min-height: 40px;}
.info-label { width: 80px; font-size: 14px; color: #333; font-weight: 500; flex-shrink: 0;}
.info-value { flex: 1; font-size: 14px; color: #666; margin-left: 12px;}
.status-frozen { color: #ff4949; font-weight: 500;}
.status-normal { color: #17B3A3; font-weight: 500;}
/* 操作按钮 */.action-buttons { display: flex; padding: 16px; gap: 12px; background: white; margin-top: auto;}
.action-btn { flex: 1; padding: 12px; border-radius: 20px; font-size: 14px; cursor: pointer; transition: all 0.2s ease; border: none;}
.split-btn { background: #17B3A3; color: white;}
.split-btn:hover { background: #0d8f7f;}
.merge-btn { background: white; color: #17B3A3; border: 1px solid #17B3A3;}
.merge-btn:hover { background: #17B3A3; color: white;}
.action-btn:active { transform: scale(0.98);}
/* 对话框样式 */.dialog-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.5); z-index: 9999; display: flex; align-items: center; justify-content: center; padding: 20px;}
.dialog-modal { background: white; border-radius: 12px; width: 100%; max-width: 400px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); overflow: hidden;}
.dialog-header { background: #17B3A3; color: white; padding: 16px 20px; text-align: center;}
.dialog-title { font-size: 16px; font-weight: 500;}
.dialog-body { padding: 20px;}
.split-input-section,.merge-input-section { margin-bottom: 20px;}
.split-input,.merge-input { width: 100%;}
.split-input ::v-deep .el-input__inner,
.numInput /deep/ .el-input__inner{ text-align: right;}/deep/ .inlineNumber input::-webkit-outer-spin-button,/deep/ .inlineNumber input::-webkit-inner-spin-button { -webkit-appearance: none;
}/deep/ .inlineNumber input[type="number"]{ -moz-appearance: textfield; padding-right: 5px !important;}
.merge-input ::v-deep .el-input__inner { height: 48px; border: 1px solid #17B3A3; border-radius: 8px; font-size: 16px; text-align: center;}
.dialog-footer { padding: 16px 20px; display: flex; justify-content: center; gap: 12px; border-top: 1px solid #f0f0f0;}
.btn-split,.btn-merge,.btn-cancel { padding: 10px 20px; border-radius: 6px; font-size: 14px; cursor: pointer; transition: all 0.2s; border: none; outline: none;}
.btn-split,.btn-merge { background: #17B3A3; color: white;}
.btn-split:hover:not(:disabled),.btn-merge:hover:not(:disabled) { background: #0d8f7f;}
.btn-split:disabled,.btn-merge:disabled { background: #c0c4cc; cursor: not-allowed;}
.btn-cancel { background: #f5f5f5; color: #666;}
.btn-cancel:hover { background: #e6e6e6;}
/* 响应式设计 */@media (max-width: 360px) { .header-bar { padding: 8px 12px; }
.search-container { padding: 8px 12px; }
.label-info-card { margin: 6px 12px; padding: 12px; }
.info-label { width: 70px; font-size: 13px; }
.info-value { font-size: 13px; }
.action-buttons { padding: 12px; }}</style>
|