sard-uniapp 1.3.0 → 1.4.0-beta
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/changelog.md +4 -0
- package/components/checkbox/common.d.ts +2 -2
- package/components/checkbox-group/checkbox-group.vue +6 -4
- package/components/checkbox-input/common.d.ts +2 -4
- package/components/config/index.d.ts +20 -0
- package/components/config/index.js +10 -0
- package/components/qrcode/common.d.ts +23 -0
- package/components/qrcode/common.js +2 -0
- package/components/qrcode/index.d.ts +1 -0
- package/components/qrcode/index.js +1 -0
- package/components/qrcode/index.scss +17 -0
- package/components/qrcode/qrcode.d.ts +47 -0
- package/components/qrcode/qrcode.vue +107 -0
- package/components/qrcode/variables.scss +4 -0
- package/components/radio/common.d.ts +2 -2
- package/components/radio-group/radio-group.vue +6 -4
- package/components/radio-input/common.d.ts +2 -4
- package/global.d.ts +3 -4
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/index.scss +1 -0
- package/package.json +2 -1
- package/utils/index.d.ts +1 -0
- package/utils/index.js +1 -0
- package/utils/is.d.ts +6 -0
- package/utils/is.js +8 -0
- package/utils/qrcode.d.ts +8 -0
- package/utils/qrcode.js +616 -0
package/changelog.md
CHANGED
|
@@ -30,9 +30,9 @@ export declare const defaultOptionKeys: {
|
|
|
30
30
|
label: string;
|
|
31
31
|
value: string;
|
|
32
32
|
};
|
|
33
|
-
export
|
|
33
|
+
export type CheckboxGroupOption = {
|
|
34
34
|
[key: PropertyKey]: any;
|
|
35
|
-
}
|
|
35
|
+
} | string | number | boolean;
|
|
36
36
|
export interface CheckboxGroupOptionKeys {
|
|
37
37
|
label?: string;
|
|
38
38
|
value?: string;
|
|
@@ -13,11 +13,11 @@
|
|
|
13
13
|
<template v-if="options">
|
|
14
14
|
<sar-checkbox
|
|
15
15
|
v-for="option in options"
|
|
16
|
-
:key="option[fieldKeys.value]"
|
|
17
|
-
:value="option[fieldKeys.value]"
|
|
16
|
+
:key="isPrimitive(option) ? option : option[fieldKeys.value]"
|
|
17
|
+
:value="isPrimitive(option) ? option : option[fieldKeys.value]"
|
|
18
18
|
:validate-event="false"
|
|
19
19
|
>
|
|
20
|
-
{{ option[fieldKeys.label] }}
|
|
20
|
+
{{ isPrimitive(option) ? option : option[fieldKeys.label] }}
|
|
21
21
|
</sar-checkbox>
|
|
22
22
|
</template>
|
|
23
23
|
</slot>
|
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
checkboxGroupPropsDefaults,
|
|
33
33
|
defaultOptionKeys
|
|
34
34
|
} from "../checkbox/common";
|
|
35
|
-
import { classNames, createBem, stringifyStyle } from "../../utils";
|
|
35
|
+
import { classNames, createBem, isPrimitive, stringifyStyle } from "../../utils";
|
|
36
36
|
import { useFormItemContext } from "../form/common";
|
|
37
37
|
import SarCheckbox from "../checkbox/checkbox.vue";
|
|
38
38
|
export default _defineComponent({
|
|
@@ -96,6 +96,8 @@ export default _defineComponent({
|
|
|
96
96
|
);
|
|
97
97
|
const __returned__ = { props, emit, bem, formItemContext, fieldKeys, innerValue, toggle, get classNames() {
|
|
98
98
|
return classNames;
|
|
99
|
+
}, get isPrimitive() {
|
|
100
|
+
return isPrimitive;
|
|
99
101
|
}, get stringifyStyle() {
|
|
100
102
|
return stringifyStyle;
|
|
101
103
|
}, SarCheckbox };
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import { type CheckboxGroupProps } from '../checkbox/common';
|
|
1
|
+
import { type CheckboxGroupOption, type CheckboxGroupProps } from '../checkbox/common';
|
|
2
2
|
import { type PopoutInputProps } from '../popout-input/common';
|
|
3
|
-
export
|
|
4
|
-
[key: PropertyKey]: any;
|
|
5
|
-
}
|
|
3
|
+
export type CheckboxInputOption = CheckboxGroupOption;
|
|
6
4
|
export interface CheckboxInputProps extends CheckboxGroupProps, Omit<PopoutInputProps, 'modelValue'> {
|
|
7
5
|
visible?: boolean;
|
|
8
6
|
title?: string;
|
|
@@ -200,6 +200,16 @@ export declare const defaultConfig: {
|
|
|
200
200
|
transitionDuration: number;
|
|
201
201
|
doneDuration: number;
|
|
202
202
|
};
|
|
203
|
+
qrcode: {
|
|
204
|
+
ecl: "M";
|
|
205
|
+
size: string;
|
|
206
|
+
canvasSize: number;
|
|
207
|
+
type: "canvas";
|
|
208
|
+
text: string;
|
|
209
|
+
color: string;
|
|
210
|
+
bgColor: string;
|
|
211
|
+
quietZoneModules: number;
|
|
212
|
+
};
|
|
203
213
|
radioGroup: {
|
|
204
214
|
direction: "vertical";
|
|
205
215
|
validateEvent: boolean;
|
|
@@ -538,6 +548,16 @@ export declare function useConfigContext(): DeepPartial<{
|
|
|
538
548
|
transitionDuration: number;
|
|
539
549
|
doneDuration: number;
|
|
540
550
|
};
|
|
551
|
+
qrcode: {
|
|
552
|
+
ecl: "M";
|
|
553
|
+
size: string;
|
|
554
|
+
canvasSize: number;
|
|
555
|
+
type: "canvas";
|
|
556
|
+
text: string;
|
|
557
|
+
color: string;
|
|
558
|
+
bgColor: string;
|
|
559
|
+
quietZoneModules: number;
|
|
560
|
+
};
|
|
541
561
|
radioGroup: {
|
|
542
562
|
direction: "vertical";
|
|
543
563
|
validateEvent: boolean;
|
|
@@ -201,6 +201,16 @@ export const defaultConfig = {
|
|
|
201
201
|
transitionDuration: 300,
|
|
202
202
|
doneDuration: 0,
|
|
203
203
|
},
|
|
204
|
+
qrcode: {
|
|
205
|
+
ecl: 'M',
|
|
206
|
+
size: '320rpx',
|
|
207
|
+
canvasSize: 400,
|
|
208
|
+
type: 'canvas',
|
|
209
|
+
text: '',
|
|
210
|
+
color: '#000',
|
|
211
|
+
bgColor: '#fff',
|
|
212
|
+
quietZoneModules: 2,
|
|
213
|
+
},
|
|
204
214
|
radioGroup: {
|
|
205
215
|
direction: 'vertical',
|
|
206
216
|
validateEvent: true,
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type StyleValue } from 'vue';
|
|
2
|
+
export interface QrcodeProps {
|
|
3
|
+
rootStyle?: StyleValue;
|
|
4
|
+
rootClass?: string;
|
|
5
|
+
text?: string;
|
|
6
|
+
ecl?: 'L' | 'M' | 'Q' | 'H';
|
|
7
|
+
type?: 'canvas' | 'image';
|
|
8
|
+
size?: string;
|
|
9
|
+
canvasSize?: number;
|
|
10
|
+
color?: string;
|
|
11
|
+
bgColor?: string;
|
|
12
|
+
quietZoneModules?: number;
|
|
13
|
+
}
|
|
14
|
+
export declare const qrcodePropsDefaults: {
|
|
15
|
+
ecl: "M";
|
|
16
|
+
size: string;
|
|
17
|
+
canvasSize: number;
|
|
18
|
+
type: "canvas";
|
|
19
|
+
text: string;
|
|
20
|
+
color: string;
|
|
21
|
+
bgColor: string;
|
|
22
|
+
quietZoneModules: number;
|
|
23
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type { QrcodeProps } from './common';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { type QrcodeProps } from './common';
|
|
2
|
+
declare const _default: import("vue").DefineComponent<__VLS_WithDefaults<__VLS_TypePropsToOption<QrcodeProps>, {
|
|
3
|
+
ecl: "M";
|
|
4
|
+
size: string;
|
|
5
|
+
canvasSize: number;
|
|
6
|
+
type: "canvas";
|
|
7
|
+
text: string;
|
|
8
|
+
color: string;
|
|
9
|
+
bgColor: string;
|
|
10
|
+
quietZoneModules: number;
|
|
11
|
+
}>, {}, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<__VLS_WithDefaults<__VLS_TypePropsToOption<QrcodeProps>, {
|
|
12
|
+
ecl: "M";
|
|
13
|
+
size: string;
|
|
14
|
+
canvasSize: number;
|
|
15
|
+
type: "canvas";
|
|
16
|
+
text: string;
|
|
17
|
+
color: string;
|
|
18
|
+
bgColor: string;
|
|
19
|
+
quietZoneModules: number;
|
|
20
|
+
}>>>, {
|
|
21
|
+
text: string;
|
|
22
|
+
ecl: "M" | "H" | "L" | "Q";
|
|
23
|
+
size: string;
|
|
24
|
+
type: "canvas" | "image";
|
|
25
|
+
canvasSize: number;
|
|
26
|
+
color: string;
|
|
27
|
+
bgColor: string;
|
|
28
|
+
quietZoneModules: number;
|
|
29
|
+
}, {}>;
|
|
30
|
+
export default _default;
|
|
31
|
+
type __VLS_WithDefaults<P, D> = {
|
|
32
|
+
[K in keyof Pick<P, keyof P>]: K extends keyof D ? __VLS_Prettify<P[K] & {
|
|
33
|
+
default: D[K];
|
|
34
|
+
}> : P[K];
|
|
35
|
+
};
|
|
36
|
+
type __VLS_Prettify<T> = {
|
|
37
|
+
[K in keyof T]: T[K];
|
|
38
|
+
} & {};
|
|
39
|
+
type __VLS_NonUndefinedable<T> = T extends undefined ? never : T;
|
|
40
|
+
type __VLS_TypePropsToOption<T> = {
|
|
41
|
+
[K in keyof T]-?: {} extends Pick<T, K> ? {
|
|
42
|
+
type: import('vue').PropType<__VLS_NonUndefinedable<T[K]>>;
|
|
43
|
+
} : {
|
|
44
|
+
type: import('vue').PropType<T[K]>;
|
|
45
|
+
required: true;
|
|
46
|
+
};
|
|
47
|
+
};
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<view :class="qrcodeClass" :style="qrcodeStyle">
|
|
3
|
+
<canvas type="2d" :class="bem.e('canvas')" :id="canvasId"></canvas>
|
|
4
|
+
<image :src="dataURL" mode="aspectFit" :class="bem.e('image')" />
|
|
5
|
+
</view>
|
|
6
|
+
</template>
|
|
7
|
+
|
|
8
|
+
<script>
|
|
9
|
+
import { mergeDefaults as _mergeDefaults, defineComponent as _defineComponent } from "vue";
|
|
10
|
+
import { computed, getCurrentInstance, onMounted, shallowRef } from "vue";
|
|
11
|
+
import {
|
|
12
|
+
classNames,
|
|
13
|
+
stringifyStyle,
|
|
14
|
+
createBem,
|
|
15
|
+
uniqid,
|
|
16
|
+
qrcode
|
|
17
|
+
} from "../../utils";
|
|
18
|
+
import { qrcodePropsDefaults } from "./common";
|
|
19
|
+
export default _defineComponent({
|
|
20
|
+
...{
|
|
21
|
+
options: {
|
|
22
|
+
virtualHost: true,
|
|
23
|
+
styleIsolation: "shared"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
__name: "qrcode",
|
|
27
|
+
props: _mergeDefaults({
|
|
28
|
+
rootStyle: { type: [Boolean, null, String, Object, Array], required: false, skipCheck: true },
|
|
29
|
+
rootClass: { type: String, required: false },
|
|
30
|
+
text: { type: String, required: false },
|
|
31
|
+
ecl: { type: String, required: false },
|
|
32
|
+
type: { type: String, required: false },
|
|
33
|
+
size: { type: String, required: false },
|
|
34
|
+
canvasSize: { type: Number, required: false },
|
|
35
|
+
color: { type: String, required: false },
|
|
36
|
+
bgColor: { type: String, required: false },
|
|
37
|
+
quietZoneModules: { type: Number, required: false }
|
|
38
|
+
}, qrcodePropsDefaults),
|
|
39
|
+
setup(__props, { expose: __expose }) {
|
|
40
|
+
__expose();
|
|
41
|
+
const props = __props;
|
|
42
|
+
const bem = createBem("qrcode");
|
|
43
|
+
const canvasId = uniqid();
|
|
44
|
+
const canvasRef = shallowRef();
|
|
45
|
+
const qrcodeMap = computed(() => {
|
|
46
|
+
return qrcode(props.text, {
|
|
47
|
+
ecl: props.ecl
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
const dataURL = computed(() => {
|
|
51
|
+
const canvas = canvasRef.value;
|
|
52
|
+
if (!canvas) {
|
|
53
|
+
return "";
|
|
54
|
+
}
|
|
55
|
+
const map = qrcodeMap.value;
|
|
56
|
+
const size = props.canvasSize;
|
|
57
|
+
canvas.width = size;
|
|
58
|
+
canvas.height = size;
|
|
59
|
+
const context = canvas.getContext("2d");
|
|
60
|
+
const moduleSize = size / (map.length + props.quietZoneModules * 2);
|
|
61
|
+
const margin = moduleSize * props.quietZoneModules;
|
|
62
|
+
const path = canvas.createPath2D ? canvas.createPath2D() : new Path2D();
|
|
63
|
+
map.forEach((row, rowIndex) => {
|
|
64
|
+
row.forEach((col, colIndex) => {
|
|
65
|
+
if (col === 1) {
|
|
66
|
+
path.rect(
|
|
67
|
+
colIndex * moduleSize + margin,
|
|
68
|
+
rowIndex * moduleSize + margin,
|
|
69
|
+
moduleSize,
|
|
70
|
+
moduleSize
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
context.clearRect(0, 0, size, size);
|
|
76
|
+
context.fillStyle = props.bgColor;
|
|
77
|
+
context.fillRect(0, 0, size, size);
|
|
78
|
+
context.fillStyle = props.color;
|
|
79
|
+
context.fill(path);
|
|
80
|
+
return canvas.toDataURL();
|
|
81
|
+
});
|
|
82
|
+
const instance = getCurrentInstance();
|
|
83
|
+
onMounted(() => {
|
|
84
|
+
uni.createSelectorQuery().in(instance).select(`#${canvasId}`).node((res) => {
|
|
85
|
+
if (res && res.node) {
|
|
86
|
+
canvasRef.value = res.node;
|
|
87
|
+
}
|
|
88
|
+
}).exec();
|
|
89
|
+
});
|
|
90
|
+
const qrcodeClass = computed(() => {
|
|
91
|
+
return classNames(bem.b(), props.rootClass);
|
|
92
|
+
});
|
|
93
|
+
const qrcodeStyle = computed(() => {
|
|
94
|
+
return stringifyStyle(props.rootStyle, {
|
|
95
|
+
width: props.size,
|
|
96
|
+
height: props.size
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
const __returned__ = { props, bem, canvasId, canvasRef, qrcodeMap, dataURL, instance, qrcodeClass, qrcodeStyle };
|
|
100
|
+
return __returned__;
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
</script>
|
|
104
|
+
|
|
105
|
+
<style lang="scss">
|
|
106
|
+
@import './index.scss';
|
|
107
|
+
</style>
|
|
@@ -24,9 +24,9 @@ export declare const defaultOptionKeys: {
|
|
|
24
24
|
label: string;
|
|
25
25
|
value: string;
|
|
26
26
|
};
|
|
27
|
-
export
|
|
27
|
+
export type RadioGroupOption = {
|
|
28
28
|
[key: PropertyKey]: any;
|
|
29
|
-
}
|
|
29
|
+
} | string | number | boolean;
|
|
30
30
|
export interface RadioGroupOptionKeys {
|
|
31
31
|
label?: string;
|
|
32
32
|
value?: string;
|
|
@@ -13,11 +13,11 @@
|
|
|
13
13
|
<template v-if="options">
|
|
14
14
|
<sar-radio
|
|
15
15
|
v-for="option in options"
|
|
16
|
-
:key="option[fieldKeys.value]"
|
|
17
|
-
:value="option[fieldKeys.value]"
|
|
16
|
+
:key="isPrimitive(option) ? option : option[fieldKeys.value]"
|
|
17
|
+
:value="isPrimitive(option) ? option : option[fieldKeys.value]"
|
|
18
18
|
:validate-event="false"
|
|
19
19
|
>
|
|
20
|
-
{{ option[fieldKeys.label] }}
|
|
20
|
+
{{ isPrimitive(option) ? option : option[fieldKeys.label] }}
|
|
21
21
|
</sar-radio>
|
|
22
22
|
</template>
|
|
23
23
|
</slot>
|
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
radioGroupPropsDefaults,
|
|
33
33
|
defaultOptionKeys
|
|
34
34
|
} from "../radio/common";
|
|
35
|
-
import { classNames, stringifyStyle, createBem } from "../../utils";
|
|
35
|
+
import { classNames, stringifyStyle, createBem, isPrimitive } from "../../utils";
|
|
36
36
|
import { useFormItemContext } from "../form/common";
|
|
37
37
|
import SarRadio from "../radio/radio.vue";
|
|
38
38
|
export default _defineComponent({
|
|
@@ -96,6 +96,8 @@ export default _defineComponent({
|
|
|
96
96
|
return classNames;
|
|
97
97
|
}, get stringifyStyle() {
|
|
98
98
|
return stringifyStyle;
|
|
99
|
+
}, get isPrimitive() {
|
|
100
|
+
return isPrimitive;
|
|
99
101
|
}, SarRadio };
|
|
100
102
|
return __returned__;
|
|
101
103
|
}
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import { type RadioGroupProps } from '../radio/common';
|
|
1
|
+
import { type RadioGroupOption, type RadioGroupProps } from '../radio/common';
|
|
2
2
|
import { type PopoutInputProps } from '../popout-input/common';
|
|
3
|
-
export
|
|
4
|
-
[key: PropertyKey]: any;
|
|
5
|
-
}
|
|
3
|
+
export type RadioInputOption = RadioGroupOption;
|
|
6
4
|
export interface RadioInputProps extends RadioGroupProps, Omit<PopoutInputProps, 'modelValue'> {
|
|
7
5
|
visible?: boolean;
|
|
8
6
|
title?: string;
|
package/global.d.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import '@vue/runtime-core'
|
|
2
|
-
|
|
3
1
|
import SarAccordion from './components/accordion/accordion'
|
|
4
2
|
import SarAccordionItem from './components/accordion-item/accordion-item'
|
|
5
3
|
import SarActionSheet from './components/action-sheet/action-sheet'
|
|
@@ -59,6 +57,7 @@ import SarPopup from './components/popup/popup'
|
|
|
59
57
|
import SarProgressBar from './components/progress-bar/progress-bar'
|
|
60
58
|
import SarProgressCircle from './components/progress-circle/progress-circle'
|
|
61
59
|
import SarPullDownRefresh from './components/pull-down-refresh/pull-down-refresh'
|
|
60
|
+
import SarQrcode from './components/qrcode/qrcode'
|
|
62
61
|
import SarRadio from './components/radio/radio'
|
|
63
62
|
import SarRadioGroup from './components/radio-group/radio-group'
|
|
64
63
|
import SarRadioInput from './components/radio-input/radio-input'
|
|
@@ -86,8 +85,7 @@ import SarToast from './components/toast/toast'
|
|
|
86
85
|
import SarToastAgent from './components/toast-agent/toast-agent'
|
|
87
86
|
import SarUpload from './components/upload/upload'
|
|
88
87
|
|
|
89
|
-
declare module '
|
|
90
|
-
// GlobalComponents for Volar
|
|
88
|
+
declare module 'vue' {
|
|
91
89
|
export interface GlobalComponents {
|
|
92
90
|
SarAccordion: typeof SarAccordion
|
|
93
91
|
SarAccordionItem: typeof SarAccordionItem
|
|
@@ -148,6 +146,7 @@ declare module '@vue/runtime-core' {
|
|
|
148
146
|
SarProgressBar: typeof SarProgressBar
|
|
149
147
|
SarProgressCircle: typeof SarProgressCircle
|
|
150
148
|
SarPullDownRefresh: typeof SarPullDownRefresh
|
|
149
|
+
SarQrcode: typeof SarQrcode
|
|
151
150
|
SarRadio: typeof SarRadio
|
|
152
151
|
SarRadioGroup: typeof SarRadioGroup
|
|
153
152
|
SarRadioInput: typeof SarRadioInput
|
package/index.d.ts
CHANGED
|
@@ -51,6 +51,7 @@ export * from './components/popup';
|
|
|
51
51
|
export * from './components/progress-bar';
|
|
52
52
|
export * from './components/progress-circle';
|
|
53
53
|
export * from './components/pull-down-refresh';
|
|
54
|
+
export * from './components/qrcode';
|
|
54
55
|
export * from './components/radio';
|
|
55
56
|
export * from './components/radio-input';
|
|
56
57
|
export * from './components/rate';
|
package/index.js
CHANGED
|
@@ -51,6 +51,7 @@ export * from './components/popup';
|
|
|
51
51
|
export * from './components/progress-bar';
|
|
52
52
|
export * from './components/progress-circle';
|
|
53
53
|
export * from './components/pull-down-refresh';
|
|
54
|
+
export * from './components/qrcode';
|
|
54
55
|
export * from './components/radio';
|
|
55
56
|
export * from './components/radio-input';
|
|
56
57
|
export * from './components/rate';
|
package/index.scss
CHANGED
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
@use './components/progress-bar/variables.scss' as *;
|
|
40
40
|
@use './components/progress-circle/variables.scss' as *;
|
|
41
41
|
@use './components/pull-down-refresh/variables.scss' as *;
|
|
42
|
+
@use './components/qrcode/variables.scss' as *;
|
|
42
43
|
@use './components/rate/variables.scss' as *;
|
|
43
44
|
@use './components/radio/variables.scss' as *;
|
|
44
45
|
@use './components/radio-input/variables.scss' as *;
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "sard-uniapp",
|
|
3
3
|
"name": "sard-uniapp",
|
|
4
4
|
"displayName": "sard-uniapp",
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.4.0-beta",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"description": "sard-uniapp 是一套基于 Uniapp + Vue3 框架开发的兼容多端的 UI 组件库",
|
|
8
8
|
"keywords": [
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
"@vitejs/plugin-vue": "^4.2.3",
|
|
34
34
|
"@vitejs/plugin-vue-jsx": "^3.1.0",
|
|
35
35
|
"@vue/test-utils": "^2.4.6",
|
|
36
|
+
"canvas": "^2.11.2",
|
|
36
37
|
"region-data": "^1.2.1",
|
|
37
38
|
"vitest": "^1.0.4"
|
|
38
39
|
},
|
package/utils/index.d.ts
CHANGED
package/utils/index.js
CHANGED
package/utils/is.d.ts
CHANGED
|
@@ -40,3 +40,9 @@ export declare function isUndefined(target: any): target is undefined;
|
|
|
40
40
|
* @return {boolean}
|
|
41
41
|
*/
|
|
42
42
|
export declare function isNullish(target: any): target is null | undefined;
|
|
43
|
+
/**
|
|
44
|
+
* @description: 判断是否为原始类型
|
|
45
|
+
* @param {any} target
|
|
46
|
+
* @return {boolean}
|
|
47
|
+
*/
|
|
48
|
+
export declare function isPrimitive(target: any): target is string | number | boolean;
|
package/utils/is.js
CHANGED
|
@@ -54,3 +54,11 @@ export function isUndefined(target) {
|
|
|
54
54
|
export function isNullish(target) {
|
|
55
55
|
return target === null || target === undefined;
|
|
56
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* @description: 判断是否为原始类型
|
|
59
|
+
* @param {any} target
|
|
60
|
+
* @return {boolean}
|
|
61
|
+
*/
|
|
62
|
+
export function isPrimitive(target) {
|
|
63
|
+
return isString(target) || isNumber(target) || isBoolean(target);
|
|
64
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const ALPHANUMERIC_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:";
|
|
2
|
+
interface QrcodeOptions {
|
|
3
|
+
ecl?: ECL;
|
|
4
|
+
}
|
|
5
|
+
export type ECL = 'L' | 'M' | 'Q' | 'H';
|
|
6
|
+
export declare const ECLList: ECL[];
|
|
7
|
+
export declare function qrcode(text: string, options?: QrcodeOptions): number[][];
|
|
8
|
+
export {};
|
package/utils/qrcode.js
ADDED
|
@@ -0,0 +1,616 @@
|
|
|
1
|
+
/*
|
|
2
|
+
|-------------------------------------------------------------------------------
|
|
3
|
+
| Error Correction Coding
|
|
4
|
+
|-------------------------------------------------------------------------------
|
|
5
|
+
| https://dev.to/maxart2501/let-s-develop-a-qr-code-generator-part-iii-error-correction-1kbm
|
|
6
|
+
|
|
|
7
|
+
*/
|
|
8
|
+
const LOG = new Uint8Array(256);
|
|
9
|
+
const EXP = new Uint8Array(256);
|
|
10
|
+
(() => {
|
|
11
|
+
for (let exponent = 1, value = 1; exponent < 256; exponent++) {
|
|
12
|
+
value = value > 127 ? (value << 1) ^ 285 : value << 1;
|
|
13
|
+
LOG[value] = exponent % 255;
|
|
14
|
+
EXP[exponent % 255] = value;
|
|
15
|
+
}
|
|
16
|
+
})();
|
|
17
|
+
function mul(a, b) {
|
|
18
|
+
return a && b ? EXP[(LOG[a] + LOG[b]) % 255] : 0;
|
|
19
|
+
}
|
|
20
|
+
function div(a, b) {
|
|
21
|
+
return EXP[(LOG[a] + LOG[b] * 254) % 255];
|
|
22
|
+
}
|
|
23
|
+
function polyMul(poly1, poly2) {
|
|
24
|
+
const coeffs = new Uint8Array(poly1.length + poly2.length - 1);
|
|
25
|
+
for (let index = 0; index < coeffs.length; index++) {
|
|
26
|
+
let coeff = 0;
|
|
27
|
+
for (let p1index = 0; p1index <= index; p1index++) {
|
|
28
|
+
const p2index = index - p1index;
|
|
29
|
+
coeff ^= mul(poly1[p1index], poly2[p2index]);
|
|
30
|
+
}
|
|
31
|
+
coeffs[index] = coeff;
|
|
32
|
+
}
|
|
33
|
+
return coeffs;
|
|
34
|
+
}
|
|
35
|
+
function polyRest(dividend, divisor) {
|
|
36
|
+
const quotientLength = dividend.length - divisor.length + 1;
|
|
37
|
+
let rest = new Uint8Array(dividend);
|
|
38
|
+
for (let count = 0; count < quotientLength; count++) {
|
|
39
|
+
if (rest[0]) {
|
|
40
|
+
const factor = div(rest[0], divisor[0]);
|
|
41
|
+
const subtr = new Uint8Array(rest.length);
|
|
42
|
+
subtr.set(polyMul(divisor, new Uint8Array([factor])), 0);
|
|
43
|
+
rest = rest.map((value, index) => value ^ subtr[index]).slice(1);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
rest = rest.slice(1);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return rest;
|
|
50
|
+
}
|
|
51
|
+
const cacheGeneratorPoly = {};
|
|
52
|
+
function getGeneratorPoly(degree) {
|
|
53
|
+
if (cacheGeneratorPoly[degree]) {
|
|
54
|
+
return cacheGeneratorPoly[degree];
|
|
55
|
+
}
|
|
56
|
+
let lastPoly = new Uint8Array([1]);
|
|
57
|
+
for (let index = 0; index < degree; index++) {
|
|
58
|
+
lastPoly = polyMul(lastPoly, new Uint8Array([1, EXP[index]]));
|
|
59
|
+
}
|
|
60
|
+
return (cacheGeneratorPoly[degree] = lastPoly);
|
|
61
|
+
}
|
|
62
|
+
function getECC(data, degree) {
|
|
63
|
+
const messagePoly = new Uint8Array(data.length + degree);
|
|
64
|
+
messagePoly.set(data, 0);
|
|
65
|
+
return polyRest(messagePoly, getGeneratorPoly(degree));
|
|
66
|
+
}
|
|
67
|
+
/*
|
|
68
|
+
|-------------------------------------------------------------------------------
|
|
69
|
+
| QR Code Generator
|
|
70
|
+
|-------------------------------------------------------------------------------
|
|
71
|
+
| https://www.thonky.com/qr-code-tutorial/
|
|
72
|
+
|
|
|
73
|
+
*/
|
|
74
|
+
const ECCodewordsPerBlock = [
|
|
75
|
+
[
|
|
76
|
+
7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28,
|
|
77
|
+
28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
|
|
78
|
+
30, 30,
|
|
79
|
+
],
|
|
80
|
+
[
|
|
81
|
+
10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26,
|
|
82
|
+
26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
|
|
83
|
+
28, 28,
|
|
84
|
+
],
|
|
85
|
+
[
|
|
86
|
+
13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26,
|
|
87
|
+
30, 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
|
|
88
|
+
30, 30,
|
|
89
|
+
],
|
|
90
|
+
[
|
|
91
|
+
17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26,
|
|
92
|
+
28, 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
|
|
93
|
+
30, 30,
|
|
94
|
+
],
|
|
95
|
+
];
|
|
96
|
+
const ECBlocks = [
|
|
97
|
+
[
|
|
98
|
+
1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12,
|
|
99
|
+
12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25,
|
|
100
|
+
],
|
|
101
|
+
[
|
|
102
|
+
1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17,
|
|
103
|
+
18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49,
|
|
104
|
+
],
|
|
105
|
+
[
|
|
106
|
+
1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23,
|
|
107
|
+
25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68,
|
|
108
|
+
],
|
|
109
|
+
[
|
|
110
|
+
1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25,
|
|
111
|
+
34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81,
|
|
112
|
+
],
|
|
113
|
+
];
|
|
114
|
+
function appendBits(num, length, bits) {
|
|
115
|
+
for (let i = length - 1; i >= 0; i--) {
|
|
116
|
+
bits.push((num >>> i) & 1);
|
|
117
|
+
}
|
|
118
|
+
return bits;
|
|
119
|
+
}
|
|
120
|
+
var Mode;
|
|
121
|
+
(function (Mode) {
|
|
122
|
+
Mode[Mode["Numeric"] = 0] = "Numeric";
|
|
123
|
+
Mode[Mode["Alphanumeric"] = 1] = "Alphanumeric";
|
|
124
|
+
Mode[Mode["Byte"] = 2] = "Byte";
|
|
125
|
+
})(Mode || (Mode = {}));
|
|
126
|
+
function getMode(text) {
|
|
127
|
+
if (/^[0-9]*$/.test(text)) {
|
|
128
|
+
return Mode.Numeric;
|
|
129
|
+
}
|
|
130
|
+
if (/^[0-9A-Z $%*+-./:]*$/.test(text)) {
|
|
131
|
+
return Mode.Alphanumeric;
|
|
132
|
+
}
|
|
133
|
+
return Mode.Byte;
|
|
134
|
+
}
|
|
135
|
+
function getModeIndicator(mode) {
|
|
136
|
+
switch (mode) {
|
|
137
|
+
case Mode.Numeric:
|
|
138
|
+
return 0b0001;
|
|
139
|
+
case Mode.Alphanumeric:
|
|
140
|
+
return 0b0010;
|
|
141
|
+
default:
|
|
142
|
+
return 0b0100;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
function encodeNumeric(text) {
|
|
146
|
+
const bits = [];
|
|
147
|
+
(text.match(/.{1,3}/g) || []).forEach((item) => {
|
|
148
|
+
appendBits(+item, (item.length - 1) * 3 + 4, bits);
|
|
149
|
+
});
|
|
150
|
+
return bits;
|
|
151
|
+
}
|
|
152
|
+
export const ALPHANUMERIC_CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:';
|
|
153
|
+
function encodeAlphaNumeric(text) {
|
|
154
|
+
const bits = [];
|
|
155
|
+
(text.match(/.{1,2}/g) || []).forEach((item) => {
|
|
156
|
+
const i1 = ALPHANUMERIC_CHARS.indexOf(item[0]);
|
|
157
|
+
const i2 = ALPHANUMERIC_CHARS.indexOf(item[1]);
|
|
158
|
+
if (i2 === -1) {
|
|
159
|
+
appendBits(i1, 6, bits);
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
appendBits(i1 * 45 + i2, 11, bits);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
return bits;
|
|
166
|
+
}
|
|
167
|
+
function toUtf8ByteArray(text) {
|
|
168
|
+
text = encodeURI(text);
|
|
169
|
+
const bytes = [];
|
|
170
|
+
for (let i = 0; i < text.length; i++) {
|
|
171
|
+
if (text.charAt(i) !== '%') {
|
|
172
|
+
bytes.push(text.charCodeAt(i));
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
bytes.push(parseInt(text.slice(i + 1, i + 3), 16));
|
|
176
|
+
i += 2;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return bytes;
|
|
180
|
+
}
|
|
181
|
+
function encodeByte(text) {
|
|
182
|
+
const bits = [];
|
|
183
|
+
toUtf8ByteArray(text).forEach((item) => {
|
|
184
|
+
appendBits(item, 8, bits);
|
|
185
|
+
});
|
|
186
|
+
return bits;
|
|
187
|
+
}
|
|
188
|
+
function encodeData(mode, text) {
|
|
189
|
+
switch (mode) {
|
|
190
|
+
case Mode.Numeric:
|
|
191
|
+
return encodeNumeric(text);
|
|
192
|
+
case Mode.Alphanumeric:
|
|
193
|
+
return encodeAlphaNumeric(text);
|
|
194
|
+
default:
|
|
195
|
+
return encodeByte(text);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
function getFunctionModules(version) {
|
|
199
|
+
let count = 225 + 8 * version;
|
|
200
|
+
if (version > 6) {
|
|
201
|
+
count += 36;
|
|
202
|
+
}
|
|
203
|
+
if (version > 1) {
|
|
204
|
+
const alignCount = ~~(version / 7) + 2;
|
|
205
|
+
count += (25 * alignCount - 10) * alignCount - 55;
|
|
206
|
+
}
|
|
207
|
+
return count;
|
|
208
|
+
}
|
|
209
|
+
function getSize(version) {
|
|
210
|
+
return version * 4 + 17;
|
|
211
|
+
}
|
|
212
|
+
function getTotalDataModules(version) {
|
|
213
|
+
return Math.pow(getSize(version), 2) - getFunctionModules(version);
|
|
214
|
+
}
|
|
215
|
+
function getECCodewords(eclIndex, version) {
|
|
216
|
+
const codewords = ECCodewordsPerBlock[eclIndex][version - 1];
|
|
217
|
+
const blocks = ECBlocks[eclIndex][version - 1];
|
|
218
|
+
return codewords * blocks;
|
|
219
|
+
}
|
|
220
|
+
function getRawDataCodewords(eclIndex, version) {
|
|
221
|
+
return getTotalDataModules(version) / 8 - getECCodewords(eclIndex, version);
|
|
222
|
+
}
|
|
223
|
+
const charCountIndicatorTable = {
|
|
224
|
+
[Mode.Numeric]: [10, 12, 14],
|
|
225
|
+
[Mode.Alphanumeric]: [9, 11, 13],
|
|
226
|
+
[Mode.Byte]: [8, 16, 16],
|
|
227
|
+
};
|
|
228
|
+
function getCharCountIndicator(mode, version) {
|
|
229
|
+
return charCountIndicatorTable[mode][~~((version + 7) / 17)];
|
|
230
|
+
}
|
|
231
|
+
function getVersion(mode, eclIndex, codewords) {
|
|
232
|
+
for (let version = 1; version <= 40; version++) {
|
|
233
|
+
const rawDataCodewords = getRawDataCodewords(eclIndex, version);
|
|
234
|
+
if (rawDataCodewords >=
|
|
235
|
+
codewords + (4 + getCharCountIndicator(mode, version)) / 8) {
|
|
236
|
+
return version;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return 40;
|
|
240
|
+
}
|
|
241
|
+
function addFinderPatterns(map, size) {
|
|
242
|
+
for (let i = 0; i < 3; i++) {
|
|
243
|
+
const originX = i === 1 ? size - 7 : 0;
|
|
244
|
+
const originY = i === 2 ? size - 7 : 0;
|
|
245
|
+
for (let y = 0; y < 7; y++) {
|
|
246
|
+
for (let x = 0; x < 7; x++) {
|
|
247
|
+
const bit = x === 0 ||
|
|
248
|
+
x === 6 ||
|
|
249
|
+
y === 0 ||
|
|
250
|
+
y === 6 ||
|
|
251
|
+
(x > 1 && x < 5 && y > 1 && y < 5)
|
|
252
|
+
? 1
|
|
253
|
+
: 0;
|
|
254
|
+
map[originY + y][originX + x] = bit;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
function addSeparatorPatterns(map, size) {
|
|
260
|
+
for (let i = 0; i < 8; i++) {
|
|
261
|
+
map[i][7] = 0;
|
|
262
|
+
map[i][size - 8] = 0;
|
|
263
|
+
map[size - 8 + i][7] = 0;
|
|
264
|
+
map[7][i] = 0;
|
|
265
|
+
map[7][size - 8 + i] = 0;
|
|
266
|
+
map[size - 8][i] = 0;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
function getAlignmentPatternLocations(version) {
|
|
270
|
+
if (version == 1) {
|
|
271
|
+
return [];
|
|
272
|
+
}
|
|
273
|
+
const alignCount = ~~(version / 7) + 2;
|
|
274
|
+
const step = version === 32
|
|
275
|
+
? 26
|
|
276
|
+
: Math.ceil((version * 4 + 4) / (alignCount * 2 - 2)) * 2;
|
|
277
|
+
const locations = [6];
|
|
278
|
+
for (let location = getSize(version) - 7; locations.length < alignCount; location -= step) {
|
|
279
|
+
locations.splice(1, 0, location);
|
|
280
|
+
}
|
|
281
|
+
return locations;
|
|
282
|
+
}
|
|
283
|
+
function addAlignmentPatterns(map, version) {
|
|
284
|
+
const locations = getAlignmentPatternLocations(version);
|
|
285
|
+
for (const locY of locations) {
|
|
286
|
+
for (const locX of locations) {
|
|
287
|
+
if (map[locY][locX] !== null) {
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
for (let y = 0; y < 5; y++) {
|
|
291
|
+
for (let x = 0; x < 5; x++) {
|
|
292
|
+
map[locY - 2 + y][locX - 2 + x] =
|
|
293
|
+
x === 0 || x === 4 || y === 0 || y === 4 || (x === 2 && y === 2)
|
|
294
|
+
? 1
|
|
295
|
+
: 0;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
function addTimingPatterns(map, size) {
|
|
302
|
+
const length = size - 16;
|
|
303
|
+
for (let i = 0; i < length; i++) {
|
|
304
|
+
const n = i % 2 === 0 ? 1 : 0;
|
|
305
|
+
map[6][8 + i] = n;
|
|
306
|
+
map[8 + i][6] = n;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
function addDarkPattern(map, size) {
|
|
310
|
+
map[size - 8][8] = 1;
|
|
311
|
+
}
|
|
312
|
+
function addFormatInformationPatterns(map, size, fillBit) {
|
|
313
|
+
for (let i = 0; i < 15; i++) {
|
|
314
|
+
const bit = fillBit(15 - 1 - i);
|
|
315
|
+
// 横条
|
|
316
|
+
map[8][i < 6 ? i : i < 7 ? i + 1 : size - 15 + i] = bit;
|
|
317
|
+
// 竖条
|
|
318
|
+
map[size - 1 - (i < 7 ? i : i < 9 ? size - 16 + i : size - 15 + i)][8] = bit;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
function addVersionInformationPatterns(map, size, fillBit) {
|
|
322
|
+
for (let i = 0; i < 18; i++) {
|
|
323
|
+
// 左下
|
|
324
|
+
map[size - 11 + (i % 3)][~~(i / 3)] = fillBit(i);
|
|
325
|
+
// 右上
|
|
326
|
+
map[~~(i / 3)][size - 11 + ~~(i % 3)] = fillBit(i);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
function placeDataBits(map, size, dataBits) {
|
|
330
|
+
const length = size * size;
|
|
331
|
+
let b = 0;
|
|
332
|
+
for (let i = 0; i < length; i++) {
|
|
333
|
+
const rest = ~~(i / 2) % size;
|
|
334
|
+
const quo = ~~(i / (size * 2));
|
|
335
|
+
const y = quo % 2 === 0 ? size - 1 - rest : rest;
|
|
336
|
+
let x = size - 2 - (quo * 2 - (i % 2 === 0 ? 1 : 0));
|
|
337
|
+
if (x < 7) {
|
|
338
|
+
x--;
|
|
339
|
+
}
|
|
340
|
+
if (map[y][x] === null) {
|
|
341
|
+
map[y][x] = dataBits[b++] + 2;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
const maskModes = [
|
|
346
|
+
(row, col) => (row + col) % 2 === 0,
|
|
347
|
+
(row) => row % 2 === 0,
|
|
348
|
+
(_, col) => col % 3 === 0,
|
|
349
|
+
(row, col) => (row + col) % 3 === 0,
|
|
350
|
+
(row, col) => (~~(row / 2) + ~~(col / 3)) % 2 === 0,
|
|
351
|
+
(row, col) => ((row * col) % 2) + ((row * col) % 3) === 0,
|
|
352
|
+
(row, col) => (((row * col) % 2) + ((row * col) % 3)) % 2 === 0,
|
|
353
|
+
(row, col) => (((row + col) % 2) + ((row * col) % 3)) % 2 === 0,
|
|
354
|
+
];
|
|
355
|
+
function getLeastFiveSameColoredPenalty(getEach, size) {
|
|
356
|
+
let score = 0;
|
|
357
|
+
for (let i = 0; i < size; i++) {
|
|
358
|
+
let sum = 0;
|
|
359
|
+
let last = -1;
|
|
360
|
+
for (let j = 0; j < size; j++) {
|
|
361
|
+
const bit = getEach(i, j);
|
|
362
|
+
if (sum === 0 || last === bit) {
|
|
363
|
+
sum++;
|
|
364
|
+
last = bit;
|
|
365
|
+
}
|
|
366
|
+
if (last !== bit || j === size - 1) {
|
|
367
|
+
if (sum >= 5) {
|
|
368
|
+
score += sum - 2;
|
|
369
|
+
}
|
|
370
|
+
sum = 1;
|
|
371
|
+
last = bit;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
return score;
|
|
376
|
+
}
|
|
377
|
+
function getTwoByTwoSameColoredPenalty(map, size) {
|
|
378
|
+
let score = 0;
|
|
379
|
+
for (let y = 0; y < size - 2; y++) {
|
|
380
|
+
for (let x = 0; x < size - 2; x++) {
|
|
381
|
+
if ((map[y][x] + map[y][x + 1] + map[y + 1][x] + map[y + 1][x + 1]) % 4 ===
|
|
382
|
+
0) {
|
|
383
|
+
score += 3;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
return score;
|
|
388
|
+
}
|
|
389
|
+
function getFinderLikePenalty(map, size) {
|
|
390
|
+
let score = 0;
|
|
391
|
+
for (let i = 0; i < size - 11; i++) {
|
|
392
|
+
for (let j = 0; j < size - 11; j++) {
|
|
393
|
+
let rowBits = 0;
|
|
394
|
+
let colBits = 0;
|
|
395
|
+
for (let k = 0; k < 11; k++) {
|
|
396
|
+
rowBits = (rowBits << 1) + map[i][j + k];
|
|
397
|
+
colBits = (colBits << 1) + map[j + k][i];
|
|
398
|
+
}
|
|
399
|
+
if (rowBits === 0b10111010000 || rowBits === 0b00001011101) {
|
|
400
|
+
score += 40;
|
|
401
|
+
}
|
|
402
|
+
if (colBits === 0b10111010000 || colBits === 0b00001011101) {
|
|
403
|
+
score += 40;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
return score;
|
|
408
|
+
}
|
|
409
|
+
function getBalancePenalty(map) {
|
|
410
|
+
const darkModules = map.reduce((sum, row) => sum + row.reduce((sum, bit) => sum + bit, 0), 0);
|
|
411
|
+
const percent = (darkModules / (map.length * map.length)) * 100;
|
|
412
|
+
const score = Math.min(Math.abs(Math.floor(percent / 5) * 5 - 50) / 5, Math.abs(Math.ceil(percent / 5) * 5 - 50) / 5) * 10;
|
|
413
|
+
return score;
|
|
414
|
+
}
|
|
415
|
+
const errorCorrectionBit = [0b01, 0b00, 0b11, 0b10];
|
|
416
|
+
function getFormatInformationBits(eclIndex, maskNum) {
|
|
417
|
+
const ecMaskBits = (errorCorrectionBit[eclIndex] << 3) | maskNum;
|
|
418
|
+
let rest = ecMaskBits;
|
|
419
|
+
for (let i = 0; i < 10; i++) {
|
|
420
|
+
rest = (rest << 1) ^ ((rest >>> 9) * 0b10100110111);
|
|
421
|
+
}
|
|
422
|
+
return ((ecMaskBits << 10) | rest) ^ 0b101010000010010;
|
|
423
|
+
}
|
|
424
|
+
function getVersionInformationBits(version) {
|
|
425
|
+
let rest = version;
|
|
426
|
+
for (let i = 0; i < 12; i++) {
|
|
427
|
+
rest = (rest << 1) ^ ((rest >>> 11) * 0b1111100100101);
|
|
428
|
+
}
|
|
429
|
+
return (version << 12) | rest;
|
|
430
|
+
}
|
|
431
|
+
export const ECLList = ['L', 'M', 'Q', 'H'];
|
|
432
|
+
export function qrcode(text, options = {}) {
|
|
433
|
+
const { ecl = 'M' } = options;
|
|
434
|
+
let eclIndex = ECLList.indexOf(ecl);
|
|
435
|
+
if (eclIndex < 0) {
|
|
436
|
+
eclIndex = 1;
|
|
437
|
+
}
|
|
438
|
+
const mode = getMode(text);
|
|
439
|
+
const modeIndicator = getModeIndicator(mode);
|
|
440
|
+
let version = 0;
|
|
441
|
+
let size = 0;
|
|
442
|
+
let dataBits = [];
|
|
443
|
+
const groups = [];
|
|
444
|
+
let map = [];
|
|
445
|
+
let maskNum = 0;
|
|
446
|
+
let maskMap = [];
|
|
447
|
+
// 数据编码
|
|
448
|
+
{
|
|
449
|
+
// 使用选定模式进行编码
|
|
450
|
+
dataBits = encodeData(mode, text);
|
|
451
|
+
const codewords = dataBits.length / 8;
|
|
452
|
+
const charLength = mode === Mode.Byte ? codewords : text.length;
|
|
453
|
+
// 确定数据的最小版本
|
|
454
|
+
version = getVersion(mode, eclIndex, codewords);
|
|
455
|
+
size = getSize(version);
|
|
456
|
+
// 添加模式指示器、字符计数指示器
|
|
457
|
+
dataBits = [
|
|
458
|
+
...appendBits(modeIndicator, 4, []),
|
|
459
|
+
...appendBits(charLength, getCharCountIndicator(mode, version), []),
|
|
460
|
+
...dataBits,
|
|
461
|
+
];
|
|
462
|
+
// 确定此QR码所需的数据位数
|
|
463
|
+
const rawDataBits = getRawDataCodewords(eclIndex, version) * 8;
|
|
464
|
+
// 如有必要,添加0结束符
|
|
465
|
+
appendBits(0, Math.min(rawDataBits - dataBits.length, 4), dataBits);
|
|
466
|
+
// 添加更多的0使长度成为8的倍数
|
|
467
|
+
appendBits(0, (8 - (dataBits.length % 8)) % 8, dataBits);
|
|
468
|
+
// 如果字符串仍然太短,添加填充字节
|
|
469
|
+
for (let padByte = 0b11101100; dataBits.length < rawDataBits; padByte ^= 0b11101100 ^ 0b00010001) {
|
|
470
|
+
appendBits(padByte, 8, dataBits);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
// 纠错编码
|
|
474
|
+
{
|
|
475
|
+
const codewords = [];
|
|
476
|
+
dataBits.forEach((bit, i) => {
|
|
477
|
+
codewords[i >> 3] |= bit << (7 - (i & 7));
|
|
478
|
+
});
|
|
479
|
+
const blocks = ECBlocks[eclIndex][version - 1];
|
|
480
|
+
const g2Blocks = ~~(getTotalDataModules(version) / 8) % blocks;
|
|
481
|
+
const g1Blocks = blocks - g2Blocks;
|
|
482
|
+
const totalDC = getRawDataCodewords(eclIndex, version);
|
|
483
|
+
const g1DCPerBlock = ~~(totalDC / blocks);
|
|
484
|
+
const g2DCPerBlock = g2Blocks === 0 ? 0 : (totalDC - g1DCPerBlock * g1Blocks) / g2Blocks;
|
|
485
|
+
const eccNum = ECCodewordsPerBlock[eclIndex][version - 1];
|
|
486
|
+
let currentIndex = 0;
|
|
487
|
+
for (const [blocks, DCPerBlock] of [
|
|
488
|
+
[g1Blocks, g1DCPerBlock],
|
|
489
|
+
[g2Blocks, g2DCPerBlock],
|
|
490
|
+
]) {
|
|
491
|
+
for (let b = 0; b < blocks; b++) {
|
|
492
|
+
const dataCodewords = new Uint8Array(codewords.slice(currentIndex, currentIndex + DCPerBlock));
|
|
493
|
+
groups.push({
|
|
494
|
+
data: dataCodewords,
|
|
495
|
+
ecc: getECC(dataCodewords, eccNum),
|
|
496
|
+
});
|
|
497
|
+
currentIndex += DCPerBlock;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
// 构造最后信息
|
|
502
|
+
{
|
|
503
|
+
// 将块进行交叉
|
|
504
|
+
const dataCodewords = [];
|
|
505
|
+
const ecCodewords = [];
|
|
506
|
+
let i = 0;
|
|
507
|
+
while (true) {
|
|
508
|
+
const result = groups.map((block) => {
|
|
509
|
+
const dataCodeWord = block.data[i];
|
|
510
|
+
if (dataCodeWord !== undefined) {
|
|
511
|
+
dataCodewords.push(dataCodeWord);
|
|
512
|
+
}
|
|
513
|
+
const ecCodeWord = block.ecc[i];
|
|
514
|
+
if (ecCodeWord !== undefined) {
|
|
515
|
+
ecCodewords.push(ecCodeWord);
|
|
516
|
+
}
|
|
517
|
+
return dataCodeWord === undefined && ecCodeWord === undefined;
|
|
518
|
+
});
|
|
519
|
+
if (result.every((done) => done)) {
|
|
520
|
+
break;
|
|
521
|
+
}
|
|
522
|
+
i++;
|
|
523
|
+
}
|
|
524
|
+
// 将交错纠错码字放在交错数据码字之后
|
|
525
|
+
const interleavedCodewords = dataCodewords.concat(ecCodewords);
|
|
526
|
+
dataBits = [];
|
|
527
|
+
interleavedCodewords.forEach((codewords) => {
|
|
528
|
+
appendBits(codewords, 8, dataBits);
|
|
529
|
+
});
|
|
530
|
+
// 如果有必要,添加剩余位
|
|
531
|
+
appendBits(0, getTotalDataModules(version) - dataBits.length, dataBits);
|
|
532
|
+
}
|
|
533
|
+
// 模块在矩阵中的放置
|
|
534
|
+
{
|
|
535
|
+
map = Array(size)
|
|
536
|
+
.fill(null)
|
|
537
|
+
.map(() => Array(size).fill(null));
|
|
538
|
+
// 添加查找器图案
|
|
539
|
+
addFinderPatterns(map, size);
|
|
540
|
+
// 添加分隔符
|
|
541
|
+
addSeparatorPatterns(map, size);
|
|
542
|
+
// 添加对齐图案
|
|
543
|
+
addAlignmentPatterns(map, version);
|
|
544
|
+
// 添加时序图案
|
|
545
|
+
addTimingPatterns(map, size);
|
|
546
|
+
// 添加黑暗图案
|
|
547
|
+
addDarkPattern(map, size);
|
|
548
|
+
// 添加格式信息占位图案
|
|
549
|
+
addFormatInformationPatterns(map, size, () => 0);
|
|
550
|
+
// 添加版本信息占位图案
|
|
551
|
+
if (version >= 7) {
|
|
552
|
+
addVersionInformationPatterns(map, size, () => 0);
|
|
553
|
+
}
|
|
554
|
+
// 放置数据位
|
|
555
|
+
placeDataBits(map, size, dataBits);
|
|
556
|
+
}
|
|
557
|
+
// 数据掩码
|
|
558
|
+
{
|
|
559
|
+
let lowerScore = Infinity;
|
|
560
|
+
for (let i = 0; i < 8; i++) {
|
|
561
|
+
const maskMode = maskModes[i];
|
|
562
|
+
// 生成掩码图案
|
|
563
|
+
const seedMap = map.map((row, rowIndex) => row.map((col, colIndex) => {
|
|
564
|
+
if (col < 2) {
|
|
565
|
+
return col;
|
|
566
|
+
}
|
|
567
|
+
col = col - 2;
|
|
568
|
+
return maskMode(rowIndex, colIndex) ? col ^ 1 : col;
|
|
569
|
+
}));
|
|
570
|
+
// 计算罚分
|
|
571
|
+
let score = 0;
|
|
572
|
+
// 评估条件1
|
|
573
|
+
// 行
|
|
574
|
+
score += getLeastFiveSameColoredPenalty((i, j) => seedMap[i][j], size);
|
|
575
|
+
// 列
|
|
576
|
+
score += getLeastFiveSameColoredPenalty((i, j) => seedMap[j][i], size);
|
|
577
|
+
// 评估条件2
|
|
578
|
+
score += getTwoByTwoSameColoredPenalty(seedMap, size);
|
|
579
|
+
// 评估条件3
|
|
580
|
+
score += getFinderLikePenalty(seedMap, size);
|
|
581
|
+
// 评估条件4
|
|
582
|
+
score += getBalancePenalty(seedMap);
|
|
583
|
+
// 选择八种掩码图案的最低罚分
|
|
584
|
+
if (score < lowerScore) {
|
|
585
|
+
lowerScore = score;
|
|
586
|
+
maskNum = i;
|
|
587
|
+
maskMap = seedMap;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
// 格式和版本信息
|
|
592
|
+
{
|
|
593
|
+
// 添加格式信息图案
|
|
594
|
+
const formatBits = getFormatInformationBits(eclIndex, maskNum);
|
|
595
|
+
addFormatInformationPatterns(map, size, (i) => (formatBits >> i) & 1);
|
|
596
|
+
{
|
|
597
|
+
// 添加版本信息图案
|
|
598
|
+
if (version > 6) {
|
|
599
|
+
const versionBits = getVersionInformationBits(version);
|
|
600
|
+
addVersionInformationPatterns(map, size, (i) => (versionBits >> i) & 1);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
// 输出最终矩阵
|
|
605
|
+
{
|
|
606
|
+
maskMap.forEach((row, y) => {
|
|
607
|
+
row.forEach((col, x) => {
|
|
608
|
+
const mapCol = map[y][x];
|
|
609
|
+
if (mapCol !== 0 && mapCol !== 1) {
|
|
610
|
+
map[y][x] = col;
|
|
611
|
+
}
|
|
612
|
+
});
|
|
613
|
+
});
|
|
614
|
+
return map;
|
|
615
|
+
}
|
|
616
|
+
}
|