565 lines
13 KiB
Vue
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>
|