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 CHANGED
@@ -1,3 +1,7 @@
1
+ # [1.4.0-beta](https://github.com/sutras/sard-uniapp/compare/v1.3.0...v1.4.0-beta) (2024-08-01)
2
+
3
+
4
+
1
5
  # [1.3.0](https://github.com/sutras/sard-uniapp/compare/v1.2.2...v1.3.0) (2024-07-16)
2
6
 
3
7
 
@@ -30,9 +30,9 @@ export declare const defaultOptionKeys: {
30
30
  label: string;
31
31
  value: string;
32
32
  };
33
- export interface CheckboxGroupOption {
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 interface CheckboxInputOption {
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,2 @@
1
+ import { defaultConfig } from '../config';
2
+ export const qrcodePropsDefaults = defaultConfig.qrcode;
@@ -0,0 +1 @@
1
+ export type { QrcodeProps } from './common';
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,17 @@
1
+ @use '../style/base' as *;
2
+
3
+ @include bem(qrcode) {
4
+ @include b() {
5
+ @include universal;
6
+ }
7
+
8
+ @include e(canvas) {
9
+ display: none;
10
+ }
11
+
12
+ @include e(image) {
13
+ @include universal;
14
+ width: 100%;
15
+ height: 100%;
16
+ }
17
+ }
@@ -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>
@@ -0,0 +1,4 @@
1
+ // #variables
2
+ // page {
3
+ // }
4
+ // #endvariables
@@ -24,9 +24,9 @@ export declare const defaultOptionKeys: {
24
24
  label: string;
25
25
  value: string;
26
26
  };
27
- export interface RadioGroupOption {
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 interface RadioInputOption {
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 '@vue/runtime-core' {
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.3.0",
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
@@ -6,3 +6,4 @@ export * from './file';
6
6
  export * from './is';
7
7
  export * from './utils';
8
8
  export * from './lwa.slim';
9
+ export * from './qrcode';
package/utils/index.js CHANGED
@@ -6,3 +6,4 @@ export * from './file';
6
6
  export * from './is';
7
7
  export * from './utils';
8
8
  export * from './lwa.slim';
9
+ export * from './qrcode';
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 {};
@@ -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
+ }