Files
shj-app/assets/js/js_sdk/index.js
T

1843 lines
415 KiB
JavaScript
Raw Normal View History

2026-06-29 17:35:49 +08:00
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
function __awaiter(thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
};
function t(t,e,i,s){return new(i||(i=Promise))(function(n,r){function a(t){try{h(s.next(t));}catch(t){r(t);}}function o(t){try{h(s.throw(t));}catch(t){r(t);}}function h(t){var e;t.done?n(t.value):(e=t.value,e instanceof i?e:new i(function(t){t(e);})).then(a,o);}h((s=s.apply(t,[])).next());})}"function"==typeof SuppressedError&&SuppressedError;class e{static formatDate(t,e){let i=t||"yyyy-MM-dd HH:mm:ss.SSS";const s={"y+":(e=e||new Date).getFullYear(),"M+":e.getMonth()+1,"d+":e.getDate(),"h+":e.getHours(),"H+":e.getHours(),"m+":e.getMinutes(),"s+":e.getSeconds(),"q+":Math.floor((e.getMonth()+3)/3),"S+":e.getMilliseconds()};let n;for(const t in s)if(n=new RegExp(`(${t})`).exec(i)){const e=`${s[t]}`,r=`00${e}`,a=n[1].length>r.length?0:r.length-n[1].length;i=i.replace(n[1],1===n[1].length?e:r.substring(a));}return i}static randomUUID(t){if("object"==typeof crypto){if("function"==typeof crypto.randomUUID)return t?crypto.randomUUID():crypto.randomUUID().toUpperCase();if("function"==typeof crypto.getRandomValues&&"function"==typeof Uint8Array){const e="10000000-1000-4000-8000-100000000000".replace(/[018]/g,t=>{const e=Number(t);return (e^crypto.getRandomValues(new Uint8Array(1))[0]&15>>e/4).toString(16)});return t?e.toUpperCase():e}}let e=(new Date).getTime(),i="undefined"!=typeof performance&&performance.now&&1e3*performance.now()||0;const s="xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,t=>{let s=16*Math.random();return e>0?(s=(e+s)%16|0,e=Math.floor(e/16)):(s=(i+s)%16|0,i=Math.floor(i/16)),("x"===t?s:3&s|8).toString(16)});return t?s.toUpperCase():s}static getRandomValues(t,e){const i=e&&e>0?e:256,s=[],n=t&&t>0?t:1;let r=(new Date).getTime(),a="undefined"!=typeof performance&&performance.now&&1e3*performance.now()||0;for(let t=0;t<n;t++){let e=Math.random()*i;r>0?(e=(r+e)%i|0,r=Math.floor(r/i)):(e=(a+e)%i|0,a=Math.floor(a/i)),s[t]=e;}return s}static getRandomStr(t){const e=t&&t>0?t:8;if("object"==typeof crypto&&"function"==typeof crypto.getRandomValues&&"function"==typeof Uint8Array){const t=crypto.getRandomValues(new Uint8Array(e));return Array.from(t).map(t=>(15&t).toString(16)).join("")}return this.getRandomValues(e,16).map(t=>(15&t).toString(16)).join("")}static createRect(t,e,i,s){return {x:t||0,y:e||0,width:i,height:s}}static parseRect(t){if(!t)return;t=t.trim();const e=/^\[?\(?\s*([0-9.+-]+)\s*,\s*([0-9.+-]+)\s*,\s*([0-9.+-]+)\s*,\s*([0-9.+-]+)\s*\)?]?$/.exec(t);return e?{x:parseFloat(e[1]),y:parseFloat(e[2]),width:parseFloat(e[3]),height:parseFloat(e[4])}:void 0}static isInnerRect(t,e){return t.x>=e.x&&t.x+t.width<=e.x+e.width&&t.y>=e.y&&t.y+t.height<=e.y+e.height}static hasIntersection(t,e){return t.x<e.x+e.width&&e.x<t.x+t.width&&t.y<e.y+e.height&&e.y<t.y+t.height}static getUnionRect(t,e){const i=Math.min(t.x,e.x),s=Math.min(t.y,e.y);return {x:i,y:s,width:Math.max(t.x+t.width,e.x+e.width)-i,height:Math.max(t.y+t.height,e.y+e.height)-s}}static rotate90(t){const e=t.x+.5*t.width,i=t.y+.5*t.height,s=t.width,n=t.height;return t.width>0&&t.height>0&&(t.width=n,t.height=s,t.x=e-.5*n,t.y=i-.5*s),t}}var i,s,n,r,a,o,h,c,d,u;!function(t){t[t.NONE=0]="NONE",t[t.ERROR=1]="ERROR",t[t.WARN=2]="WARN",t[t.INFO=3]="INFO",t[t.LOG=4]="LOG";}(i||(i={}));class l{static innerLog(t,...e){this.output?this.output.log(t,...e):console.log(t,...e);}static innerWarn(t,...e){this.output?this.output.warn(t,...e):console.warn(t,...e);}static innerError(t,...e){this.output?this.output.error(t,...e):console.error(t,...e);}static setLevel(t){t!==this.logLevel&&(this.logRecords.push(this.logLevel),this.logLevel=t,this.logRecords.length>this.RecordMaxSize&&this.logRecords.shift(),this.info(`<<================ setLogLevel(${t}) ================>>`));}static setLogOutput(t){this.output=t;}static restorePrev(){const t=this.logLevel;if(this.logRecords.length>0){const e=this.logRecords.shift();return this.logLevel=void 0===e?i.WARN:e,t}return -1}static logWithLevelFunc(t,i,s,...n){const r=e.formatDate("HH:mm:ss.SSS");"string"!=typeof s?1===n.length&&"string"==typeof n[0]?(t.call(l,`${i}[${r}]: ${n[0]||""}`),t.call(l,s)):t.call(l,s,...n):t.call(l,`
class UniCanvas {
get width() {
return this._width;
}
set width(v) {
this._width = v;
}
get height() {
return this._height;
}
set height(v) {
this._height = v;
}
get canvasId() {
return this.mContext.canvasId;
}
constructor(context) {
this._width = 0;
this._height = 0;
this.mContext = context;
}
getContext(options) {
if (!this.mCanvasContext) {
this.mCanvasContext = uni.createCanvasContext(this.mContext.canvasId || "", this.mContext.componentInstance);
}
return this.mCanvasContext;
}
toDataURL(type, quality) {
return this.mDataUrl || "";
}
getImageData() {
return new Promise((resolve) => {
const context = this.mCanvasContext;
if (!context) {
resolve(undefined);
return;
}
const funcType = typeof context.getImageData;
l.info(`【UniCanvas.Request】 getImageData: typeof context.getImageData = ${funcType}`);
if (this.mContext.isAlipay) {
// 支付宝小程序旧版本,只能异步获取ImageData;
// 备注:这个地方会报错,报 toJSON 错误,但是不影响打印,后期需要升级 context 获取方式;
if (this.mContext.canvas) {
// 新版本不需要特殊处理
resolve(undefined);
}
else {
// 旧版本需要异步处理;
if (funcType === "function") {
const timer = setTimeout(() => {
l.warn(`【UniCanvas.Response.timeout】 getImageData 请求超时!`);
resolve(undefined);
}, 5000);
const resolveData = (data) => {
clearTimeout(timer);
resolve(data);
};
const res = context.getImageData({
x: 0,
y: 0,
width: this.width,
height: this.height,
success: (res) => {
l.info(`【UniCanvas.Response.success】 getImageData:`);
clearTimeout(timer);
resolveData(res);
},
fail: (resp) => {
l.warn(resp, `【UniCanvas.Response.fail】 getImageData:`, resp);
clearTimeout(timer);
resolveData(undefined);
},
});
// 钉钉小程序返回值是 Promise,支付宝小程序返回值为空。
if (Le.isPromise(res)) {
Promise.resolve(res)
.then((_res) => {
// logger.info(`【UniCanvas.Response.then】 getImageData:`, _res);
})
.catch((err) => {
l.info(`【UniCanvas.Response.catch】 getImageData:`, err);
resolveData(undefined);
});
}
}
else {
l.warn(`【UniCanvas.Response】 getImageData: 不支持的方法:`);
resolve(undefined);
}
}
}
else if (typeof uni.canvasGetImageData === "function") {
// APP、微信小程序等,需要通过 canvasGetImageData 来获取imageData 对象;
l.info(`#### 【Request】 uni.canvasGetImageData:`);
uni.canvasGetImageData({
x: 0,
y: 0,
width: this.width,
height: this.height,
canvasId: this.mContext.canvasId || "",
success: (res) => {
l.info(`#### 【Response.success】 uni.canvasGetImageData:`);
resolve(res);
},
fail: (res) => {
l.error(`#### 【Response.fail】uni.canvasGetImageData:`);
l.error(JSON.stringify(res));
resolve(undefined);
},
}, this.mContext.componentInstance);
}
else {
resolve(undefined);
}
});
}
static getTempFilePath(options, callback) {
l.info(`#### 【Request】uni.canvasToTempFilePath:`);
let timer = setTimeout(() => {
l.warn(`#### 【Response.timeout】uni.canvasToTempFilePath:`);
callback("");
}, 3000);
//
uni.canvasToTempFilePath({
// 保存canvas为图片
canvasId: options.canvasId || "",
canvas: options.canvas,
quality: 1,
success: (res) => {
l.info(res, `#### 【Response.success】uni.canvasToTempFilePath:`);
// 备注:钉钉小程序需要通过 filePath来获取
// uni.setStorageSync('filePath',res.tempFilePath) //保存临时文件路径到缓存
clearTimeout(timer);
callback(res.tempFilePath || res.filePath);
},
fail: (res) => {
l.warn(res, `#### 【Response.fail】uni.canvasToTempFilePath:`);
clearTimeout(timer);
callback("");
},
}, options.componentInstance);
}
start(options) {
this.mDataUrl = "";
}
draw(context, printMode, reserve) {
l.log(`---- UniCanvas.draw: typeof context.draw = ${typeof context.draw}`);
return new Promise((resolve) => {
if (!context) {
resolve("");
return;
}
const getTempPathAction = () => {
const ddContext = context;
const funcType = this.mContext.isAlipay ? typeof ddContext.toTempFilePath : undefined;
console.warn(`---- typeof context.toTempFilePath = ${funcType}`);
// 备注:支付宝小程序也支持 toTempFilePath,但是返回的图片是空白。
if (this.mContext.isDingTalk && funcType === "function") {
const timer = setTimeout(() => {
l.log(`【UniCanvas.Response.timeout】 dd.Context.toTempFilePath:`);
resolve("");
}, 3000);
const resolveUrl = (url) => {
clearTimeout(timer);
resolve(url);
};
//
l.log(`【UniCanvas.Request】 dd.Context.toTempFilePath:`);
const res = ddContext.toTempFilePath({
x: 0,
y: 0,
width: this.width,
height: this.height,
success: (res) => {
const url = res.tempFilePath || res.url || "";
this.mDataUrl = url;
l.log(`【UniCanvas.Response.success】 context.toTempFilePath:`, res);
resolveUrl(url);
},
fail: (err) => {
l.log(`【UniCanvas.Response.fail】 context.toTempFilePath:`, err);
console.warn(`---- is fail to getTempFilePath: `, err);
resolveUrl("");
},
});
// 钉钉小程序返回值是 Promise,支付宝小程序返回值为空。
if (Le.isPromise(res)) {
Promise.resolve(res)
.then((res) => {
l.log(`【UniCanvas.Response.then】 context.toTempFilePath:`, res);
})
.catch((err) => {
l.log(`【UniCanvas.Response.catch】 context.toTempFilePath:`, err);
resolveUrl("");
});
}
}
else {
UniCanvas.getTempFilePath(this.mContext, (tempPath) => {
this.mDataUrl = tempPath;
resolve(tempPath);
});
}
};
if (this.mContext.isAlipay) {
// 支付宝小程序 draw函数没有回调。
if (typeof context.draw === "function") {
context.draw(reserve);
}
//
if (printMode) {
resolve("");
}
else {
setTimeout(() => {
getTempPathAction();
}, this.mContext.drawTimeout || 100);
}
}
else if (typeof context.draw === "function") {
let hasDraw = false;
context.draw(reserve, () => {
hasDraw = true;
if (!printMode) {
// 微信小程序中为了避免生成预览图片黑块的问题
setTimeout(getTempPathAction, this.mContext.drawTimeout || 100);
}
else {
resolve("");
}
});
setTimeout(() => {
if (!hasDraw) {
l.warn(`---- Canvas.draw 函数长时间未响应,请检查目标 Canvas 是否已经销毁。`);
resolve("");
}
}, 3000);
}
else if (this.mContext.canvas && !printMode) {
// 为了避免出现黑块,暂时等待1秒,具体待测试。
setTimeout(() => {
getTempPathAction();
}, 10);
}
else {
resolve("");
}
});
}
saveBase64Image(dataUrl, callback) {
const groups = /^data:(\S+)\/(\S+);base64,.*/.exec(dataUrl || "");
if (groups && plus && plus.nativeObj.Bitmap) {
if (groups[2] === "svg+xml") {
callback && callback(dataUrl);
}
else {
const fileName = `border.${groups[2]}`;
const bitmap = new plus.nativeObj.Bitmap(`bitmap${Date.now()}`);
try {
bitmap.loadBase64Data(dataUrl, () => {
const filePath = `_doc/temp/${fileName}`;
bitmap.save(filePath, { overwrite: true }, (res) => {
bitmap.clear();
callback && callback(res);
}, (res) => {
bitmap.clear();
l.warn(`---- 图片保存失败!`);
l.warn(res);
callback && callback(dataUrl);
});
}, (res) => {
bitmap.clear();
l.warn(`---- base64 字符串加载失败:`);
l.warn(res);
callback && callback(dataUrl);
});
}
catch (error) {
l.warn(`---- base64 字符串load 异常!`);
l.warn(error);
bitmap.clear();
callback && callback(dataUrl);
}
}
}
else {
callback && callback(dataUrl);
}
}
loadImage(options) {
const opts = typeof options === "string" ? { src: options } : options;
l.log(`UniCanvas.loadImage: ${opts.src.length > 100 ? opts.src.substring(0, 100) : opts.src}`);
return new Promise((resolve) => {
if (opts.src.startsWith("http")) {
uni.getImageInfo({
src: opts.src,
success: (res) => {
l.log(`res, 【Response.success】uni.getImageInfo:`);
resolve({
width: res.width,
height: res.height,
dzSrc: res.path,
});
},
fail: (res) => {
l.warn(res, `【Response.fail】uni.getImageInfo:`);
resolve("");
},
});
}
else if (opts.src.startsWith("data:")) {
if (opts.withSize) {
this.saveBase64Image(opts.src, (result) => {
if (result && typeof result !== "string" && result.width && result.height) {
resolve({
width: result.width,
height: result.height,
dzSrc: result.target,
});
}
else {
resolve(opts.src);
}
});
}
else {
resolve(opts.src);
}
}
else {
resolve(opts.src);
}
});
}
}
class UniContext extends xe {
static createInstance(options, prevContext) {
const opts = options || {};
if (prevContext) {
const prevCanvas = prevContext.CanvasElement;
if (options.canvas && options.canvas === prevCanvas) {
return prevContext;
}
else if (options.canvasId &&
prevCanvas instanceof UniCanvas &&
options.canvasId === prevCanvas.canvasId) {
return prevContext;
}
}
//
return opts.canvas || opts.canvasId ? new UniContext(opts) : undefined;
}
static isDingTalk() {
try {
if (typeof dd === "object") {
return true;
}
}
catch (error) {
l.log(`---- UniContext.isDingTalk.exception: `, error);
}
return false;
}
constructor(context) {
super(Object.assign({ apiMode: true }, (context || {})));
//
this.init(context || {});
//
l.log(`==== 【UniContext】constructor(${this.instanceId}) ====`, this.mOptions);
}
init(options) {
const opts = options || {};
// #ifdef APP-PLUS
opts.isAppPlus = true;
// #endif
// #ifdef H5
opts.isH5 = true;
// #endif
// #ifdef MP-WEIXIN
opts.isWeiXin = true;
// #endif
// #ifdef MP-ALIPAY
opts.isAlipay = true;
// #endif
//
if (opts.isAlipay) {
opts.isDingTalk = UniContext.isDingTalk();
}
//
this.mOptions = opts;
}
createCanvas() {
const context = this.mOptions;
l.info(`【UniContext】createCanvas: create by options: `, this.mOptions);
//
if (!this.mCanvas) {
if (document && typeof document.createElement === "function") {
l.log('【UniContext】createCanvas by document.createElement("canvas")!');
// H5环境适配
this.mCanvas = document.createElement("canvas");
}
else if (context.canvas && typeof context.canvas.getContext === "function") {
l.log("【UniContext】createCanvas by options.canvas!");
// 新版在屏 canvas 适配,canvas必须支持获取绘制上下文才可以使用
this.mCanvas = context.canvas;
}
else if (context.canvasId) {
l.log("【UniContext】createCanvas by canvasId!");
// 旧版在屏 canvas 适配
this.mCanvas = new UniCanvas(this.mOptions);
}
else {
l.log("【UniContext】createCanvas by uni.createOffscreenCanvas!");
// 离屏 canvas 适配
if (typeof uni.createOffscreenCanvas === "function") {
this.mCanvas = uni.createOffscreenCanvas({ type: "2d" });
}
if (!this.mCanvas) {
if (context.isWeiXin) {
l.warn("================================");
l.warn("==== 真机调试请使用 2.0 模式 ====");
l.warn("================================");
}
else {
l.warn("=======================================");
l.warn("==== 当前环境不支持 OffscreenCanvas ====");
l.warn("=======================================");
}
}
else {
l.info("==================================");
l.info("==== OffscreenCanvas创建成功! ====");
l.info("==================================");
}
}
}
//
return this.mCanvas;
}
/**
* 通过 canvas 来创建 Image 对象。
*/
createImage(canvas, src, timeout) {
// 注意:
// 1. 不知道为什么,通过canvas.createImage创建的Image在小程序中第二次加载图片的时候不会触发 onload 方法;
// 2. Image本身就可以加载网络图片,所以也不需要使用 getImageInfo来缓存;
// 3. 网络图片可以通过添加随机参数来避免第二次无法加载的问题,本地图片该方法不可行;
// 4. 加载完之后,不能立即将 image.src 设置为空,设置为空后图片显示黑块;
return new Promise((resolve) => {
const imgSrc = src.startsWith("http") ? `${src}?${new Date().getTime()}${Math.random()}` : src;
// const canvas = uni.createOffscreenCanvas({ type: "2d" }) as any;
const image = canvas.createImage();
const subImagePath = imgSrc.length > 100 ? `${imgSrc.substring(0, 100)}...` : imgSrc;
let processed = false;
l.info(`---- canvas.createImage: ${subImagePath}`);
image.onload = () => {
l.info(`---- image.onload`);
if (processed) {
l.warn(`图片加载时间太长了,已经进行超时处理了!`);
}
else {
processed = true;
resolve(image);
}
};
image.onerror = (res) => {
l.warn(res, `---- image.onerror:${subImagePath}`);
processed = true;
resolve(imgSrc);
};
//
setTimeout(() => {
if (!processed) {
l.warn(`---- 图片加载超时:`);
processed = true;
resolve(image);
}
}, timeout || 3000);
image.src = imgSrc;
});
}
loadImage(options) {
const _super = Object.create(null, {
loadImage: { get: () => super.loadImage }
});
return __awaiter(this, void 0, void 0, function* () {
l.log(`---- UniContext.loadImage:`);
const opts = typeof options === "string" ? { src: options } : options;
if (!opts || !opts.src) {
return null;
}
const canvas = this.createCanvas();
if (canvas instanceof UniCanvas) {
// 通过 canvasId 来创建上下文的情况;
return canvas.loadImage(opts);
}
else if (document && typeof document.createElement === "function") {
return _super.loadImage.call(this, opts.src);
}
else if (canvas && canvas === this.mOptions.canvas) {
// 新版的在屏 canvas 适配;
return this.createImage(canvas, opts.src, 0);
}
else if (typeof uni.createOffscreenCanvas === "function") {
// 兼容微信小程序离屏Canvas
return this.createImage(canvas, opts.src, 0);
}
else {
return _super.loadImage.call(this, opts.src);
}
});
}
startJob(options) {
l.log(`#### UniContext.startJob - before super.startJob:`);
// 通过 querySelector 方式获取到 canvas后,已经直接将canvas配置到了 DzCanvas的初始化函数中了;
// 所以底层不会再调用 createCanvas了,所以此处需要重新获取下 canvas。
if (!this.mCanvas) {
this.createCanvas();
}
if (!this.mCanvas) {
l.warn(`#### UniContext.startJob: 未获取到有效的 canvas 实例,无法进行标签绘制!`, this.mCanvas);
return undefined;
}
//
const res = super.startJob(options);
l.log(`#### UniContext.startJob - after super.startJob:`, res);
if (!res)
return res;
//
if (this.mCanvas && this.mCanvas instanceof UniCanvas) {
this.mCanvas.start(res);
}
return Object.assign(Object.assign({}, res), { canvas: this.mCanvas });
}
onCanvasClear(canvas, context) {
// if (canvas && canvas instanceof UniCanvas) {
// canvas.draw(context as any, this.IsApiMode, true);
// }
}
commitJob() {
l.info(`---- [UniContext] DrawContext.commitJob ====> before super.commitJob:`);
return super.commitJob().then((res) => {
l.info(`---- [UniContext] DrawContext.commitJob ====> after super.commitJob:`, res);
if (!res)
return res;
//
const canvas = this.mCanvas;
const jobInfo = this.jobOptions || {};
if (canvas && canvas instanceof UniCanvas) {
const context = canvas.getContext();
return canvas.draw(context, !res.isPreview).then((v) => {
if (!res.dataUrl)
res.dataUrl = v;
res.tempFilePath = v;
if (!res.isPreview || jobInfo.withImageData) {
return canvas.getImageData().then((data) => {
res.imageData = data;
return res;
});
}
else {
return res;
}
});
}
else if (canvas && canvas === this.mOptions.canvas) {
l.info(`---- 获取新版在屏Canvas对应的预览图片!`, this.mOptions);
// 新版的在屏 canvas 适配;
if (this.mOptions.isAlipay && res.isPreview && !res.dataUrl) {
const aliCanvas = canvas;
const funcType = typeof aliCanvas.toTempFilePath;
l.info(`【Request】canvas.toTempFilePath: canvas.toTempFilePath = ${funcType}`);
if (funcType === "function") {
// const aliCanvas: Record<string, any> = this.mContext.canvas;
return new Promise((resolve) => {
const timer = setTimeout(() => {
resolve(res);
}, 3000);
aliCanvas.toTempFilePath({
x: 0,
y: 0,
width: aliCanvas.width,
height: aliCanvas.height,
success: (result) => {
l.info(`【Response.success】canvas.toTempFilePath: `, result);
res.dataUrl = result.tempFilePath;
clearTimeout(timer);
resolve(res);
},
fail: (err) => {
l.warn(`【Response.fail】canvas.toTempFilePath: `, err);
clearTimeout(timer);
resolve(res);
},
});
});
}
}
}
//
return res;
});
}
}
class BleAdapter {
static getInstance() {
return this._instance || (this._instance = new BleAdapter());
}
constructor(options) {
this.mConnectionMap = new Map();
this.mCharacteristicMap = new Map();
this.mContext = options || {};
l.log(`==== 【BleAdapter】constructor(options) ====`, this.mContext);
// #ifdef APP-PLUS
this.mContext.isAppPlus = true;
// #endif
// #ifdef H5
this.mContext.isH5 = true;
// #endif
// #ifdef MP-WEIXIN
this.mContext.isWeiXin = true;
// #endif
// #ifdef MP-ALIPAY
this.mContext.isAlipay = true;
// #endif
//
if (this.mContext.isAlipay) {
this.mContext.isDingTalk = UniContext.isDingTalk();
}
//
if (typeof uni.getDeviceInfo === "function") {
this.mDeviceInfo = uni.getDeviceInfo();
l.log(`---- deviceInfo:`, this.mDeviceInfo);
}
if (typeof uni.getSystemInfo === "function") {
uni.getSystemInfo({
success: (res) => {
this.mSystemInfo = res;
l.log(`---- mSystemInfo: `, res);
},
});
}
}
get isH5() {
return this.mContext.isH5;
}
get Context() {
return this.mContext;
}
get platform() {
var _a, _b, _c;
return ((_a = this.mDeviceInfo) === null || _a === void 0 ? void 0 : _a.platform) || ((_b = this.mSystemInfo) === null || _b === void 0 ? void 0 : _b.osName) || ((_c = this.mSystemInfo) === null || _c === void 0 ? void 0 : _c.platform) || "";
}
isAndroid() {
return this.platform.toLowerCase() === "android";
}
isHarmonyOS() {
const osName = this.platform;
return osName === "ohos" || osName.toLowerCase().indexOf("harmony") >= 0;
}
getDeviceInfo() {
return this.mDeviceInfo;
}
androidAPILevel() {
if (this.isAndroid() && this.mContext.isAppPlus) {
const apiLevel = this.mSystemInfo ? this.mSystemInfo.osAndroidAPILevel : 0;
if (typeof apiLevel === "number" && apiLevel > 0) {
return apiLevel;
}
else {
const Build = plus.android.importClass("android.os.Build");
const version = Build ? Build.VERSION.SDK_INT : 0;
return typeof version === "number" ? version : 0;
}
}
else {
return 0;
}
}
requestPermissions() {
return new Promise((resolve) => {
const sdkVersion = this.androidAPILevel();
if (sdkVersion >= 23) {
const permissions = [];
if (sdkVersion >= 31) {
permissions.push("android.permission.BLUETOOTH_SCAN");
permissions.push("android.permission.BLUETOOTH_CONNECT");
}
if (sdkVersion >= 29) {
permissions.push("android.permission.ACCESS_FINE_LOCATION");
}
else {
permissions.push("android.permission.ACCESS_COARSE_LOCATION");
}
l.log(`#### 【Request】plus.android.requestPermissions, sdkVersion = ${sdkVersion}`);
plus.android.requestPermissions(permissions, (res) => {
l.log(res, `#### 【Response.success】plus.android.requestPermissions:`);
resolve(true);
}, (res) => {
l.warn(res, `#### 【Response.error】plus.android.requestPermissions:`);
resolve(false);
});
}
else {
resolve(true);
}
});
}
/**
* 蓝牙授权认证。
*/
authorize(options) {
l.log(`【BleAdapter】authorize(options)`, options, this.mContext);
if (this.mContext.isAppPlus) {
return this.requestPermissions().then((result) => {
if (result) {
return Le.success(true, options);
}
else {
return Le.complete(Ee.ERROR_AUTH_FAILED, "蓝牙权限申请失败!", options, undefined);
}
});
}
else if (this.mContext.isH5) {
return Promise.resolve(Le.success(true, options));
}
else if (this.mContext.isAlipay) {
// 支付宝小程序不支持 authorize 接口
return Promise.resolve(Le.success(true, options));
}
//
return new Promise((resolve) => {
try {
l.info("#### 【Request】authorize ----> getSetting");
if (typeof uni.getSetting !== "function" || typeof uni.authorize !== "function") {
Le.success("不支持蓝牙认证相关接口!", options, resolve);
return;
}
uni.getSetting({
success: (res) => {
const authSetting = res.authSetting;
if (authSetting["scope.bluetooth"] || authSetting["bluetooth"]) {
l.info(`####【Response.success】authorize.getSetting: 蓝牙权限获取成功!`, res);
Le.success("蓝牙权限已授权", options, resolve);
}
else {
l.info(`####【Response.success】authorize.getSetting: 蓝牙权限未授权!`, res);
l.info(`【Request】authorize:`);
uni.authorize({
scope: "scope.bluetooth",
success: (res2) => {
l.info(`#### 【Response.success】authorize: 蓝牙权限授权成功!`, res2);
Le.success("蓝牙权限授权成功!", options, resolve);
},
fail: (res2) => {
l.warn(`#### 【Response.fail】authorize: 蓝牙权限授权失败!`, res2);
// 如果用户拒绝了蓝牙授权申请,则需要提示用户去设置中开启蓝牙权限。
uni.showModal({
title: "蓝牙权限未授权",
content: "请前往设置界面开启蓝牙权限",
confirmText: "去设置",
cancelText: "取消",
showCancel: true,
success: (modalRes) => {
if (modalRes.confirm) {
l.info(`#### 【Response.success】用户点击了去设置按钮!`, modalRes);
uni.openSetting({
success: (settingRes) => {
if (settingRes.authSetting["scope.bluetooth"]) {
l.info(`#### 【Response.success】蓝牙权限已授权!`, settingRes);
Le.success("蓝牙权限已授权", options, resolve);
}
else {
l.warn(`#### 【Response.success】蓝牙权限仍未授权!`, settingRes);
Le.complete(Ee.ERROR_AUTH_FAILED, "蓝牙权限仍未授权", options, resolve);
}
},
fail: (settingRes) => {
l.warn(`#### 【Response.fail】openSetting, 权限设置界面打开失败!`, settingRes);
Le.complete(Ee.ERROR_AUTH_FAILED, "权限设置界面打开失败", options, resolve);
},
});
}
else {
l.warn(`---- 用户点击了取消按钮!`, modalRes);
Le.complete(Ee.ERROR_AUTH_FAILED, "用户取消了蓝牙权限设置", options, resolve);
}
},
fail: (modalRes) => {
l.warn(`#### 【Response.fail】showModal:`, modalRes);
Le.complete(Ee.ERROR_AUTH_FAILED, "蓝牙权限设置弹窗打开失败", options, resolve);
},
});
},
});
}
},
fail: (res) => {
l.warn(`#### 【Response.fail】蓝牙权限获取失败!`, res);
Le.complete(Ee.ERROR_AUTH_FAILED, res, options, resolve);
},
});
}
catch (error) {
l.warn(`#### 【Response.error】蓝牙权限获取异常!`, error);
Le.complete(Ee.ERROR_AUTH_FAILED, "获取蓝牙权限异常,请检查网络连接或小程序设置。", options, resolve);
}
});
}
createAdapterStateChangeAction() {
if (!this.mAdapterStateChangeAction) {
this.mAdapterStateChangeAction = (res) => {
l.info("==== onBluetoothAdapterStateChange: ====");
l.info(JSON.stringify(res));
if (typeof res.available === "string") {
res.available = res.available === "true";
}
if (res.available === false && this.mConnectionMap.size > 0) {
for (const item of this.mConnectionMap.values()) {
item.connected = false;
if (typeof item.connectStateChange === "function") {
item.connectStateChange({ deviceId: item.deviceId, connected: false });
}
}
}
if (typeof this.mAdapterStateChange === "function") {
this.mAdapterStateChange(res);
}
};
const adapter = this.Context.bleAdapter;
if (adapter && typeof adapter.onBluetoothAdapterStateChange === "function") {
adapter.onBluetoothAdapterStateChange(this.mAdapterStateChangeAction);
}
else {
uni.onBluetoothAdapterStateChange(this.mAdapterStateChangeAction);
}
}
}
openAdapter(options) {
const opts = options || {};
const isOpened = this.mIsAdapterOpened;
l.log(`【BleAdapter】openAdapter(isAdapterOpened: ${isOpened}, force: ${opts.force})`, options);
return new Promise((resolve) => {
if (this.mIsAdapterOpened && !opts.force) {
l.log(`---- 蓝牙适配器已经打开!`);
Le.success("蓝牙适配器已打开!", options, resolve);
}
else if (this.mContext.isH5) {
// 在浏览器中调用 uni.openBluetoothAdapter 会抛异常,并且也无法try cache,所以直接返回失败即可。
Le.complete(Ee.ERROR_UN_SUPPORTED, "H5环境中不支持蓝牙操作!", options, resolve);
}
else {
this.authorize({
complete: () => {
try {
l.info("#### 【Request】openBluetoothAdapter:", options);
uni.openBluetoothAdapter({
success: (openRes) => {
this.mIsAdapterOpened = true;
l.info(`#### 【Response.success】openBluetoothAdapter!`, openRes);
//
if (!this.mAdapterStateChangeAction) {
this.createAdapterStateChangeAction();
}
//
Le.success(openRes, options, resolve);
},
fail: (res) => {
const errCode = res.errCode || res.code || 0;
if (errCode <= 0) {
l.info(`#### 【Response.fail】蓝牙适配器已打开,errCode = ${errCode}`, res);
Le.success(res, options, resolve);
}
else {
l.warn("#### 【Response.fail】openBluetoothAdapter:", res);
if (res.errCode === 3 || res.errno === 3) {
// 如果用户拒绝了蓝牙授权申请,则需要提示用户去设置中开启蓝牙权限。
Le.complete(Ee.ERROR_OPEN_ADAPTER, "请在系统的应用设置里面找到HBuilder,打开权限管理,检查蓝牙、附近设备等权限是否已打开。如果已经打开,可以尝试先拒绝,然后重新允许。", options, resolve);
}
else {
Le.complete(Ee.ERROR_OPEN_ADAPTER, res, options, resolve);
}
}
},
});
}
catch (error) {
l.warn("####【Exception】openBluetoothAdapter:", error);
Le.complete(Ee.ERROR_OPEN_ADAPTER, error, options, resolve);
}
},
fail: (res) => {
Le.onResult(res, options, resolve);
},
});
}
});
}
resetAllEventListener() {
if (typeof uni.offBluetoothAdapterStateChange === "function") {
l.info(`#### uni.offBluetoothAdapterStateChange:`);
uni.offBluetoothAdapterStateChange();
//
if (this.mAdapterStateChangeAction) {
this.mAdapterStateChangeAction = undefined;
}
}
// 关闭蓝牙适配器前停止特征值监听事件
this.resetCharacteristicValueChange();
}
closeAdapter(options) {
return __awaiter(this, void 0, void 0, function* () {
l.info("### 【Request】: closeBluetoothAdapter: ", options);
// 先断开所有已连接设备,在 lpapi中可能关闭的不彻底
if (this.mConnectionMap.size > 0) {
for (const item of this.mConnectionMap.values()) {
yield this.disconnect({ deviceId: item.deviceId });
}
this.mConnectionMap.clear();
}
//
return new Promise((resolve) => {
this.resetAllEventListener();
uni.closeBluetoothAdapter({
success: (res) => {
l.info(`#### 【Response.success】closeBluetoothAdapter:`, res);
},
fail: (res) => {
l.warn(`#### 【Response.fail】closeBluetoothAdapter:`, res);
},
complete: (res) => {
// 蓝牙适配器关闭后,打印机会断开链接,此时需要通知下LPAPI
this.mIsAdapterOpened = false;
resolve({ statusCode: Ee.OK, resultInfo: res });
},
});
});
});
}
resetAdapter(options) {
const opts = typeof options === "function" ? { callback: options } : options || {};
return this.closeAdapter().then(() => {
// 重新打开蓝牙适配器。
return this.openAdapter().then((res) => {
return Le.onResult(res, opts);
});
});
}
startDiscovery(options) {
const opts = options || {};
const onSuccessCallback = (result) => {
if (result.statusCode === Ee.OK) {
const scanInterval = opts.interval || 200;
return new Promise((resolve) => {
l.info("### 【Request】startBluetoothDevicesDiscovery: ", options);
uni.startBluetoothDevicesDiscovery({
interval: scanInterval,
allowDuplicatesKey: true,
success: (scanRes) => {
this.mAdapterStateChange = opts.adapterStateChange;
l.info(`#### 【Response.success】: startBluetoothDevicesDiscovery:`, scanRes);
if (this.mDeviceGetTimer && this.mDeviceGetTimer > 0) {
clearInterval(this.mDeviceGetTimer);
}
this.mDeviceGetTimer = setInterval(() => {
uni.getBluetoothDevices({
success: (res) => {
const devices = !res.devices && Array.isArray(res) ? res : res.devices || [];
// 过滤掉没有名称的设备
const filters = devices.filter((v) => !!(v.name || v.localName));
if (filters.length > 0) {
// logger.log(`---- onBluetoothDeviceFound:`, res);
opts.deviceFound && opts.deviceFound(filters);
}
},
});
}, scanInterval);
//
if (typeof uni.getBluetoothDevices === "function") {
uni.getBluetoothDevices({
success: (res) => {
// logger.log(`#### 【Response.success: getBluetoothDevices:`);
const devices = !res.devices && Array.isArray(res) ? res : res.devices || [];
// 过滤掉没有名称的设备
const filters = devices.filter((v) => !!(v.name || v.localName));
if (filters.length > 0) {
opts.deviceFound && opts.deviceFound(filters);
}
},
});
}
//
Le.success(scanRes, options, resolve);
},
fail: (res) => {
l.warn(`#### 【Response.fail】: startBluetoothDevicesDiscovery:`, JSON.stringify(res));
Le.fail(res, options, resolve);
},
});
});
}
else {
l.warn("========== 蓝牙适配器打开失败! ==========");
return Le.onResult(result, options);
}
};
// 在Uni中重置蓝牙适配器后,倒是可以自动断开设备链接了,但是特征值的监听无法取消,重新监听特征值后,会接收到很多无效的数据。
// 所以最好还是不要重置适配器了
if (opts.resetAdapter) {
return this.resetAdapter().then(onSuccessCallback);
}
else {
return this.openAdapter().then(onSuccessCallback);
}
}
stopDiscovery(options) {
return new Promise((resolve) => {
// 停止蓝牙搜索
l.info(`###【Request】stopBluetoothDevicesDiscovery:`);
uni.stopBluetoothDevicesDiscovery({
success: (res) => {
l.info(`###【Response.success】stopBluetoothDevicesDiscovery:`);
Le.success(res, options, resolve);
// 停止设备检测;
// 在 UniApp中 onBluetoothDeviceFound 是全局的,注册后无法取消,暂时先用 getBluetoothDevices方法试试。
if (this.mDeviceGetTimer && this.mDeviceGetTimer > 0) {
clearInterval(this.mDeviceGetTimer);
this.mDeviceGetTimer = 0;
}
// 停止搜索后,支付宝小程序不会触发该回调,所以手动触发下。
this.mAdapterStateChange && this.mAdapterStateChange({ discovering: false });
this.mAdapterStateChange = undefined;
},
fail: (res) => {
l.warn(`###【Response.fail】stopBluetoothDevicesDiscovery:`, JSON.stringify(res));
Le.fail(res, options, resolve);
},
});
});
}
getFoundDevices(options) {
return new Promise((resolve) => {
l.info("####【Request】getBluetoothDevices:", options);
uni.getBluetoothDevices({
success: (res) => {
l.info("####【Response.success】getBluetoothDevices:", res);
const devices = !res.devices && Array.isArray(res) ? res : res.devices || [];
// 过滤掉没有名称的设备
const filters = devices.filter((v) => !!(v.name || v.localName));
Le.success(filters, options, resolve);
},
fail: (res) => {
l.warn("####【Response.fail】getBluetoothDevices:", res);
Le.fail([], options, resolve);
},
});
});
}
createConnectionStateChangeAction() {
if (!this.mConnectionStateChangeAction) {
this.mConnectionStateChangeAction = (resp) => {
l.info(`========== onConnectionStateChange ==========`);
l.info(JSON.stringify(resp));
for (const item of this.mConnectionMap.values()) {
if (resp.deviceId === item.deviceId) {
if (typeof item.connectStateChange === "function") {
item.connectStateChange(resp);
}
item.connected = resp.connected;
}
}
};
uni.onBLEConnectionStateChange(this.mConnectionStateChangeAction);
}
}
connect(options) {
const deviceId = options.deviceId || "";
return new Promise((resolve) => {
this.openAdapter()
.then(() => {
l.info(`####【Request】createConnection:`, options);
const connectOptions = {
deviceId: deviceId,
success: (res) => {
l.info(`---- 【Response.success】createConnection:`, res);
this.mCharacteristicMap.clear();
// 监控打印机连接状态
if (!this.mConnectionStateChangeAction) {
this.createConnectionStateChangeAction();
}
this.mConnectionMap.set(deviceId, {
deviceId: deviceId,
connected: true,
connectStateChange: options.connectionStateChange,
});
// 经测试:在有些环境下,打印机连接成功之后不能立马进行后续操作,否则无效;
// IOS环境下,DF2需要200毫秒延时,DF4 100毫秒足够了,保险起见,默认统一设置300毫秒
let awaitTimes = 300;
if (this.mContext.isAppPlus) {
l.log("============== APP-PLUS ============");
if (this.isAndroid()) {
// 在安卓环境下,打印机链接成功后大概要等待800毫秒才可以进行后续操作,保险起见,此处设置1000毫秒的延时;
awaitTimes = 1000;
}
}
setTimeout(() => {
Le.success(res, options, resolve);
}, awaitTimes);
},
fail: (res) => {
l.warn(`---- 【Response.fail】createConnection:`, JSON.stringify(res));
let errCode = res.errCode || res.code;
if (errCode === 10010) {
errCode = -1;
}
// 支付宝小程序中既没有errCode 也没有 code
if (typeof errCode === "number" && errCode <= 0) {
// this.mConnected = true;
// this.mConnectStatusMap.set(deviceId, true);
if (!this.mConnectionStateChangeAction) {
this.createConnectionStateChangeAction();
}
this.mConnectionMap.set(deviceId, {
deviceId: deviceId,
connected: true,
connectStateChange: options.connectionStateChange,
});
Le.success(res, options, resolve);
}
else {
Le.complete(Ee.ERROR_CONNECT_FAILED, res, options, resolve);
}
},
};
if (!this.mContext.isAlipay && typeof options.timeout === "number") {
// 支付宝小程序不能配置 timeout,否则可能会出现链接不上的问题。
connectOptions.timeout = options.timeout;
}
//
if (this.mContext.isAlipay) {
my.connectBLEDevice(connectOptions);
}
else {
uni.createBLEConnection(connectOptions);
}
})
.catch((error) => {
l.warn("----【Exception】createConnection:", error);
Le.complete(Ee.ERROR_CONNECT_FAILED, error, options);
});
});
}
resetCharacteristicValueChange() {
l.info(`#### ===== 重置已连接设备的所有特征值变化通知事件 ===== ####`);
if (this.mCharacteristicValueChangeMap) {
this.mCharacteristicValueChangeMap.clear();
//
if (typeof uni.offBLECharacteristicValueChange === "function") {
uni.offBLECharacteristicValueChange();
this.mCharacteristicValueChangeMap = undefined;
}
}
}
disconnect(options) {
return __awaiter(this, void 0, void 0, function* () {
const opts = typeof options === "string" ? { deviceId: options } : options || {};
const deviceId = opts.deviceId || "";
const connectInfo = deviceId ? this.mConnectionMap.get(deviceId) : undefined;
//
if (!deviceId) {
l.warn(`### 【Request】disconnect: 参数错误,deviceId = ${deviceId}`, options);
return Promise.resolve(Le.complete(Ee.ERROR_PARAM, "参数错误", options));
}
if (!connectInfo) {
l.info(`#### 设备未连接!`);
return Le.success("设备未连接", options);
}
//
return new Promise((resolve) => {
const api = uni;
// 重置连接状态变化通知事件
if (typeof uni.offBLEConnectionStateChange === "function") {
// 微信小程序、飞书小程序适配;
l.info(`---- offBLEConnectionStateChange:`);
uni.offBLEConnectionStateChange();
this.mConnectionStateChangeAction = undefined;
}
else if (typeof api.offBLEConnectionStateChanged === "function") {
// 支付宝/钉钉小程序适配;
l.info(`---- offBLEConnectionStateChanged:`);
api.offBLEConnectionStateChanged();
this.mConnectionStateChangeAction = undefined;
}
//
l.info(`#### ===== 重置已连接设备的所有特征值变化通知事件 ===== ####`);
this.resetCharacteristicValueChange();
//
l.info(`####【Request】closeConnection: ${opts.name || deviceId}`, options);
if (!this.mIsAdapterOpened || !connectInfo.connected) {
this.mConnectionMap.delete(deviceId);
l.info(`#### 设备未连接!`, connectInfo);
Le.success("设备未连接", options, resolve);
}
else {
const closeOptions = {
deviceId: deviceId,
success: (res) => {
l.log(`####【Response.success】closeConnection:`, res);
connectInfo.connected = false;
this.mConnectionMap.delete(deviceId);
this.mCharacteristicMap.clear();
Le.success(res, options, resolve);
},
fail: (res) => {
connectInfo.connected = false;
l.warn(`####【Response.fail】closeConnection:`, res);
Le.fail(res, options, resolve);
},
};
if (this.mContext.isAlipay) {
my.disconnectBLEDevice(closeOptions).catch((error) => {
l.warn(`#### 【Response.Exception】closeConnection:`, error);
return Le.complete(Ee.ERROR_EXCEPTION, error, options);
});
}
else {
uni.closeBLEConnection(closeOptions);
}
// 特殊情况下设备断开连接无响应
setTimeout(() => {
if (connectInfo.connected) {
connectInfo.connected = false;
l.warn(`####【Response.timeout】closeConnection:`);
Le.complete(Ee.ERROR_RESPONSE_TIMEOUT, undefined, options, resolve);
}
}, 5000);
}
}).catch((error) => {
this.mConnectionMap.delete(deviceId);
l.warn(`#### 【Response.Exception】closeConnection:`, error);
return Le.complete(Ee.ERROR_EXCEPTION, error, options);
});
});
}
getConnectedDevices(options) {
if (typeof uni.getConnectedBluetoothDevices !== "function") {
l.warn(`#### 【Request】getConnectedDevices: 当前环境不支持获取已连接设备!`);
return Promise.resolve(Le.complete(Ee.ERROR_UN_SUPPORTED, [], options));
}
//
return new Promise((resolve) => {
this.openAdapter().then((res) => {
if (res.statusCode === 0) {
l.info(`###【Request】getConnectedDevices:`);
let devices = undefined;
uni.getConnectedBluetoothDevices({
services: [],
success: (res) => {
l.info(res, `#### 【Response.success】: getConnectedBluetoothDevices`);
devices = (res === null || res === void 0 ? void 0 : res.devices) || [];
Le.success(devices, options, resolve);
},
fail: (res) => {
l.info(res, `#### 【Response.fail】: getConnectedBluetoothDevices`);
devices = [];
Le.fail([], options, resolve);
},
});
setTimeout(() => {
if (!devices) {
Le.fail([], options, resolve);
}
}, 100);
}
else {
Le.fail([], options, resolve);
}
});
});
}
setBleMtu(options) {
const deviceId = options.deviceId || "";
return new Promise((resolve) => {
const platform = this.platform;
if (Le.isIOS(platform)) {
// IOS 系统使用不做处理,使用默认的MTU大小;
resolve({ status: -1 });
}
else {
const writeWaits = options.waits || 0;
let delayTime = 0;
if (this.mContext.isAppPlus) {
if (options.chipType === "DF5" || options.mtu >= 500) {
delayTime = 0;
}
else {
delayTime = 5;
}
}
const waits = writeWaits + delayTime;
let supportMtu = true;
if (this.mContext.isAlipay) {
// 支付宝小程序下的钉钉小程序不支持 setBLEMTU 设置。
supportMtu = my && typeof my.setBLEMTU === "function";
}
else {
supportMtu = typeof uni.setBLEMTU === "function";
}
l.info(`####【Request】setBleMTU: { deviceId: ${deviceId}, mtu: ${options.mtu}, platform: ${platform}}, supported: ${supportMtu}`);
// 在支付宝小程序中,不管是不是DF4芯片,必须要写上下MTU(即便回调失败),后续才可以正常发送数据,否则打印异常。
// 支付宝小程序虽然协商失败,但是必须要协商;
// 钉钉小程序不支持MTU协商
if (supportMtu) {
let resMtu = 0;
if (typeof uni.onBLEMTUChange === "function") {
l.info(`####【Request】onBLEMTUChange:`);
uni.onBLEMTUChange((res) => {
l.info(`---- onBleMTUChange: ${res.mtu}`, res);
resMtu = res.mtu;
uni.offBLEMTUChange();
});
}
uni.setBLEMTU({
deviceId: deviceId,
mtu: 512,
success: (resp) => {
l.info(`#### 【Response.success】: setBleMTU`);
l.info(JSON.stringify(resp));
const mtu = resp.mtu > 0 ? resp.mtu : resMtu;
if (mtu > 0) {
// 小程序中会执行这行代码
resolve({ status: 0, mtu: mtu > options.mtu ? options.mtu : mtu, waits: waits });
}
else {
// uniapp中不支持 getBLEMTU,小程序中可以通过 onBLEMTUChange 来获取,所以此处暂时直接返回,后期根据实际情况在进行调整。
// uniapp中不支持 onBLEMTUChange 回调,所以会执行这行代码。
resolve({ status: 0, waits: waits });
}
},
fail: (resp) => {
l.warn(`#### 【Response.fail】 setBLEMtu: MTU协商失败,正在尝试读取当前mtu大小`, resp);
uni.getBLEMTU({
deviceId: deviceId,
success: (getRes) => {
l.info(`#### 【Response.success】: getBleMTU`, getRes);
if (getRes.mtu && getRes.mtu > options.mtu) {
resolve({ status: 0, mtu: resp.mtu || options.mtu, waits: waits });
}
else if (options.chipType && !options.required) {
// DF4及之后的蓝牙芯片不需要进行MTU的协商处理;
resolve({ status: -1, waits: waits });
}
else {
resolve({ status: 2, waits: waits });
}
},
fail: (getRes) => {
l.warn(`#### 【Response.fail】: getBleMTU`, getRes);
resolve({ status: 2, waits: waits });
},
});
},
});
}
else {
if (options.chipType && !options.required) {
l.info(`####【Response.unsupported】setBleMTU: 当前环境不支持MTU协商!DFType = ${options.chipType}`);
// DF4及之后的蓝牙芯片不需要进行MTU的协商处理;
resolve({ status: -1, waits: waits });
}
else {
l.warn(`####【Response.unsupported】setBleMTU: 当前环境不支持MTU协商!DFType = ${options.chipType}`);
resolve({ status: 1, waits: waits });
}
}
}
});
}
getGATTServices(options) {
const deviceId = options.deviceId || "";
return new Promise((resolve) => {
l.info(`#### 【Request】getGattService: ${deviceId}`);
uni.getBLEDeviceServices({
deviceId: deviceId,
success: (resp) => {
l.info(`#### 【Response.success】getGattService: ${deviceId}`);
// logger.info(JSON.stringify(resp));
const services = (resp.services || []).map((v) => {
return Object.assign(v, { uuid: v.uuid || v.serviceId || "" });
});
if (services.length <= 0) {
l.warn(`---- 服务列表为空:${JSON.stringify(resp)}`);
}
Le.successResult(services, options, resolve);
},
fail: (resp) => {
l.warn(`#### 【Response.fail】getGattService: ${deviceId}`);
l.warn(JSON.stringify(resp));
Le.failResult([], options, resolve);
},
});
});
}
getGATTCharacteristics(options) {
const deviceId = options.deviceId || "";
const serviceId = options.serviceId || "";
return new Promise((resolve) => {
l.info(`#### 【Request】getCharacteristicList(${options.serviceId})`);
const cacheList = serviceId ? this.mCharacteristicMap.get(serviceId) : undefined;
if (cacheList) {
l.info(`#### 【Response.Cache】getCharacteristicList(${options.serviceId})`);
Le.successResult(cacheList, options, resolve);
return;
}
uni.getBLEDeviceCharacteristics({
deviceId: deviceId,
serviceId: serviceId,
success: (res) => {
l.info(`#### 【Response.success】getCharacteristicList(${options.serviceId})`);
// logger.log(JSON.stringify(res));
const characterList = res.characteristics || [];
if (characterList.length <= 0) {
l.warn(`---- 特征值列表为空:${JSON.stringify(res)}`);
}
const list = characterList.map((v) => {
const prop = v.properties || {};
let propValue = 0;
if (prop.read)
propValue |= Qe.READ;
if (prop.write) {
if (prop.writeNoResponse)
propValue |= Qe.WRITE_NO_RESPONSE;
else
propValue |= Qe.WRITE;
}
if (prop.notify)
propValue |= Qe.NOTIFY;
if (prop.indicate)
propValue |= Qe.INDICATE;
return Object.assign(v, {
serviceId: options.serviceId,
uuid: v.uuid || v.characteristicId || "",
properties: propValue,
});
});
if (list.length > 0) {
this.mCharacteristicMap.set(serviceId, list);
}
Le.successResult(list, options, resolve);
},
fail: (res) => {
l.warn(`#### 【Response.fail】getCharacteristicList(${options.serviceId})`, res);
Le.failResult([], options, resolve);
},
});
});
}
onBLECharacteristicValueChange(characterId, callback) {
// 如果不支持关闭ble特征值变化通知的功能,则统一注册,并且只注册一次后,根据实际需要进行分发处理。
l.log(`#### 【Register】CharacterValueChanged[${Le.getShortUUID(characterId)}]`);
if (!this.mCharacteristicValueChangeMap) {
l.info(`#### ===== 开始监视已连接设备的所有特征值变化通知事件 ===== ####`);
this.mCharacteristicValueChangeMap = new Map();
const action = (resp) => {
if (this.mContext.isDingTalk) {
// 钉钉小程序信用流控返回相关数据的时候,特征值 UUID 是错误的,需要特殊处理下。
const map = this.mCharacteristicValueChangeMap;
const keyList = map ? Array.from(map.keys()) : [];
let respId = keyList.find((v) => Le.isMatchedUUID(v, resp.characteristicId));
const data = typeof resp.value === "string"
? Le.hex16ToArrayBuffer(resp.value)
: new Uint8Array(resp.value);
if ((data[0] === 1 && data.length === 2) || (data[0] === 2 && data.length === 3)) {
const fcId = keyList.find((v) => Le.isMatchedUUID("FF03", v));
if (fcId)
respId = fcId;
}
const callback = respId && map ? map.get(respId) : undefined;
if (typeof callback === "function") {
callback(resp);
}
}
else if (this.mCharacteristicValueChangeMap) {
for (const item of this.mCharacteristicValueChangeMap) {
if (Le.isMatchedUUID(item[0], resp.characteristicId)) {
item[1](resp);
break;
}
}
}
};
//
const adapter = this.Context.bleAdapter;
if (adapter && typeof adapter.onBLECharacteristicValueChange === "function") {
adapter.onBLECharacteristicValueChange(action);
}
else {
uni.onBLECharacteristicValueChange(action);
}
}
//
this.mCharacteristicValueChangeMap.set(characterId, callback);
}
offBLECharacteristicValueChange(cid) {
const map = this.mCharacteristicValueChangeMap;
if (!cid || !map)
return;
//
const shortId = Le.getShortUUID(cid);
const remainIds = Array.from(map.keys()).map((v) => Le.getShortUUID(v));
map.delete(cid);
l.log(`#### 【Unregister】CharacteristicValueChange: [${shortId}], remainCharacteristics: [${remainIds}]`);
// 备注:此处不用取消特征值的侦听操作,在断开打印机的时候统一取消;
}
read(options) {
return __awaiter(this, void 0, void 0, function* () {
const deviceId = options.deviceId || "";
const cid = options.characteristicId || "";
const shortId = Le.getShortUUID(cid);
const connectInfo = deviceId ? this.mConnectionMap.get(deviceId) : undefined;
//
if (!deviceId || !options.serviceId || !cid) {
l.warn(`#### 【Request】readCharacteristic, 参数错误:`, options);
return Le.complete(Ee.ERROR_PARAM, "参数错误", options);
}
if (!(connectInfo === null || connectInfo === void 0 ? void 0 : connectInfo.connected)) {
l.warn(`#### 【Request】readCharacteristic, 设备连接已断开:`, options);
return Le.complete(Ee.ERROR_DISCONNECTED, "设备连接已断开", options);
}
//
return new Promise((resolve) => {
l.info(`#### 【Request】readCharacteristic(uuid: ${shortId})`);
// 监控特征值变化
let resolved = false;
if (this.mContext.isAppPlus || this.mContext.isWeiXin) {
this.onBLECharacteristicValueChange(cid, (res) => {
const value = typeof res.value === "string"
? Le.hex16ToArrayBuffer(res.value)
: new Uint8Array(res.value);
const hexStr = typeof res.value === "string" ? res.value : Le.arrayBufferToHex16(value);
l.info(`#### 【Response】onReadCharacteristicValueChange[${shortId}]: [${hexStr}]`);
// 执行回调。
if (res.characteristicId == cid) {
resolved = true;
Le.success(value, options, resolve);
// 取消特征值侦听
this.offBLECharacteristicValueChange(cid);
}
});
}
//
uni.readBLECharacteristicValue({
deviceId: deviceId,
serviceId: options.serviceId,
characteristicId: cid,
success: (res) => {
l.info(`#### 【Response.success】readBLECharacteristicValue: [${shortId}]:`, res);
l.log(JSON.stringify(res));
if (!(this.mContext.isAppPlus || this.mContext.isWeiXin)) {
// 支付宝、钉钉、飞书等小程序直接返回;
const result = res.characteristic || {};
Le.success(result.value || "", options, resolve);
}
else {
// AppPlus 或者 微信小程序需等待 onBLECharacteristicValueChange 来处理。
const timeout = 1500;
setTimeout(() => {
if (!resolved) {
l.warn(`#### 【Response.timeout】readBLECharacteristicValue: 响应超时, timeout: ${timeout}!`);
Le.complete(Ee.ERROR_RESPONSE_TIMEOUT, "", options, resolve);
}
}, timeout);
}
},
fail: (res) => {
l.warn(`#### 【Response.fail】readBLECharacteristicValue: [${shortId}]:`, res);
Le.complete(Ee.ERROR_READ_CHARACTERISTIC, "", options, resolve);
},
});
});
});
}
notify(options) {
return __awaiter(this, void 0, void 0, function* () {
const deviceId = options.deviceId || "";
const characteristicId = options.characteristicId || "";
const shortId = Le.getShortUUID(characteristicId);
const connectInfo = deviceId ? this.mConnectionMap.get(deviceId) : undefined;
if (!deviceId || !options.serviceId || !characteristicId) {
l.warn(`#### 【Request】notifyCharacteristic, 参数错误:`, options);
return Le.complete(Ee.ERROR_PARAM, "参数错误", options);
}
if (!(connectInfo === null || connectInfo === void 0 ? void 0 : connectInfo.connected)) {
l.warn(`#### 【Request】notifyCharacteristic, 设备连接已断开:`, options);
return Le.complete(Ee.ERROR_DISCONNECTED, "设备连接已断开", options);
}
//
return new Promise((resolve) => {
const state = typeof options.state === "boolean" ? options.state : true;
// 取消数据监听回调,避免二次链接的时候造成的数据干扰。
if (state) {
l.info(`#### 【Request.start】 - notifyCharacteristic[${shortId}]:`);
}
else {
this.offBLECharacteristicValueChange(characteristicId);
l.info(`#### 【Request.stop 】 - notifyCharacteristic[${shortId}]:`);
}
// 为了避免遗漏数据,监听之前先注册回调;
if (state) {
l.info(`####【Request】onBLECharacteristicValueChange`);
this.onBLECharacteristicValueChange(characteristicId, (result) => {
const respVal = typeof result.value === "string"
? Le.hex16ToArrayBuffer(result.value)
: new Uint8Array(result.value);
// const respId = LPAUtils.getShortUUID(result.characteristicId || "");
// const hexStr = LPAUtils.arrayBufferToHex16(respVal);
// logger.log(`#### 【Response】onNotifyCharacteristicValueChange[${respId}]: [${hexStr}]`, result);
if (result.characteristicId === characteristicId) {
options.dataReceived && options.dataReceived(respVal);
}
});
}
const res = uni.notifyBLECharacteristicValueChange({
deviceId: deviceId,
serviceId: options.serviceId,
characteristicId: characteristicId,
state: state,
success: (res) => {
l.info(`####【Response.success】: notifyCharacteristic[${shortId}], S[${state}]:`, res);
if (state) {
// 备注:在多次同时侦听流控特征值与数据特征值的时候,偶尔会接收不到数据。
// 所以需要在特征值侦听成功后,多等待一端时间后,在执行数据的写入操作。
setTimeout(() => {
Le.success(res, options, resolve);
}, 10);
}
else {
Le.success(res, options, resolve);
}
},
fail: (res) => {
l.warn(`#### 【Response.fail】: notifyCharacteristic, S[${state}]:`, res);
if (state) {
Le.complete(Ee.ERROR_NOTIFY_CHARACTERISTIC, res, options, resolve);
}
else {
Le.success(res, options, resolve);
}
},
});
// 钉钉小程序返回值是个 Promise,并且可能会抛异常,此处做下处理
Promise.resolve(res).catch((error) => {
l.warn(`#### 【Response.Exception】: notifyBLECharacteristicValueChange: `, error);
return Le.complete(Ee.ERROR_EXCEPTION, error, options);
});
});
});
}
write(options) {
return __awaiter(this, void 0, void 0, function* () {
const deviceId = options.deviceId || "";
const serviceId = options.serviceId || "";
const characteristicId = options.characteristicId || "";
const data = options.value || options.data;
const writeValue = this.mContext.isAlipay ? Le.arrayBufferToHex16(data, "") : data.buffer;
const pkgName = `[P ${options.pkgIndex} - SUP ${options.subPkgIndex}]`;
//
if (!deviceId || !serviceId || !characteristicId || !data) {
l.warn(`【Request】writeCharacteristic: ${pkgName}-参数错误`, options);
return Le.complete(Ee.ERROR_PARAM, "参数错误", options);
}
//
const connectInfo = this.mConnectionMap.get(deviceId);
if (!connectInfo || !connectInfo.connected) {
l.warn(`####【Request】writeCharacteristic: ${pkgName}-设备连接已断开, info: `, connectInfo);
return Le.complete(Ee.ERROR_DISCONNECTED, "设备连接已断开", options);
}
//
return new Promise((resolve) => {
if (this.mShowWriteLog) {
l.log(`#### 【Request】writeBLECharacteristicValue: ${pkgName}`);
}
uni.writeBLECharacteristicValue({
deviceId: deviceId,
serviceId: serviceId,
characteristicId: characteristicId,
value: writeValue,
// 备注:在HarmonyOS版的微信小程序中,必须要配置 writeType。
// writeType: "writeNoResponse",
writeType: options.withResponse ? "write" : "writeNoResponse",
success: (res) => {
if (this.mShowWriteLog) {
l.log(`#### 【Response.success】writeBLECharacteristicValue: ${pkgName}`, res);
}
Le.success(res, options, resolve);
// 关闭写入日志
this.mShowWriteLog = false;
},
fail: (res) => {
l.warn(`#### 【Response.fail】writeBLECharacteristicValue: ${pkgName}, writeWaits: ${options.writeWaits}`, res);
Le.complete(Ee.ERROR_WRITE_CHARACTERISTIC, res, options, resolve);
// 如果数据写入失败,则打开写入日志信息,供参考;
this.mShowWriteLog = true;
},
});
});
});
}
}
class LPAPIFactory {
/**
* 获取LPAPI接口实例。
*/
static getInstance(options) {
if (!LPAPIFactory.api) {
LPAPIFactory.api = LPAPIFactory.createInstance(options);
}
else if (options) {
const prevContext = LPAPIFactory.api.drawContext;
const context = UniContext.createInstance(options, prevContext);
if (context && context !== prevContext) {
LPAPIFactory.api.setDrawContext(context);
}
LPAPIFactory.api.setOptions(options);
}
//
return LPAPIFactory.api;
}
static initApi(options) {
return __awaiter(this, void 0, void 0, function* () {
const opts = (typeof Option === "function" ? { callback: options } : options || {});
const api = this.getInstance(options);
//
const res = yield api.openAdapter({ force: true });
if (typeof opts.callback === "function") {
opts.callback(api, res);
}
//
return api;
});
}
static resetApi(options) {
if (LPAPIFactory.api) {
return LPAPIFactory.api.resetAdapter(options).then((_resp) => {
return LPAPIFactory.getInstance(options);
});
}
else {
return Promise.resolve(this.getInstance(options));
}
}
// /**
// * 销毁蓝牙实例,同时关闭蓝牙适配器,释放所有已连接的资源。
// */
// public static quitApi() {
// if (LPAPIFactory.api) {
// LPAPIFactory.api.quit();
// // 相关的 onDeviceFound 和 onCharacteristicValueChange 在UniApp中没有对应off相关事件,
// // 如果采用多实例模式的话,相关的回调函数还是在 第一个实例中,其他实例估计无法获取回调信息。
// // LPAPIFactory.api = undefined;
// }
// }
static createInstance(options) {
const opts = options || {};
//
Ri.setLogLevel(typeof opts.logLevel === "number" ? opts.logLevel : opts.showLog);
l.info(`======== LPAPIFactory.createInstance() ========`, options);
//
const adapter = new BleAdapter(options);
const context = adapter.isH5 ? undefined : UniContext.createInstance(opts);
l.info(`---- context: `, context);
return Ri.create(Object.assign(Object.assign({}, opts), { adapter: adapter, context: context, createContext: (args) => {
return adapter.isH5 ? undefined : new UniContext(args);
}, onQuit: () => {
LPAPIFactory.api = undefined;
} }));
}
}
class UniPreview extends Te {
constructor(options) {
super(options);
}
createDrawContext(options) {
return new UniContext(Object.assign(Object.assign({}, options), { apiMode: false }));
}
attachTo(options) {
if (!this.drawContext) {
this.drawContext = this.createDrawContext(options);
}
// 如果未指定目标容器,则只是根据给定的容器大小来计算canvas的大小,并渲染 canvas 内容即可。
const viewWidth = options.viewWidth || 0;
const viewHeight = options.viewHeight || 0;
return this.raiseViewChanged(viewWidth, viewHeight);
}
}
function definePrintConfig(options) {
return options;
}
function definePageConfig(page) {
return page;
}
function defineDrawConfig(options) {
return options;
}
export { a as Alignment, h as AntiColorMode, y as Barcode, Mt as Barcode1DEncoder, Re as Barcode2DEncoder, Pe as Barcode2DType, s as BarcodeTextPos, b as BarcodeType, C as BitMatrix, hi as BleConnection, c as BorderAlign, Qe as CharacteristicProperty, Ie as DegreeContent, ai as DeviceConnection, ui as DeviceManager, xe as DrawContext, ve as DrawType, p as DzCanvas, R as DzChar, P as DzCharCodes, Ge as DzCommand, be as DzImageData, Ue as DzPackage, mi as DzPrinter, Se as DzTextDecoder, I as DzTextEncoder, e as DzUtil, qe as EDataSendMode, Ze as EPrintStatus, Bt as EccLevel, me as FinderPattern, r as FontStyle, Me as GBKUtils, i as LOG_LEVEL, Ne as LPAEmitter, Ri as LPAPI, LPAPIFactory, Le as LPAUtils, Xe as LPA_DeviceConnectType, ze as LPA_DeviceType, we as LPA_GapType, Ye as LPA_GattProperty, ii as LPA_JobAction, Ae as LPA_JobName, Oe as LPA_PrintDarkness, _e as LPA_PrintSpeed, De as LPA_Printable, Ee as LPA_Result, Te as LabelContext, g as LineSpaceParser, Fe as PackageBuffer, $e as PackageBufferList, Ke as PrintPackage, Ce as QRCode, Nt as QRUtils, si as RequestMessage, o as RotateMode, f as ScaleCanvas, u as ScaleUnit, fi as SupportPrinterMatcher, n as TextDecoration, UniPreview, ti as ValueType, Pi as WdfxParser, d as WrapMode, defineDrawConfig, definePageConfig, definePrintConfig, l as logger };