daShangDao_psiWebApp/src/views/printerManager/printerManager.vue
97694731 939d55c950
Some checks failed
CI / build (18.x) (push) Failing after 1m34s
CI / build (20.x) (push) Failing after 34s
CI / deploy-preview (push) Has been skipped
CI / lint (push) Failing after 34s
CI / test (push) Failing after 34s
CI / security (push) Failing after 35s
6.22lct bug 修改
2026-06-22 17:10:51 +08:00

309 lines
10 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="printer-manager">
<el-card class="printer-card">
<template #header>
<div style="display: flex; justify-content: space-between; align-items: center; width: 100%;">
<span>打印机管理</span>
<el-button type="primary" @click="refreshPrinters" :loading="loading">
<el-icon>
<Refresh />
</el-icon>
刷新打印机列表
</el-button>
</div>
</template>
<el-form label-width="140px" label-position="left" class="printer-form">
<el-form-item label="条码打印机">
<div class="printer-row">
<el-select v-model="barcodePrinter" placeholder="请选择条码打印机" style="width: 400px" clearable>
<el-option v-for="p in printers" :key="p.name" :label="p.name" :value="p.name" />
</el-select>
<el-button type="primary" @click="confirmBarcodePrinter">确定</el-button>
</div>
</el-form-item>
<el-form-item label="快递单打印机">
<div class="printer-row">
<el-select v-model="expressPrinter" placeholder="请选择快递单打印机" style="width: 400px" clearable>
<el-option v-for="p in printers" :key="p.name" :label="p.name" :value="p.name" />
</el-select>
<el-button type="primary" @click="confirmExpressPrinter">确定</el-button>
</div>
</el-form-item>
<el-form-item label="小票纸大小">
<div class="printer-row">
<el-select v-model="paperSize" placeholder="请选择小票纸大小" style="width: 400px" clearable>
<el-option label="60*30" value="60*30" />
<el-option label="60*40" value="60*40" />
<el-option label="40*60" value="40*60" />
</el-select>
<el-button type="primary" @click="confirmPaperSize">确定</el-button>
</div>
</el-form-item>
<!-- TODO -->
<el-form-item label="切换模式快捷键">
<div class="printer-row">
<el-input :model-value="'Alt+c'" disabled style="width: 200px" />
<img v-if="barcodeImages['Alt+c']" :src="barcodeImages['Alt+c']" />
<el-button type="primary" @click="handlePrintBarcode('Alt+c')">打印条码</el-button>
</div>
</el-form-item>
<el-form-item label="拍照快捷键">
<div class="printer-row">
<el-input :model-value="'Alt+a'" disabled style="width: 200px" />
<img v-if="barcodeImages['Alt+a']" :src="barcodeImages['Alt+a']" />
<el-button type="primary" @click="handlePrintBarcode('Alt+a')">打印条码</el-button>
</div>
</el-form-item>
<el-form-item label="创建波次快捷键">
<div class="printer-row">
<el-input :model-value="'Alt+x'" disabled style="width: 200px" />
<img v-if="barcodeImages['Alt+x']" :src="barcodeImages['Alt+x']" />
<el-button type="primary" @click="handlePrintBarcode('Alt+x')">打印条码</el-button>
</div>
</el-form-item>
<el-form-item label="生成波次条形码快捷键">
<div class="printer-row">
<el-input :model-value="'Alt+b'" disabled style="width: 200px" />
<img v-if="barcodeImages['Alt+b']" :src="barcodeImages['Alt+b']" />
<el-button type="primary" @click="handlePrintBarcode('Alt+b')">打印条码</el-button>
</div>
</el-form-item>
</el-form>
<el-empty v-if="!loading && printers.length === 0" description="未检测到可用打印机" />
</el-card>
<el-card class="printer-card">
<template #header>
<div style="display: flex; justify-content: space-between; align-items: center; width: 100%;">
<span>摄像头配置</span>
<el-button type="primary" @click="refreshCameras" :loading="cameraLoading">
<el-icon>
<Refresh />
</el-icon>
刷新摄像头列表
</el-button>
</div>
</template>
<el-form label-width="140px" label-position="left" class="printer-form">
<el-form-item label="摄像头">
<div class="printer-row">
<el-select v-model="selectedCamera" placeholder="请选择摄像头" style="width: 400px" clearable>
<el-option v-for="c in cameras" :key="c.deviceId" :label="c.label" :value="c.deviceId" />
</el-select>
<el-button type="primary" @click="confirmCamera">确定</el-button>
</div>
</el-form-item>
</el-form>
<el-empty v-if="!cameraLoading && cameras.length === 0" description="未检测到可用摄像头" />
</el-card>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { Refresh } from '@element-plus/icons-vue'
import { getSimplePrinterList, initLodop, createPrintTask } from '@/api/print'
import JsBarcode from 'jsbarcode'
const STORAGE_KEY_BARCODE = 'printer_barcode'
const STORAGE_KEY_EXPRESS = 'printer_express'
const STORAGE_KEY_PAPER_SIZE = 'printer_paper_size'
const STORAGE_KEY_CAMERA = 'printer_camera'
const loading = ref(false)
const printers = ref([])
const barcodePrinter = ref('')
const expressPrinter = ref('')
const paperSize = ref('')
const cameraLoading = ref(false)
const cameras = ref([])
const selectedCamera = ref('')
const shortcutItems = ['Alt+c', 'Alt+a', 'Alt+x', 'Alt+b']
const barcodeImages = reactive({})
shortcutItems.forEach(key => {
barcodeImages[key] = ''
})
const fetchBarcodeImage = (text) => {
try {
const canvas = document.createElement('canvas')
JsBarcode(canvas, text, {
format: 'CODE128',
fontSize: 14,
margin: 5,
displayValue: true,
lineColor: '#000000',
width: 1.5,
height: 40
})
barcodeImages[text] = canvas.toDataURL('image/png')
} catch {
// ignore barcode load error
}
}
/** 使用 C-Lodop 和 localStorage 中保存的打印机打印条码 */
const handlePrintBarcode = async (key) => {
const imageUrl = barcodeImages[key]
if (!imageUrl) {
ElMessage.warning({ message: '条形码尚未生成,无法打印', customClass: 'scan-warning-message' })
return
}
const printerName = localStorage.getItem(STORAGE_KEY_BARCODE)
if (!printerName) {
ElMessage.error({ message: '条码打印机未选择', customClass: 'scan-error-message' })
return
}
try {
const LODOP = await createPrintTask('barcode', { content: key })
LODOP.SET_PRINTER_INDEX(printerName)
LODOP.PRINT()
ElMessage.success({ message: '打印任务已发送', duration: 1000, customClass: 'scan-success-message' })
} catch (error) {
ElMessage.error({ message: '打印失败:' + (error.message || '未知错误'), customClass: 'scan-error-message' })
}
}
const confirmBarcodePrinter = () => {
if (barcodePrinter.value) {
localStorage.setItem(STORAGE_KEY_BARCODE, barcodePrinter.value)
ElMessage.success({ message: '条码打印机已保存', duration: 1000, customClass: 'scan-success-message' })
} else {
localStorage.removeItem(STORAGE_KEY_BARCODE)
ElMessage.success({ message: '条码打印机已清除', duration: 1000, customClass: 'scan-success-message' })
}
}
const confirmPaperSize = () => {
if (paperSize.value) {
localStorage.setItem(STORAGE_KEY_PAPER_SIZE, paperSize.value)
ElMessage.success({ message: '小票纸大小已保存', duration: 1000, customClass: 'scan-success-message' })
}
}
const confirmExpressPrinter = () => {
if (expressPrinter.value) {
localStorage.setItem(STORAGE_KEY_EXPRESS, expressPrinter.value)
ElMessage.success({ message: '快递单打印机已保存', duration: 1000, customClass: 'scan-success-message' })
} else {
localStorage.removeItem(STORAGE_KEY_EXPRESS)
ElMessage.success({ message: '快递单打印机已清除', duration: 1000, customClass: 'scan-success-message' })
}
}
const fetchCameras = async () => {
cameraLoading.value = true
try {
const devices = await navigator.mediaDevices.enumerateDevices()
cameras.value = devices.filter((d) => d.kind === 'videoinput').map((d) => ({
deviceId: d.deviceId,
label: d.label || `摄像头 ${d.deviceId.slice(0, 8)}`
}))
} catch {
ElMessage.error({ message: '获取摄像头列表失败', customClass: 'scan-error-message' })
cameras.value = []
} finally {
cameraLoading.value = false
}
}
const refreshCameras = () => {
fetchCameras()
}
const confirmCamera = () => {
if (selectedCamera.value) {
localStorage.setItem(STORAGE_KEY_CAMERA, selectedCamera.value)
ElMessage.success({ message: '摄像头已保存', duration: 1000, customClass: 'scan-success-message' })
} else {
localStorage.removeItem(STORAGE_KEY_CAMERA)
ElMessage.success({ message: '摄像头已清除', duration: 1000, customClass: 'scan-success-message' })
}
}
const fetchPrinters = async () => {
loading.value = true
try {
const list = await getSimplePrinterList()
printers.value = list.map((name) => ({ name }))
} catch (error) {
ElMessage.error({ message: '获取打印机列表失败', customClass: 'scan-error-message' })
printers.value = []
} finally {
loading.value = false
}
}
const refreshPrinters = () => {
fetchPrinters()
}
onMounted(() => {
barcodePrinter.value = localStorage.getItem(STORAGE_KEY_BARCODE) || ''
expressPrinter.value = localStorage.getItem(STORAGE_KEY_EXPRESS) || ''
paperSize.value = localStorage.getItem(STORAGE_KEY_PAPER_SIZE) || ''
selectedCamera.value = localStorage.getItem(STORAGE_KEY_CAMERA) || ''
fetchPrinters()
fetchCameras()
shortcutItems.forEach(key => fetchBarcodeImage(key))
})
</script>
<style scoped>
.printer-manager {
padding: 20px;
}
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.page-header h2 {
margin: 0;
font-size: 22px;
color: #303133;
}
.printer-card {
min-height: 200px;
}
.printer-form {
max-width: 600px;
}
.printer-row {
display: flex;
align-items: center;
gap: 12px;
}
.printer-form :deep(.el-form-item) {
display: flex;
align-items: center;
margin-bottom: 18px;
}
.printer-form :deep(.el-form-item__label) {
align-self: center;
line-height: normal;
}
</style>