From 93b2208675f659d6531278895b388cf7900aa32c Mon Sep 17 00:00:00 2001 From: geht Date: Fri, 17 Apr 2026 19:31:09 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8E=E6=89=93=E5=8D=B0=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E7=9A=84=E6=89=93=E5=8D=B0=E6=9C=BA=E5=81=9A?= =?UTF-8?q?=E4=BA=A4=E4=BA=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/views/print/template/index.vue | 8 +-- .../template/native/NativePrintDesigner.vue | 68 ++++++++++++++++++- .../template/utils/printNativeViaPrintDot.ts | 6 +- 3 files changed, 75 insertions(+), 7 deletions(-) diff --git a/jeecgboot-vue3/src/views/print/template/index.vue b/jeecgboot-vue3/src/views/print/template/index.vue index d4c534bb..44d17575 100644 --- a/jeecgboot-vue3/src/views/print/template/index.vue +++ b/jeecgboot-vue3/src/views/print/template/index.vue @@ -212,6 +212,7 @@ } from './utils/printDotBridge'; import { normalizeImportedNativeSchema } from './native/core/nativeSchemaNormalize'; import { renderNativePrintHtml } from './native/core/printRenderer'; + import { PRINT_TEMPLATE_SELECTED_PRINTER_KEY } from './utils/printNativeViaPrintDot'; defineOptions({ name: 'PrintTemplateList' }); @@ -304,7 +305,6 @@ }); let hiprint: any = null; - const PRINTER_STORAGE_KEY = 'print_template_selected_printer'; function resolveHiprint(module: any) { const defaultExport = module?.default || {}; hiprint = module?.hiprint || defaultExport?.hiprint || (window as any)?.hiprint; @@ -1637,10 +1637,10 @@ selectedPrinterName, (value) => { if (!value) { - localStorage.removeItem(PRINTER_STORAGE_KEY); + localStorage.removeItem(PRINT_TEMPLATE_SELECTED_PRINTER_KEY); return; } - localStorage.setItem(PRINTER_STORAGE_KEY, value); + localStorage.setItem(PRINT_TEMPLATE_SELECTED_PRINTER_KEY, value); }, { immediate: false }, ); @@ -1651,7 +1651,7 @@ } catch (_error) { // ignore } - selectedPrinterName.value = localStorage.getItem(PRINTER_STORAGE_KEY) || '__system_default__'; + selectedPrinterName.value = localStorage.getItem(PRINT_TEMPLATE_SELECTED_PRINTER_KEY) || '__system_default__'; await refreshPrinterOptions(false); if (!selectedPrinterName.value) { selectedPrinterName.value = '__system_default__'; diff --git a/jeecgboot-vue3/src/views/print/template/native/NativePrintDesigner.vue b/jeecgboot-vue3/src/views/print/template/native/NativePrintDesigner.vue index b97ca25b..6783f556 100644 --- a/jeecgboot-vue3/src/views/print/template/native/NativePrintDesigner.vue +++ b/jeecgboot-vue3/src/views/print/template/native/NativePrintDesigner.vue @@ -13,6 +13,19 @@ 下移图层 即时预览 打印 + + + 刷新打印机 + PrintDot 打印 保存模板 @@ -307,7 +320,8 @@ import PropertiesPanel from './components/PropertiesPanel.vue'; import ToolbarPalette from './components/ToolbarPalette.vue'; import { printHtml } from './core/printService'; - import { printNativeSchemaViaPrintDot } from '../utils/printNativeViaPrintDot'; + import { fetchPrintDotPrinters, type PrintDotPrinter } from '../utils/printDotBridge'; + import { printNativeSchemaViaPrintDot, PRINT_TEMPLATE_SELECTED_PRINTER_KEY } from '../utils/printNativeViaPrintDot'; import { renderNativePrintHtml, resolvePrintPageCount } from './core/printRenderer'; import { generateNativeMockDataObject } from './core/nativeMockData'; import { buildNativeTemplateStylePayload } from './core/nativeTemplateStyleSerialize'; @@ -344,6 +358,56 @@ const templateId = ref(''); const saving = ref(false); const printDotLoading = ref(false); + /** PrintDot 桥接返回的打印机列表(设计器内下拉) */ + const printDotPrintersLoading = ref(false); + const printDotPrinterList = ref([]); + /** 与模板列表共用 localStorage,保证列表与设计器选同一台机 */ + const designerPrintDotPrinter = ref( + localStorage.getItem(PRINT_TEMPLATE_SELECTED_PRINTER_KEY) || '__system_default__', + ); + + const printDotPrinterSelectOptions = computed(() => { + const opts: Array<{ label: string; value: string }> = [ + { label: '系统默认打印机', value: '__system_default__' }, + ]; + const seen = new Set(['__system_default__']); + printDotPrinterList.value.forEach((p) => { + const name = String(p.name || '').trim(); + if (!name || seen.has(name)) return; + seen.add(name); + opts.push({ + label: p.isDefault ? `${name}(PrintDot 默认)` : name, + value: name, + }); + }); + return opts; + }); + + watch(designerPrintDotPrinter, (v) => { + if (!v) { + localStorage.removeItem(PRINT_TEMPLATE_SELECTED_PRINTER_KEY); + return; + } + localStorage.setItem(PRINT_TEMPLATE_SELECTED_PRINTER_KEY, v); + }); + + async function refreshDesignerPrintDotPrinters(showTip = true) { + printDotPrintersLoading.value = true; + try { + const list = await fetchPrintDotPrinters(); + printDotPrinterList.value = list; + if (showTip) { + createMessage.success(`PrintDot 已连接,共 ${list.length} 台打印机`); + } + } catch (e: any) { + printDotPrinterList.value = []; + if (showTip) { + createMessage.warning(e?.message || '无法连接 PrintDot,请确认本机客户端已启动'); + } + } finally { + printDotPrintersLoading.value = false; + } + } const previewVisible = ref(false); const previewHtml = ref(''); const selectedTableColumn = ref<{ elementId: string; columnKey: string } | null>(null); @@ -1333,6 +1397,7 @@ schema: state.schema, data: previewData.value, jobName: String(meta.templateCode || '').trim() || 'native-print', + printerSelection: designerPrintDotPrinter.value || '__system_default__', }); createMessage.success('已通过 PrintDot 提交打印'); } catch (error: any) { @@ -1707,6 +1772,7 @@ await loadTemplate(); generateCanvasJson(); generateMockData({ syncManual: true, showMessage: false }); + void refreshDesignerPrintDotPrinters(false); }); onUnmounted(() => { diff --git a/jeecgboot-vue3/src/views/print/template/utils/printNativeViaPrintDot.ts b/jeecgboot-vue3/src/views/print/template/utils/printNativeViaPrintDot.ts index b76d64b5..d18e9671 100644 --- a/jeecgboot-vue3/src/views/print/template/utils/printNativeViaPrintDot.ts +++ b/jeecgboot-vue3/src/views/print/template/utils/printNativeViaPrintDot.ts @@ -3,7 +3,8 @@ import { renderNativePrintHtml } from '../native/core/printRenderer'; import { buildPdfBase64FromHtmlFragment, extractBodyInnerHtmlFromFullDocument } from './printHtmlToPdfBase64'; import { fetchPrintDotPrinters, printDotSendPdf, resolvePrintDotPrinterName } from './printDotBridge'; -const PRINTER_STORAGE_KEY = 'print_template_selected_printer'; +/** 与模板列表页共用,便于设计器与列表选择同一台 PrintDot 打印机 */ +export const PRINT_TEMPLATE_SELECTED_PRINTER_KEY = 'print_template_selected_printer'; /** * 原生模板:渲染 HTML → 转 PDF → 经 PrintDot 本地桥接器送打印机 @@ -21,7 +22,8 @@ export async function printNativeSchemaViaPrintDot(params: { paginate: true, }); const printers = await fetchPrintDotPrinters(); - const fromStore = params.printerSelection ?? localStorage.getItem(PRINTER_STORAGE_KEY) ?? '__system_default__'; + const fromStore = + params.printerSelection ?? localStorage.getItem(PRINT_TEMPLATE_SELECTED_PRINTER_KEY) ?? '__system_default__'; const resolved = resolvePrintDotPrinterName(fromStore, printers); if (!resolved) { throw new Error('未解析到可用打印机:请在模板列表选择打印机,或启动 PrintDot 后刷新打印机列表');