init commit

This commit is contained in:
iqudoo
2026-04-30 10:16:43 +08:00
commit 2c8eb4f587
44 changed files with 17574 additions and 0 deletions

View File

@@ -0,0 +1,137 @@
import { toAny } from "../../utils/toany";
import { printDebug, printWarn } from "../../utils/logger";
import { createUUID } from "../../utils/uuid";
let _events = {};
let _callbacks = {};
let _bridge = "__bridge_client__";
function _callRuntime(func, ...options) {
let funcs = func.split('.');
let instant = window;
while (funcs.length > 1) {
instant = instant[funcs.shift()];
}
if (instant && funcs.length == 1) {
if (instant.hasOwnProperty(funcs[0])) {
return instant[funcs[0]](...options);
}
}
}
function onCallback(name, callback) {
if (callback && typeof callback === 'function') {
_callbacks[name] = callback;
}
}
function _checkInit() {
let methodName = `${_bridge}_handle_callback`;
if (!window[methodName]) {
window[methodName] = (res) => {
let { method, payload, code, request_id } = toAny(res, {});
let data = toAny(payload, {});
if (request_id) {
_callbacks[request_id] && _callbacks[request_id](code, data);
} else if (_events[method]) {
_events[method].forEach((callback) => {
callback && callback(data);
});
}
}
}
}
export function inRuntime() {
return !!window[_bridge]
}
export function onEvent(method, callback) {
_checkInit();
if (method && callback && typeof callback == 'function') {
let list = _events[method] || [];
if (list.indexOf(callback) == -1) {
list.push(callback);
}
_events[method] = list;
}
}
export function offEvent(method, callback) {
_checkInit();
if (method && callback && typeof callback == 'function') {
let list = _events[method] || [];
let index = list.indexOf(callback);
if (index >= 0) {
list.splice(index, 1);
}
}
}
export function bridgeSync(method, data) {
_checkInit();
let result = toAny(_callRuntime(`${_bridge}.call`, method, toAny(data, "")));
let options = [method];
if (data) {
options.push("params:")
options.push(data)
}
if (result) {
options.push("result:")
options.push(result)
}
printDebug('bridge call >>>', ...options);
return result;
}
export function bridgeAsync(method, data, timeout) {
return new Promise((resolve, reject) => {
if (inRuntime()) {
let called = false;
let timeObj = null;
if (timeout > 0) {
timeObj = setTimeout(() => {
called = true;
timeObj = null;
reject("bridgeAsync timeout");
}, timeout);
}
let request_id = createUUID() + "_" + Date.now();
onCallback(request_id, (code, data) => {
if (called) {
return;
}
if (timeObj) {
clearTimeout(timeObj);
}
if (code == 0) {
resolve(data);
} else {
reject(data);
}
});
bridgeSync(method, Object.assign({ request_id }, data));
} else {
reject(`Can't bridgeAsync, because not in runtime`);
}
}).then(res => {
let options = [method];
if (data) {
options.push("params:")
options.push(data)
}
if (res) {
options.push("resp:")
options.push(res)
}
printDebug('bridge resp >>>', ...options);
return res;
}).catch(err => {
if (data) {
printWarn('bridge err >>>', method, "params:", data, err);
} else {
printWarn('bridge err >>>', method, err);
}
throw err;
})
}

27
src/services/config.js Normal file
View File

@@ -0,0 +1,27 @@
let _defineConfig = {
version: "${lib_version}"
}
let _defConfig = {
}
let _customConfig = {
}
export function getVersion() {
return _defineConfig.version;
}
export function getConfig(key) {
let item = _customConfig[key];
if (!item) {
item = _defConfig[key];
}
return item;
}
export function setConfig(config) {
if (config && typeof config == "object") {
Object.assign(_customConfig, config);
}
}

50
src/services/encrypt.js Normal file
View File

