815 lines
21 KiB
Vue
815 lines
21 KiB
Vue
<template>
|
|
<view class="container">
|
|
<!-- 状态栏 -->
|
|
<view class="status-section">
|
|
<view class="status-item">
|
|
<text class="status-label">蓝牙适配器</text>
|
|
<text class="status-value" :class="adapterState.available ? 'available' : ''">
|
|
{{ adapterState.available ? '就绪' : '未就绪' }}
|
|
</text>
|
|
</view>
|
|
<view class="status-item">
|
|
<text class="status-label">打印机</text>
|
|
<text class="status-value" :class="{ connected: connected }">
|
|
{{ connected ? '已连接' : '未连接' }}
|
|
</text>
|
|
</view>
|
|
<view class="status-item">
|
|
<text class="status-label">连接方式</text>
|
|
<text class="status-value auto">自动</text>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 连接信息 -->
|
|
<view class="section" v-if="savedConfig">
|
|
<view class="section-header">
|
|
<text class="section-title">已保存的配置</text>
|
|
</view>
|
|
<view class="config-info">
|
|
<view class="config-row">
|
|
<text class="config-label">设备名称</text>
|
|
<text class="config-value">{{ savedConfig.deviceName }}</text>
|
|
</view>
|
|
<view class="config-row">
|
|
<text class="config-label">服务UUID</text>
|
|
<text class="config-value uuid">{{ savedConfig.serviceId }}</text>
|
|
</view>
|
|
<view class="config-row">
|
|
<text class="config-label">特征值UUID</text>
|
|
<text class="config-value uuid">{{ savedConfig.writeCharId }}</text>
|
|
</view>
|
|
<view class="config-row" v-if="savedConfig.paperWidth && savedConfig.paperHeight">
|
|
<text class="config-label">纸尺寸</text>
|
|
<text class="config-value">{{ savedConfig.paperWidth }}mm x {{ savedConfig.paperHeight }}mm</text>
|
|
</view>
|
|
<view class="config-row" v-else-if="savedConfig.paperSize">
|
|
<text class="config-label">纸尺寸</text>
|
|
<text class="config-value">{{ savedConfig.paperSize }}</text>
|
|
</view>
|
|
<view class="config-row" v-if="savedConfig.lastUpdated">
|
|
<text class="config-label">保存时间</text>
|
|
<text class="config-value">{{ formatDate(savedConfig.lastUpdated) }}</text>
|
|
</view>
|
|
</view>
|
|
<view class="config-actions">
|
|
<button type="warn" size="mini" @click="clearConfig">清除配置</button>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 无配置提示 -->
|
|
<view class="section empty-section" v-else>
|
|
<text class="empty-text">暂无保存的配置</text>
|
|
<text class="empty-hint">请先在蓝牙打印页面连接打印机并保存配置</text>
|
|
<button type="primary" size="mini" @click="goToBlePrint">前往配置</button>
|
|
</view>
|
|
|
|
<!-- 打印控制区 -->
|
|
<view class="section print-section" v-if="connected">
|
|
<view class="section-header">
|
|
<text class="section-title">打印控制</text>
|
|
<text class="printer-name">{{ connectedDeviceName }}</text>
|
|
</view>
|
|
|
|
<!-- 指令类型选择 -->
|
|
<view class="command-type">
|
|
<view class="type-label">指令类型:</view>
|
|
<view class="type-switch">
|
|
<view
|
|
class="type-btn"
|
|
:class="{ active: commandType === 'CPCL' }"
|
|
@click="commandType = 'CPCL'">
|
|
CPCL
|
|
</view>
|
|
<view
|
|
class="type-btn"
|
|
:class="{ active: commandType === 'TSPL' }"
|
|
@click="commandType = '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">100mmx100mm</text>
|
|
</view>
|
|
<view
|
|
class="paper-size-item"
|
|
:class="{ selected: paperWidth === 150 && paperHeight === 150 }"
|
|
@click="selectPaperSize(150, 150)">
|
|
<text class="paper-size-name">150mmx150mm</text>
|
|
</view>
|
|
<view
|
|
class="paper-size-item"
|
|
:class="{ selected: paperWidth === 200 && paperHeight === 200 }"
|
|
@click="selectPaperSize(200, 200)">
|
|
<text class="paper-size-name">200mmx200mm</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" @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 connecting-section" v-else-if="savedConfig && isConnecting">
|
|
<view class="connecting-content">
|
|
<view class="loading-icon"></view>
|
|
<text class="connecting-text">正在自动连接...</text>
|
|
<text class="connecting-hint">{{ savedConfig.deviceName }}</text>
|
|
</view>
|
|
<button type="default" size="mini" @click="cancelConnect">取消</button>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
import * as iconv from 'iconv-lite';
|
|
import { mapGetters, mapActions } from 'vuex';
|
|
|
|
export default {
|
|
data() {
|
|
return {
|
|
adapterState: {
|
|
available: false,
|
|
discovering: false
|
|
},
|
|
isConnecting: false,
|
|
connectedDeviceId: '',
|
|
connectedDeviceName: '',
|
|
connected: false,
|
|
writeServiceId: '',
|
|
writeCharacteristicId: '',
|
|
|
|
commandType: 'CPCL',
|
|
printContent: ''
|
|
}
|
|
},
|
|
computed: {
|
|
// 从 Vuex store 映射配置
|
|
...mapGetters('print', ['savedConfig', 'paperWidth', 'paperHeight']),
|
|
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.loadConfig()
|
|
},
|
|
onUnload() {
|
|
this.cleanup()
|
|
},
|
|
methods: {
|
|
// 映射 Vuex actions
|
|
...mapActions('print', ['updatePaperSize', 'clearPrinterConfig']),
|
|
|
|
formatDate(dateStr) {
|
|
const date = new Date(dateStr)
|
|
return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')} ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`
|
|
},
|
|
loadConfig() {
|
|
// 配置通过 mapGetters 从 store 获取,自动同步
|
|
// 如果有保存的配置,自动尝试连接
|
|
if (this.savedConfig?.deviceId) {
|
|
setTimeout(() => {
|
|
this.autoConnect()
|
|
}, 500)
|
|
}
|
|
},
|
|
async clearConfig() {
|
|
try {
|
|
await this.clearPrinterConfig()
|
|
uni.showToast({ title: '配置已清除', icon: 'success' })
|
|
} catch (error) {
|
|
console.error('清除配置失败:', error)
|
|
}
|
|
},
|
|
goToBlePrint() {
|
|
uni.navigateTo({
|
|
url: '/pages/testpages/bleprint'
|
|
})
|
|
},
|
|
async autoConnect() {
|
|
if (!this.savedConfig) return
|
|
|
|
this.isConnecting = true
|
|
|
|
try {
|
|
await this.initBluetooth()
|
|
await this.createConnection()
|
|
|
|
this.connected = true
|
|
this.connectedDeviceId = this.savedConfig.deviceId
|
|
this.connectedDeviceName = this.savedConfig.deviceName
|
|
this.writeServiceId = this.savedConfig.serviceId
|
|
this.writeCharacteristicId = this.savedConfig.writeCharId
|
|
this.isConnecting = false
|
|
uni.showToast({ title: '连接成功', icon: 'none' })
|
|
|
|
} catch (err) {
|
|
this.isConnecting = false
|
|
uni.showToast({ title: '连接失败', icon: 'none' })
|
|
}
|
|
},
|
|
initBluetooth() {
|
|
return new Promise((resolve, reject) => {
|
|
uni.openBluetoothAdapter({
|
|
success: () => {
|
|
this.adapterState.available = true
|
|
uni.onBLEConnectionStateChange((res) => {
|
|
if (!res.connected && this.connectedDeviceId === res.deviceId) {
|
|
this.connected = false
|
|
this.connectedDeviceId = ''
|
|
this.connectedDeviceName = ''
|
|
}
|
|
})
|
|
resolve()
|
|
},
|
|
fail: (err) => {
|
|
this.adapterState.available = false
|
|
// 检测蓝牙是否未打开
|
|
if (err.errMsg && (err.errMsg.includes('not available') || err.errMsg.includes('available'))) {
|
|
uni.showModal({
|
|
title: '蓝牙未开启',
|
|
content: '请先开启手机蓝牙,然后重试',
|
|
confirmText: '重试',
|
|
cancelText: '取消',
|
|
success: (res) => {
|
|
if (res.confirm) {
|
|
// 延迟重试
|
|
setTimeout(() => {
|
|
this.autoConnect()
|
|
}, 300)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
reject(err)
|
|
}
|
|
})
|
|
})
|
|
},
|
|
createConnection() {
|
|
return new Promise((resolve, reject) => {
|
|
uni.createBLEConnection({
|
|
deviceId: this.savedConfig.deviceId,
|
|
timeout: 15000,
|
|
success: () => {
|
|
resolve()
|
|
},
|
|
fail: (err) => {
|
|
reject(err)
|
|
}
|
|
})
|
|
})
|
|
},
|
|
cancelConnect() {
|
|
this.isConnecting = false
|
|
},
|
|
disconnectPrinter() {
|
|
if (!this.connectedDeviceId) return
|
|
uni.closeBLEConnection({
|
|
deviceId: this.connectedDeviceId,
|
|
success: () => {
|
|
this.connected = false
|
|
this.connectedDeviceId = ''
|
|
this.connectedDeviceName = ''
|
|
}
|
|
})
|
|
},
|
|
clearContent() {
|
|
this.printContent = ''
|
|
},
|
|
/**
|
|
* 选择打印纸尺寸
|
|
* @param {number} width - 纸张宽度(mm)
|
|
* @param {number} height - 纸张高度(mm)
|
|
*/
|
|
async selectPaperSize(width, height) {
|
|
try {
|
|
await this.updatePaperSize({ width, height })
|
|
uni.showToast({
|
|
title: `已选择 ${width}mm x ${height}mm`,
|
|
icon: 'none',
|
|
duration: 1500
|
|
})
|
|
} catch (error) {
|
|
console.error('更新纸张尺寸失败:', error)
|
|
}
|
|
},
|
|
// 获取当前配置的纸张尺寸对应的SIZE命令
|
|
getSizeCommand() {
|
|
const { paperWidth, paperHeight, commandType } = this
|
|
if (commandType === 'CPCL') {
|
|
return `SIZE ${paperWidth}mm ${paperHeight}mm`
|
|
} else if (commandType === 'TSPL') {
|
|
return `SIZE ${paperWidth} mm ${paperHeight} mm`
|
|
}
|
|
return ''
|
|
},
|
|
useTemplate(type) {
|
|
// 如果是标签类型,使用废物标签模板
|
|
if (type === 'label') {
|
|
const wasteData = {
|
|
mingcheng: '废弃电池',
|
|
leibie: '电子废物',
|
|
daima: 'HW01',
|
|
xingtai: '固体',
|
|
zhuyaochengfen: '锂、钴',
|
|
youhaichengfen: '重金属',
|
|
zhuyi: '防潮防火',
|
|
shibiema: '2025-001',
|
|
danwei: '环保公司',
|
|
lianxi: '13800000000',
|
|
riqi: '2025-12-31',
|
|
zhongliang: '5.2KG',
|
|
beizhu: '需专业处理',
|
|
biaoshi: '●',
|
|
// qrcode: 'http://www.opmonitor.com/?hw=7G6MOYELnFHZfF1Y0F8e6gjxR2K2pALN'
|
|
qrcode: 'https://www.cswm.org.cn/qr/?code=7G6MOYELnFHZfF1Y0F8e6gjxR2K2pALNJJNhJSuOg17LV6IZfuDWUnF2qC3Rsdejs&type=WFBQ'
|
|
}
|
|
this.useWasteLabel(wasteData)
|
|
return
|
|
}
|
|
|
|
const sizeCmd = this.getSizeCommand()
|
|
const gapCmd = this.commandType === 'CPCL' ? 'GAP 2mm' : 'GAP 2 mm'
|
|
const templates = {
|
|
qrcode: {
|
|
CPCL: `! 0 200 200 240 1\n${sizeCmd}\n${gapCmd}\nCENTER\nQR 300 10 M 2 A 0\nMA,https://www.example.com\nENDQR\nPRINT`,
|
|
TSPL: `${sizeCmd}\n${gapCmd}\nCLS\nQRCODE 50 50 L 5 0 A 0 "https://www.example.com"\nPRINT`
|
|
},
|
|
barcode: {
|
|
CPCL: `! 0 200 200 240 1\n${sizeCmd}\n${gapCmd}\nCENTER\nBARCODE 128 1 1 60 1 0 10 40 1234567890\nPRINT`,
|
|
TSPL: `${sizeCmd}\n${gapCmd}\nCLS\nBARCODE 50 50 "128" 50 1 0 2 2 "1234567890"\nPRINT`
|
|
}
|
|
}
|
|
this.printContent = templates[type][this.commandType]
|
|
},
|
|
stringToBuffer(str) {
|
|
try {
|
|
const uint8Array = iconv.encode(str, 'gbk');
|
|
return uint8Array.buffer;
|
|
} catch (e) {
|
|
console.warn('编码失败,使用回退方案', e)
|
|
const buffer = new ArrayBuffer(str.length)
|
|
const dataView = new DataView(buffer)
|
|
for (let i = 0; i < str.length; i++) {
|
|
dataView.setUint8(i, str.charCodeAt(i) & 0xFF)
|
|
}
|
|
return buffer
|
|
}
|
|
},
|
|
doPrint() {
|
|
if (!this.connected) {
|
|
uni.showToast({ title: '请先连接打印机', icon: 'none' })
|
|
return
|
|
}
|
|
if (!this.printContent.trim()) {
|
|
uni.showToast({ title: '请输入打印内容', icon: 'none' })
|
|
return
|
|
}
|
|
if (!this.writeCharacteristicId) {
|
|
uni.showToast({ title: '未配置特征值', icon: 'none' })
|
|
return
|
|
}
|
|
const buffer = this.stringToBuffer(this.printContent)
|
|
|
|
uni.writeBLECharacteristicValue({
|
|
deviceId: this.connectedDeviceId,
|
|
serviceId: this.writeServiceId,
|
|
characteristicId: this.writeCharacteristicId,
|
|
value: buffer,
|
|
writeType: 'writeNoResponse',
|
|
success: () => {
|
|
uni.showToast({ title: '打印成功', icon: 'none' })
|
|
},
|
|
fail: (err) => {
|
|
console.log(err)
|
|
uni.showToast({ title: '发送失败', icon: 'error' })
|
|
}
|
|
})
|
|
},
|
|
generateWasteLabel(data) {
|
|
const sizeCmd = this.getSizeCommand()
|
|
const gapCmd = this.commandType === 'CPCL' ? 'GAP 2mm' : 'GAP 2 mm'
|
|
|
|
if (this.commandType === 'CPCL') {
|
|
return this.generateCPCLWasteLabel(data, sizeCmd, gapCmd)
|
|
} else {
|
|
return this.generateTSPLWasteLabel(data, sizeCmd, gapCmd)
|
|
}
|
|
},
|
|
generateCPCLWasteLabel(data, sizeCmd, gapCmd) {
|
|
const fieldOrder = [
|
|
'mingcheng', 'leibie', 'daima', 'xingtai',
|
|
'zhuyaochengfen', 'youhaichengfen', 'zhuyi',
|
|
'shibiema', 'danwei',
|
|
'lianxi', 'riqi',
|
|
'zhongliang', 'beizhu', 'biaoshi'
|
|
]
|
|
|
|
const fieldLabels = {
|
|
mingcheng: '名称', leibie: '类别', daima: '代码', xingtai: '形态',
|
|
zhuyaochengfen: '主要成分', youhaichengfen: '有害成分', zhuyi: '注意',
|
|
shibiema: '识别码', danwei: '单位', lianxi: '联系', riqi: '日期',
|
|
zhongliang: '重量', beizhu: '备注', biaoshi: '标识'
|
|
}
|
|
const conf = [
|
|
{x: 192, y: 100, L: '24mm', T: 'mm', label: '名称', field: 'mingcheng'},
|
|
{x: 192, y: 150, L: '24mm', T: 'mm', label: '类别', field: 'leibie'},
|
|
{x: 192, y: 200, L: '24mm', T: 'mm', label: '代码', field: 'daima'},
|
|
{x: 480, y: 200, L: '60mm', T: 'mm', label: '形态', field: 'xingtai'},
|
|
{x: 192, y: 250, L: '24mm', T: 'mm', label: '主要成分', field: 'zhuyaochengfen'},
|
|
{x: 192, y: 350, L: '24mm', T: 'mm', label: '有害成分', field: 'youhaichengfen'},
|
|
|
|
{x: 192, y: 448, L: '24mm', T: '56mm', label: '注意', field: 'zhuyi'},
|
|
|
|
{x: 216, y: 536, L: '27mm', T: '67mm', label: '识别码', field: 'shibiema'},
|
|
{x: 256, y: 584, L: '32mm', T: '73mm', label: '单位', field: 'danwei'},
|
|
|
|
{x: 288, y: 632, L: '36mm', T: 'mm', label: '联系', field: 'lianxi'},
|
|
|
|
{x: 192, y: 680, L: '24mm', T: 'mm', label: '日期', field: 'riqi'},
|
|
{x: 480, y: 680, L: '60mm', T: 'mm', label: '重量', field: 'zhongliang'},
|
|
{x: 150, y: 728, L: 'mm', T: '88mm', label: '备注', field: 'beizhu'},
|
|
|
|
{x: 480, y: 448, L: '24mm', T: 'mm', label: '标识', field: 'biaoshi'},
|
|
]
|
|
|
|
let commands = []
|
|
commands.push('! 0 200 200 800 1')
|
|
commands.push(sizeCmd)
|
|
// commands.push(gapCmd)
|
|
// commands.push('GAP-SENSE')
|
|
// commands.push('CENTER')
|
|
|
|
fieldOrder.forEach((field, index) => {
|
|
if (data[field]) {
|
|
const {x, y } = conf[index]
|
|
const label = fieldLabels[field] || field
|
|
const content = `${data[field]}`
|
|
commands.push(`T 1 0 ${x} ${y} ${content}`)
|
|
}
|
|
})
|
|
|
|
if (data.qrcode) {
|
|
// commands.push(`B QR 624 584 M 2 U 5`)
|
|
commands.push(`B QR 624 584 M 2 U 4`)
|
|
commands.push(`L0,${data.qrcode}`)
|
|
commands.push('ENDQR')
|
|
}
|
|
|
|
commands.push('FORM')
|
|
commands.push('PRINT')
|
|
return commands.join('\n')
|
|
},
|
|
generateTSPLWasteLabel(data, sizeCmd, gapCmd) {
|
|
const fieldOrder = [
|
|
'mingcheng', 'leibie', 'daima', 'xingtai',
|
|
'zhuyaochengfen', 'youhaichengfen', 'zhuyi',
|
|
'shibiema', 'danwei', 'lianxi', 'riqi',
|
|
'zhongliang', 'beizhu'
|
|
]
|
|
|
|
const fieldLabels = {
|
|
mingcheng: '名称', leibie: '类别', daima: '代码', xingtai: '形态',
|
|
zhuyaochengfen: '主要成分', youhaichengfen: '有害成分', zhuyi: '注意',
|
|
shibiema: '识别码', danwei: '单位', lianxi: '联系', riqi: '日期',
|
|
zhongliang: '重量', beizhu: '备注'
|
|
}
|
|
|
|
let commands = []
|
|
commands.push(sizeCmd)
|
|
commands.push(gapCmd)
|
|
commands.push('CLS')
|
|
|
|
fieldOrder.forEach((field, index) => {
|
|
if (data[field]) {
|
|
const y = 20 + (index * 25)
|
|
const label = fieldLabels[field] || field
|
|
const content = `${label}: ${data[field]}`
|
|
commands.push(`TEXT 20 ${y} "4" 0 1 1 "${content}"`)
|
|
}
|
|
})
|
|
|
|
if (data.qrcode) {
|
|
commands.push(`QRCODE 150 400 L 5 0 A 0 "${data.qrcode}"`)
|
|
}
|
|
|
|
commands.push('PRINT')
|
|
return commands.join('\n')
|
|
},
|
|
useWasteLabel(data) {
|
|
this.printContent = this.generateWasteLabel(data)
|
|
},
|
|
cleanup() {
|
|
if (this.connectedDeviceId) {
|
|
uni.closeBLEConnection({
|
|
deviceId: this.connectedDeviceId
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.container {
|
|
padding: 20rpx;
|
|
background: #f5f5f5;
|
|
min-height: 100vh;
|
|
}
|
|
.status-section {
|
|
display: flex;
|
|
background: #fff;
|
|
padding: 20rpx;
|
|
border-radius: 12rpx;
|
|
margin-bottom: 20rpx;
|
|
}
|
|
.status-item {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
}
|
|
.status-label {
|
|
font-size: 24rpx;
|
|
color: #999;
|
|
margin-bottom: 8rpx;
|
|
}
|
|
.status-value {
|
|
font-size: 28rpx;
|
|
font-weight: bold;
|
|
color: #666;
|
|
}
|
|
.status-value.available {
|
|
color: #007aff;
|
|
}
|
|
.status-value.connected {
|
|
color: #34c759;
|
|
}
|
|
.status-value.auto {
|
|
color: #ff9500;
|
|
}
|
|
.section {
|
|
background: #fff;
|
|
border-radius: 12rpx;
|
|
padding: 20rpx;
|
|
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;
|
|
}
|
|
.printer-name {
|
|
font-size: 24rpx;
|
|
color: #34c759;
|
|
}
|
|
.config-info {
|
|
background: #f8f8f8;
|
|
border-radius: 8rpx;
|
|
padding: 16rpx;
|
|
margin-bottom: 16rpx;
|
|
}
|
|
.config-row {
|
|
display: flex;
|
|
margin-bottom: 12rpx;
|
|
}
|
|
.config-row:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
.config-label {
|
|
font-size: 24rpx;
|
|
color: #666;
|
|
min-width: 140rpx;
|
|
}
|
|
.config-value {
|
|
font-size: 24rpx;
|
|
color: #333;
|
|
flex: 1;
|
|
}
|
|
.config-value.uuid {
|
|
font-family: 'Courier New', monospace;
|
|
font-size: 22rpx;
|
|
word-break: break-all;
|
|
}
|
|
.config-actions {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
}
|
|
.empty-section {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
padding: 60rpx 20rpx;
|
|
}
|
|
.empty-text {
|
|
font-size: 30rpx;
|
|
color: #666;
|
|
margin-bottom: 16rpx;
|
|
}
|
|
.empty-hint {
|
|
font-size: 24rpx;
|
|
color: #999;
|
|
margin-bottom: 32rpx;
|
|
text-align: center;
|
|
}
|
|
.print-section {
|
|
min-height: 400rpx;
|
|
}
|
|
.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-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: 180rpx;
|
|
border: 1rpx solid #ddd;
|
|
border-radius: 8rpx;
|
|
padding: 16rpx;
|
|
font-size: 26rpx;
|
|
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;
|
|
}
|
|
.connecting-section {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
padding: 60rpx 20rpx;
|
|
}
|
|
.connecting-content {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
margin-bottom: 32rpx;
|
|
}
|
|
.loading-icon {
|
|
width: 60rpx;
|
|
height: 60rpx;
|
|
border: 4rpx solid #007aff;
|
|
border-top-color: transparent;
|
|
border-radius: 50%;
|
|
animation: rotate 1s linear infinite;
|
|
margin-bottom: 24rpx;
|
|
}
|
|
@keyframes rotate {
|
|
to { transform: rotate(360deg); }
|
|
}
|
|
.connecting-text {
|
|
font-size: 28rpx;
|
|
color: #333;
|
|
margin-bottom: 8rpx;
|
|
}
|
|
.connecting-hint {
|
|
font-size: 24rpx;
|
|
color: #999;
|
|
}
|
|
</style>
|