| | |
| | | <script lang="ts" setup> |
| | | import type { MesWmBarcodeApi } from '#/api/mes/wm/barcode'; |
| | | |
| | | import { nextTick, ref } from 'vue'; |
| | | |
| | | import { Button, message } from 'ant-design-vue'; |
| | | |
| | | import { createBarcode, getBarcodeByBusiness } from '#/api/mes/wm/barcode'; |
| | | |
| | | import WlsBarcode from './barcode.vue'; |
| | | |
| | | defineOptions({ name: 'WlsPrinterLabel' }); |
| | | |
| | | withDefaults( |
| | | /** 标签上附加展示的业务字段(可选,默认不展示) */ |
| | | interface LabelField { |
| | | label: string; |
| | | value?: number | string; |
| | | } |
| | | |
| | | const props = withDefaults( |
| | | defineProps<{ |
| | | bizCode?: string; |
| | | bizId?: number; |
| | | bizType?: string; |
| | | /** 业务名称,作为标签主信息展示在条码下方 */ |
| | | bizName?: string; |
| | | bizType?: number; |
| | | /** 标签标题 */ |
| | | title?: string; |
| | | /** 按钮文案 */ |
| | | labelText?: string; |
| | | /** 附加字段,一般不需要传 */ |
| | | fields?: LabelField[]; |
| | | }>(), |
| | | { |
| | | bizCode: undefined, |
| | | bizId: undefined, |
| | | bizName: undefined, |
| | | bizType: undefined, |
| | | title: '', |
| | | labelText: '标签打印', |
| | | fields: () => [], |
| | | }, |
| | | ); |
| | | |
| | | function handlePrint() { |
| | | message.warning('标签打印功能暂未实现,敬请期待'); |
| | | const barcodeRef = ref<InstanceType<typeof WlsBarcode>>(); |
| | | const barcode = ref<MesWmBarcodeApi.Barcode>(); |
| | | const renderKey = ref(0); |
| | | const printing = ref(false); |
| | | /** 打印窗口:需在用户点击时同步打开,否则会被浏览器拦截 */ |
| | | const printWindow = ref<null | Window>(null); |
| | | |
| | | function escapeHtml(value: string) { |
| | | return value |
| | | .replaceAll('&', '&') |
| | | .replaceAll('<', '<') |
| | | .replaceAll('>', '>'); |
| | | } |
| | | |
| | | /** 构建标签 HTML:标题 + 条码图 + 名称(居中),附加字段可选 */ |
| | | function buildLabelHtml(base64: string) { |
| | | const nameHtml = props.bizName |
| | | ? `<div class="label-name">${escapeHtml(props.bizName)}</div>` |
| | | : ''; |
| | | const infoHtml = props.fields |
| | | .filter((item) => item.value !== undefined && item.value !== '') |
| | | .map( |
| | | (item) => |
| | | `<p><span class="info-label">${escapeHtml(item.label)}</span>${escapeHtml(String(item.value))}</p>`, |
| | | ) |
| | | .join(''); |
| | | return `<!DOCTYPE html> |
| | | <html><head><meta charset="UTF-8"><title>${escapeHtml(props.title || '打印标签')}</title> |
| | | <style> |
| | | *{margin:0;padding:0;box-sizing:border-box} |
| | | body{font-family:Arial,"Microsoft YaHei",sans-serif;display:flex;justify-content:center;padding:12px} |
| | | .label{width:320px;padding:8px;text-align:center} |
| | | .label-title{font-size:13px;color:#333;margin-bottom:6px} |
| | | .label-barcode{width:200px;margin:0 auto} |
| | | .label-barcode img{display:block;width:100%} |
| | | .label-name{margin-top:8px;font-size:16px;font-weight:700;line-height:1.4;word-break:break-all} |
| | | .label-info{margin-top:8px;font-size:12px;line-height:1.6;text-align:left;color:#333} |
| | | .label-info p{margin:2px 0} |
| | | .info-label{color:#666;margin-right:4px} |
| | | .info-label::after{content:":"} |
| | | @page{margin:5mm} |
| | | @media print{body{padding:0}.label{width:auto;max-width:100%}} |
| | | </style> |
| | | </head><body><div class="label"> |
| | | ${props.title ? `<div class="label-title">${escapeHtml(props.title)}</div>` : ''} |
| | | <div class="label-barcode"><img src="${base64}" alt="条码" /></div> |
| | | ${nameHtml} |
| | | ${infoHtml ? `<div class="label-info">${infoHtml}</div>` : ''} |
| | | </div></body></html>`; |
| | | } |
| | | |
| | | /** 条码图渲染完成,把标签内容写入已打开的打印窗口并调起打印 */ |
| | | function handleBarcodeDone() { |
| | | const win = printWindow.value; |
| | | const base64 = barcodeRef.value?.getImageBase64(); |
| | | printWindow.value = null; |
| | | if (!win || win.closed) { |
| | | return; |
| | | } |
| | | if (!base64) { |
| | | message.warning('条码渲染失败,无法打印标签'); |
| | | win.close(); |
| | | return; |
| | | } |
| | | win.document.open(); |
| | | win.document.write(buildLabelHtml(base64)); |
| | | win.document.close(); |
| | | setTimeout(() => win.print(), 500); |
| | | } |
| | | |
| | | /** 打印标签:无条码时先生成,再渲染条码图后打印 */ |
| | | async function handlePrint() { |
| | | if (!props.bizType || !props.bizId) { |
| | | message.warning('缺少业务类型或业务编号,无法打印标签'); |
| | | return; |
| | | } |
| | | if (printing.value) { |
| | | return; |
| | | } |
| | | // 在用户手势内同步打开窗口,避免弹窗被拦截 |
| | | const win = window.open('', '_blank'); |
| | | if (!win) { |
| | | message.error('无法打开打印窗口,请检查浏览器设置'); |
| | | return; |
| | | } |
| | | win.document.write( |
| | | '<!DOCTYPE html><html><head><meta charset="UTF-8"></head><body style="font-family:Arial;padding:20px">正在生成标签,请稍候…</body></html>', |
| | | ); |
| | | printWindow.value = win; |
| | | printing.value = true; |
| | | try { |
| | | let data = await getBarcodeByBusiness(props.bizType, props.bizId); |
| | | if (!data?.content) { |
| | | await createBarcode({ |
| | | bizCode: props.bizCode || '', |
| | | bizId: props.bizId, |
| | | bizName: props.bizName || '', |
| | | bizType: props.bizType, |
| | | }); |
| | | data = await getBarcodeByBusiness(props.bizType, props.bizId); |
| | | } |
| | | if (!data?.content) { |
| | | message.warning('条码生成失败,无法打印标签'); |
| | | win.close(); |
| | | printWindow.value = null; |
| | | return; |
| | | } |
| | | barcode.value = data; |
| | | renderKey.value += 1; |
| | | await nextTick(); |
| | | } catch { |
| | | message.error('打印标签失败,请重试'); |
| | | win.close(); |
| | | printWindow.value = null; |
| | | } finally { |
| | | printing.value = false; |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <template> |
| | | <Button type="link" @click="handlePrint"> |
| | | <Button :loading="printing" @click="handlePrint"> |
| | | {{ labelText }} |
| | | </Button> |
| | | <!-- 隐藏的条码渲染容器,渲染完成后取出图片写入打印窗口 --> |
| | | <div |
| | | v-if="barcode?.content" |
| | | class="pointer-events-none fixed top-0 -left-[9999px] h-0 w-0 overflow-hidden" |
| | | > |
| | | <WlsBarcode |
| | | :key="renderKey" |
| | | ref="barcodeRef" |
| | | :content="barcode.content" |
| | | :format="barcode.format" |
| | | :height="150" |
| | | :width="280" |
| | | @done="handleBarcodeDone" |
| | | /> |
| | | </div> |
| | | </template> |