@@ -0,0 +1,50 @@
import ase from "browserify-cipher";
import { hex_md5 } from "../utils/md5";
import { toAny } from "../utils/toany";
import { printDebug } from "../utils/logger";
import { getAppId } from "./env";
let _encryptConfig = {
"transformation": "AES/CBC/PKCS5Padding",
"algorithm": "aes-128-cbc",
"key": "ohogame",
"key_size": 16
}
export function encryptData({ iv, data }) {
try {
let encrypt_data = data;
let app = getAppId();
let options = _encryptConfig;
let realIv = Buffer.from(hex_md5(iv + "_" + app).substring(4, 4 + options.key_size));
let realKey = hex_md5(options.key + "_" + app).substring(4, 4 + options.key_size);
let realData = Buffer.from(encrypt_data);
let decipher = ase.createCipheriv(options.algorithm, realKey, realIv)
decipher.setAutoPadding(true)
let decoded = decipher.update(realData, 'binary', 'base64');
decoded += decipher.final('base64');
return decoded;
} catch (e) {
printDebug("encryptData fail", e);
}
return "";
}
export function decryptData({ iv, data }) {
try {
let encrypt_data = data;
let app = getAppId();
let options = _encryptConfig;
let realIv = Buffer.from(hex_md5(iv + "_" + app).substring(4, 4 + options.key_size));
let realKey = hex_md5(options.key + "_" + app).substring(4, 4 + options.key_size);
let realData = Buffer.from(encrypt_data.split(" ").join("+"), 'base64');
let decipher = ase.createDecipheriv(options.algorithm, realKey, realIv)
decipher.setAutoPadding(true)
let decoded = decipher.update(realData, 'binary', 'utf8');
decoded += decipher.final('utf8');
return toAny(decoded);
} catch (e) {
printDebug("decryptData fail", e);
}
return "";
}

17
src/services/env.js Normal file
View File

@@ -0,0 +1,17 @@
import _global from "../polyfill/_global";
export function execFunc(target, func, ...options) {
if (target) {
let funcs = func.split('.');
let instant = target;
while (funcs.length > 1) {
instant = instant[funcs.shift()];
}
if (instant && funcs.length == 1) {
if (instant.hasOwnProperty(funcs[0])) {
return instant[funcs[0]](...options);
}
}
}
}

View File

