Files
pet/uni_modules/uview-next/components/u-barcode/u-barcode.vue
T
2025-08-12 15:04:01 +08:00

249 lines
5.9 KiB
Vue

<template>
<view class="u-barcode" :style="[{
width: $u.addUnit(barcodeWidth),
height: $u.addUnit(barcodeHeight)
},$u.addStyle(customStyle)]">
<canvas
class="u-barcode__canvas"
:type="canvasType"
:canvas-id="canvasId"
:id="canvasId"
></canvas>
</view>
</template>
<script>
import props from './props.js';
import JsBarcode from './JsBarcode/JsBarcode.js';
import mixin from '../../libs/mixin/mixin'
import mpMixin from '../../libs/mixin/mpMixin';
/**
* Barcode 条形码组件
* @description 支持生成条形码和二维码的组件,基于JsBarcode.js库
* @tutorial https://uviewui.com/components/barcode.html
*
* @property {String} value 条形码内容
* @property {String} type 条形码类型,支持code128、code39、ean13、qr等
* @property {String|Number} width 条形码宽度
* @property {String|Number} height 条形码高度
* @property {String} color 条形码颜色
* @property {String} backgroundColor 背景颜色
* @property {Boolean} displayValue 是否显示文本
* @property {String} textPosition 文本位置,top/bottom
* @property {Object|String} customStyle 自定义样式
* @property {String} fontOptions 字体选项
* @property {String} font 字体
* @property {String} textAlign 文本对齐方式,left/center/right
* @property {Number} textMargin 文本边距
* @property {Number} fontSize 字体大小
* @example <u-barcode value="123456789" type="code128"></u-barcode>
*/
export default {
name: "u-barcode",
mixins: [mpMixin, mixin, props],
data() {
return {
canvasId: 'barcodeId_' + uni.$u.guid(),
ctx: null,
barcodeWidth: null,
barcodeHeight: null,
textHeight: 30
}
},
computed: {
is2d() {
// #ifdef MP-WEIXIN
return this.canvasType == '2d';
// #endif
// #ifndef MP-WEIXIN
return false;
// #endif
}
},
watch: {
value: {
handler(newVal) {
if (newVal) {
this.generateBarcode()
}
}
}
},
mounted() {
this.$nextTick(() => {
this.generateBarcode()
})
},
methods: {
// 生成条形码
async generateBarcode() {
const data = {};
JsBarcode(data, this.value, {
format: this.format,
width: this.width,
height: this.height,
displayValue: false,
});
// 获取编码数据
const encodings = data.encodings[0];
if (!encodings) {
return;
}
const { data: binaryData } = encodings;
this.barcodeWidth = binaryData.length * this.width;
this.barcodeHeight = this.height + (this.displayValue ? this.textHeight : 0);
await uni.$u.sleep(100);
await this.initCanvas(binaryData);
},
// 初始化canvas
async initCanvas(binaryData) {
const query = uni
.createSelectorQuery()
.in(this)
.select(`#${this.canvasId}`);
if (this.is2d) {
let canvas = await new Promise((resolve) => {
query
.fields({
node: true,
size: true,
})
.exec((res) => {
resolve(res[0].node);
});
});
canvas.width = this.barcodeWidth;
canvas.height = this.barcodeHeight;
this.ctx = canvas.getContext('2d', { willReadFrequently: true });
} else {
// #ifdef MP-ALIPAY
this.ctx = uni.createCanvasContext(this.canvasId);
// #endif
// #ifndef MP-ALIPAY
this.ctx = uni.createCanvasContext(this.canvasId, this);
// #endif
}
// 清空画布并设置背景色
if (this.is2d) {
this.ctx.fillStyle = this.backgroundColor;
this.ctx.fillRect(0, 0, this.barcodeWidth, this.barcodeHeight);
} else {
this.ctx.setFillStyle(this.backgroundColor);
this.ctx.fillRect(0, 0, this.barcodeWidth, this.barcodeHeight);
}
this.drawBarcode(binaryData);
},
// 绘制条形码
drawBarcode(binaryData) {
const startX = 0;
let startY = this.textPosition === 'top' ? this.textHeight : 0;
// 绘制条形码
if (this.is2d) {
this.ctx.fillStyle = this.color;
} else {
this.ctx.setFillStyle(this.color);
}
let drawnBars = 0;
for (let i = 0; i < binaryData.length; i++) {
if (binaryData[i] === '1') {
const x = startX + i * this.width;
this.ctx.fillRect(x, startY, this.width, this.height);
drawnBars++;
}
}
// 绘制文本
if (this.displayValue) {
this.drawText();
}
if (!this.is2d) {
this.ctx.draw();
}
},
// 绘制文本
drawText() {
const text = this.value;
const textColor = this.color;
// 构建字体字符串
let fontString = `${this.fontSize}px ${this.font}`;
if (this.fontOptions) {
fontString = `${this.fontOptions} ${fontString}`;
}
// 设置文本样式
if (this.is2d) {
this.ctx.font = fontString;
this.ctx.fillStyle = textColor;
this.ctx.textAlign = this.textAlign;
this.ctx.textBaseline = 'middle';
} else {
this.ctx.setFontSize(this.fontSize);
this.ctx.setFillStyle(textColor);
this.ctx.setTextAlign(this.textAlign);
this.ctx.setTextBaseline('middle');
}
// 计算文本位置
let textX;
if (this.textAlign === 'left') {
textX = this.textMargin;
} else if (this.textAlign === 'right') {
textX = this.barcodeWidth - this.textMargin;
} else {
// center
textX = this.barcodeWidth / 2;
}
let textY;
if (this.textPosition === 'top') {
// 文本在上方
textY = this.fontSize / 2 + this.textMargin;
} else {
// 文本在下方
textY = this.barcodeHeight - this.fontSize / 2 - this.textMargin;
}
// 绘制文本
this.ctx.fillText(text, textX, textY);
}
}
}
</script>
<style lang="scss" scoped>
@import "../../libs/css/components.scss";
.u-barcode {
position: relative;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
&__canvas {
width: 100%;
height: 100%;
}
}
</style>