<template>
|
<view class="container">
|
<!-- 操作栏 -->
|
<view class="header no-print" style="margin-top: 10px;">
|
<text class="print-title">打印标签</text>
|
<view @click="navigateBack" class="back-btn">返回</view>
|
</view>
|
|
<!-- 主内容区:确保v-if和v-else相邻 -->
|
<view v-if="tagData" class="tag-card">
|
<!-- 头部(含 logo) -->
|
<view class="card-header">
|
<text class="card-title">成品托标签</text>
|
<!-- 确保 logo 路径正确 -->
|
<image
|
:src="logoUrl"
|
class="card-logo"
|
mode="widthFix"
|
alt="双林新火炬工厂logo"
|
/>
|
</view>
|
|
<!-- 内容区(调整二维码位置) -->
|
<view class="card-body">
|
<view class="info-list">
|
<view class="info-item">
|
<text class="item-label">产品型号:</text>
|
<text class="item-value">{{ tagData.productModel || 'N/A' }}</text>
|
</view>
|
<view class="info-item">
|
<text class="item-label">工单号:</text>
|
<text class="item-value">{{ tagData.palletNo || 'N/A' }}</text>
|
</view>
|
<view class="info-item">
|
<text class="item-label">批次号:</text>
|
<text class="item-value">{{ tagData.batchNo || 'N/A' }}</text>
|
</view>
|
<view class="info-item">
|
<text class="item-label">库存地点:</text>
|
<text class="item-value">{{ tagData.workshop ||'N/A' }}</text>
|
</view>
|
<view class="info-item">
|
<text class="item-label">工单号:</text>
|
<text class="item-value">{{ tagData.produceDate || formatDate(new Date()) }}</text>
|
</view>
|
<view class="info-item">
|
<text class="item-label">任务号:</text>
|
<text class="item-value">{{ tagData.planNum || 'N/A' }}</text>
|
</view>
|
</view>
|
<!-- 二维码区(左移 + 防溢出) -->
|
<view class="qr-section">
|
<uv-qrcode
|
:value="qrContent"
|
:size="80"
|
style="width: 80rpx; height: 80rpx; margin-right: 35px; margin-top: 20px;"
|
></uv-qrcode>
|
</view>
|
</view>
|
|
<!-- 底部 -->
|
<view class="card-footer">
|
<text class="footer-text">双林新火炬工厂 · 质量追溯</text>
|
</view>
|
</view>
|
|
<!-- 空状态:与上面的v-if相邻,解决报错 -->
|
<view v-else class="empty-state">
|
<text>未获取到标签数据</text>
|
</view>
|
</view>
|
</template>
|
|
<script>
|
import uvQrcode from '@/uni_modules/uv-qrcode/components/uv-qrcode/uv-qrcode.vue';
|
import io from '@hyoga/uni-socket.io';
|
import { PrinterCommands } from '@/common/util/printer-commands.js';
|
|
export default {
|
components: { uvQrcode },
|
data() {
|
return {
|
tagData: null, // 从外部接收的数据
|
qrContent: '',
|
logoUrl: '/static/index_logo.png', // 确保此路径有图片
|
printerConfig: null
|
};
|
},
|
onLoad(options) {
|
try {
|
// 接收传递的标签数据
|
if (options.tagData) {
|
this.tagData = JSON.parse(decodeURIComponent(options.tagData));
|
// 生成二维码内容
|
this.qrContent = `PALLET:${this.tagData.palletNo || ''};BATCH:${this.tagData.batchNo || ''}`;
|
}
|
} catch (e) {
|
console.error('标签数据解析错误:', e);
|
uni.showToast({ title: '数据加载失败', icon: 'none' });
|
}
|
this.printerConfig = uni.getStorageSync('printerConfig') || null;
|
},
|
methods: {
|
formatDate(date) {
|
const year = date.getFullYear();
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
const day = String(date.getDate()).padStart(2, '0');
|
return `${year}-${month}-${day}`;
|
},
|
navigateBack() {
|
uni.navigateBack(1);
|
},
|
async handlePrint() {
|
// 打印逻辑保持不变
|
if (!this.tagData) {
|
uni.showToast({ title: '标签数据为空', icon: 'none' });
|
return;
|
}
|
|
this.printerConfig = uni.getStorageSync('printerConfig') || null;
|
if (!this.printerConfig?.ip || !this.printerConfig?.port) {
|
uni.showModal({
|
title: '未配置打印机',
|
content: '是否前往设置?',
|
success: (res) => {
|
if (res.confirm) uni.navigateTo({ url: '/pages/user/location' });
|
}
|
});
|
return;
|
}
|
|
try {
|
uni.showLoading({ title: '打印中...', mask: true });
|
const qrBase64 = this.$refs.qrcode?.toDataURL();
|
if (!qrBase64) throw new Error('获取二维码失败');
|
|
const qrBuffer = this.base64ToArrayBuffer(qrBase64);
|
const printData = { ...this.tagData, qrBuffer, printTime: new Date().toLocaleString() };
|
const commands = new PrinterCommands(this.printerConfig.model || 'generic');
|
const printCommand = commands.buildProductTag(printData);
|
|
await this.sendPrintCommand(printCommand);
|
uni.hideLoading();
|
uni.showToast({ title: '标签打印成功', icon: 'success' });
|
} catch (error) {
|
uni.hideLoading();
|
uni.showToast({ title: error.message || '打印失败', icon: 'none' });
|
}
|
},
|
sendPrintCommand(command) {
|
return new Promise((resolve, reject) => {
|
if (!this.printerConfig?.ip || !this.printerConfig?.port) {
|
reject(new Error('打印机配置不完整'));
|
return;
|
}
|
|
const protocol = this.printerConfig.model === 'zebra' ? 'tcp://' : 'ws://';
|
const socket = io(`${protocol}${this.printerConfig.ip}:${this.printerConfig.port}`, {
|
transports: ['websocket'],
|
timeout: 5000
|
});
|
|
let isResolved = false;
|
socket.on('connect', () => {
|
socket.emit('print', command, (response) => {
|
socket.disconnect();
|
if (!isResolved) {
|
isResolved = true;
|
response.success ? resolve() : reject(new Error(response.error || '打印失败'));
|
}
|
});
|
});
|
|
socket.on('connect_error', (err) => {
|
if (!isResolved) {
|
isResolved = true;
|
reject(new Error(`连接失败: ${err.message}`));
|
}
|
});
|
|
setTimeout(() => {
|
if (!isResolved) {
|
isResolved = true;
|
socket.disconnect();
|
reject(new Error('打印超时'));
|
}
|
}, 10000);
|
});
|
},
|
base64ToArrayBuffer(base64) {
|
const binaryString = uni.base64Decode(base64);
|
const len = binaryString.length;
|
const bytes = new Uint8Array(len);
|
for (let i = 0; i < len; i++) {
|
bytes[i] = binaryString.charCodeAt(i);
|
}
|
return bytes.buffer;
|
}
|
}
|
};
|
</script>
|
|
<style scoped>
|
.container {
|
background-color: #fff;
|
padding: 20rpx;
|
min-height: 100vh;
|
}
|
|
/* 操作栏 */
|
.header {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
padding: 10rpx 0;
|
border-bottom: 1px solid #eee;
|
}
|
.print-title {
|
font-size: 32rpx;
|
font-weight: bold;
|
color: #333;
|
}
|
.back-btn {
|
font-size: 28rpx;
|
color: #007AFF;
|
}
|
|
/* 标签卡片 */
|
.tag-card {
|
width: 680rpx; /* 加宽卡片避免内容溢出 */
|
margin: 30rpx auto;
|
border: 1px solid #eee;
|
border-radius: 8rpx;
|
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.05);
|
padding: 10rpx;
|
}
|
|
/* 卡片头部(含 logo) */
|
.card-header {
|
display: flex;
|
align-items: center;
|
padding: 20rpx;
|
border-bottom: 2px solid #007AFF;
|
}
|
.card-title {
|
font-size: 32rpx;
|
font-weight: bold;
|
color: #333;
|
flex: 1;
|
}
|
.card-logo {
|
width: 120rpx;
|
height: auto;
|
}
|
|
/* 卡片内容 */
|
.card-body {
|
display: flex;
|
padding: 20rpx;
|
gap: 10rpx; /* 控制信息区和二维码间距 */
|
}
|
.info-list {
|
flex: 1;
|
}
|
.info-item {
|
display: flex;
|
margin-bottom: 15rpx;
|
line-height: 1.6;
|
}
|
.item-label {
|
width: 140rpx;
|
font-size: 26rpx;
|
color: #666;
|
font-weight: 500;
|
}
|
.item-value {
|
flex: 1;
|
font-size: 26rpx;
|
color: #333;
|
word-break: break-all;
|
}
|
|
/* 二维码区(左移调整) */
|
.qr-section {
|
display: flex;
|
flex-direction: column;
|
align-items: center;
|
margin-left: 10rpx; /* 左移减少间距 */
|
}
|
.uv-qrcode {
|
width: 140rpx !important;
|
height: 140rpx !important;
|
}
|
.qr-tip {
|
margin-top: 10rpx;
|
font-size: 22rpx;
|
color: #666;
|
text-align: center;
|
}
|
|
/* 卡片底部 */
|
.card-footer {
|
padding: 15rpx 20rpx;
|
border-top: 1px dashed #ccc;
|
text-align: center;
|
}
|
.footer-text {
|
font-size: 22rpx;
|
color: #999;
|
}
|
|
/* 空状态 */
|
.empty-state {
|
display: flex;
|
justify-content: center;
|
align-items: center;
|
height: 300rpx;
|
color: #999;
|
font-size: 28rpx;
|
}
|
|
/* 打印控制 */
|
@media print {
|
.no-print {
|
display: none;
|
}
|
.tag-card {
|
width: 100%;
|
box-shadow: none;
|
border: none;
|
}
|
}
|
</style>
|