工具类代码
import QRCode from 'qrcode';
import JSZip from 'jszip';
import dayjs from 'dayjs';
class MaterialCodeGenerator {
constructor() {
this.loading = false;
}
/**
* 生成物料码
*
* @param {Array} materials 物料数据
* @param {Array} customFields 自定义字段
*/
async generateQRCode(materials, customFields) {
if (materials.length === 0) {
throw new Error('请先生成物料数据');
}
const arr = [];
this.loading = true;
materials.forEach(async (materialInfo, index) => {
arr.push(
this.drawMaterialCode(
await QRCode.toDataURL(materialInfo.supplierCode),
{ ...materialInfo, now: this.formatDateTime(dayjs()) },
customFields
)
);
if (index === materials.length - 1) {
Promise.all(arr).then(async (res) => {
await this.packageAndDownload(res);
this.loading = false;
});
}
});
}
/**
* 绘制物料码
*
* @param {string} qrCodeData 二维码数据
* @param {Object} materialInfo 物料信息
* @param {Array} customFields 自定义字段
* @returns {Promise} 返回绘制好的二维码图片
*/
async drawMaterialCode(qrCodeData, materialInfo, customFields) {
const fieldHeight = 30; // 每个字段的高度
const baseHeight = 40; // 标题高度
const margin = 30; // 底部边缘留白
const canvasHeight = baseHeight + customFields.length * fieldHeight + margin;
return new Promise((resolve) => {
const canvas = document.createElement('canvas');
canvas.width = 400; // 增加宽度
canvas.height = canvasHeight; // 根据字段数量动态计算高度
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#FFFFFF';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#000';
ctx.font = 'bold 24px Arial';
ctx.textAlign = 'center';
ctx.fillText('产品二维码', canvas.width / 2, 40);
const qrImg = new Image();
qrImg.src = qrCodeData;
qrImg.onload = () => {
ctx.drawImage(qrImg, canvas.width - 120, canvas.height / 2 - 60, 120, 120);
ctx.font = '18px Arial';
ctx.textAlign = 'start';
let yPos = 80; // 初始文本位置
customFields.forEach((field) => {
ctx.fillText${field.label}: ${materialInfo[field.value]}, 20, yPos);
yPos += fieldHeight;
});
ctx.strokeStyle = '#000000';
ctx.lineWidth = 1;
ctx.strokeRect(1, 1, canvas.width - 2, canvas.height - 2);
const imageURL = canvas.toDataURL('image/png');
resolve({ url: imageURL, ...materialInfo });
};
});
}
/**
* 打包并下载物料码
*
* @param {Array} res 物料码数据
*/
async packageAndDownload(res) {
const zip = new JSZip();
res.forEach(({ url, id }) => {
zip.file${id}.png, url.split('base64,')[1], { base64: true });
});
zip.generateAsync({ type: 'blob' }).then((content) => {
const a = document.createElement('a');
const url = URL.createObjectURL(content);
a.href = url;
a.download = '产品物料码.zip';
a.click();
URL.revokeObjectURL(url);
});
}
/**
* 格式化日期时间
*
* @param {Date} date 日期时间
* @returns {string} 格式化后的日期时间
*/
formatDateTime(date) {
return dayjs(date).format('YYYY-MM-DD HH:mm:ss');
}
}
export default MaterialCodeGenerator;
调用生成物料实例
const customFields = [
{
label: '产品名称',
value: 'productName'
},
{
label: '打印日期',
value: 'now'
},
{
label: '颜色',
value: 'color'
},
{
label: '编号',
value: 'colorCode'
},
{
label: '数量',
value: 'quantity'
},
{
label: '供应商编码',
value: 'supplierCode'
},
{
label: '批次人',
value: 'printPerson'
}
]
const materialCodeGenerator = new MaterialCodeGenerator()
materialCodeGenerator
.generateQRCode(this.materials, customFields)
.then(() => {
message.success('物料码生成成功')
})
.catch((error) => {
message.error(`物料码生成失败: ${error.message}`)
})
完整代码
<template>
<div class="container">
<div class="buttons">
<a-button @click="generateRandomMaterials">生成随机物料数据</a-button>
<a-button @click="generateQRCode" :loading="loading">生成物料码</a-button>
</div>
<a-table :columns="columns" :dataSource="materials" :rowKey="(record) => record.id"> </a-table>
</div>
</template>
<script>
import dayjs from 'dayjs'
import { Button, message, Table } from 'ant-design-vue'
import MaterialCodeGenerator from './utils/index'
class SequentialIdGenerator {
constructor(start = 1000000000) {
if (start < 1000000000 || start > 9999999999) {
throw new Error('Start ID must be a 10-digit number.')
}
this.currentId = start
}
generateId() {
if (this.currentId > 9999999999) {
throw new Error('Maximum ID limit reached.')
}
return this.currentId++
}
}
const idGenerator = new SequentialIdGenerator() // 默认从1000000000开始
export default {
components: {
AButton: Button,
ATable: Table
},
data() {
return {
loading: false,
materials: [],
columns: [
{
title: 'ID',
dataIndex: 'id',
key: 'id'
},
{
title: '产品名称',
dataIndex: 'productName',
key: 'productName'
},
{
title: '颜色',
dataIndex: 'color',
key: 'color'
},
{
title: '编号',
dataIndex: 'colorCode',
key: 'colorCode'
},
{
title: '数量',
dataIndex: 'quantity',
key: 'quantity'
},
{
title: '供应商编码',
dataIndex: 'supplierCode',
key: 'supplierCode'
},
{
title: '批次人',
dataIndex: 'printPerson',
key: 'printPerson'
}
]
}
},
methods: {
/**
* 生成物料码
*
* @returns {Promise}
*/
async generateQRCode() {
if (this.materials.length === 0) {
message.error('请先生成物料数据')
return
}
const customFields = [
{
label: '产品名称',
value: 'productName'
},
{
label: '打印日期',
value: 'now'
},
{
label: '颜色',
value: 'color'
},
{
label: '编号',
value: 'colorCode'
},
{
label: '数量',
value: 'quantity'
},
{
label: '供应商编码',
value: 'supplierCode'
},
{
label: '批次人',
value: 'printPerson'
}
]
const materialCodeGenerator = new MaterialCodeGenerator()
materialCodeGenerator
.generateQRCode(this.materials, customFields)
.then(() => {
message.success('物料码生成成功')
})
.catch((error) => {
message.error(`物料码生成失败: ${error.message}`)
})
},
/**
* 生成随机物料数据
*/
generateRandomMaterials() {
const numItems = 50 // 生成50条数据
const productNames = ['氨纶方格布1', '氨纶方格布2', '尼龙布', '棉麻混纺', '涤纶布']
const colors = ['白色', '黑色', '红色', '蓝色', '绿色']
const supplierCodes = ['20102939', '20103948', '20104857', '20105766', '20106675']
const persons = ['小林', '张三', '李四', '王五', '赵六']
this.materials = []
for (let i = 0; i < numItems; i++) {
const material = {
id: this.generateId(), // 增加唯一id以便用作key
productName: productNames[Math.floor(Math.random() * productNames.length)],
color: colors[Math.floor(Math.random() * colors.length)],
colorCode: `T00${Math.floor(Math.random() * 1000) + 900}A`,
quantity: Math.floor(Math.random() * 1000) + 1,
supplierCode: supplierCodes[Math.floor(Math.random() * supplierCodes.length)],
printPerson: persons[Math.floor(Math.random() * persons.length)]
}
this.materials.push(material)
}
},
/**
* 格式化日期时间
*
* @param {Date} date 日期时间
* @returns {string} 格式化后的日期时间
*/
formatDateTime(date) {
return dayjs(date).format('YYYY-MM-DD HH:mm:ss')
},
/**
* 生成唯一ID
*/
generateId() {
return idGenerator.generateId()
}
}
}
</script>
<style>
.container {
padding: 20px;
width: 800px;
height: 600px;
margin: auto;
display: flex;
flex-direction: column;
gap: 10px;
.buttons {
display: flex;
gap: 10px;
}
}
</style>