Files
uni-pda/pages/settings/devices/printTest.vue
T
2026-06-02 15:28:25 +08:00

565 lines
13 KiB
Vue

<template>
<view class="container">
<!-- 状态栏 -->
<view class="status-section">
<view class="status-item">
<text class="status-label">打印机</text>
<text class="status-value" :class="{ connected: connected }">
{{ connected ? '已连接' : '未连接' }}
</text>
</view>
<view class="status-item" v-if="connected">
<text class="status-label">设备名称</text>
<text class="status-value device-name">{{ printerConfig.deviceName }}</text>
</view>
</view>
<!-- 操作按钮 -->
<view class="section action-section" v-if="!connected">
<button class="btn btn-primary" :loading="isConnecting" @click="handleConnect">
{{ isConnecting ? '连接中...' : '重新连接' }}
</button>
</view>
<!-- 打印控制区 -->
<view class="section print-section" v-if="connected">
<view class="section-header">
<text class="section-title">打印测试</text>
</view>
<!-- 指令类型选择 -->
<view class="command-type">
<text class="type-label">指令类型:</text>
<view class="type-switch">
<view
class="type-btn"
:class="{ active: commandType === 'CPCL' }"
@click="selectCommandType('CPCL')">
CPCL
</view>
<view
class="type-btn"
:class="{ active: commandType === 'TSPL' }"
@click="selectCommandType('TSPL')">
TSPL
</view>
</view>
</view>
<!-- 纸尺寸选择 -->
<view class="paper-size-section">
<text class="paper-size-label">打印纸尺寸</text>
<view class="paper-size-grid">
<view
class="paper-size-item"
:class="{ selected: paperWidth === 100 && paperHeight === 100 }"
@click="selectPaperSize(100, 100)">
<text class="paper-size-name">100mm x 100mm</text>
</view>
<view
class="paper-size-item"
:class="{ selected: paperWidth === 150 && paperHeight === 150 }"
@click="selectPaperSize(150, 150)">
<text class="paper-size-name">150mm x 150mm</text>
</view>
<view
class="paper-size-item"
:class="{ selected: paperWidth === 200 && paperHeight === 200 }"
@click="selectPaperSize(200, 200)">
<text class="paper-size-name">200mm x 200mm</text>
</view>
</view>
</view>
<!-- 打印内容 -->
<view class="print-content">
<view class="content-header">
<text class="content-title">打印内容</text>
<view class="content-actions">
<button size="mini" @click="clearContent">清空</button>
<button size="mini" type="primary" :loading="isPrinting" @click="doPrint">打印</button>
</view>
</view>
<textarea
class="content-textarea"
v-model="printContent"
:placeholder="contentPlaceholder">
</textarea>
</view>
<!-- 快捷模板 -->
<view class="template-section">
<text class="template-title">快捷模板</text>
<scroll-view class="template-list" scroll-x>
<view class="template-tags">
<view class="template-tag" @click="useTemplate('qrcode')">二维码</view>
<view class="template-tag" @click="useTemplate('barcode')">条码</view>
<view class="template-tag" @click="useTemplate('label')">废料标签</view>
</view>
</scroll-view>
</view>
</view>
<!-- 断开连接 -->
<view class="section" v-if="connected">
<button class="btn btn-outline" @click="handleDisconnect">断开连接</button>
</view>
</view>
</template>
<script>
import printerService from '@/utils/BluetoothPrinterService.new.js';
export default {
data() {
return {
isConnecting: false,
isPrinting: false,
connected: false,
connectingText: '正在检查配置...',
printerConfig: {},
commandType: 'CPCL',
paperWidth: 100,
paperHeight: 100,
printContent: ''
}
},
computed: {
contentPlaceholder() {
const sizeCmd = this.commandType === 'CPCL'
? `SIZE ${this.paperWidth}mm ${this.paperHeight}mm`
: `SIZE ${this.paperWidth} mm ${this.paperHeight} mm`
const placeholders = {
'TSPL': `输入TSPL指令,如:\n${sizeCmd}\nGAP 2 mm\nCLS\nTEXT 50 50 "4" 0 1 1 "Hello"\nPRINT`,
'CPCL': `输入CPCL指令,如:\n! 0 200 200 240 1\n${sizeCmd}\nGAP 2mm\nTEXT 4 0 10 10 EN Hello\nPRINT`
}
return placeholders[this.commandType] || '输入打印指令...'
}
},
onLoad() {
this.loadPrinterConfig()
this.syncConfigFromService()
this.checkAndConnect()
},
onShow() {
if (!this.connected && !this.isConnecting) {
this.checkAndConnect()
}
},
onUnload() {
this.handleDisconnect()
},
mounted() {
printerService.onConnectionChange(this.handleConnectionChange)
},
beforeDestroy() {
printerService.offConnectionChange(this.handleConnectionChange)
},
methods: {
handleConnectionChange(connected) {
this.connected = connected
if (connected) {
this.loadPrinterConfig()
this.syncConfigFromService()
}
},
/**
* 从 printerService 同步配置
*/
syncConfigFromService() {
const serviceConfig = printerService.getConfig()
if (serviceConfig.commandType) {
this.commandType = serviceConfig.commandType
}
if (serviceConfig.paperWidth && serviceConfig.paperHeight) {
this.paperWidth = serviceConfig.paperWidth
this.paperHeight = serviceConfig.paperHeight
}
},
loadPrinterConfig() {
const storeState = uni.getStorageSync('printerConfig') || {}
const serviceConfig = printerService.getConfig()
this.printerConfig = {
deviceId: serviceConfig.deviceId || storeState.deviceId || '',
serviceId: serviceConfig.serviceId || storeState.serviceId || '',
writeCharId: serviceConfig.writeCharId || storeState.writeCharId || '',
deviceName: serviceConfig.deviceName || storeState.deviceName || ''
}
},
/**
* 检查配置并自动连接
*/
async checkAndConnect() {
this.loadPrinterConfig()
if (!this.printerConfig.deviceId) {
this.connectingText = '未配置打印机,请先配置设备'
return
}
if (!this.printerConfig.serviceId || !this.printerConfig.writeCharId) {
this.connectingText = '配置不完整,请重新配置设备'
return
}
if (printerService.isConnected()) {
this.connected = true
return
}
this.isConnecting = true
this.connectingText = '正在连接...'
try {
const success = await printerService.autoConnect()
this.connected = success
if (success) {
this.connectingText = '连接成功'
this.syncConfigFromService()
} else {
this.connectingText = '连接失败,请检查设备是否开启'
}
} catch (err) {
this.connectingText = '连接异常: ' + (err.message || '未知错误')
} finally {
this.isConnecting = false
}
},
/**
* 手动连接
*/
async handleConnect() {
if (this.isConnecting) return
this.isConnecting = true
this.connectingText = '正在连接...'
this.loadPrinterConfig()
if (!this.printerConfig.deviceId) {
uni.showToast({ title: '请先配置打印机设备', icon: 'none' })
this.connectingText = '未配置打印机'
this.isConnecting = false
return
}
try {
const success = await printerService.autoConnect()
this.connected = success
if (success) {
uni.showToast({ title: '连接成功', icon: 'none' })
this.syncConfigFromService()
} else {
if (!this.printerConfig.serviceId || !this.printerConfig.writeCharId) {
uni.showToast({ title: '配置不完整,请重新配置', icon: 'none' })
this.connectingText = '配置不完整'
} else {
uni.showToast({ title: '连接失败,请检查设备', icon: 'none' })
this.connectingText = '连接失败'
}
}
} catch (err) {
uni.showToast({ title: '连接异常', icon: 'none' })
this.connectingText = '连接异常'
} finally {
this.isConnecting = false
}
},
/**
* 断开连接
*/
async handleDisconnect() {
if (!this.connected && !this.deviceId) return
try {
await printerService.disconnect()
this.connected = false
this.connectingText = '已断开连接'
} catch (err) {
this.connected = false
}
},
/**
* 选择指令类型
*/
selectCommandType(type) {
this.commandType = type
printerService.setCommandType(type)
// 切换指令类型时同步更新纸张尺寸
printerService.setPaperSize(this.paperWidth, this.paperHeight)
},
/**
* 选择打印纸尺寸
*/
selectPaperSize(width, height) {
this.paperWidth = width
this.paperHeight = height
printerService.setPaperSize(width, height)
},
/**
* 快捷模板
*/
useTemplate(type) {
if (type === 'label') {
const testData = {
mingcheng: '测试废物',
leibie: '危险废物',
daima: 'HW49',
xingtai: '液态',
zhuyaochengfen: '测试成分',
youhaichengfen: '无',
zhuyi: '防潮',
shibiema: 'TEST' + Date.now(),
danwei: '测试单位',
lianxi: '13800138000',
riqi: new Date().toISOString().split('T')[0],
zhongliang: '10kg',
beizhu: '测试备注',
biaoshi: '✓',
qrcode: 'https://www.cswm.org.cn/qr/?code=7G6MOYELnFHZfF1Y0F8e6gjxR2K2pALNJJNhJSuOg17LV6IZfuDWUnF2qC3Rsdejs&type=WFBQ'
}
// 确保 service 使用当前的 commandType 和 paperSize
printerService.setCommandType(this.commandType)
printerService.setPaperSize(this.paperWidth, this.paperHeight)
this.printContent = printerService.generateWasteLabel(testData)
return
}
if (type === 'qrcode') {
printerService.setCommandType(this.commandType)
printerService.setPaperSize(this.paperWidth, this.paperHeight)
this.printContent = printerService.generateQRCodeTemplate('https://www.example.com')
return
}
if (type === 'barcode') {
printerService.setCommandType(this.commandType)
printerService.setPaperSize(this.paperWidth, this.paperHeight)
this.printContent = printerService.generateBarcodeTemplate('1234567890')
return
}
},
/**
* 清空打印内容
*/
clearContent() {
this.printContent = ''
},
/**
* 执行打印
*/
async doPrint() {
if (this.isPrinting) return
if (!this.printContent.trim()) {
uni.showToast({ title: '请输入打印内容', icon: 'none' })
return
}
this.isPrinting = true
try {
// 确保打印前同步配置
printerService.setCommandType(this.commandType)
printerService.setPaperSize(this.paperWidth, this.paperHeight)
await printerService.printText(this.printContent)
uni.showToast({ title: '打印成功', icon: 'success' })
} catch (err) {
uni.showToast({ title: '打印失败: ' + (err.message || err || '未知错误'), icon: 'none' })
} finally {
this.isPrinting = false
}
}
}
}
</script>
<style scoped>
.container {
padding: 20rpx;
background: #f5f5f5;
min-height: 100vh;
}
.status-section {
display: flex;
background: #fff;
padding: 30rpx;
border-radius: 12rpx;
margin-bottom: 20rpx;
}
.status-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
}
.status-label {
font-size: 26rpx;
color: #999;
margin-bottom: 12rpx;
}
.status-value {
font-size: 32rpx;
font-weight: bold;
color: #666;
}
.status-value.connected {
color: #34c759;
}
.device-name {
font-size: 26rpx;
color: #34c759;
max-width: 300rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.section {
background: #fff;
border-radius: 12rpx;
padding: 24rpx;
margin-bottom: 20rpx;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16rpx;
}
.section-title {
font-size: 30rpx;
font-weight: bold;
color: #333;
}
.action-section button {
width: 100%;
margin-bottom: 20rpx;
}
.action-section button:last-child {
margin-bottom: 0;
}
/* 指令类型 */
.command-type {
display: flex;
align-items: center;
margin-bottom: 16rpx;
}
.type-label {
font-size: 26rpx;
color: #666;
margin-right: 16rpx;
}
.type-switch {
display: flex;
border: 1rpx solid #007aff;
border-radius: 8rpx;
overflow: hidden;
}
.type-btn {
padding: 12rpx 24rpx;
font-size: 24rpx;
color: #007aff;
background: #fff;
}
.type-btn.active {
background: #007aff;
color: #fff;
}
/* 纸尺寸 */
.paper-size-section {
margin-bottom: 20rpx;
}
.paper-size-label {
font-size: 26rpx;
color: #666;
margin-bottom: 12rpx;
display: block;
}
.paper-size-grid {
display: flex;
flex-wrap: wrap;
gap: 16rpx;
}
.paper-size-item {
flex: 1;
min-width: 200rpx;
padding: 20rpx;
background: #f0f0f0;
border-radius: 8rpx;
border: 2rpx solid transparent;
text-align: center;
}
.paper-size-item.selected {
background: #e8f4ff;
border-color: #007aff;
}
.paper-size-name {
font-size: 24rpx;
color: #333;
}
.paper-size-item.selected .paper-size-name {
color: #007aff;
font-weight: bold;
}
/* 打印内容 */
.print-section {
min-height: 400rpx;
}
.print-content {
margin-bottom: 20rpx;
}
.content-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12rpx;
}
.content-title {
font-size: 26rpx;
color: #666;
}
.content-actions {
display: flex;
gap: 12rpx;
}
.content-textarea {
width: 100%;
height: 280rpx;
border: 1rpx solid #ddd;
border-radius: 8rpx;
padding: 16rpx;
font-size: 24rpx;
font-family: 'Courier New', monospace;
box-sizing: border-box;
}
/* 快捷模板 */
.template-section {
margin-bottom: 20rpx;
}
.template-title {
font-size: 26rpx;
color: #666;
margin-bottom: 12rpx;
display: block;
}
.template-list {
width: 100%;
}
.template-tags {
display: flex;
gap: 12rpx;
padding: 4rpx;
}
.template-tag {
padding: 12rpx 24rpx;
background: #f0f0f0;
border-radius: 8rpx;
font-size: 24rpx;
color: #333;
}
.template-tag:active {
background: #e0e0e0;
}
</style>