feat:

修改二维码生成逻辑,改为一次性二维码,存储更多信息
This commit is contained in:
砂糖
2025-08-22 11:30:23 +08:00
parent 4301301312
commit 3c694130c2
8 changed files with 711 additions and 47 deletions

View File

@@ -0,0 +1,295 @@
<template>
<div class="barcode-simple-layout">
<!-- 仅保留预览区 -->
<div class="barcode-preview-area">
<div class="iframe-wrapper">
<iframe ref="previewIframe" class="barcode-iframe" frameborder="0" :style="iframeStyle"></iframe>
</div>
</div>
</div>
</template>
<script>
import QRCode from 'qrcode';
export default {
name: 'BarcodeRenderer',
props: {
barcodes: {
type: Array,
required: true
},
// 可通过props传入配置保持一定灵活性
layoutConfig: {
type: Object,
default: () => ({
perRow: 3,
barcodeWidth: 180,
paperSize: 'A4',
paperOrientation: 'portrait',
customPaperWidth: 210,
customPaperHeight: 297
})
}
},
data() {
return {
barcodeConfigs: [], // 二维码配置数组 [{code, count, textTpl}]
};
},
computed: {
// 从传入的配置中获取参数
perRow() {
return this.layoutConfig.perRow || 3;
},
barcodeWidth() {
return this.layoutConfig.barcodeWidth || 180;
},
barcodeHeight() {
return this.layoutConfig.barcodeWidth || 180; // 保持宽高一致
},
paperSize() {
return this.layoutConfig.paperSize || 'A4';
},
paperOrientation() {
return this.layoutConfig.paperOrientation || 'portrait';
},
customPaperWidth() {
return this.layoutConfig.customPaperWidth || 210;
},
customPaperHeight() {
return this.layoutConfig.customPaperHeight || 297;
},
// 展开后的二维码列表(考虑生成数量)
expandedBarcodes() {
let arr = [];
this.barcodeConfigs.forEach(cfg => {
for (let i = 0; i < (cfg.count || 1); i++) {
arr.push(cfg.code);
}
});
return arr;
},
// 展开后的文字列表
expandedBarcodeTexts() {
let arr = [];
this.barcodeConfigs.forEach(cfg => {
for (let i = 0; i < (cfg.count || 1); i++) {
let text = cfg.textTpl || cfg.code;
text = text.replace(/\{\{n\}\}/g, i + 1); // 替换序号变量
arr.push(text);
}
});
return arr;
},
// 按行分组的二维码
barcodeRows() {
const rows = [];
const arr = this.expandedBarcodes;
for (let i = 0; i < arr.length; i += this.perRow) {
rows.push(arr.slice(i, i + this.perRow));
}
return rows;
},
// 按行分组的文字
barcodeTextRows() {
const rows = [];
const arr = this.expandedBarcodeTexts;
for (let i = 0; i < arr.length; i += this.perRow) {
rows.push(arr.slice(i, i + this.perRow));
}
return rows;
},
// iframe样式
iframeStyle() {
const { width, height } = this.getPaperPx();
return {
width: width + 'px',
minHeight: height + 'px',
height: height + 'px',
background: '#fff',
border: 'none',
display: 'block',
margin: 0,
padding: 0
};
}
},
watch: {
// 监听barcodes变化重新初始化和渲染
barcodes: {
handler(newVal) {
this.barcodeConfigs = newVal.map(b => ({
code: b.code || b,
count: b.count || 1,
textTpl: b.textTpl || (b.code || b)
}));
this.$nextTick(this.renderPreviewIframe);
},
immediate: true,
deep: true
},
// 监听配置变化,重新渲染
layoutConfig: {
handler() {
this.$nextTick(this.renderPreviewIframe);
},
deep: true
},
// 监听二维码配置变化,重新渲染
barcodeConfigs: {
handler() {
this.$nextTick(this.renderPreviewIframe);
},
deep: true
}
},
methods: {
// 生成二维码ID
getBarcodeId(row, col) {
return `barcode-canvas-${row}-${col}`;
},
// 获取二维码下方文字
getBarcodeText(row, col, code) {
if (
this.barcodeTextRows[row] &&
typeof this.barcodeTextRows[row][col] !== 'undefined' &&
this.barcodeTextRows[row][col] !== null &&
this.barcodeTextRows[row][col] !== ''
) {
return this.barcodeTextRows[row][col];
}
return code;
},
// 将纸张尺寸从mm转换为px1mm ≈ 3.78px
getPaperPx() {
let width, height;
if (this.paperSize === 'A4') {
width = 210 * 3.78;
height = 297 * 3.78;
} else if (this.paperSize === 'A5') {
width = 148 * 3.78;
height = 210 * 3.78;
} else if (this.paperSize === 'A6') {
width = 105 * 3.78;
height = 148 * 3.78;
} else {
width = this.customPaperWidth * 3.78;
height = this.customPaperHeight * 3.78;
}
// 处理横向/纵向
if (this.paperOrientation === 'landscape') {
return { width: height, height: width };
}
return { width, height };
},
// 生成打印用的HTML
getPrintHtml() {
const { width, height } = this.getPaperPx();
let html = `
<html>
<head>
<title>二维码预览</title>
<style>
body { margin: 0; padding: 0; background: #fff; }
.barcode-list { width: ${width}px; min-height: ${height}px; margin: 0 auto; background: #fff; padding: 20px; box-sizing: border-box; }
.barcode-row { display: flex; margin-bottom: 24px; justify-content: space-around; }
.barcode-item { flex: 1; text-align: center; padding: 0 10px; box-sizing: border-box; }
.barcode-text { margin-top: 8px; font-size: 14px; word-break: break-all; }
html, body { overflow-x: hidden; }
</style>
</head>
<body>
<div class="barcode-list">
`;
// 生成二维码行
this.barcodeRows.forEach((row, rowIndex) => {
html += '<div class="barcode-row">';
row.forEach((code, colIndex) => {
const id = this.getBarcodeId(rowIndex, colIndex);
const text = this.getBarcodeText(rowIndex, colIndex, code);
html += `<div class="barcode-item">
<canvas id="${id}" width="${this.barcodeWidth}" height="${this.barcodeHeight}"></canvas>
<div class="barcode-text">${text}</div>
</div>`;
});
html += '</div>';
});
html += '</div></body></html>';
return html;
},
// 渲染预览iframe
async renderPreviewIframe() {
const iframe = this.$refs.previewIframe;
if (!iframe) return;
const doc = iframe.contentDocument || iframe.contentWindow.document;
doc.open();
doc.write(this.getPrintHtml());
doc.close();
// 绘制二维码
setTimeout(async () => {
for (let rowIndex = 0; rowIndex < this.barcodeRows.length; rowIndex++) {
const row = this.barcodeRows[rowIndex];
for (let colIndex = 0; colIndex < row.length; colIndex++) {
const code = row[colIndex];
const id = this.getBarcodeId(rowIndex, colIndex);
const el = doc.getElementById(id);
if (el) {
await QRCode.toCanvas(el, code, {
width: this.barcodeWidth,
height: this.barcodeHeight,
margin: 0
});
}
}
}
}, 50);
}
},
mounted() {
// 初始化二维码配置
this.barcodeConfigs = this.barcodes.map(b => ({
code: b.code || b,
count: b.count || 1,
textTpl: b.textTpl || (b.code || b)
}));
this.renderPreviewIframe();
}
};
</script>
<style scoped>
.barcode-simple-layout {
display: flex;
width: 100%;
min-height: 400px;
}
.barcode-preview-area {
flex: 1;
background: #fff;
padding: 24px;
display: flex;
flex-direction: column;
height: 100vh;
overflow: auto;
}
.iframe-wrapper {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: flex-start;
background: #f8f8f8;
overflow: auto;
padding: 20px;
box-sizing: border-box;
}
.barcode-iframe {
border-radius: 4px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
</style>