@@ -0,0 +1,339 @@
import { inRuntime, bridgeAsync } from "../bridge/appbridge";
import { isSupportWebScan, startScanForWeb, stopScanForWeb, isSupportImageScan, startScanForImage } from "../web";
import { isSupportWxScan, startScanForWx } from "../wx";
import { startScanner, stopScanner } from "../scanner";
import { getConfig } from "../config";
let _scan_status = "closed";
let _scan_status_listener = null;
let _scan_listener_list = [];
let _scan_resolve = null;
let _scan_closing = false;
let _scan_next_start_time = 0;
const SCAN_RESTART_DELAY = 2000;
function __checkScanner() {
if (_scan_listener_list.length > 0) {
startScanner((result) => {
__scannerResult(result);
});
} else {
stopScanner();
}
}
function __match(result, match) {
let reg = null;
if (!!match && typeof match === "string") {
reg = new RegExp(match);
}
if (!!reg) {
return reg.test(result);
}
return true;
}
function __result(result) {
let matched = false;
for (let i = 0; i < _scan_listener_list.length; i++) {
const item = _scan_listener_list[i];
if (item.listener && __match(result, item.match)) {
matched = true;
item.listener({ result, key: item.key });
console.log("__result", { result, key: item.key });
}
}
if (matched) {
_scan_next_start_time = Date.now() + SCAN_RESTART_DELAY;
}
return matched;
}
function __hasMatchedListener(result) {
for (let i = 0; i < _scan_listener_list.length; i++) {
const item = _scan_listener_list[i];
if (item.listener && __match(result, item.match)) {
return true;
}
}
return false;
}
function __scanning() {
if (_scan_status !== "scanning") {
_scan_status = "scanning";
if (_scan_status_listener) {
_scan_status_listener({ status: "scanning" });
}
}
}
function __closed() {
if (_scan_status !== "closed") {
_scan_status = "closed";
if (_scan_status_listener) {
_scan_status_listener({ status: "closed" });
}
}
}
function __finishScan() {
const resolve = _scan_resolve;
_scan_resolve = null;
__closed();
return resolve;
}
function __stopCurrentScan() {
if (inRuntime()) {
return bridgeAsync("stopScan").catch(() => {
// no thing
});
} else if (isSupportWebScan()) {
return stopScanForWeb().catch(() => {
// no thing
});
}
return Promise.resolve();
}
function __scannerResult(result) {
if (!__hasMatchedListener(result)) {
return;
}
if (isScanning()) {
const resolve = __finishScan();
_scan_closing = true;
__stopCurrentScan().then(() => {
setTimeout(() => {
_scan_closing = false;
}, 0);
});
__result(result);
resolve && resolve({
result
});
return;
}
__result(result);
}
function __startBridgeScan() {
return bridgeAsync("startScan", {
closeable: true
}).then(resp => {
if (!isScanning()) {
return resp;
}
if (!resp || !resp.result) {
return resp;
}
if (__result(resp.result)) {
return resp;
}
if (isScanning()) {
return __startBridgeScan();
}
return resp;
}).catch(err => {
if (!isScanning()) {
return err;
}
if (!err || !err.result) {
return err;
}
if (__result(err.result)) {
return err;
}
if (isScanning()) {
return __startBridgeScan();
}
return err;
});
}
function __startWxScan() {
return startScanForWx({
needResult: 1,
scanType: ["qrCode", "barCode"]
}).then(resp => {
if (!isScanning()) {
return resp;
}
if (!resp || !resp.result) {
return resp;
}
if (__result(resp.result)) {
return resp;
}
if (isScanning()) {
return __startWxScan();
}
return resp;
}).catch(err => {
if (!isScanning()) {
return err;
}
if (!err || !err.result) {
return err;
}
if (__result(err.result)) {
return err;
}
if (isScanning()) {
return __startWxScan();
}
return err;
});
}
function __startImageScan() {
return startScanForImage().then(resp => {
if (!isScanning()) {
return resp;
}
if (!resp || !resp.result) {
return resp;
}
if (__result(resp.result)) {
return resp;
}
if (isScanning()) {
return __startImageScan();
}
return resp;
}).catch(err => {
if (!isScanning()) {
return err;
}
if (!err || !err.result) {
return err;
}
if (__result(err.result)) {
return err;
}
if (isScanning()) {
return __startImageScan();
}
return err;
});
}
export function isScanning() {
return _scan_status === "scanning";
}
export function clear() {
for (let i = 0; i < _scan_listener_list.length; i++) {
const item = _scan_listener_list[i];
item.cancel();
}
_scan_listener_list.length = 0;
__checkScanner();
}
export function onScanListener(listener, key, match, level) {
if (typeof listener !== 'function') {
return;
}
let item = null;
for (let i = 0; i < _scan_listener_list.length; i++) {
const _i = _scan_listener_list[i];
if (_i.listener === listener) {
item = _i;
break;
}
}
if (item) {
item.key = key;
item.level = level;
item.match = match;
} else {
item = {
key,
match,
level,
listener,
cancel: () => {
const index = _scan_listener_list.indexOf(item);
if (index !== -1) {
const items = _scan_listener_list.splice(index, 1);
for (let i = 0; i < items.length; i++) {
const item = items[i];
item.listener && item.listener({ cancel: 1 });
}
}
}
};
_scan_listener_list.push(item);
}
// 根据level排序
_scan_listener_list.sort((a, b) => b.level - a.level);
__checkScanner();
return item;
}
export function offScanListener(listener) {
for (let i = 0; i < _scan_listener_list.length; i++) {
const _i = _scan_listener_list[i];
if (_i.listener === listener) {
_i.cancel();
break;
}
}
__checkScanner();
}
export function setStatusListener(listener) {
if (typeof listener !== 'function') {
return;
}
_scan_status_listener = listener;
}
export function getStatus() {
return _scan_status;
}
export function stopScan() {
if (!isScanning()) {
return;
}
__stopCurrentScan().then(() => {
__closed();
});
}
export function startScan() {
if (isScanning() || _scan_closing || Date.now() < _scan_next_start_time) {
return;
}
Promise.resolve().then(() => {
__scanning();
let scannerPromise = new Promise(resolve => {
_scan_resolve = resolve;
});
let scanPromise = Promise.resolve();
if (inRuntime()) {
console.log("startScanForBridge");
scanPromise = __startBridgeScan();
} else if (isSupportWxScan()) {
console.log("startScanForWx");
scanPromise = __startWxScan();
} else if (isSupportWebScan()) {
console.log("startScanForWeb");
scanPromise = startScanForWeb(getConfig("webCanvasStyle"), __result);
} else if (isSupportImageScan()) {
console.log("startScanForImage");
scanPromise = __startImageScan();
} else {
console.log("not support scanner");
}
return Promise.race([scanPromise, scannerPromise]);
}).finally(() => {
_scan_resolve = null;
__closed();
});
}

