@cloudbase/framework-plugin-low-code 0.7.10-beta.2 → 0.7.11-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/builder/core/generate.d.ts.map +1 -1
- package/lib/builder/core/generate.js +6 -6
- package/lib/builder/core/index.d.ts.map +1 -1
- package/lib/builder/service/builder/webpack.d.ts +1 -1
- package/lib/builder/service/builder/webpack.d.ts.map +1 -1
- package/lib/builder/service/builder/webpack.js +17 -28
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +25 -73
- package/package.json +1 -1
- package/template/html/index.html.ejs +31 -19
- package/template/mp/app/weapps-api.js +27 -95
- package/template/mp/app.js +60 -76
- package/template/mp/common/util.js +3 -312
- package/template/mp/datasources/index.js +16 -5
- package/template/mp/package.json +2 -2
- package/template/package.json +1 -1
- package/template/src/app/global-api.js +41 -154
- package/template/src/datasources/index.js.tpl +14 -2
- package/template/src/handlers/FieldMiddleware/renderer.jsx +25 -85
- package/template/src/handlers/actionHandler/utils.js +10 -31
- package/template/src/handlers/lifecycle.js +10 -21
- package/template/src/handlers/render.jsx +6 -17
- package/template/src/handlers/utils/common.js +2 -6
- package/template/src/handlers/utils/widgets.js +8 -37
- package/template/src/index.jsx +3 -3
- package/template/src/pages/app.tpl +2 -0
- package/template/src/utils/formatEnum.js +1 -1
- package/template/src/utils/index.js +0 -14
- package/template/src/utils/ScanCodeComponent.js +0 -345
- package/template/src/utils/date.js +0 -324
- package/template/src/utils/scan-code-action.js +0 -27
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* 生命周期处理函数
|
|
4
4
|
*/
|
|
5
|
-
import
|
|
5
|
+
import { throttle } from 'lodash';
|
|
6
6
|
import { wx } from '@tcwd/weapps-sdk';
|
|
7
7
|
import * as querystring from 'querystring';
|
|
8
8
|
// 小程序 端使用lifeCycle
|
|
@@ -33,7 +33,7 @@ export function initLifeCycle(
|
|
|
33
33
|
onTabItemTap,
|
|
34
34
|
},
|
|
35
35
|
app,
|
|
36
|
-
mainApp
|
|
36
|
+
mainApp,
|
|
37
37
|
) {
|
|
38
38
|
if (!process.env.isMiniprogram) {
|
|
39
39
|
window.$$global = window.$$global || {};
|
|
@@ -61,17 +61,14 @@ export function initLifeCycle(
|
|
|
61
61
|
mainApp.onAppHide && mainApp.onAppHide(onAppHide);
|
|
62
62
|
mainApp.onError && mainApp.onError(onAppError);
|
|
63
63
|
mainApp.onPageNotFound && mainApp.onPageNotFound(onAppPageNotFound);
|
|
64
|
-
mainApp.onUnhandledRejection &&
|
|
65
|
-
mainApp.onUnhandledRejection(onAppUnhandledRejection);
|
|
64
|
+
mainApp.onUnhandledRejection && mainApp.onUnhandledRejection(onAppUnhandledRejection);
|
|
66
65
|
|
|
67
66
|
// 预留等客户端来触发
|
|
68
67
|
window.addEventListener('appLaunch', (...args) => onAppLaunchCb(...args));
|
|
69
68
|
window.addEventListener('appShow', (...args) => onAppShow(...args));
|
|
70
69
|
window.addEventListener('appHide', (...args) => onAppHide(...args));
|
|
71
70
|
window.addEventListener('error', (...args) => onAppError(...args));
|
|
72
|
-
window.addEventListener('unhandledRejection', (...args) =>
|
|
73
|
-
onAppUnhandledRejection(...args)
|
|
74
|
-
);
|
|
71
|
+
window.addEventListener('unhandledRejection', (...args) => onAppUnhandledRejection(...args));
|
|
75
72
|
window.$$global.alreadyInitAppLifeCycle = true;
|
|
76
73
|
}
|
|
77
74
|
|
|
@@ -84,9 +81,7 @@ export function initLifeCycle(
|
|
|
84
81
|
window.addEventListener('wxunload', (...args) => onPageUnload(...args));
|
|
85
82
|
// 页面级别特殊事件
|
|
86
83
|
if (typeof onPullDownRefresh === 'function') {
|
|
87
|
-
window.addEventListener('pulldownrefresh', (...args) =>
|
|
88
|
-
onPullDownRefresh(...args)
|
|
89
|
-
);
|
|
84
|
+
window.addEventListener('pulldownrefresh', (...args) => onPullDownRefresh(...args));
|
|
90
85
|
}
|
|
91
86
|
|
|
92
87
|
if (typeof onPageScroll === 'function') {
|
|
@@ -143,7 +138,7 @@ export function pageLifeCycleMount(
|
|
|
143
138
|
onShareTimeline,
|
|
144
139
|
onTabItemTap,
|
|
145
140
|
},
|
|
146
|
-
app = {}
|
|
141
|
+
app = {},
|
|
147
142
|
) {
|
|
148
143
|
let queryText = location.href.split('?')[1];
|
|
149
144
|
let query = querystring.parse(queryText);
|
|
@@ -162,22 +157,16 @@ export function pageLifeCycleMount(
|
|
|
162
157
|
if (typeof onPullDownRefresh === 'function') {
|
|
163
158
|
app.onPullDownRefresh(onPullDownRefresh);
|
|
164
159
|
}
|
|
165
|
-
if (
|
|
166
|
-
typeof onPageScroll === 'function' ||
|
|
167
|
-
typeof onReachBottom === 'function'
|
|
168
|
-
) {
|
|
160
|
+
if (typeof onPageScroll === 'function' || typeof onReachBottom === 'function') {
|
|
169
161
|
window.onscroll = throttle(() => {
|
|
170
162
|
//变量scrollTop是滚动条滚动时,滚动条上端距离顶部的距离
|
|
171
|
-
let scrollTop =
|
|
172
|
-
document.documentElement.scrollTop || document.body.scrollTop;
|
|
163
|
+
let scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
|
|
173
164
|
|
|
174
165
|
//变量windowHeight是可视区的高度
|
|
175
|
-
let windowHeight =
|
|
176
|
-
document.documentElement.clientHeight || document.body.clientHeight;
|
|
166
|
+
let windowHeight = document.documentElement.clientHeight || document.body.clientHeight;
|
|
177
167
|
|
|
178
168
|
//变量scrollHeight是滚动条的总高度(当前可滚动的页面的总高度)
|
|
179
|
-
let scrollHeight =
|
|
180
|
-
document.documentElement.scrollHeight || document.body.scrollHeight;
|
|
169
|
+
let scrollHeight = document.documentElement.scrollHeight || document.body.scrollHeight;
|
|
181
170
|
if (typeof onPageScroll === 'function') {
|
|
182
171
|
onPageScroll({
|
|
183
172
|
scrollTop: window.pageYOffset,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import
|
|
2
|
+
import { set, cloneDeep } from 'lodash';
|
|
3
3
|
import { ForContext, getComponentRenderList } from './FieldMiddleware/renderer';
|
|
4
4
|
import { isScopeSlot } from '../utils/index';
|
|
5
5
|
import { observer } from 'mobx-react-lite';
|
|
@@ -9,9 +9,7 @@ export function getComponentChildren(component, context = {}) {
|
|
|
9
9
|
if (!properties) {
|
|
10
10
|
return [];
|
|
11
11
|
}
|
|
12
|
-
const list = Object.values(properties).sort(
|
|
13
|
-
(a, b) => (a['x-index'] || 0) - (b['x-index'] || 0)
|
|
14
|
-
);
|
|
12
|
+
const list = Object.values(properties).sort((a, b) => (a['x-index'] || 0) - (b['x-index'] || 0));
|
|
15
13
|
const {
|
|
16
14
|
virtualFields,
|
|
17
15
|
codeContext,
|
|
@@ -81,9 +79,7 @@ function getRenderList(props) {
|
|
|
81
79
|
// preClassName.current = className;
|
|
82
80
|
// }
|
|
83
81
|
|
|
84
|
-
containerEl['x-props'].classNameList = Array.from(
|
|
85
|
-
new Set([className, ...classNameList])
|
|
86
|
-
);
|
|
82
|
+
containerEl['x-props'].classNameList = Array.from(new Set([className, ...classNameList]));
|
|
87
83
|
}
|
|
88
84
|
|
|
89
85
|
if (xProps && xProps.sourceKey) {
|
|
@@ -117,14 +113,7 @@ function getRenderList(props) {
|
|
|
117
113
|
export function generateSlotMetaMap(componentSchema, context, options = {}) {
|
|
118
114
|
const slots = {};
|
|
119
115
|
const { properties = {} } = componentSchema;
|
|
120
|
-
const {
|
|
121
|
-
scopeContext,
|
|
122
|
-
codeContext,
|
|
123
|
-
virtualFields,
|
|
124
|
-
dataContext,
|
|
125
|
-
updateContext,
|
|
126
|
-
forContext,
|
|
127
|
-
} = context;
|
|
116
|
+
const { scopeContext, codeContext, virtualFields, dataContext, updateContext, forContext } = context;
|
|
128
117
|
// eslint-disable-next-line guard-for-in
|
|
129
118
|
for (const key in properties) {
|
|
130
119
|
const child = properties[key];
|
|
@@ -135,8 +124,8 @@ export function generateSlotMetaMap(componentSchema, context, options = {}) {
|
|
|
135
124
|
type: isHOC ? 'HOC' : 'ELEMENT',
|
|
136
125
|
node: isHOC
|
|
137
126
|
? (props) => {
|
|
138
|
-
let clonedScopeContext =
|
|
139
|
-
|
|
127
|
+
let clonedScopeContext = cloneDeep(scopeContext);
|
|
128
|
+
set(clonedScopeContext, `${componentSchema.key}.${key}`, props);
|
|
140
129
|
return (
|
|
141
130
|
<AppRender
|
|
142
131
|
key={key}
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import config from '../../datasources/config'
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
async function loginScope() {
|
|
5
|
-
const { auth } = await getTcbInstance();
|
|
6
|
-
return auth.loginScope();
|
|
7
|
-
}
|
|
2
|
+
import { auth } from '@cloudbase/weda-client';
|
|
3
|
+
const { loginScope, getAccessToken } = auth;
|
|
8
4
|
|
|
9
5
|
export function getComponentId(key) {
|
|
10
6
|
return `__weapps-component-wrapper-${key}`;
|
|
@@ -58,12 +58,7 @@ export function getDom(element, options) {
|
|
|
58
58
|
return Promise.resolve(result);
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
export function findWidgets(
|
|
62
|
-
widget,
|
|
63
|
-
parentType,
|
|
64
|
-
filterFn,
|
|
65
|
-
includeInvisibleDescendants
|
|
66
|
-
) {
|
|
61
|
+
export function findWidgets(widget, parentType, filterFn, includeInvisibleDescendants) {
|
|
67
62
|
if (!widget) return [];
|
|
68
63
|
|
|
69
64
|
let { children = [] } = widget;
|
|
@@ -94,12 +89,7 @@ export function retryDataBinds(tryTime = 10) {
|
|
|
94
89
|
retryDataBinds(tryTime - 1);
|
|
95
90
|
}, 0);
|
|
96
91
|
}
|
|
97
|
-
export function createWidgets(
|
|
98
|
-
widgetProps,
|
|
99
|
-
dataBinds,
|
|
100
|
-
scopeContext = {},
|
|
101
|
-
context = {}
|
|
102
|
-
) {
|
|
92
|
+
export function createWidgets(widgetProps, dataBinds, scopeContext = {}, context = {}) {
|
|
103
93
|
const nodeTree = createWidgetTree(widgetProps, dataBinds);
|
|
104
94
|
const widgets = runFor(nodeTree, {}, null, null, scopeContext, context);
|
|
105
95
|
return widgets;
|
|
@@ -114,14 +104,7 @@ export function createWidgets(
|
|
|
114
104
|
* @param {*} context
|
|
115
105
|
* @returns top level widgets or for dispose
|
|
116
106
|
*/
|
|
117
|
-
function runFor(
|
|
118
|
-
curForNode,
|
|
119
|
-
forItems,
|
|
120
|
-
parentLevelWidgets,
|
|
121
|
-
parentWidget,
|
|
122
|
-
scopeContext,
|
|
123
|
-
context
|
|
124
|
-
) {
|
|
107
|
+
function runFor(curForNode, forItems, parentLevelWidgets, parentWidget, scopeContext, context) {
|
|
125
108
|
const nodeId = curForNode.id;
|
|
126
109
|
if (!curForNode.value) {
|
|
127
110
|
return createSubTree(curForNode, {}, scopeContext, context);
|
|
@@ -148,8 +131,7 @@ export function createWidgets(
|
|
|
148
131
|
dfsTree(curForNode, (node) => {
|
|
149
132
|
const arr = parentLevelWidgets[node.id];
|
|
150
133
|
arr.splice(0, arr.length);
|
|
151
|
-
parentWidget &&
|
|
152
|
-
remove(parentWidget.children, ({ id }) => id === node.id);
|
|
134
|
+
parentWidget && remove(parentWidget.children, ({ id }) => id === node.id);
|
|
153
135
|
});
|
|
154
136
|
|
|
155
137
|
forList.forEach((item, index) => {
|
|
@@ -180,10 +162,7 @@ export function createWidgets(
|
|
|
180
162
|
w.findWidgets = (type, includeInvisibleDescendants) =>
|
|
181
163
|
findWidgets(w, w.widgetType, type, includeInvisibleDescendants);
|
|
182
164
|
w.getWidgetsByType = (type, includeInvisibleDescendants) =>
|
|
183
|
-
w.findWidgets(
|
|
184
|
-
(currentWidget) => currentWidget.widgetType === type,
|
|
185
|
-
includeInvisibleDescendants
|
|
186
|
-
);
|
|
165
|
+
w.findWidgets((currentWidget) => currentWidget.widgetType === type, includeInvisibleDescendants);
|
|
187
166
|
w.getOwnerWidget = () => null; // 寻找父widget,默认返回null, 后续会覆写
|
|
188
167
|
w._getInstanceRef = () => null; // 默认初始值
|
|
189
168
|
Object.defineProperty(w, '_methods', {
|
|
@@ -221,12 +200,7 @@ export function createWidgets(
|
|
|
221
200
|
const dispose = autorun(() => {
|
|
222
201
|
try {
|
|
223
202
|
// Computed data bind in the next tick since data bind may read widgets data
|
|
224
|
-
w[prop] = dataBinds[node.id][prop](
|
|
225
|
-
subForItems,
|
|
226
|
-
undefined,
|
|
227
|
-
context,
|
|
228
|
-
scopeContext
|
|
229
|
-
);
|
|
203
|
+
w[prop] = dataBinds[node.id][prop](subForItems, undefined, context, scopeContext);
|
|
230
204
|
disposeError = false;
|
|
231
205
|
} catch (e) {
|
|
232
206
|
options.showLog && console.error(e);
|
|
@@ -252,10 +226,7 @@ export function createWidgets(
|
|
|
252
226
|
|
|
253
227
|
// run for of next level
|
|
254
228
|
dfsTree(curForNode, (node, parentNode) => {
|
|
255
|
-
if (
|
|
256
|
-
node.forCount === curForNode.forCount + 1 &&
|
|
257
|
-
dataBinds[node.id]?._waFor
|
|
258
|
-
) {
|
|
229
|
+
if (node.forCount === curForNode.forCount + 1 && dataBinds[node.id]?._waFor) {
|
|
259
230
|
widgets[node.id]._disposers = { dataBinds: [] };
|
|
260
231
|
const dispose = runFor(
|
|
261
232
|
node,
|
|
@@ -263,7 +234,7 @@ export function createWidgets(
|
|
|
263
234
|
widgets,
|
|
264
235
|
node.parent && widgets[node.parent.id],
|
|
265
236
|
scopeContext,
|
|
266
|
-
context
|
|
237
|
+
context,
|
|
267
238
|
);
|
|
268
239
|
curForNode.id && widgets[curForNode.id]._disposers.push(dispose);
|
|
269
240
|
}
|
package/template/src/index.jsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react'
|
|
2
2
|
import * as ReactDOM from 'react-dom'
|
|
3
|
-
import {
|
|
3
|
+
import { _WEDA_CLOUD_SDK as WEDA_CLOUD_SDK, auth } from '@cloudbase/weda-client'
|
|
4
4
|
import App from './router'
|
|
5
5
|
import './utils/monitor-jssdk.min'
|
|
6
6
|
import './index.less'
|
|
@@ -18,7 +18,7 @@ if (process.env.isApp) {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
// 设置数据源请求的 loading 及 toast 处理
|
|
21
|
-
setConfig({
|
|
21
|
+
WEDA_CLOUD_SDK.setConfig({
|
|
22
22
|
beforeDSRequest: (cfg) => {
|
|
23
23
|
if (!cfg.options || !cfg.options.showLoading) return
|
|
24
24
|
app.showLoading()
|
|
@@ -40,7 +40,7 @@ setConfig({
|
|
|
40
40
|
if (!(loginConfig.web?.length > 0) || (params?.data?.mode === 'c' && skip)) {
|
|
41
41
|
return params;
|
|
42
42
|
}
|
|
43
|
-
const { accessToken } = await getAccessToken();
|
|
43
|
+
const { accessToken } = await auth.getAccessToken();
|
|
44
44
|
if (accessToken) {
|
|
45
45
|
params.data.accessToken = accessToken;
|
|
46
46
|
}
|
|
@@ -86,6 +86,7 @@ export default function App() {
|
|
|
86
86
|
const isPure = !!pureSrc;
|
|
87
87
|
|
|
88
88
|
React.useEffect(() => {
|
|
89
|
+
|
|
89
90
|
Object.assign($page, {
|
|
90
91
|
id: '<%= pageName %>',
|
|
91
92
|
state: observable(initPageState),
|
|
@@ -102,6 +103,7 @@ export default function App() {
|
|
|
102
103
|
app.utils.set($page.dataset.state, keyPath, userSetState[keyPath]);
|
|
103
104
|
});
|
|
104
105
|
};
|
|
106
|
+
app.__internal__.activePage = $page
|
|
105
107
|
|
|
106
108
|
checkAuth(app, app.id, $page).then((checkAuthResult) => {
|
|
107
109
|
setWeDaHasLogin(checkAuthResult)
|
|
@@ -30,7 +30,7 @@ function getEnumOptions(optionName) {
|
|
|
30
30
|
}
|
|
31
31
|
return '';
|
|
32
32
|
}
|
|
33
|
-
|
|
33
|
+
async function getGeneralOptions(optionName) {
|
|
34
34
|
return app.cloud.callWedaApi({
|
|
35
35
|
action: 'DescribeGeneralOptionsDetailList',
|
|
36
36
|
data: {
|
|
@@ -12,20 +12,6 @@ export function createComputed(funcs, bindContext = null) {
|
|
|
12
12
|
return obj;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
/*
|
|
16
|
-
根据 object对象的path路径获取值。 如果解析 value 是 undefined 会以 defaultValue 取代。
|
|
17
|
-
*/
|
|
18
|
-
export function getter(context, path, defaultValue = undefined) {
|
|
19
|
-
return lodashGet(context, path, defaultValue);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/*
|
|
23
|
-
设置 object对象中对应 path 属性路径上的值,如果path不存在,则创建。 缺少的索引属性会创建为数组,而缺少的属性会创建为对象。 使用_.setWith 定制path创建
|
|
24
|
-
*/
|
|
25
|
-
export function setter(context, path, value = undefined) {
|
|
26
|
-
return lodashSet(context, path, value);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
15
|
export function checkVisible({ _visible }) {
|
|
30
16
|
return _visible !== false && _visible !== '';
|
|
31
17
|
}
|
|
@@ -1,345 +0,0 @@
|
|
|
1
|
-
import React, { useRef, useEffect, useImperativeHandle, useState, useCallback, useMemo } from 'react';
|
|
2
|
-
import ReactDOM from 'react-dom';
|
|
3
|
-
|
|
4
|
-
import { BrowserMultiFormatReader, NotFoundException, BarcodeFormat, DecodeHintType } from '@zxing/library';
|
|
5
|
-
import { app } from '../app/global-api';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export const FORMAT = {
|
|
9
|
-
0: { scanType: 'AZTEC', wxtype: null },
|
|
10
|
-
1: { scanType: 'CODABAR', wxtype: 'barCode' },
|
|
11
|
-
2: { scanType: 'CODE_39', wxtype: 'barCode' },
|
|
12
|
-
3: { scanType: 'CODE_93', wxtype: 'barCode' },
|
|
13
|
-
4: { scanType: 'CODE_128', wxtype: 'barCode' },
|
|
14
|
-
5: { scanType: 'DATA_MATRIX', wxtype: 'qrCode' },
|
|
15
|
-
6: { scanType: 'EAN_8', wxtype: 'barCode' },
|
|
16
|
-
7: { scanType: 'EAN_13', wxtype: 'barCode' },
|
|
17
|
-
8: { scanType: 'ITF', wxtype: 'barCode' },
|
|
18
|
-
9: { scanType: 'MAXICODE', wxtype: null },
|
|
19
|
-
10: { scanType: 'PDF_417', wxtype: 'qrCode' },
|
|
20
|
-
11: { scanType: 'QR_CODE', wxtype: 'qrCode' },
|
|
21
|
-
12: { scanType: 'RSS_14', wxtype: 'barCode' },
|
|
22
|
-
13: { scanType: 'RSS_EXPANDED', wxtype: 'barCode' },
|
|
23
|
-
14: { scanType: 'UPC_A', wxtype: 'barCode' },
|
|
24
|
-
15: { scanType: 'UPC_E', wxtype: 'barCode' },
|
|
25
|
-
16: { scanType: 'UPC_EAN_EXTENSION', wxtype: 'barCode' },
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
const hints = new Map();
|
|
29
|
-
const formats = [
|
|
30
|
-
BarcodeFormat.QR_CODE,
|
|
31
|
-
BarcodeFormat.UPC_A,
|
|
32
|
-
BarcodeFormat.UPC_E,
|
|
33
|
-
BarcodeFormat.EAN_8,
|
|
34
|
-
BarcodeFormat.EAN_13,
|
|
35
|
-
BarcodeFormat.CODE_39,
|
|
36
|
-
BarcodeFormat.CODE_93,
|
|
37
|
-
BarcodeFormat.CODE_128,
|
|
38
|
-
BarcodeFormat.DATA_MATRIX,
|
|
39
|
-
BarcodeFormat.PDF_417,
|
|
40
|
-
];
|
|
41
|
-
hints.set(DecodeHintType.POSSIBLE_FORMATS, formats);
|
|
42
|
-
|
|
43
|
-
const Codescanner = React.forwardRef(({ events = {}, closeScanCode, scanType, onInit }, fref) => {
|
|
44
|
-
const ref = useRef();
|
|
45
|
-
const {
|
|
46
|
-
fail = () => {},
|
|
47
|
-
success = () => {},
|
|
48
|
-
complete = () => {},
|
|
49
|
-
} = events;
|
|
50
|
-
|
|
51
|
-
const inited = useRef(false);
|
|
52
|
-
const codeReader = new BrowserMultiFormatReader(hints);
|
|
53
|
-
const start = async () => {
|
|
54
|
-
setTimeout(() => {
|
|
55
|
-
// try harder after 5 sceonds
|
|
56
|
-
hints.set(DecodeHintType.TRY_HARDER, true);
|
|
57
|
-
codeReader.timeBetweenDecodingAttempts = 1500;
|
|
58
|
-
codeReader.hints = hints;
|
|
59
|
-
}, 5000);
|
|
60
|
-
const devices = await codeReader.listVideoInputDevices();
|
|
61
|
-
|
|
62
|
-
if (devices.length) {
|
|
63
|
-
try {
|
|
64
|
-
await codeReader.decodeFromConstraints(
|
|
65
|
-
{ video: { facingMode: 'environment' } },
|
|
66
|
-
ref.current,
|
|
67
|
-
(result, err) => {
|
|
68
|
-
if (!inited.current) {
|
|
69
|
-
inited.current = true;
|
|
70
|
-
onInit();
|
|
71
|
-
}
|
|
72
|
-
if (result) {
|
|
73
|
-
if (scanType.includes(FORMAT[result.format].wxtype)) {
|
|
74
|
-
success(wechatLikeResult(result));
|
|
75
|
-
complete();
|
|
76
|
-
closeScanCode();
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (err && !err instanceof NotFoundException) {
|
|
81
|
-
fail(err);
|
|
82
|
-
complete();
|
|
83
|
-
}
|
|
84
|
-
},
|
|
85
|
-
);
|
|
86
|
-
} catch (err) {
|
|
87
|
-
fail(err);
|
|
88
|
-
complete();
|
|
89
|
-
}
|
|
90
|
-
} else {
|
|
91
|
-
fail(new Error('No camera detect'));
|
|
92
|
-
complete();
|
|
93
|
-
}
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
const stop = () => {
|
|
97
|
-
codeReader.reset();
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
useImperativeHandle(fref, () => ({
|
|
101
|
-
start,
|
|
102
|
-
stop,
|
|
103
|
-
}));
|
|
104
|
-
|
|
105
|
-
useEffect(() => {
|
|
106
|
-
start().catch(fail);
|
|
107
|
-
return () => {
|
|
108
|
-
stop();
|
|
109
|
-
};
|
|
110
|
-
}, []);
|
|
111
|
-
return (
|
|
112
|
-
<>
|
|
113
|
-
<video
|
|
114
|
-
autoPlay
|
|
115
|
-
ref={ref}
|
|
116
|
-
id="weapp-scancode-video"
|
|
117
|
-
></video>
|
|
118
|
-
</>
|
|
119
|
-
);
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
function fileToImage(file) {
|
|
124
|
-
return new Promise((resolve, reject) => {
|
|
125
|
-
const reader = new FileReader();
|
|
126
|
-
reader.readAsDataURL(file);
|
|
127
|
-
reader.onload = async (ev) => {
|
|
128
|
-
const img = new Image();
|
|
129
|
-
img.src = ev.target.result;
|
|
130
|
-
resolve(img);
|
|
131
|
-
};
|
|
132
|
-
reader.onerror = reject;
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const SCAN_CODE_STATE = 'scan-code-modal';
|
|
137
|
-
export default function ScanCode({ root, options }) {
|
|
138
|
-
const {
|
|
139
|
-
onlyFromCamera,
|
|
140
|
-
scanType,
|
|
141
|
-
success: successCallback,
|
|
142
|
-
fail,
|
|
143
|
-
complete,
|
|
144
|
-
enableDefaultBehavior,
|
|
145
|
-
} = options;
|
|
146
|
-
useEffect(() => {
|
|
147
|
-
// 覆盖一次返回按键为关闭
|
|
148
|
-
if (history.state?.SCANCODE !== SCAN_CODE_STATE) {
|
|
149
|
-
history.pushState({ SCANCODE: SCAN_CODE_STATE }, null);
|
|
150
|
-
}
|
|
151
|
-
const onPopState = () => {
|
|
152
|
-
closeScanCode();
|
|
153
|
-
};
|
|
154
|
-
window.addEventListener('popstate', onPopState);
|
|
155
|
-
return () => {
|
|
156
|
-
if (history.state?.SCANCODE === SCAN_CODE_STATE) {
|
|
157
|
-
history.back();
|
|
158
|
-
}
|
|
159
|
-
window.removeEventListener('popstate', onPopState);
|
|
160
|
-
};
|
|
161
|
-
}, [closeScanCode]);
|
|
162
|
-
const ref = useRef();
|
|
163
|
-
const closeScanCode = () => {
|
|
164
|
-
ref.current?.stop?.();
|
|
165
|
-
ReactDOM.render(null, root);
|
|
166
|
-
};
|
|
167
|
-
const success = useCallback((res) => {
|
|
168
|
-
const { result } = res;
|
|
169
|
-
if (enableDefaultBehavior) {
|
|
170
|
-
if (/^https?:\/\//.test(result)) {
|
|
171
|
-
window.open(result);
|
|
172
|
-
} else {
|
|
173
|
-
app.showModal({
|
|
174
|
-
title: '扫描到以下内容',
|
|
175
|
-
content: result,
|
|
176
|
-
showCancel: false,
|
|
177
|
-
confirmColor: '#006eff',
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
successCallback(res);
|
|
182
|
-
});
|
|
183
|
-
const [isCameraInit, setIscameraInit] = useState(false);
|
|
184
|
-
const onInitCamera = () => {
|
|
185
|
-
setIscameraInit(true);
|
|
186
|
-
};
|
|
187
|
-
const [modalErrMessage, setModalErrMessage] = useState('');
|
|
188
|
-
const handleModalClick = () => {
|
|
189
|
-
setModalErrMessage('');
|
|
190
|
-
};
|
|
191
|
-
const fileChanged = async (ev) => {
|
|
192
|
-
const { files } = ev.target;
|
|
193
|
-
if (files.length <= 0) return;
|
|
194
|
-
const file = files[0];
|
|
195
|
-
const img = await fileToImage(file);
|
|
196
|
-
hints.set(DecodeHintType.TRY_HARDER, true);
|
|
197
|
-
const codeReader = new BrowserMultiFormatReader(hints);
|
|
198
|
-
try {
|
|
199
|
-
const result = await codeReader.decodeFromImage(img);
|
|
200
|
-
success(wechatLikeResult(result));
|
|
201
|
-
closeScanCode();
|
|
202
|
-
} catch (err) {
|
|
203
|
-
onScanFail(err);
|
|
204
|
-
}
|
|
205
|
-
};
|
|
206
|
-
const scanTypeText = useMemo(() => scanType.map((type) => {
|
|
207
|
-
switch (type) {
|
|
208
|
-
case 'qrCode':
|
|
209
|
-
return '二维码';
|
|
210
|
-
case 'barCode':
|
|
211
|
-
return '条码';
|
|
212
|
-
default:
|
|
213
|
-
return type;
|
|
214
|
-
}
|
|
215
|
-
}).join(' / '), [scanType]);
|
|
216
|
-
const onScanFail = (err) => {
|
|
217
|
-
if (err instanceof NotFoundException) {
|
|
218
|
-
setModalErrMessage(`未发现${scanTypeText}`);
|
|
219
|
-
setIscameraInit(false);
|
|
220
|
-
} else if (err.message === 'Permission denied') {
|
|
221
|
-
setModalErrMessage('请打开相机权限以获取扫码功能');
|
|
222
|
-
} else if (err.message === 'No camera detect') {
|
|
223
|
-
setModalErrMessage('未能检测到相机设备');
|
|
224
|
-
} else {
|
|
225
|
-
setModalErrMessage(err.message);
|
|
226
|
-
}
|
|
227
|
-
setIscameraInit(false);
|
|
228
|
-
fail(err);
|
|
229
|
-
};
|
|
230
|
-
if (modalErrMessage) {
|
|
231
|
-
return <div className="weapp-scancode-modal" onClick={handleModalClick}>
|
|
232
|
-
<div className="weapp-scancode-modal-main">
|
|
233
|
-
<div className="weapp-scancode-scan-wrapper">
|
|
234
|
-
<p className="weapp-scancode-scan-not-found">{modalErrMessage}</p>
|
|
235
|
-
<p>点击重新扫码</p>
|
|
236
|
-
</div>
|
|
237
|
-
<CloseButton onClick={closeScanCode} />
|
|
238
|
-
</div>
|
|
239
|
-
</div>;
|
|
240
|
-
}
|
|
241
|
-
return (
|
|
242
|
-
<div className="weapp-scancode-modal">
|
|
243
|
-
<div className="weapp-scancode-modal-main">
|
|
244
|
-
<Codescanner
|
|
245
|
-
events={{ fail: onScanFail, success, complete }}
|
|
246
|
-
ref={ref}
|
|
247
|
-
closeScanCode={closeScanCode}
|
|
248
|
-
scanType={scanType}
|
|
249
|
-
onInit={(onInitCamera)}
|
|
250
|
-
/>
|
|
251
|
-
|
|
252
|
-
{isCameraInit && <><CloseButton onClick={closeScanCode} />
|
|
253
|
-
<div className="weapp-scancode-scan-wrapper">
|
|
254
|
-
<div className="weapp-scancode-scan-square">
|
|
255
|
-
<div className="weapp-scancode-scan-bar"></div>
|
|
256
|
-
</div>
|
|
257
|
-
<p className="weapp-scancode-scan-tip">{scanTypeText}</p>
|
|
258
|
-
</div>
|
|
259
|
-
</>
|
|
260
|
-
}
|
|
261
|
-
{!onlyFromCamera && isCameraInit && <div
|
|
262
|
-
className="weapp-scancode-img-selector"
|
|
263
|
-
>
|
|
264
|
-
<input onChange={fileChanged} type="file" id="weapp-scancode-img-picker-input" accept="image/*" style={{ display: 'none' }} />
|
|
265
|
-
<label
|
|
266
|
-
htmlFor="weapp-scancode-img-picker-input"
|
|
267
|
-
className="weapp-scancode-img-picker"
|
|
268
|
-
>
|
|
269
|
-
<span>
|
|
270
|
-
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
|
271
|
-
<title>icon/album</title>
|
|
272
|
-
<g id="icon/album" stroke="none" strokeWidth="1" fill="none" fillRule="evenodd">
|
|
273
|
-
<rect id="矩形" x="0" y="0" width="24" height="24"></rect>
|
|
274
|
-
<path d="M21,4 C21.5522847,4 22,4.44771525 22,5 L22,19 C22,19.5522847 21.5522847,20 21,20 L3,20 C2.44771525,20 2,19.5522847 2,19 L2,5 C2,4.44771525 2.44771525,4 3,4 L21,4 Z M20.5,5.5 L3.5,5.5 L3.5,13.932 L8.34720227,9.89314397 C8.69729746,9.60139798 9.19512095,9.58601647 9.56028418,9.84165631 L9.65637439,9.91809179 L14.036,13.86 L16.8907001,11.8207928 C17.2650251,11.5533999 17.7734822,11.5758744 18.1227552,11.8752513 L18.1227552,11.8752513 L20.5,13.913 L20.5,5.5 Z" id="形状结合" fill="#FFFFFF" fillRule="nonzero"></path>
|
|
275
|
-
</g>
|
|
276
|
-
</svg>
|
|
277
|
-
</span>
|
|
278
|
-
</label>
|
|
279
|
-
</div>}
|
|
280
|
-
</div>
|
|
281
|
-
</div>
|
|
282
|
-
);
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
function CloseButton({ onClick }) {
|
|
286
|
-
return (
|
|
287
|
-
<a
|
|
288
|
-
className="weapp-scancode-close-button"
|
|
289
|
-
aria-label="close modal"
|
|
290
|
-
onClick={onClick}
|
|
291
|
-
>
|
|
292
|
-
<svg width="12px" height="12px" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
293
|
-
<path fillRule="evenodd" clipRule="evenodd" d="M12.5348 2.04999L7.9998 6.58499L3.4638 2.04999L2.0498 3.46499L6.5858 8.00099L2.0498 12.536L3.4638 13.95L7.9998 9.41499L12.5348 13.95L13.9498 12.536L9.4138 8.00099L13.9498 3.46499L12.5348 2.04999Z" fill="currentColor"/>
|
|
294
|
-
</svg>
|
|
295
|
-
</a>
|
|
296
|
-
);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
function wechatLikeResult(zxingResult) {
|
|
300
|
-
const wechatResult = {
|
|
301
|
-
result: zxingResult.text || zxingResult.result,
|
|
302
|
-
scanType: FORMAT[zxingResult.format].wxtype,
|
|
303
|
-
};
|
|
304
|
-
return wechatResult;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// https://github.com/AlloyTeam/AlloyImage/blob/master/src/module/filter/toGray.js
|
|
308
|
-
function toGray(imgData) {
|
|
309
|
-
const { data } = imgData;
|
|
310
|
-
|
|
311
|
-
for (let i = 0, n = data.length;i < n;i += 4) {
|
|
312
|
-
const gray = parseInt((0.299 * data[i] + 0.578 * data[i + 1] + 0.114 * data[i + 2]), 10);
|
|
313
|
-
// eslint-disable-next-line no-multi-assign
|
|
314
|
-
data[i + 2] = data[i + 1] = data[i] = gray;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
imgData.data.set(data);
|
|
318
|
-
|
|
319
|
-
return imgData;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
// https://github.com/AlloyTeam/AlloyImage/blob/master/src/module/filter/sharp.js
|
|
323
|
-
function sharp(imgData, arg = []) {
|
|
324
|
-
const lamta = arg[0] || 0.6;
|
|
325
|
-
const { data } = imgData;
|
|
326
|
-
const { width } = imgData;
|
|
327
|
-
|
|
328
|
-
for (let i = 0, n = data.length;i < n;i += 4) {
|
|
329
|
-
const ii = i / 4;
|
|
330
|
-
const row = parseInt(ii / width, 10);
|
|
331
|
-
const col = ii % width;
|
|
332
|
-
if (row === 0 || col === 0) continue;
|
|
333
|
-
|
|
334
|
-
const A = ((row - 1) * width + (col - 1)) * 4;
|
|
335
|
-
const B = ((row - 1) * width + col) * 4;
|
|
336
|
-
const E = (ii - 1) * 4;
|
|
337
|
-
|
|
338
|
-
for (let j = 0;j < 3;j ++) {
|
|
339
|
-
const delta = data[i + j] - (data[B + j] + data[E + j] + data[A + j]) / 3;
|
|
340
|
-
data[i + j] += delta * lamta;
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
return imgData;
|
|
345
|
-
}
|