249 lines
5.9 KiB
Vue
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> |