673 lines
17 KiB
Vue
673 lines
17 KiB
Vue
<template>
|
||
<view>
|
||
<view class="uni-list">
|
||
<!-- 隐藏画布,用于标签绘制 -->
|
||
<canvas :id="canvasId" :canvas-id="canvasId" type="2d"
|
||
:style="{ width: labelWidth + 'px', height: labelHeight + 'px' }"
|
||
style="position: fixed; left: -999999rpx; top: -999999rpx" />
|
||
<!-- 打印机列表 -->
|
||
<view class="uni-list-cell">
|
||
<view class="uni-list-cell-left">打印机:</view>
|
||
<view class="uni-list-cell-db">
|
||
<picker :value="deviceIndex" :range="deviceList" @change="onDeviceChanged" range-key="name">
|
||
<view class="uni-input">{{ deviceList[deviceIndex].name }}</view>
|
||
</picker>
|
||
</view>
|
||
</view>
|
||
<!-- 出纸方向 -->
|
||
<view class="uni-list-cell">
|
||
<view class="uni-list-cell-left">出纸方向:</view>
|
||
<view class="uni-list-cell-db">
|
||
<picker :value="rotationIndex" :range="rotationList" @change="onRotationChanged" range-key="name">
|
||
<view class="uni-input">{{ rotationList[rotationIndex].name }}</view>
|
||
</picker>
|
||
</view>
|
||
</view>
|
||
<!-- 标签纸类型 -->
|
||
<view class="uni-list-cell">
|
||
<view class="uni-list-cell-left">纸张类型:</view>
|
||
<view class="uni-list-cell-db">
|
||
<picker :value="gapIndex" :range="gapList" @change="onGapTypeChanged" range-key="name">
|
||
<view class="uni-input">{{ gapList[gapIndex].name }}</view>
|
||
</picker>
|
||
</view>
|
||
</view>
|
||
<!-- 打印浓度 -->
|
||
<view class="uni-list-cell">
|
||
<view class="uni-list-cell-left">打印浓度:</view>
|
||
<view class="uni-list-cell-db">
|
||
<picker :value="darknessIndex" :range="darknessList" @change="onDarknessChanged" range-key="name">
|
||
<view class="uni-input">{{ darknessList[darknessIndex].name }}</view>
|
||
</picker>
|
||
</view>
|
||
</view>
|
||
<!-- 打印速度 -->
|
||
<view class="uni-list-cell">
|
||
<view class="uni-list-cell-left">打印速度:</view>
|
||
<view class="uni-list-cell-db">
|
||
<picker :value="speedIndex" :range="speedList" @change="onPrintSpeedChanged" range-key="name">
|
||
<view class="uni-input">{{ speedList[speedIndex].name }}</view>
|
||
</picker>
|
||
</view>
|
||
</view>
|
||
<!-- 图片处理灰度阈值 -->
|
||
<view class="uni-list-cell">
|
||
<view class="uni-list-cell-left">灰度阈值:</view>
|
||
<view class="uni-list-cell-db">
|
||
<input class="uni-input" type="number" v-model="threshold" />
|
||
</view>
|
||
</view>
|
||
<!-- 打印方式,两种不同的写法 -->
|
||
<view class="uni-list-cell uni-list-cell-pd">
|
||
<view class="uni-list-cell-db">JSON配置模式</view>
|
||
<switch :checked="jsonMode" @change="onPrintModeChanged" style="transform: scale(0.75)" />
|
||
</view>
|
||
</view>
|
||
<view class="button-sp-area">
|
||
<button class="mini-btn" type="warn" size="mini" plain="true" @click="handleStopDiscovery">停止搜索</button>
|
||
<button class="mini-btn" type="primary" size="mini" plain="true" @click="handleStartDiscovery">
|
||
搜索打印机
|
||
</button>
|
||
</view>
|
||
<view class="button-sp-area">
|
||
<button class="mini-btn" type="warn" size="mini" plain="true" @click="handleClosePrinter">
|
||
关闭打印机
|
||
</button>
|
||
<button class="mini-btn" type="primary" size="mini" plain="true" @click="handleOpenPrinter">
|
||
打开打印机
|
||
</button>
|
||
<button class="mini-btn" type="primary" size="mini" @click="handlePrintLabel">打印</button>
|
||
</view>
|
||
<view style="text-align: center; padding: 10px">
|
||
<view style="margin: 10px; border: solid lightgray 1px">
|
||
<image v-for="item in previewList" :src="item.value" :key="item.key" class="image" mode="widthFix"
|
||
style="margin: 10rpx; border: dashed lightgray 1px" />
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import {
|
||
DrawType,
|
||
LPAPIFactory,
|
||
LPAPI
|
||
} from "../../uni_modules/dothan-lpapi-ble/js_sdk";
|
||
|
||
export default {
|
||
data() {
|
||
return {
|
||
canvasId: "lpapi-ble-uni1",
|
||
labelWidth: 960,
|
||
labelHeight: 960,
|
||
deviceList: [{
|
||
name: "未检测到打印机",
|
||
}, ],
|
||
rotationIndex: 0,
|
||
rotationList: [{
|
||
name: "横向打印",
|
||
value: 0
|
||
},
|
||
{
|
||
name: "右转90度",
|
||
value: 90
|
||
},
|
||
{
|
||
name: "转转180度",
|
||
value: 180
|
||
},
|
||
{
|
||
name: "左转90度",
|
||
value: 270
|
||
},
|
||
],
|
||
deviceIndex: 0,
|
||
gapList: [{
|
||
name: "随打印机设置",
|
||
value: 255
|
||
},
|
||
{
|
||
name: "小票纸",
|
||
value: 0
|
||
},
|
||
{
|
||
name: "不干胶",
|
||
value: 2
|
||
},
|
||
{
|
||
name: "卡纸",
|
||
value: 3
|
||
},
|
||
],
|
||
gapIndex: 0,
|
||
darknessList: [{
|
||
name: "随打印机设置",
|
||
value: 255
|
||
},
|
||
{
|
||
name: "6 (正常)",
|
||
value: 6
|
||
},
|
||
{
|
||
name: "7",
|
||
value: 7
|
||
},
|
||
{
|
||
name: "8",
|
||
value: 8
|
||
},
|
||
{
|
||
name: "9",
|
||
value: 9
|
||
},
|
||
{
|
||
name: "10 (较浓)",
|
||
value: 10
|
||
},
|
||
{
|
||
name: "11",
|
||
value: 11
|
||
},
|
||
{
|
||
name: "12",
|
||
value: 12
|
||
},
|
||
{
|
||
name: "13",
|
||
value: 13
|
||
},
|
||
{
|
||
name: "14",
|
||
value: 14
|
||
},
|
||
{
|
||
name: "15 (最浓)",
|
||
value: 15
|
||
},
|
||
],
|
||
darknessIndex: 0,
|
||
speedList: [{
|
||
name: "随打印机设置",
|
||
value: 255
|
||
},
|
||
{
|
||
name: "最慢",
|
||
value: 1
|
||
},
|
||
{
|
||
name: "较慢",
|
||
value: 2
|
||
},
|
||
{
|
||
name: "正常",
|
||
value: 3
|
||
},
|
||
{
|
||
name: "较快",
|
||
value: 4
|
||
},
|
||
{
|
||
name: "最快",
|
||
value: 5
|
||
},
|
||
],
|
||
speedIndex: 0,
|
||
isAppPlus: false,
|
||
isWeiXin: false,
|
||
isAlipay: false,
|
||
/**
|
||
* 图片预览列表
|
||
* @type {{value: string; key: string;}[]}
|
||
***/
|
||
previewList: [],
|
||
threshold: 128,
|
||
jsonMode: false,
|
||
/**
|
||
* @type {LPAPI}
|
||
*/
|
||
lpapi: undefined,
|
||
};
|
||
},
|
||
onLoad(options) {
|
||
console.log("========== onLoad ==========");
|
||
//option为object类型,会序列化上个页面传递的参数
|
||
console.log(`---- type = ${options.type}`, options);
|
||
|
||
// #ifdef APP-PLUS
|
||
this.isAppPlus = true;
|
||
// #endif
|
||
// #ifdef MP-ALIPAY
|
||
this.isAlipay = true;
|
||
// #endif
|
||
// #ifdef MP-WEIXIN
|
||
this.isWeiXin = true;
|
||
// #endif
|
||
//
|
||
this.lpapi = LPAPIFactory.getInstance({
|
||
// 日志信息显示级别,值为 0 - 4,0表示不显示调试信息,4表示显示所有调试信息
|
||
showLog: 4,
|
||
// bleAdapter: bleAdapter,
|
||
// writeExtendWaits: 3,
|
||
// dataSendMode: 1,
|
||
});
|
||
// 搜索蓝牙设备
|
||
this.lpapi.startDiscovery({
|
||
timeout: 0,
|
||
deviceFound: (devices) => {
|
||
this.onDeviceFound(devices);
|
||
},
|
||
});
|
||
// 微信小程序、支付宝小程序创建绘制上下文的时候,可以不用传递 canvasId,此时会通过离屏Canvas来创建绘制上下文环境。
|
||
if (this.isWeiXin) {
|
||
this.context = this.lpapi.createDrawContext();
|
||
} else {
|
||
// 备注:支付宝小程序新版本有问题,需要去掉 canvas 中的 type="2d"才可以。
|
||
this.context = this.lpapi.createDrawContext({
|
||
// 用于进行标签绘制的画布ID
|
||
canvasId: this.canvasId,
|
||
});
|
||
}
|
||
//
|
||
if (this.context) {
|
||
this.lpapi.setDrawContext(this.context);
|
||
// 预览标签内容
|
||
this.handlePreviewLabel();
|
||
}
|
||
},
|
||
onCanvasReady() {
|
||
console.log("========== onCanvasReady ==========");
|
||
console.log(`---- isAlipay: ${this.isAlipay}`);
|
||
if (this.isAlipay) {
|
||
// 支付宝小程序中在屏 Canvas的使用的时候,1:配置 type="2d",2:设置 onReady 事件,在onReady中通过 selectorQuery来获取canvas实例。
|
||
// this.queryCanvas((canvas) => {
|
||
// this.context = this.lpapi.createDrawContext({
|
||
// canvas: canvas,
|
||
// });
|
||
// if (this.context) {
|
||
// this.lpapi.setDrawContext(this.context);
|
||
// }
|
||
// this.handlePreviewLabel();
|
||
// });
|
||
}
|
||
},
|
||
onShow() {
|
||
console.log("========== onShow ==========");
|
||
// 建议在 onShow的时候,修改下标签的绘制上下文,避免因在context没有更新导致的 canvas不显示的问题。
|
||
if (this.context) {
|
||
this.lpapi.setDrawContext(this.context);
|
||
}
|
||
},
|
||
onUnload() {
|
||
console.log("========== onUnload ==========");
|
||
this.handleClosePrinter();
|
||
},
|
||
methods: {
|
||
queryCanvas(callback) {
|
||
// 微信小程序、支付宝小程序等 最新版本初始化方式
|
||
// 注意:canvas 中必须配置 type="2d" 才能生效。
|
||
try {
|
||
uni.createSelectorQuery()
|
||
.select(`#${this.canvasId}`)
|
||
.node()
|
||
.exec((res) => {
|
||
console.log(`------- exec.result: --------`);
|
||
console.log(res);
|
||
const canvas = res[0]?.node;
|
||
// this.printCanvas = canvas;
|
||
callback(canvas);
|
||
});
|
||
} catch (e) {
|
||
console.warn(`---- 不支持通过该方式获取 Canvas 对象!`);
|
||
console.warn(e);
|
||
callback(undefined);
|
||
}
|
||
},
|
||
onDeviceChanged(e) {
|
||
console.log(`---------- onDeviceChanged:${e.detail.value} ----------`);
|
||
this.deviceIndex = e ? e.detail.value : 0;
|
||
},
|
||
onRotationChanged(e) {
|
||
console.log(`---------- onRotationChanged: ${e.detail.value} ----------`);
|
||
this.rotationIndex = e ? e.detail.value : 0;
|
||
},
|
||
onGapTypeChanged(e) {
|
||
console.log(`---------- onGapTypeChanged:${e.detail.value} ----------`);
|
||
this.gapIndex = e ? e.detail.value : 0;
|
||
},
|
||
onDarknessChanged(e) {
|
||
console.log(`---------- onDarknessChanged:${e.detail.value} ----------`);
|
||
this.darknessIndex = e ? e.detail.value : 0;
|
||
},
|
||
onPrintSpeedChanged(e) {
|
||
console.log(`---------- onPrintSpeedChanged:${e.detail.value} ----------`);
|
||
this.speedIndex = e ? e.detail.value : 0;
|
||
},
|
||
onPrintModeChanged(e) {
|
||
console.log(`---------- onPrintModeChanged: ${e.detail.value} ----------`);
|
||
this.jsonMode = e.detail.value;
|
||
this.handlePreviewLabel();
|
||
},
|
||
handleStartDiscovery() {
|
||
uni.showLoading({
|
||
title: "正在搜索打印机...",
|
||
});
|
||
this.lpapi.startDiscovery({
|
||
timeout: 5000,
|
||
deviceFound: (devices) => {
|
||
this.onDeviceFound(devices);
|
||
},
|
||
adapterStateChange: (result) => {
|
||
if (!result.discovering) {
|
||
uni.hideLoading();
|
||
}
|
||
},
|
||
});
|
||
},
|
||
handleStopDiscovery() {
|
||
this.lpapi.stopDiscovery();
|
||
},
|
||
handleOpenPrinter() {
|
||
const currDevice = this.getDevice();
|
||
const isAlipay = this.isAlipay;
|
||
if (currDevice && currDevice.deviceId) {
|
||
//
|
||
uni.showLoading({
|
||
title: "正在链接打印机...",
|
||
});
|
||
return this.lpapi
|
||
.openPrinter({
|
||
name: currDevice.name,
|
||
deviceId: currDevice.deviceId,
|
||
// 如果打印机链接失败的话,可以尝试连续进行多次链接。
|
||
tryTimes: isAlipay ? 1 : 5,
|
||
success: (resp) => {
|
||
console.log(`---- 【打印机链接成功】`);
|
||
console.log(resp.resultInfo);
|
||
uni.hideLoading();
|
||
//
|
||
uni.showToast({
|
||
title: "打印机链接成功!",
|
||
icon: "success",
|
||
});
|
||
},
|
||
fail: (resp) => {
|
||
console.warn(`---- 【打印机链接失败】:`);
|
||
console.warn(JSON.stringify(resp));
|
||
//
|
||
uni.hideLoading();
|
||
//
|
||
uni.showToast({
|
||
title: "打印机链接失败!",
|
||
icon: "fail",
|
||
});
|
||
},
|
||
})
|
||
.then((res) => {
|
||
return res.statusCode === 0;
|
||
});
|
||
} else {
|
||
console.warn("---- 未检测到打印机!");
|
||
uni.showToast({
|
||
title: "未检测到打印机",
|
||
icon: "fail",
|
||
});
|
||
if (typeof callback === "function") {
|
||
callback(false);
|
||
}
|
||
return false;
|
||
}
|
||
},
|
||
handleClosePrinter() {
|
||
console.log(`---- 关闭打印机!`);
|
||
this.lpapi.closePrinter();
|
||
},
|
||
onDeviceFound(devices) {
|
||
console.log(`---- 检测到打印机:`);
|
||
console.log(devices);
|
||
if (devices && devices.length > 0) {
|
||
this.deviceList.splice(0);
|
||
for (let item of devices) {
|
||
this.deviceList.push(item);
|
||
//
|
||
// const advertisData = item.advertisData ? new Uint8Array(item.advertisData) : undefined;
|
||
// const advDataStr = advertisData ? LPAUtils.arrayBufferToHex16(advertisData) : "-";
|
||
// console.log(`---- advertisData [${item.name}] : [${advDataStr}]`);
|
||
}
|
||
}
|
||
},
|
||
getDevice() {
|
||
return this.deviceList[this.deviceIndex];
|
||
},
|
||
getOrientation() {
|
||
return this.rotationList[this.rotationIndex].value;
|
||
},
|
||
getGapType() {
|
||
return this.gapList[this.gapIndex].value;
|
||
},
|
||
getPrintDarkness() {
|
||
return this.darknessList[this.darknessIndex].value;
|
||
},
|
||
getPrintSpeed() {
|
||
return this.speedList[this.speedIndex].value;
|
||
},
|
||
getThreshold() {
|
||
return Number(this.threshold);
|
||
},
|
||
updateCanvas(jobInfo) {
|
||
// 如果通过 createSelectorQuery 获取到了 Canvas 实例,则不需要延迟处理;
|
||
// 在微信小程序、支付宝小程序中是支持该功能的;
|
||
return new Promise((resolve) => {
|
||
// 更新canvas大小;
|
||
if (jobInfo.canvas) {
|
||
this.labelWidth = jobInfo.canvas.width;
|
||
this.labelHeight = jobInfo.canvas.height;
|
||
// 等 Canvas 大小更新后在执行后续绘制操作;
|
||
setTimeout(() => {
|
||
resolve(true);
|
||
}, 100);
|
||
} else {
|
||
resolve(true);
|
||
}
|
||
});
|
||
},
|
||
handlePreviewLabel() {
|
||
// 1. 先清空当前的图片列表
|
||
this.previewList.splice(0);
|
||
// 2. 创建并绘制标签内容
|
||
this.printLabel(true).then((res) => {
|
||
console.warn(`----- previewLabel.result: ${res.statusCode}`, res.errMsg);
|
||
// 3. 标签绘制完毕后,预览标签内容
|
||
if (res.statusCode === 0) {
|
||
console.warn(`---- 所有页面全部生成完毕!`);
|
||
res.previewData.forEach((value) => {
|
||
this.previewList.push({
|
||
value: value,
|
||
// 为了保证 v-for循环的唯一性,随便生成一个随机ID
|
||
key: `${new Date().getTime()}-${Math.random() * 1000}`,
|
||
});
|
||
});
|
||
} else {
|
||
console.warn(`---- 标签预览失败!`, res);
|
||
}
|
||
});
|
||
},
|
||
async handlePrintLabel() {
|
||
// 1. 连接打印机
|
||
const openResult = await this.handleOpenPrinter();
|
||
if (!openResult) {
|
||
uni.showToast({
|
||
title: "打印机连接失败!",
|
||
icon: "success",
|
||
});
|
||
return;
|
||
}
|
||
// 2. 创建标签后直接打印
|
||
this.printLabel(false).then((res) => {
|
||
if (res.statusCode === 0) {
|
||
uni.showToast({
|
||
title: "打印成功!",
|
||
icon: "success",
|
||
});
|
||
} else {
|
||
console.warn(`---- 打印失败!`);
|
||
if (res.errMsg) {
|
||
uni.showToast({
|
||
title: res.errMsg,
|
||
icon: "error",
|
||
});
|
||
}
|
||
}
|
||
});
|
||
},
|
||
printLabel(isPreview) {
|
||
if (this.jsonMode) {
|
||
return this.printLabelWithJsonMode(isPreview);
|
||
} else {
|
||
return this.printLabelWithDrawMode(isPreview);
|
||
}
|
||
},
|
||
/**
|
||
* 通过 draw 的方式绘制标签内容。
|
||
*/
|
||
async printLabelWithDrawMode(isPreview) {
|
||
console.log(`---- qrcodePrintWithDrawMode:`);
|
||
const api = this.lpapi;
|
||
//
|
||
const labelWidth = 40;
|
||
const labelHeight = 30;
|
||
const margin = 2;
|
||
//
|
||
const jobInfo = api.startJob({
|
||
context: this.context,
|
||
width: labelWidth,
|
||
height: labelHeight,
|
||
orientation: this.getOrientation(),
|
||
isPreview: isPreview,
|
||
});
|
||
//
|
||
await this.updateCanvas(jobInfo);
|
||
// 绘制外边框
|
||
api.drawRectangle({
|
||
width: labelWidth,
|
||
height: labelHeight,
|
||
});
|
||
api.draw1DBarcode({
|
||
text: "12345678",
|
||
x: margin,
|
||
y: margin,
|
||
width: labelWidth - margin * 2,
|
||
height: labelHeight - margin * 2,
|
||
textHeight: 5,
|
||
barcodeType: 22,
|
||
});
|
||
//
|
||
return api.commitJob({
|
||
gapType: this.getGapType(),
|
||
printDarkness: this.getPrintDarkness(),
|
||
printSpeed: this.getPrintSpeed(),
|
||
});
|
||
},
|
||
/**
|
||
* 通过 JSON 方式绘制标签内容。
|
||
*/
|
||
printLabelWithJsonMode(isPreview) {
|
||
console.log(`---- qrcodePrintWithJsonMode:`);
|
||
const device = this.getDevice();
|
||
const labelWidth = 40;
|
||
const labelHeight = 30;
|
||
const margin = 2;
|
||
//
|
||
return this.lpapi.print({
|
||
// 配置标签信息
|
||
jobInfo: {
|
||
jobWidth: labelWidth,
|
||
jobHeight: labelHeight,
|
||
orientation: this.getOrientation(),
|
||
isPreview: isPreview, // 当前任务是否是预览任务
|
||
},
|
||
// 配置打印机信息
|
||
printerInfo: {
|
||
name: device?.name,
|
||
deviceId: device?.deviceId,
|
||
},
|
||
jobPage: [{
|
||
type: DrawType.rect,
|
||
width: labelWidth,
|
||
height: labelHeight,
|
||
},
|
||
{
|
||
type: DrawType.barcode,
|
||
text: "12345678",
|
||
x: margin,
|
||
y: margin,
|
||
width: labelWidth - margin * 2,
|
||
height: labelHeight - margin * 2,
|
||
textHeight: 5,
|
||
barcodeType: 22,
|
||
},
|
||
],
|
||
onJobCreated: (jobInfo) => {
|
||
// 通过双向绑定方式更新画布大小
|
||
return this.updateCanvas(jobInfo);
|
||
},
|
||
onPageComplete(res) {
|
||
// 标签页面渲染完毕时的回调函数,预览模式下可展示标签打印效果。
|
||
},
|
||
onPagePrintComplete(res) {
|
||
// 标签页面打印完毕时的回调函数。
|
||
},
|
||
});
|
||
},
|
||
},
|
||
};
|
||
</script>
|
||
|
||
<style>
|
||
button {
|
||
margin-top: 15upx;
|
||
margin-bottom: 15upx;
|
||
height: 40px;
|
||
}
|
||
|
||
.content {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.logo {
|
||
height: 200rpx;
|
||
width: 200rpx;
|
||
margin: 200rpx auto 50rpx auto;
|
||
}
|
||
|
||
.text-area {
|
||
display: flex;
|
||
justify-content: center;
|
||
}
|
||
|
||
.title {
|
||
font-size: 36rpx;
|
||
color: #8f8f94;
|
||
}
|
||
|
||
button {
|
||
margin-top: 30rpx;
|
||
margin-bottom: 30rpx;
|
||
}
|
||
|
||
.button-sp-area {
|
||
margin: 0 auto;
|
||
width: 90%;
|
||
display: flex;
|
||
}
|
||
|
||
.mini-btn {
|
||
flex: 1;
|
||
margin-right: 10rpx;
|
||
height: 40px;
|
||
line-height: 40px;
|
||
font-size: 18px;
|
||
}
|
||
</style> |