View File

@@ -0,0 +1,90 @@
let _scannerCallback = null;
let _scannerStatus = "closed";
let _scannerValue = "";
let _scannerTimer = null;
let _scannerLastInputTime = 0;
const SCANNER_INPUT_INTERVAL = 100;
function clearScannerValue() {
_scannerValue = "";
_scannerLastInputTime = 0;
if (_scannerTimer) {
clearTimeout(_scannerTimer);
_scannerTimer = null;
}
}
function delayClearScannerValue() {
if (_scannerTimer) {
clearTimeout(_scannerTimer);
}
_scannerTimer = setTimeout(() => {
clearScannerValue();
}, SCANNER_INPUT_INTERVAL);
}
function normalizeScannerValue(value) {
return value.replace(/[\uFF01-\uFF5E]/g, char => {
return String.fromCharCode(char.charCodeAt(0) - 0xFEE0);
}).replace(/\u3002/g, ".");
}
function stopScannerEvent(event) {
event.preventDefault && event.preventDefault();
event.stopPropagation && event.stopPropagation();
}
function onScannerKeydown(event) {
if (_scannerStatus !== "scanning") {
return;
}
if (event.ctrlKey || event.metaKey || event.altKey) {
return;
}
if (event.key === "Enter") {
const result = normalizeScannerValue(_scannerValue);
if (result) {
stopScannerEvent(event);
}
console.log("onScannerKeydown", result);
clearScannerValue();
if (result && _scannerCallback) {
_scannerCallback(result);
}
return;
}
if (!event.key || event.key.length !== 1) {
return;
}
const now = Date.now();
if (_scannerLastInputTime && now - _scannerLastInputTime > SCANNER_INPUT_INTERVAL) {
clearScannerValue();
}
_scannerLastInputTime = now;
_scannerValue += event.key;
delayClearScannerValue();
}
export function startScanner(callback){
if (!callback || typeof callback !== "function") {
return;
}
_scannerCallback = callback;
if (_scannerStatus === "scanning") {
return;
}
_scannerStatus = "scanning";
clearScannerValue();
window.addEventListener("keydown", onScannerKeydown);
}
export function stopScanner(){
if (_scannerStatus !== "scanning") {
return;
}
_scannerStatus = "closed";
_scannerCallback = null;
clearScannerValue();
window.removeEventListener("keydown", onScannerKeydown);
}

327
src/services/web/index.js Normal file
View File

