@unif/react-native-hms-scan 0.6.1 → 1.0.0
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/README.md +24 -72
- package/android/src/main/AndroidManifest.xml +0 -9
- package/android/src/main/java/com/unif/reactnativehmsscan/HmsScanImage.kt +135 -0
- package/android/src/main/java/com/unif/reactnativehmsscan/HmsScanModule.kt +163 -147
- package/android/src/main/java/com/unif/reactnativehmsscan/HmsScanResultMapper.kt +17 -9
- package/android/src/main/java/com/unif/reactnativehmsscan/HmsScanView.kt +57 -19
- package/android/src/main/java/com/unif/reactnativehmsscan/HmsScanViewManager.kt +1 -1
- package/ios/HmsScanModule.mm +13 -37
- package/ios/HmsScanResultMapper.h +4 -3
- package/ios/HmsScanResultMapper.mm +31 -19
- package/ios/HmsScanView.h +1 -1
- package/ios/HmsScanView.mm +140 -25
- package/lib/module/HmsScanView/HmsScanView.js +66 -0
- package/lib/module/HmsScanView/HmsScanView.js.map +1 -0
- package/lib/module/HmsScanView/HmsScanView.web.js +26 -0
- package/lib/module/HmsScanView/HmsScanView.web.js.map +1 -0
- package/lib/module/HmsScanView/types.js +4 -0
- package/lib/module/HmsScanView/types.js.map +1 -0
- package/lib/module/HmsScanViewNativeComponent.ts +7 -5
- package/lib/module/NativeHmsScan.js +1 -1
- package/lib/module/NativeHmsScan.js.map +1 -1
- package/lib/module/ScanError.js +12 -0
- package/lib/module/ScanError.js.map +1 -0
- package/lib/module/Scanner/DeniedOverlay.js +1 -1
- package/lib/module/Scanner/DeniedOverlay.js.map +1 -1
- package/lib/module/Scanner/ResultFail.js +3 -2
- package/lib/module/Scanner/ResultFail.js.map +1 -1
- package/lib/module/Scanner/ResultFocus/ResultFocus.js +56 -0
- package/lib/module/Scanner/ResultFocus/ResultFocus.js.map +1 -0
- package/lib/module/Scanner/ResultFocus/styles.js +39 -0
- package/lib/module/Scanner/ResultFocus/styles.js.map +1 -0
- package/lib/module/Scanner/ResultFocus/types.js +4 -0
- package/lib/module/Scanner/ResultFocus/types.js.map +1 -0
- package/lib/module/Scanner/ScanHint.js +6 -3
- package/lib/module/Scanner/ScanHint.js.map +1 -1
- package/lib/module/Scanner/ScanToolbar.js +6 -3
- package/lib/module/Scanner/ScanToolbar.js.map +1 -1
- package/lib/module/Scanner/ScanTopBar.js +6 -3
- package/lib/module/Scanner/ScanTopBar.js.map +1 -1
- package/lib/module/Scanner/Scanner.js +9 -311
- package/lib/module/Scanner/Scanner.js.map +1 -1
- package/lib/module/Scanner/Scanner.web.js +29 -0
- package/lib/module/Scanner/Scanner.web.js.map +1 -0
- package/lib/module/Scanner/ScannerInner.js +310 -0
- package/lib/module/Scanner/ScannerInner.js.map +1 -0
- package/lib/module/Scanner/styles.js +11 -0
- package/lib/module/Scanner/styles.js.map +1 -0
- package/lib/module/Scanner/types.js +4 -0
- package/lib/module/Scanner/types.js.map +1 -0
- package/lib/module/constants.js +20 -0
- package/lib/module/constants.js.map +1 -0
- package/lib/module/decodeImage.js +29 -24
- package/lib/module/decodeImage.js.map +1 -1
- package/lib/module/decodeImage.web.js +7 -0
- package/lib/module/decodeImage.web.js.map +1 -0
- package/lib/module/errors.js +24 -0
- package/lib/module/errors.js.map +1 -0
- package/lib/module/format.js +53 -47
- package/lib/module/format.js.map +1 -1
- package/lib/module/index.js +5 -19
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.web.js +8 -0
- package/lib/module/index.web.js.map +1 -0
- package/lib/module/mock.js +2 -23
- package/lib/module/mock.js.map +1 -1
- package/lib/module/permissions.js +24 -20
- package/lib/module/permissions.js.map +1 -1
- package/lib/module/permissions.web.js +10 -0
- package/lib/module/permissions.web.js.map +1 -0
- package/lib/module/types.js +1 -45
- package/lib/module/types.js.map +1 -1
- package/lib/typescript/src/HmsScanView/HmsScanView.d.ts +3 -0
- package/lib/typescript/src/HmsScanView/HmsScanView.d.ts.map +1 -0
- package/lib/typescript/src/HmsScanView/HmsScanView.web.d.ts +3 -0
- package/lib/typescript/src/HmsScanView/HmsScanView.web.d.ts.map +1 -0
- package/lib/typescript/src/HmsScanView/types.d.ts +12 -0
- package/lib/typescript/src/HmsScanView/types.d.ts.map +1 -0
- package/lib/typescript/src/HmsScanViewNativeComponent.d.ts +6 -4
- package/lib/typescript/src/HmsScanViewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/NativeHmsScan.d.ts +3 -5
- package/lib/typescript/src/NativeHmsScan.d.ts.map +1 -1
- package/lib/typescript/src/ScanError.d.ts +7 -0
- package/lib/typescript/src/ScanError.d.ts.map +1 -0
- package/lib/typescript/src/Scanner/DeniedOverlay.d.ts.map +1 -1
- package/lib/typescript/src/Scanner/ResultFail.d.ts +2 -1
- package/lib/typescript/src/Scanner/ResultFail.d.ts.map +1 -1
- package/lib/typescript/src/Scanner/ResultFocus/ResultFocus.d.ts +4 -0
- package/lib/typescript/src/Scanner/ResultFocus/ResultFocus.d.ts.map +1 -0
- package/lib/typescript/src/Scanner/ResultFocus/styles.d.ts +36 -0
- package/lib/typescript/src/Scanner/ResultFocus/styles.d.ts.map +1 -0
- package/lib/typescript/src/Scanner/ResultFocus/types.d.ts +8 -0
- package/lib/typescript/src/Scanner/ResultFocus/types.d.ts.map +1 -0
- package/lib/typescript/src/Scanner/ScanHint.d.ts.map +1 -1
- package/lib/typescript/src/Scanner/ScanToolbar.d.ts.map +1 -1
- package/lib/typescript/src/Scanner/ScanTopBar.d.ts.map +1 -1
- package/lib/typescript/src/Scanner/Scanner.d.ts +2 -45
- package/lib/typescript/src/Scanner/Scanner.d.ts.map +1 -1
- package/lib/typescript/src/Scanner/Scanner.web.d.ts +3 -0
- package/lib/typescript/src/Scanner/Scanner.web.d.ts.map +1 -0
- package/lib/typescript/src/Scanner/ScannerInner.d.ts +3 -0
- package/lib/typescript/src/Scanner/ScannerInner.d.ts.map +1 -0
- package/lib/typescript/src/Scanner/styles.d.ts +7 -0
- package/lib/typescript/src/Scanner/styles.d.ts.map +1 -0
- package/lib/typescript/src/Scanner/types.d.ts +20 -0
- package/lib/typescript/src/Scanner/types.d.ts.map +1 -0
- package/lib/typescript/src/constants.d.ts +8 -0
- package/lib/typescript/src/constants.d.ts.map +1 -0
- package/lib/typescript/src/decodeImage.d.ts +2 -12
- package/lib/typescript/src/decodeImage.d.ts.map +1 -1
- package/lib/typescript/src/decodeImage.web.d.ts +3 -0
- package/lib/typescript/src/decodeImage.web.d.ts.map +1 -0
- package/lib/typescript/src/errors.d.ts +6 -0
- package/lib/typescript/src/errors.d.ts.map +1 -0
- package/lib/typescript/src/format.d.ts +15 -12
- package/lib/typescript/src/format.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +6 -7
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/index.web.d.ts +9 -0
- package/lib/typescript/src/index.web.d.ts.map +1 -0
- package/lib/typescript/src/mock.d.ts +9 -8
- package/lib/typescript/src/mock.d.ts.map +1 -1
- package/lib/typescript/src/permissions.d.ts +4 -8
- package/lib/typescript/src/permissions.d.ts.map +1 -1
- package/lib/typescript/src/permissions.web.d.ts +4 -0
- package/lib/typescript/src/permissions.web.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +31 -56
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/package.json +9 -2
- package/src/HmsScanView/HmsScanView.tsx +67 -0
- package/src/HmsScanView/HmsScanView.web.tsx +22 -0
- package/src/HmsScanView/types.ts +17 -0
- package/src/HmsScanViewNativeComponent.ts +7 -5
- package/src/NativeHmsScan.ts +3 -5
- package/src/ScanError.ts +13 -0
- package/src/Scanner/DeniedOverlay.tsx +4 -2
- package/src/Scanner/ResultFail.tsx +23 -5
- package/src/Scanner/ResultFocus/ResultFocus.tsx +43 -0
- package/src/Scanner/ResultFocus/styles.ts +25 -0
- package/src/Scanner/ResultFocus/types.ts +8 -0
- package/src/Scanner/ScanHint.tsx +16 -11
- package/src/Scanner/ScanToolbar.tsx +35 -13
- package/src/Scanner/ScanTopBar.tsx +20 -12
- package/src/Scanner/Scanner.tsx +13 -436
- package/src/Scanner/Scanner.web.tsx +26 -0
- package/src/Scanner/ScannerInner.tsx +391 -0
- package/src/Scanner/styles.ts +6 -0
- package/src/Scanner/types.ts +36 -0
- package/src/constants.ts +55 -0
- package/src/decodeImage.ts +35 -29
- package/src/decodeImage.web.ts +8 -0
- package/src/errors.ts +26 -0
- package/src/format.ts +77 -76
- package/src/index.tsx +17 -32
- package/src/index.web.tsx +22 -0
- package/src/mock.ts +21 -29
- package/src/permissions.ts +33 -20
- package/src/permissions.web.ts +9 -0
- package/src/types.ts +38 -126
- package/lib/module/HmsScanView.js +0 -43
- package/lib/module/HmsScanView.js.map +0 -1
- package/lib/module/Scanner/ResultFocus.js +0 -171
- package/lib/module/Scanner/ResultFocus.js.map +0 -1
- package/lib/typescript/src/HmsScanView.d.ts +0 -33
- package/lib/typescript/src/HmsScanView.d.ts.map +0 -1
- package/lib/typescript/src/Scanner/ResultFocus.d.ts +0 -14
- package/lib/typescript/src/Scanner/ResultFocus.d.ts.map +0 -1
- package/src/HmsScanView.tsx +0 -87
- package/src/Scanner/ResultFocus.tsx +0 -122
package/src/format.ts
CHANGED
|
@@ -1,93 +1,94 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import { SCAN_FORMAT_SET, SCAN_CONTENT_TYPE_SET } from './constants';
|
|
2
|
+
import { ScanError } from './ScanError';
|
|
3
|
+
import type {
|
|
4
|
+
RequestedScanFormat,
|
|
5
|
+
ScanContentType,
|
|
6
|
+
ScanFormat,
|
|
7
|
+
ScanPoint,
|
|
8
|
+
ScanResult,
|
|
7
9
|
} from './types';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const CONTENT_TYPES: readonly BarcodeContentType[] = [
|
|
12
|
-
'TEXT',
|
|
13
|
-
'URL',
|
|
14
|
-
'EMAIL',
|
|
15
|
-
'PHONE',
|
|
16
|
-
'SMS',
|
|
17
|
-
'WIFI',
|
|
18
|
-
'CONTACT',
|
|
19
|
-
'EVENT',
|
|
20
|
-
'LOCATION',
|
|
21
|
-
'DRIVER',
|
|
22
|
-
'ISBN',
|
|
23
|
-
'ARTICLE',
|
|
24
|
-
'OTHER',
|
|
25
|
-
];
|
|
26
|
-
const CONTENT_SET = new Set<string>(CONTENT_TYPES);
|
|
27
|
-
|
|
28
|
-
/** 把任意字符串安全收敛成 BarcodeFormat(未知归 UNKNOWN)。 */
|
|
29
|
-
export function coerceFormat(value: unknown): BarcodeFormat {
|
|
30
|
-
return typeof value === 'string' && FORMAT_SET.has(value)
|
|
31
|
-
? (value as BarcodeFormat)
|
|
10
|
+
export function coerceFormat(value: unknown): ScanFormat {
|
|
11
|
+
return typeof value === 'string' && SCAN_FORMAT_SET.has(value)
|
|
12
|
+
? (value as ScanFormat)
|
|
32
13
|
: 'UNKNOWN';
|
|
33
14
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return typeof value === 'string' && CONTENT_SET.has(value)
|
|
38
|
-
? (value as BarcodeContentType)
|
|
15
|
+
export function coerceContentType(value: unknown): ScanContentType | undefined {
|
|
16
|
+
return typeof value === 'string' && SCAN_CONTENT_TYPE_SET.has(value)
|
|
17
|
+
? (value as ScanContentType)
|
|
39
18
|
: undefined;
|
|
40
19
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
20
|
+
/** Empty means no extra filter. Order and duplicates do not change the filter. */
|
|
21
|
+
export function formatsToCsv(value?: readonly RequestedScanFormat[]): string {
|
|
22
|
+
if (value === undefined) return '';
|
|
23
|
+
if (
|
|
24
|
+
!Array.isArray(value) ||
|
|
25
|
+
[...value].some(
|
|
26
|
+
(item) =>
|
|
27
|
+
typeof item !== 'string' ||
|
|
28
|
+
item === 'UNKNOWN' ||
|
|
29
|
+
!SCAN_FORMAT_SET.has(item)
|
|
30
|
+
)
|
|
31
|
+
) {
|
|
32
|
+
throw new ScanError({
|
|
33
|
+
reason: 'invalid_input',
|
|
34
|
+
message: 'formats 包含无效码制',
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
return [...new Set(value)].sort().join(',');
|
|
45
38
|
}
|
|
46
|
-
|
|
47
|
-
|
|
39
|
+
export function formatConfiguration(value?: readonly RequestedScanFormat[]) {
|
|
40
|
+
try {
|
|
41
|
+
return { csv: formatsToCsv(value), error: undefined };
|
|
42
|
+
} catch (error) {
|
|
43
|
+
return { csv: '', error: error as ScanError };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function cornerPoints(value: unknown): readonly ScanPoint[] | undefined {
|
|
48
47
|
if (!Array.isArray(value)) return undefined;
|
|
49
48
|
const points = value
|
|
50
49
|
.filter(
|
|
51
|
-
(p): p is
|
|
52
|
-
!!p &&
|
|
50
|
+
(p): p is ScanPoint =>
|
|
51
|
+
!!p &&
|
|
52
|
+
typeof p.x === 'number' &&
|
|
53
|
+
Number.isFinite(p.x) &&
|
|
54
|
+
typeof p.y === 'number' &&
|
|
55
|
+
Number.isFinite(p.y)
|
|
53
56
|
)
|
|
54
57
|
.map((p) => ({ x: p.x, y: p.y }));
|
|
55
|
-
return points.length
|
|
58
|
+
return points.length ? points : undefined;
|
|
56
59
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if (typeof r.value !== 'string' || r.value.length === 0) return null;
|
|
63
|
-
const result: ScanResult = {
|
|
64
|
-
value: r.value,
|
|
65
|
-
format: coerceFormat(r.format),
|
|
66
|
-
};
|
|
67
|
-
const contentType = coerceContentType(r.contentType);
|
|
68
|
-
if (contentType) result.contentType = contentType;
|
|
69
|
-
const cornerPoints = coerceCornerPoints(r.cornerPoints);
|
|
70
|
-
if (cornerPoints) result.cornerPoints = cornerPoints;
|
|
71
|
-
return result;
|
|
60
|
+
function invalidResponse(): never {
|
|
61
|
+
throw new ScanError({
|
|
62
|
+
reason: 'invalid_response',
|
|
63
|
+
message: '原生扫码结果不符合约定',
|
|
64
|
+
});
|
|
72
65
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
*/
|
|
78
|
-
export function parseResultsJson(json: string): ScanResult[] {
|
|
79
|
-
if (!json) return [];
|
|
80
|
-
let parsed: unknown;
|
|
66
|
+
/** Malformed bridge data is distinct from a successfully decoded empty batch. */
|
|
67
|
+
export function parseResultsJson(json: unknown): readonly ScanResult[] {
|
|
68
|
+
let data: unknown;
|
|
69
|
+
if (typeof json !== 'string') return invalidResponse();
|
|
81
70
|
try {
|
|
82
|
-
|
|
71
|
+
data = JSON.parse(json);
|
|
83
72
|
} catch {
|
|
84
|
-
return
|
|
85
|
-
}
|
|
86
|
-
if (!Array.isArray(parsed)) return [];
|
|
87
|
-
const out: ScanResult[] = [];
|
|
88
|
-
for (const item of parsed) {
|
|
89
|
-
const r = coerceResult(item);
|
|
90
|
-
if (r) out.push(r);
|
|
73
|
+
return invalidResponse();
|
|
91
74
|
}
|
|
92
|
-
return
|
|
75
|
+
if (!Array.isArray(data)) return invalidResponse();
|
|
76
|
+
return data.map((raw) => {
|
|
77
|
+
if (
|
|
78
|
+
!raw ||
|
|
79
|
+
typeof raw !== 'object' ||
|
|
80
|
+
typeof raw.value !== 'string' ||
|
|
81
|
+
typeof raw.format !== 'string'
|
|
82
|
+
)
|
|
83
|
+
return invalidResponse();
|
|
84
|
+
const result: ScanResult = {
|
|
85
|
+
value: raw.value,
|
|
86
|
+
format: coerceFormat(raw.format),
|
|
87
|
+
};
|
|
88
|
+
const contentType = coerceContentType(raw.contentType);
|
|
89
|
+
const points = cornerPoints(raw.cornerPoints);
|
|
90
|
+
if (contentType !== undefined) result.contentType = contentType;
|
|
91
|
+
if (points !== undefined) result.cornerPoints = points;
|
|
92
|
+
return result;
|
|
93
|
+
});
|
|
93
94
|
}
|
package/src/index.tsx
CHANGED
|
@@ -1,37 +1,22 @@
|
|
|
1
|
-
|
|
2
|
-
// 能力:定制视图扫码(底层 <HmsScanView> + 成品 <Scanner> 扫一扫页)+ 图片识别(decodeImage)+ 相机权限。
|
|
3
|
-
// 文档站:https://unif-design.github.io/react-native-hms-scan/(版本随发版自动部署)。
|
|
4
|
-
|
|
5
|
-
// ── 类型与常量 ──
|
|
6
|
-
export {
|
|
7
|
-
ALL_BARCODE_FORMATS,
|
|
8
|
-
HmsScanError,
|
|
9
|
-
} from './types';
|
|
1
|
+
export { ScanError } from './ScanError';
|
|
10
2
|
export type {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
3
|
+
ScanFormat,
|
|
4
|
+
RequestedScanFormat,
|
|
5
|
+
ScanContentType,
|
|
6
|
+
ScanPoint,
|
|
14
7
|
ScanResult,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
8
|
+
ScanFailure,
|
|
9
|
+
DecodeScanImageInput,
|
|
10
|
+
ScanCameraPermission,
|
|
11
|
+
ScanTorchState,
|
|
12
|
+
ScannerImage,
|
|
20
13
|
} from './types';
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
export { coerceFormat, coerceContentType, formatsToCsv } from './format';
|
|
24
|
-
|
|
25
|
-
// ── 底层 headless 相机组件 ──
|
|
26
|
-
export { HmsScanView } from './HmsScanView';
|
|
27
|
-
export type { HmsScanViewProps, TorchStatus } from './HmsScanView';
|
|
28
|
-
|
|
29
|
-
// ── 图片识别 ──
|
|
14
|
+
export { HmsScanView } from './HmsScanView/HmsScanView';
|
|
15
|
+
export type { HmsScanViewProps } from './HmsScanView/types';
|
|
30
16
|
export { decodeImage } from './decodeImage';
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
// ── 成品扫一扫页(聚焦款,浅色) ──
|
|
17
|
+
export {
|
|
18
|
+
getCameraPermissionStatus,
|
|
19
|
+
requestCameraPermission,
|
|
20
|
+
} from './permissions';
|
|
36
21
|
export { Scanner } from './Scanner/Scanner';
|
|
37
|
-
export type { ScannerProps } from './Scanner/
|
|
22
|
+
export type { ScannerProps } from './Scanner/types';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export { ScanError } from './ScanError';
|
|
2
|
+
export type {
|
|
3
|
+
ScanFormat,
|
|
4
|
+
RequestedScanFormat,
|
|
5
|
+
ScanContentType,
|
|
6
|
+
ScanPoint,
|
|
7
|
+
ScanResult,
|
|
8
|
+
ScanFailure,
|
|
9
|
+
DecodeScanImageInput,
|
|
10
|
+
ScanCameraPermission,
|
|
11
|
+
ScanTorchState,
|
|
12
|
+
ScannerImage,
|
|
13
|
+
} from './types';
|
|
14
|
+
export { HmsScanView } from './HmsScanView/HmsScanView.web';
|
|
15
|
+
export type { HmsScanViewProps } from './HmsScanView/types';
|
|
16
|
+
export { decodeImage } from './decodeImage.web';
|
|
17
|
+
export {
|
|
18
|
+
getCameraPermissionStatus,
|
|
19
|
+
requestCameraPermission,
|
|
20
|
+
} from './permissions.web';
|
|
21
|
+
export { Scanner } from './Scanner/Scanner.web';
|
|
22
|
+
export type { ScannerProps } from './Scanner/types';
|
package/src/mock.ts
CHANGED
|
@@ -1,43 +1,35 @@
|
|
|
1
|
-
// Jest mock for @unif/react-native-hms-scan —— 供消费者在测试中替换本库,
|
|
2
|
-
// 避免 jest 环境加载 HmsScan TurboModule / HmsScanView Fabric 组件而崩溃。
|
|
3
|
-
//
|
|
4
|
-
// 用法(消费者 jest setup 或单个测试文件):
|
|
5
|
-
//
|
|
6
|
-
// jest.mock('@unif/react-native-hms-scan', () =>
|
|
7
|
-
// require('@unif/react-native-hms-scan/mock')
|
|
8
|
-
// );
|
|
9
|
-
//
|
|
10
|
-
// 替换后 decodeImage / 权限方法都是 jest.fn;<HmsScanView> / <Scanner> 渲染为 null。
|
|
11
|
-
// 按需覆盖单次返回:
|
|
12
|
-
// (decodeImage as jest.Mock).mockResolvedValueOnce([{ value: '690...', format: 'EAN_13' }]);
|
|
13
|
-
|
|
14
1
|
import type { FC } from 'react';
|
|
15
2
|
import type {
|
|
16
|
-
|
|
17
|
-
|
|
3
|
+
DecodeScanImageInput,
|
|
4
|
+
ScanCameraPermission,
|
|
18
5
|
ScanResult,
|
|
19
6
|
} from './types';
|
|
20
|
-
import type { HmsScanViewProps } from './HmsScanView';
|
|
21
|
-
import type { ScannerProps } from './Scanner/
|
|
22
|
-
|
|
23
|
-
// 纯 JS 类型 / 常量 / Error / 码制工具:保留真实实现(不碰原生)。
|
|
24
|
-
export * from './types';
|
|
25
|
-
export { coerceFormat, coerceContentType, formatsToCsv } from './format';
|
|
7
|
+
import type { HmsScanViewProps } from './HmsScanView/types';
|
|
8
|
+
import type { ScannerProps } from './Scanner/types';
|
|
26
9
|
|
|
27
|
-
|
|
10
|
+
export { ScanError } from './ScanError';
|
|
11
|
+
export type {
|
|
12
|
+
ScanFormat,
|
|
13
|
+
RequestedScanFormat,
|
|
14
|
+
ScanContentType,
|
|
15
|
+
ScanPoint,
|
|
16
|
+
ScanResult,
|
|
17
|
+
ScanFailure,
|
|
18
|
+
DecodeScanImageInput,
|
|
19
|
+
ScanCameraPermission,
|
|
20
|
+
ScanTorchState,
|
|
21
|
+
ScannerImage,
|
|
22
|
+
} from './types';
|
|
23
|
+
export type { HmsScanViewProps, ScannerProps };
|
|
28
24
|
export const decodeImage = jest.fn(
|
|
29
|
-
(
|
|
25
|
+
(_input: Readonly<DecodeScanImageInput>): Promise<readonly ScanResult[]> =>
|
|
30
26
|
Promise.resolve([])
|
|
31
27
|
);
|
|
32
|
-
|
|
33
|
-
// ── 相机权限 ──(默认已授权)
|
|
34
28
|
export const getCameraPermissionStatus = jest.fn(
|
|
35
|
-
(): Promise<
|
|
29
|
+
(): Promise<ScanCameraPermission> => Promise.resolve('granted')
|
|
36
30
|
);
|
|
37
31
|
export const requestCameraPermission = jest.fn(
|
|
38
|
-
(): Promise<
|
|
32
|
+
(): Promise<ScanCameraPermission> => Promise.resolve('granted')
|
|
39
33
|
);
|
|
40
|
-
|
|
41
|
-
// ── 组件 ──(渲染 null,避免触碰原生)
|
|
42
34
|
export const HmsScanView: FC<HmsScanViewProps> = () => null;
|
|
43
35
|
export const Scanner: FC<ScannerProps> = () => null;
|
package/src/permissions.ts
CHANGED
|
@@ -1,27 +1,40 @@
|
|
|
1
1
|
import NativeHmsScan from './NativeHmsScan';
|
|
2
|
-
import
|
|
2
|
+
import { scanError } from './errors';
|
|
3
|
+
import { ScanError } from './ScanError';
|
|
4
|
+
import type { ScanCameraPermission } from './types';
|
|
3
5
|
|
|
4
|
-
function
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
async function permission(request: boolean): Promise<ScanCameraPermission> {
|
|
7
|
+
if (!NativeHmsScan)
|
|
8
|
+
throw new ScanError({
|
|
9
|
+
reason: 'unavailable',
|
|
10
|
+
message: 'HMS Scan 原生模块未安装',
|
|
11
|
+
});
|
|
12
|
+
try {
|
|
13
|
+
const status = await (request
|
|
14
|
+
? NativeHmsScan.requestCameraPermission()
|
|
15
|
+
: NativeHmsScan.getCameraPermissionStatus());
|
|
16
|
+
if (
|
|
17
|
+
status === 'granted' ||
|
|
18
|
+
status === 'denied' ||
|
|
19
|
+
status === 'blocked' ||
|
|
20
|
+
status === 'undetermined'
|
|
21
|
+
)
|
|
10
22
|
return status;
|
|
11
|
-
|
|
12
|
-
|
|
23
|
+
throw new ScanError({
|
|
24
|
+
reason: 'invalid_response',
|
|
25
|
+
message: '原生相机权限状态无法识别',
|
|
26
|
+
});
|
|
27
|
+
} catch (error) {
|
|
28
|
+
throw scanError(error, {
|
|
29
|
+
reason: 'unavailable',
|
|
30
|
+
message: '读取相机权限失败',
|
|
31
|
+
});
|
|
13
32
|
}
|
|
14
33
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
return normalize(await NativeHmsScan.getCameraPermissionStatus());
|
|
34
|
+
/** Reads the actual status without presenting a system permission prompt. */
|
|
35
|
+
export function getCameraPermissionStatus(): Promise<ScanCameraPermission> {
|
|
36
|
+
return permission(false);
|
|
19
37
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
* 发起相机权限请求(必要时弹系统授权框),返回请求后的状态。
|
|
23
|
-
* 若用户已永久拒绝(blocked),需引导去系统设置开启。
|
|
24
|
-
*/
|
|
25
|
-
export async function requestCameraPermission(): Promise<CameraPermissionStatus> {
|
|
26
|
-
return normalize(await NativeHmsScan.requestCameraPermission());
|
|
38
|
+
export function requestCameraPermission(): Promise<ScanCameraPermission> {
|
|
39
|
+
return permission(true);
|
|
27
40
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { unsupportedScanError } from './errors';
|
|
2
|
+
import type { ScanCameraPermission } from './types';
|
|
3
|
+
|
|
4
|
+
export async function getCameraPermissionStatus(): Promise<ScanCameraPermission> {
|
|
5
|
+
throw unsupportedScanError();
|
|
6
|
+
}
|
|
7
|
+
export async function requestCameraPermission(): Promise<ScanCameraPermission> {
|
|
8
|
+
throw unsupportedScanError();
|
|
9
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -1,144 +1,56 @@
|
|
|
1
|
-
|
|
2
|
-
// @unif/react-native-hms-scan —— 公共类型
|
|
3
|
-
//
|
|
4
|
-
// 这些是纯 JS/TS 类型与常量(不触碰原生),index 与 mock 都从这里导出。
|
|
5
|
-
// 码制 / 内容类型在原生侧(Android HmsScan.*_SCAN_TYPE / getScanTypeForm,
|
|
6
|
-
// iOS HMSScanFormatTypeCode / sceneType)已统一映射成下面的字符串枚举再回 JS。
|
|
7
|
-
// ──────────────────────────────────────────────────────────────
|
|
1
|
+
import type { SCAN_FORMATS, SCAN_CONTENT_TYPES } from './constants';
|
|
8
2
|
|
|
9
|
-
|
|
10
|
-
export type
|
|
11
|
-
| 'QR_CODE'
|
|
12
|
-
| 'AZTEC'
|
|
13
|
-
| 'DATA_MATRIX'
|
|
14
|
-
| 'PDF417'
|
|
15
|
-
| 'CODABAR'
|
|
16
|
-
| 'CODE_39'
|
|
17
|
-
| 'CODE_93'
|
|
18
|
-
| 'CODE_128'
|
|
19
|
-
| 'EAN_8'
|
|
20
|
-
| 'EAN_13'
|
|
21
|
-
| 'UPC_A'
|
|
22
|
-
| 'UPC_E'
|
|
23
|
-
| 'ITF14'
|
|
24
|
-
| 'MULTI_FUNCTIONAL'
|
|
25
|
-
| 'UNKNOWN';
|
|
3
|
+
export type ScanFormat = (typeof SCAN_FORMATS)[number];
|
|
4
|
+
export type RequestedScanFormat = Exclude<ScanFormat, 'UNKNOWN'>;
|
|
26
5
|
|
|
27
|
-
|
|
28
|
-
export const ALL_BARCODE_FORMATS: readonly BarcodeFormat[] = [
|
|
29
|
-
'QR_CODE',
|
|
30
|
-
'AZTEC',
|
|
31
|
-
'DATA_MATRIX',
|
|
32
|
-
'PDF417',
|
|
33
|
-
'CODABAR',
|
|
34
|
-
'CODE_39',
|
|
35
|
-
'CODE_93',
|
|
36
|
-
'CODE_128',
|
|
37
|
-
'EAN_8',
|
|
38
|
-
'EAN_13',
|
|
39
|
-
'UPC_A',
|
|
40
|
-
'UPC_E',
|
|
41
|
-
'ITF14',
|
|
42
|
-
'MULTI_FUNCTIONAL',
|
|
43
|
-
] as const;
|
|
6
|
+
export type ScanContentType = (typeof SCAN_CONTENT_TYPES)[number];
|
|
44
7
|
|
|
45
|
-
|
|
46
|
-
* 码内容语义类型(尽力归一)。
|
|
47
|
-
* Android 来自 HmsScan.getScanTypeForm(),iOS 来自 sceneType(精度有限,未知归 OTHER)。
|
|
48
|
-
*/
|
|
49
|
-
export type BarcodeContentType =
|
|
50
|
-
| 'TEXT'
|
|
51
|
-
| 'URL'
|
|
52
|
-
| 'EMAIL'
|
|
53
|
-
| 'PHONE'
|
|
54
|
-
| 'SMS'
|
|
55
|
-
| 'WIFI'
|
|
56
|
-
| 'CONTACT'
|
|
57
|
-
| 'EVENT'
|
|
58
|
-
| 'LOCATION'
|
|
59
|
-
| 'DRIVER'
|
|
60
|
-
| 'ISBN'
|
|
61
|
-
| 'ARTICLE'
|
|
62
|
-
| 'OTHER';
|
|
63
|
-
|
|
64
|
-
/** 取景框/解码命中的角点(图像坐标系,单位 px)。 */
|
|
65
|
-
export interface ScanCornerPoint {
|
|
8
|
+
export interface ScanPoint {
|
|
66
9
|
x: number;
|
|
67
10
|
y: number;
|
|
68
11
|
}
|
|
69
|
-
|
|
70
|
-
/** 一次扫码/解码命中的结果。 */
|
|
71
12
|
export interface ScanResult {
|
|
72
|
-
/**
|
|
13
|
+
/** Unmodified decoded text. */
|
|
73
14
|
value: string;
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
|
|
78
|
-
/** 条码四角点(可能缺省)。 */
|
|
79
|
-
cornerPoints?: ScanCornerPoint[];
|
|
15
|
+
format: ScanFormat;
|
|
16
|
+
contentType?: ScanContentType;
|
|
17
|
+
/** Coordinates in the recognition image, not screen coordinates. */
|
|
18
|
+
cornerPoints?: readonly ScanPoint[];
|
|
80
19
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
code: string;
|
|
20
|
+
export interface ScanFailure {
|
|
21
|
+
reason:
|
|
22
|
+
| 'invalid_input'
|
|
23
|
+
| 'permission_denied'
|
|
24
|
+
| 'image_unavailable'
|
|
25
|
+
| 'decode_failed'
|
|
26
|
+
| 'invalid_response'
|
|
27
|
+
| 'unavailable'
|
|
28
|
+
| 'unsupported';
|
|
91
29
|
message: string;
|
|
30
|
+
sourceCode?: string;
|
|
92
31
|
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
|
|
32
|
+
export interface DecodeScanImageInput {
|
|
33
|
+
/** A readable local file URI, borrowed until this operation settles. */
|
|
34
|
+
uri: string;
|
|
35
|
+
formats?: readonly RequestedScanFormat[];
|
|
36
|
+
}
|
|
37
|
+
export type ScanCameraPermission =
|
|
96
38
|
| 'granted'
|
|
97
39
|
| 'denied'
|
|
98
40
|
| 'blocked'
|
|
99
41
|
| 'undetermined';
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
| 'E_NO_RESULT'
|
|
109
|
-
| 'E_UNKNOWN';
|
|
110
|
-
|
|
111
|
-
/** 库统一抛出的错误类型。 */
|
|
112
|
-
export class HmsScanError extends Error {
|
|
113
|
-
readonly code: HmsScanErrorCode;
|
|
114
|
-
constructor(code: HmsScanErrorCode, message?: string) {
|
|
115
|
-
super(message ?? code);
|
|
116
|
-
this.name = 'HmsScanError';
|
|
117
|
-
this.code = code;
|
|
118
|
-
// 兼容 TS 继承内建类的 instanceof
|
|
119
|
-
Object.setPrototypeOf(this, HmsScanError.prototype);
|
|
120
|
-
}
|
|
42
|
+
export interface ScanTorchState {
|
|
43
|
+
on: boolean;
|
|
44
|
+
available?: boolean;
|
|
45
|
+
lowLight?: boolean;
|
|
46
|
+
}
|
|
47
|
+
export interface ScannerImage {
|
|
48
|
+
uri: string;
|
|
49
|
+
onSourceReleased?(): void;
|
|
121
50
|
}
|
|
122
51
|
|
|
123
|
-
/**
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
export interface ScanProduct {
|
|
128
|
-
/** 商品名(必填)。 */
|
|
129
|
-
name: string;
|
|
130
|
-
/** 品牌(如 "统一")。 */
|
|
131
|
-
brand?: string;
|
|
132
|
-
/** 字母牌字符;缺省时取 brand 或 name 的首字。 */
|
|
133
|
-
brandChar?: string;
|
|
134
|
-
/** 条码;缺省时取扫到的 value。 */
|
|
135
|
-
barcode?: string;
|
|
136
|
-
/** 规格(如 "500ml × 15 瓶/箱")。 */
|
|
137
|
-
spec?: string;
|
|
138
|
-
/** 库存短描述(如 "充足")。 */
|
|
139
|
-
stockShort?: string;
|
|
140
|
-
/** 价格展示串(如 "¥5.50")。 */
|
|
141
|
-
price?: string;
|
|
142
|
-
/** 价格下方副标题,默认 "建议零售"。 */
|
|
143
|
-
priceCaption?: string;
|
|
52
|
+
/** Native rejection payload; internal to the capability boundary. */
|
|
53
|
+
export interface NativeScanFailure {
|
|
54
|
+
code?: unknown;
|
|
55
|
+
message?: unknown;
|
|
144
56
|
}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
import { forwardRef } from 'react';
|
|
4
|
-
import NativeHmsScanView from './HmsScanViewNativeComponent';
|
|
5
|
-
import { formatsToCsv, parseResultsJson } from "./format.js";
|
|
6
|
-
|
|
7
|
-
/** 平台相关的暗光提示 / 手电状态。 */
|
|
8
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
9
|
-
/**
|
|
10
|
-
* 底层 headless 扫码相机组件:只渲染相机预览并发出扫码结果事件。
|
|
11
|
-
* 取景框 / 扫描线 / 手电按钮等 UI 由上层(如 `<Scanner>`)用普通 RN 视图叠加绘制。
|
|
12
|
-
*
|
|
13
|
-
* Android = 华为 RemoteView,iOS = HmsCustomScanViewController(Scan SDK-Plus / ScanKitFrameWork)。
|
|
14
|
-
*/
|
|
15
|
-
export const HmsScanView = /*#__PURE__*/forwardRef(function HmsScanView({
|
|
16
|
-
formats,
|
|
17
|
-
continuous = true,
|
|
18
|
-
paused = false,
|
|
19
|
-
torch = false,
|
|
20
|
-
onScanResult,
|
|
21
|
-
onScanError,
|
|
22
|
-
onTorchStatus,
|
|
23
|
-
...viewProps
|
|
24
|
-
}, ref) {
|
|
25
|
-
return /*#__PURE__*/_jsx(NativeHmsScanView, {
|
|
26
|
-
...viewProps,
|
|
27
|
-
ref: ref,
|
|
28
|
-
formatsCsv: formatsToCsv(formats),
|
|
29
|
-
continuous: continuous,
|
|
30
|
-
paused: paused,
|
|
31
|
-
torch: torch,
|
|
32
|
-
onScanResult: onScanResult ? e => onScanResult(parseResultsJson(e.nativeEvent.resultsJson)) : undefined,
|
|
33
|
-
onScanError: onScanError ? e => onScanError({
|
|
34
|
-
code: e.nativeEvent.code,
|
|
35
|
-
message: e.nativeEvent.message
|
|
36
|
-
}) : undefined,
|
|
37
|
-
onTorchStatus: onTorchStatus ? e => onTorchStatus({
|
|
38
|
-
available: e.nativeEvent.available,
|
|
39
|
-
on: e.nativeEvent.on
|
|
40
|
-
}) : undefined
|
|
41
|
-
});
|
|
42
|
-
});
|
|
43
|
-
//# sourceMappingURL=HmsScanView.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"names":["forwardRef","NativeHmsScanView","formatsToCsv","parseResultsJson","jsx","_jsx","HmsScanView","formats","continuous","paused","torch","onScanResult","onScanError","onTorchStatus","viewProps","ref","formatsCsv","e","nativeEvent","resultsJson","undefined","code","message","available","on"],"sourceRoot":"../../src","sources":["HmsScanView.tsx"],"mappings":";;AAAA,SAASA,UAAU,QAA2B,OAAO;AAErD,OAAOC,iBAAiB,MAAM,8BAA8B;AAC5D,SAASC,YAAY,EAAEC,gBAAgB,QAAQ,aAAU;;AAGzD;AAAA,SAAAC,GAAA,IAAAC,IAAA;AAyBA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,WAAW,gBAAGN,UAAU,CAGnC,SAASM,WAAWA,CACpB;EACEC,OAAO;EACPC,UAAU,GAAG,IAAI;EACjBC,MAAM,GAAG,KAAK;EACdC,KAAK,GAAG,KAAK;EACbC,YAAY;EACZC,WAAW;EACXC,aAAa;EACb,GAAGC;AACL,CAAC,EACDC,GAAG,EACH;EACA,oBACEV,IAAA,CAACJ,iBAAiB;IAAA,GACZa,SAAS;IACbC,GAAG,EAAEA,GAAI;IACTC,UAAU,EAAEd,YAAY,CAACK,OAAO,CAAE;IAClCC,UAAU,EAAEA,UAAW;IACvBC,MAAM,EAAEA,MAAO;IACfC,KAAK,EAAEA,KAAM;IACbC,YAAY,EACVA,YAAY,GACPM,CAAC,IAAKN,YAAY,CAACR,gBAAgB,CAACc,CAAC,CAACC,WAAW,CAACC,WAAW,CAAC,CAAC,GAChEC,SACL;IACDR,WAAW,EACTA,WAAW,GACNK,CAAC,IACAL,WAAW,CAAC;MACVS,IAAI,EAAEJ,CAAC,CAACC,WAAW,CAACG,IAAI;MACxBC,OAAO,EAAEL,CAAC,CAACC,WAAW,CAACI;IACzB,CAAC,CAAC,GACJF,SACL;IACDP,aAAa,EACXA,aAAa,GACRI,CAAC,IACAJ,aAAa,CAAC;MACZU,SAAS,EAAEN,CAAC,CAACC,WAAW,CAACK,SAAS;MAClCC,EAAE,EAAEP,CAAC,CAACC,WAAW,CAACM;IACpB,CAAC,CAAC,GACJJ;EACL,CACF,CAAC;AAEN,CAAC,CAAC","ignoreList":[]}
|