@@ -0,0 +1,327 @@
import { createUUID } from "../../utils/uuid";
import { getConfig } from "../config";
const scanWeb = {
uuid: null,
finish: true
}
function removeEl(id) {
try {
let el = document.getElementById(id);
document.body.removeChild(el);
} catch (error) {
}
}
function createEl(tagName, id, style, appendChild) {
let el = document.getElementById(id);
if (!el) {
el = document.createElement(tagName);
el.id = id;
el.style = style;
appendChild && document.body.appendChild(el);
}
return el;
}
function canvasDrawLine(context, width, begin, end, color) {
context.beginPath();
context.moveTo(width - begin.x, begin.y);
context.lineTo(width - end.x, end.y);
context.lineWidth = 4;
context.strokeStyle = color;
context.stroke();
}
function getBarcodeFormats(scanType) {
let formats = [];
if (!scanType) {
scanType = ["qrCode", "barCode"];
}
if (scanType.includes('qrCode')) {
formats.push('qr_code');
}
if (scanType.includes('barCode')) {
formats.push(
'ean_13',
'ean_8',
'code_128',
'code_39',
'codabar',
'upc_a',
'upc_e',
'itf',
'aztec',
'data_matrix',
'pdf417'
);
}
return formats;
}
function createBarcodeDetector(scanType) {
return Promise.resolve().then(() => {
if (typeof BarcodeDetector === 'undefined') {
throw new Error("BarcodeDetector is not supported");
}
const formats = getBarcodeFormats(scanType);
if (BarcodeDetector.getSupportedFormats) {
return BarcodeDetector.getSupportedFormats().then(supportedFormats => {
const supported = formats.filter(format => supportedFormats.indexOf(format) !== -1);
if (!supported.length) {
throw new Error("No supported barcode formats");
}
return new BarcodeDetector({ formats: supported });
});
}
return new BarcodeDetector({ formats });
});
}
function drawBarcode(context, width, barcode) {
const cornerPoints = barcode.cornerPoints;
if (cornerPoints && cornerPoints.length) {
for (let i = 0; i < cornerPoints.length; i++) {
canvasDrawLine(context, width, cornerPoints[i], cornerPoints[(i + 1) % cornerPoints.length], "#FF3B58");
}
return;
}
if (barcode.boundingBox) {
const rect = barcode.boundingBox;
const points = [
{ x: rect.x, y: rect.y },
{ x: rect.x + rect.width, y: rect.y },
{ x: rect.x + rect.width, y: rect.y + rect.height },
{ x: rect.x, y: rect.y + rect.height }
];
for (let i = 0; i < points.length; i++) {
canvasDrawLine(context, width, points[i], points[(i + 1) % points.length], "#FF3B58");
}
}
}
export function isSupportWebScan() {
return typeof navigator !== 'undefined'
&& navigator.mediaDevices
&& navigator.mediaDevices.getUserMedia
&& typeof BarcodeDetector !== 'undefined';
}
export function isSupportImageScan() {
return typeof document !== 'undefined'
&& typeof BarcodeDetector !== 'undefined'
&& typeof URL !== 'undefined'
&& URL.createObjectURL;
}
export function stopScanForWeb() {
return Promise.resolve().then(() => {
scanWeb.uuid = null;
})
}
function chooseImageFile() {
return new Promise(resolve => {
const input = document.createElement("input");
input.type = "file";
input.accept = "image/*";
input.style.display = "none";
let finished = false;
let finish = file => {
if (finished) {
return;
}
finished = true;
removeEl("__webscan_image_input__");
resolve(file);
};
input.id = "__webscan_image_input__";
input.onchange = () => {
finish(input.files && input.files[0]);
};
input.oncancel = () => {
finish(null);
};
document.body.appendChild(input);
input.click();
});
}
function detectImageFile(detector, file) {
if (!file) {
return Promise.resolve(null);
}
if (typeof createImageBitmap !== 'undefined') {
return createImageBitmap(file).then(image => {
return detector.detect(image).then(barcodes => {
image.close && image.close();
return barcodes && barcodes[0];
}).catch(err => {
image.close && image.close();
throw err;
});
});
}
return new Promise((resolve, reject) => {
const image = new Image();
const url = URL.createObjectURL(file);
image.onload = () => {
detector.detect(image).then(barcodes => {
URL.revokeObjectURL(url);
resolve(barcodes && barcodes[0]);
}).catch(err => {
URL.revokeObjectURL(url);
reject(err);
});
};
image.onerror = err => {
URL.revokeObjectURL(url);
reject(err);
};
image.src = url;
});
}
export function startScanForImage() {
return createBarcodeDetector(getConfig("scanType")).then(detector => {
return chooseImageFile().then(file => detectImageFile(detector, file));
}).then(code => {
if (code && code.rawValue) {
return {
result: code.rawValue
};
}
return {
success: false,
error: "未识别到二维码或条形码"
};
});
}
export function startScanForWeb(canvasStyle, onResult) {
return new Promise((resolve, reject) => {
try {
scanWeb.uuid = createUUID();
scanWeb.finish = false;
let videoEl = createEl("video",
"__webscan_video__",
"display: none", false);
let canvasDisplay = "";
let canvasBaseStyle = canvasStyle || "position: fixed; width: 300px; height: 240px; top: 0; left: 0; z-index: 9999;";
let canvasEl = createEl("canvas",
"__webscan_canvas__",
canvasBaseStyle + " display: none;", true);
canvasDisplay = canvasEl.style.display;
canvasEl.style.cssText = canvasBaseStyle;
canvasDisplay = canvasEl.style.display;
canvasEl.style.display = "none";
let context = canvasEl.getContext("2d");
let currentUuid = scanWeb.uuid;
videoEl.width = 300;
videoEl.height = 300;
videoEl.uuid = scanWeb.uuid;
createBarcodeDetector(getConfig("scanType")).then(detector => {
return navigator.mediaDevices.getUserMedia({
video: {
facingMode: "environment"
}
}).then(stream => {
return {
detector,
stream
};
});
}).then(function (options) {
const detector = options.detector;
const stream = options.stream;
videoEl.srcObject = stream;
videoEl.setAttribute("playsinline", true); // iOS使用
videoEl.play();
let closeWebScan = getConfig("closeWebScan", () => {
// no thing
});
let displayWebScan = getConfig("displayWebScan", (canvasEl, cancal) => {
// no thing
});
displayWebScan && displayWebScan(canvasEl, () => {
stopScanForWeb();
});
canvasEl.style.display = "none";
let detecting = false;
let displayed = false;
let closed = false;
let close = () => {
if (closed) {
return;
}
closed = true;
try {
stream.getTracks()[0].stop();
} catch (_e) { }
closeWebScan && closeWebScan();
};
let tick = () => {
try {
if (videoEl.readyState === videoEl.HAVE_ENOUGH_DATA && !detecting) {
canvasEl.height = videoEl.videoHeight;
canvasEl.width = videoEl.videoWidth;
context.setTransform(-1, 0, 0, 1, canvasEl.width, 0);
context.drawImage(videoEl, 0, 0, canvasEl.width, canvasEl.height);
context.setTransform(1, 0, 0, 1, 0, 0);
if (!displayed) {
displayed = true;
canvasEl.style.display = canvasDisplay || "";
}
detecting = true;
detector.detect(videoEl).then(barcodes => {
const code = barcodes && barcodes[0];
if (code && code.rawValue && scanWeb.uuid == currentUuid) {
if (onResult && !onResult(code.rawValue)) {
return;
}
drawBarcode(context, canvasEl.width, code);
scanWeb.uuid = null;
scanWeb.finish = true;
close();
resolve({
result: code.rawValue
})
}
}).catch(() => {
}).finally(() => {
detecting = false;
});
}
} catch (e) {
detecting = false;
}
if (scanWeb.uuid == currentUuid) {
requestAnimationFrame(() => {
tick()
});
} else {
if (!scanWeb.finish) {
reject({ cancel: 1 });
scanWeb.finish = true;
}
close();
}
if (scanWeb.finish) {
close();
}
};
requestAnimationFrame(() => {
tick();
});
}).catch(err => {
reject({ error: err });
});
} catch (e) {
reject({ error: e });
}
}).finally(() => {
removeEl("__webscan_video__");
removeEl("__webscan_canvas__");
})
}

162
src/services/wx/index.js Normal file
View File

@@ -0,0 +1,162 @@
import { getConfig } from "../config";
import { request } from "../../utils/request";
import { toAny } from "../../utils/toany";
const WX_JS_SDK_URL = "https://res.wx.qq.com/open/js/jweixin-1.6.0.js";
const WX_SCAN_API = "scanQRCode";
let _wxReadyPromise = null;
let _wxReady = false;
function getWx() {
if (typeof window === "undefined") {
return null;
}
return window.wx;
}
function loadWxScript() {
return new Promise((resolve, reject) => {
const wx = getWx();
if (wx && wx.config && wx.scanQRCode) {
resolve(wx);
return;
}
let initWechatJssdk = toAny(getConfig("initWechatJssdk"), {});
let sdkUrl = toAny(initWechatJssdk.sdkUrl, WX_JS_SDK_URL);
if (!sdkUrl) {
reject(new Error("initWechatJssdk.sdkUrl is required, but not found"));
return;
}
let script = document.getElementById("__wx_jssdk__");
if (script) {
script.addEventListener("load", () => resolve(getWx()));
script.addEventListener("error", reject);
return;
}
script = document.createElement("script");
script.id = "__wx_jssdk__";
script.src = sdkUrl;
script.onload = () => resolve(getWx());
script.onerror = reject;
document.head.appendChild(script);
});
}
function fetchWxConfig() {
let initWechatJssdk = toAny(getConfig("initWechatJssdk"), {});
let apiUrl = toAny(initWechatJssdk.apiUrl, "");
if (!apiUrl) {
return Promise.reject(new Error("initWechatJssdk.apiUrl is required, but not found"));
}
return request({
url: apiUrl,
method: "GET",
data: {
url: window.location.href.split("#")[0]
}
}).then(res => {
let data = toAny(res.data, {});
if (!data) {
return null;
}
if (data.code !== 0) {
throw new Error(data.msg || "wx config fetch failed");
}
if (data.data) {
return data.data;
}
return data;
});
}
export function isWxEnv() {
return typeof navigator !== "undefined"
&& /micromessenger/i.test(navigator.userAgent || "");
}
export function isSupportWxScan() {
const wx = getWx();
return isWxEnv()
&& _wxReady
&& wx
&& wx.scanQRCode;
}
export function initWxJssdk() {
if (!isWxEnv()) {
return Promise.resolve();
}
if (_wxReadyPromise) {
return _wxReadyPromise;
}
_wxReadyPromise = Promise.all([loadWxScript(), fetchWxConfig()]).then(items => {
const wx = items[0];
const config = items[1];
if (!wx || !wx.config) {
throw new Error("wx jssdk is not ready");
}
if (!config) {
throw new Error("wx config is empty");
}
const jsApiList = config.jsApiList || [];
if (jsApiList.indexOf(WX_SCAN_API) === -1) {
jsApiList.push(WX_SCAN_API);
}
return new Promise((resolve, reject) => {
wx.ready(() => {
_wxReady = true;
resolve(wx);
});
wx.error(err => {
_wxReady = false;
_wxReadyPromise = null;
reject(err);
});
wx.config(Object.assign({}, config, {
jsApiList
}));
});
}).catch(err => {
_wxReady = false;
_wxReadyPromise = null;
throw err;
});
return _wxReadyPromise;
}
export function startScanForWx(options) {
return initWxJssdk().then(() => {
return new Promise((resolve, reject) => {
const {
needResult = 1,
scanType = ["qrCode", "barCode"]
} = options || {};
const wx = getWx();
if (!wx || !wx.scanQRCode) {
reject(new Error("wx.scanQRCode is not supported"));
return;
}
wx.scanQRCode({
needResult,
scanType,
success: res => {
resolve({
success: true,
result: res.resultStr,
code: res.resultStr
});
},
cancel: () => {
resolve({
success: false,
error: "用户取消扫码"
});
},
fail: err => {
reject(err);
}
});
});
});
}