@tdesign/uniapp 0.7.2 → 0.8.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.
Files changed (199) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +32 -2
  3. package/dist/action-sheet/README.md +1 -1
  4. package/dist/action-sheet/action-sheet.vue +158 -150
  5. package/dist/action-sheet/props.ts +2 -2
  6. package/dist/action-sheet/type.ts +1 -1
  7. package/dist/avatar/avatar.vue +89 -87
  8. package/dist/avatar-group/avatar-group.vue +69 -67
  9. package/dist/back-top/back-top.vue +60 -58
  10. package/dist/badge/badge.vue +69 -59
  11. package/dist/button/button.vue +121 -116
  12. package/dist/button/props.ts +2 -2
  13. package/dist/button/type.ts +1 -1
  14. package/dist/calendar/calendar-header.vue +4 -4
  15. package/dist/calendar/calendar.vue +308 -297
  16. package/dist/calendar/template.vue +1 -1
  17. package/dist/cascader/README.en-US.md +2 -1
  18. package/dist/cascader/README.md +2 -1
  19. package/dist/cascader/cascader.vue +340 -328
  20. package/dist/cascader/props.ts +6 -1
  21. package/dist/cascader/type.ts +6 -0
  22. package/dist/cell/cell.vue +127 -121
  23. package/dist/cell-group/cell-group.vue +32 -30
  24. package/dist/check-tag/check-tag.vue +73 -71
  25. package/dist/checkbox/README.en-US.md +6 -6
  26. package/dist/checkbox/README.md +5 -5
  27. package/dist/checkbox/checkbox.vue +127 -127
  28. package/dist/checkbox/props.ts +6 -6
  29. package/dist/checkbox/type.ts +6 -4
  30. package/dist/checkbox-group/checkbox-group.vue +175 -173
  31. package/dist/checkbox-group/props.ts +6 -6
  32. package/dist/checkbox-group/type.ts +6 -4
  33. package/dist/col/col.vue +26 -24
  34. package/dist/collapse/collapse.vue +83 -81
  35. package/dist/collapse-panel/collapse-panel.vue +121 -119
  36. package/dist/collapse-panel/props.ts +4 -4
  37. package/dist/collapse-panel/type.ts +2 -2
  38. package/dist/color-picker/README.md +1 -1
  39. package/dist/color-picker/color-picker.vue +324 -322
  40. package/dist/color-picker/props.ts +2 -2
  41. package/dist/color-picker/template.vue +14 -10
  42. package/dist/common/common.ts +1 -0
  43. package/dist/common/style/theme/index.css +57 -61
  44. package/dist/common/utils.js +7 -2
  45. package/dist/common/validator.js +172 -0
  46. package/dist/config-provider/README.en-US.md +184 -0
  47. package/dist/config-provider/README.md +234 -0
  48. package/dist/config-provider/config-provider.vue +105 -0
  49. package/dist/config-provider/config-store.js +70 -0
  50. package/dist/config-provider/props.ts +16 -0
  51. package/dist/config-provider/reactive-state.js +39 -0
  52. package/dist/config-provider/type.ts +401 -0
  53. package/dist/config-provider/use-config.js +29 -0
  54. package/dist/config-provider/utils.js +29 -0
  55. package/dist/count-down/count-down.vue +98 -97
  56. package/dist/date-time-picker/date-time-picker.vue +410 -395
  57. package/dist/demo/demo.vue +1 -0
  58. package/dist/dialog/dialog.vue +176 -173
  59. package/dist/divider/divider.vue +38 -36
  60. package/dist/draggable/draggable.vue +60 -58
  61. package/dist/drawer/README.md +1 -1
  62. package/dist/drawer/drawer.vue +48 -46
  63. package/dist/dropdown-item/dropdown-item.vue +209 -207
  64. package/dist/dropdown-item/props.ts +4 -4
  65. package/dist/dropdown-item/type.ts +3 -3
  66. package/dist/dropdown-menu/dropdown-menu.vue +93 -99
  67. package/dist/empty/empty.vue +43 -42
  68. package/dist/fab/fab.vue +88 -86
  69. package/dist/footer/footer.vue +36 -34
  70. package/dist/form/README.en-US.md +17 -24
  71. package/dist/form/README.md +18 -25
  72. package/dist/form/form.css +1 -166
  73. package/dist/form/form.vue +251 -236
  74. package/dist/form/props.ts +2 -21
  75. package/dist/form/type.ts +7 -70
  76. package/dist/form-item/README.en-US.md +4 -5
  77. package/dist/form-item/README.md +4 -5
  78. package/dist/form-item/form-item.css +69 -96
  79. package/dist/form-item/form-item.vue +315 -336
  80. package/dist/form-item/form-model.ts +125 -173
  81. package/dist/form-item/props.ts +4 -17
  82. package/dist/form-item/type.ts +43 -1
  83. package/dist/grid/grid.vue +53 -51
  84. package/dist/grid-item/grid-item.vue +121 -119
  85. package/dist/guide/README.md +1 -1
  86. package/dist/guide/guide.vue +281 -277
  87. package/dist/icon/README.md +2 -4
  88. package/dist/icon/icon.vue +78 -76
  89. package/dist/image/README.md +1 -1
  90. package/dist/image/image.vue +103 -101
  91. package/dist/image-viewer/image-viewer.vue +160 -158
  92. package/dist/image-viewer/props.ts +2 -2
  93. package/dist/image-viewer/type.ts +1 -1
  94. package/dist/index.js +3 -0
  95. package/dist/indexes/indexes.vue +264 -267
  96. package/dist/indexes-anchor/indexes-anchor.vue +41 -41
  97. package/dist/input/input.vue +192 -192
  98. package/dist/input/props.ts +6 -6
  99. package/dist/input/type.ts +3 -3
  100. package/dist/link/link.vue +73 -71
  101. package/dist/loading/loading.vue +59 -59
  102. package/dist/locale/ar_KW.ts +157 -0
  103. package/dist/locale/en_US.ts +146 -0
  104. package/dist/locale/it_IT.ts +145 -0
  105. package/dist/locale/ja_JP.ts +132 -0
  106. package/dist/locale/ko_KR.ts +132 -0
  107. package/dist/locale/ru_RU.ts +157 -0
  108. package/dist/locale/zh_CN.ts +133 -0
  109. package/dist/locale/zh_TW.ts +132 -0
  110. package/dist/message/message.vue +181 -173
  111. package/dist/message/props.ts +2 -2
  112. package/dist/message/type.ts +1 -1
  113. package/dist/message-item/message-item.vue +192 -184
  114. package/dist/mixins/using-config.js +39 -0
  115. package/dist/navbar/navbar.vue +201 -199
  116. package/dist/notice-bar/notice-bar.vue +175 -171
  117. package/dist/notice-bar/props.ts +2 -2
  118. package/dist/notice-bar/type.ts +1 -1
  119. package/dist/npm/dayjs/esm/locale/ar.js +81 -0
  120. package/dist/npm/dayjs/esm/locale/it.js +39 -0
  121. package/dist/overlay/overlay.vue +50 -48
  122. package/dist/picker/picker.vue +168 -161
  123. package/dist/picker-item/picker-item.vue +269 -269
  124. package/dist/popover/README.md +1 -1
  125. package/dist/popover/popover.vue +262 -261
  126. package/dist/popover/props.ts +4 -4
  127. package/dist/popover/type.ts +2 -2
  128. package/dist/popup/popup.vue +46 -45
  129. package/dist/progress/README.md +1 -1
  130. package/dist/progress/progress.vue +76 -76
  131. package/dist/pull-down-refresh/props.ts +2 -2
  132. package/dist/pull-down-refresh/pull-down-refresh.vue +240 -234
  133. package/dist/pull-down-refresh/type.ts +1 -1
  134. package/dist/qrcode/README.md +1 -1
  135. package/dist/qrcode/components/qrcode-canvas/qrcode-canvas.vue +340 -338
  136. package/dist/qrcode/components/qrcode-status/qrcode-status.vue +6 -6
  137. package/dist/qrcode/qrcode.vue +93 -87
  138. package/dist/radio/props.ts +6 -6
  139. package/dist/radio/radio.vue +118 -120
  140. package/dist/radio/type.ts +3 -3
  141. package/dist/radio-group/props.ts +4 -4
  142. package/dist/radio-group/radio-group.vue +136 -134
  143. package/dist/radio-group/type.ts +4 -4
  144. package/dist/rate/computed.js +2 -2
  145. package/dist/rate/props.ts +4 -4
  146. package/dist/rate/rate.vue +155 -154
  147. package/dist/rate/type.ts +2 -2
  148. package/dist/result/result.vue +41 -39
  149. package/dist/row/row.vue +38 -36
  150. package/dist/scroll-view/scroll-view.vue +24 -22
  151. package/dist/search/props.ts +2 -2
  152. package/dist/search/search.vue +127 -125
  153. package/dist/search/type.ts +1 -1
  154. package/dist/side-bar/side-bar.vue +57 -55
  155. package/dist/side-bar-item/side-bar-item.vue +86 -86
  156. package/dist/skeleton/skeleton.vue +126 -124
  157. package/dist/slider/README.md +1 -1
  158. package/dist/slider/props.ts +2 -2
  159. package/dist/slider/slider.vue +457 -457
  160. package/dist/slider/type.ts +1 -1
  161. package/dist/step-item/step-item.vue +77 -75
  162. package/dist/stepper/props.ts +2 -2
  163. package/dist/stepper/stepper.vue +168 -149
  164. package/dist/stepper/type.ts +1 -1
  165. package/dist/steps/props.ts +2 -2
  166. package/dist/steps/steps.vue +83 -81
  167. package/dist/steps/type.ts +1 -1
  168. package/dist/sticky/sticky.vue +104 -102
  169. package/dist/swipe-cell/swipe-cell.vue +91 -89
  170. package/dist/swiper/README.md +1 -1
  171. package/dist/swiper/swiper.vue +91 -89
  172. package/dist/swiper-nav/swiper-nav.vue +38 -36
  173. package/dist/switch/props.ts +2 -2
  174. package/dist/switch/switch.vue +62 -62
  175. package/dist/switch/type.ts +1 -1
  176. package/dist/tab-bar/tab-bar.vue +88 -86
  177. package/dist/tab-bar-item/tab-bar-item.vue +85 -82
  178. package/dist/tab-panel/tab-panel.vue +66 -64
  179. package/dist/tabs/tabs.vue +294 -287
  180. package/dist/tag/tag.vue +79 -77
  181. package/dist/textarea/props.ts +6 -6
  182. package/dist/textarea/textarea.vue +127 -126
  183. package/dist/textarea/type.ts +3 -3
  184. package/dist/toast/toast.vue +107 -106
  185. package/dist/transition/transition.vue +30 -28
  186. package/dist/tree-select/tree-select.vue +151 -151
  187. package/dist/types/config-provider.d.ts +7 -0
  188. package/dist/types/index.d.ts +2 -0
  189. package/dist/types/popover.d.ts +7 -0
  190. package/dist/upload/README.en-US.md +13 -14
  191. package/dist/upload/README.md +11 -12
  192. package/dist/upload/props.ts +2 -4
  193. package/dist/upload/type.ts +12 -11
  194. package/dist/upload/upload.css +1 -1
  195. package/dist/upload/upload.vue +672 -512
  196. package/dist/watermark/watermark.vue +151 -149
  197. package/global.d.ts +2 -0
  198. package/package.json +16 -2
  199. package/dist/form/form-item-props.ts +0 -56
@@ -9,7 +9,7 @@
9
9
  type="2d"
10
10
  :canvas-id="canvasId"
11
11
  class="t-qrcode__canvas"
12
- :style="`width: ${size}px; height: ${size}px;`"
12
+ :style="'' + `width: ${size}px; height: ${size}px;`"
13
13
  />
14
14
  </view>
15
15
  </template>
@@ -23,389 +23,391 @@ import { prefix } from '../../../common/config';
23
23
  import { loadImage } from '../../../common/canvas/index';
24
24
  import { getWindowInfo, nextTick } from '../../../common/utils';
25
25
 
26
- export default uniComponent({
27
- name: 'QrcodeCanvas',
28
- options: {
29
- styleIsolation: 'shared',
30
- },
31
- externalClasses: [
32
- `${prefix}-class`,
33
- ],
34
- props: {
35
- ...props,
36
- },
37
- emits: ['drawCompleted', 'drawError'],
38
- data() {
39
- return {
40
- canvas: null,
41
- ctx: null,
42
- canvasId: `qrcode-canvas-${Math.random().toString(36)
43
- .slice(2, 11)}`,
44
- isWeb: false,
45
- };
46
- },
47
- computed: {
48
- // 使用计算属性确保有默认值
49
- actualBgColor() {
50
- return this.bgColor || '#FFFFFF';
26
+ export default {
27
+ ...uniComponent({
28
+ name: 'QrcodeCanvas',
29
+ options: {
30
+ styleIsolation: 'shared',
51
31
  },
52
- actualColor() {
53
- return this.color || '#000000';
32
+ externalClasses: [
33
+ `${prefix}-class`,
34
+ ],
35
+ props: {
36
+ ...props,
54
37
  },
55
- },
56
- watch: {
57
- value() {
58
- this.renderQRCode();
38
+ emits: ['drawCompleted', 'drawError'],
39
+ data() {
40
+ return {
41
+ canvas: null,
42
+ ctx: null,
43
+ canvasId: `qrcode-canvas-${Math.random().toString(36)
44
+ .slice(2, 11)}`,
45
+ isWeb: false,
46
+ };
59
47
  },
60
- icon() {
61
- this.renderQRCode();
48
+ computed: {
49
+ // 使用计算属性确保有默认值
50
+ actualBgColor() {
51
+ return this.bgColor || '#FFFFFF';
52
+ },
53
+ actualColor() {
54
+ return this.color || '#000000';
55
+ },
62
56
  },
63
- size() {
64
- let interval = 0;
65
- // #ifdef APP-PLUS
66
- interval = 33;
67
- // #endif
68
- setTimeout(() => {
57
+ watch: {
58
+ value() {
69
59
  this.renderQRCode();
70
- }, interval);
71
- },
72
- iconSize() {
73
- this.renderQRCode();
74
- },
75
- level() {
76
- this.renderQRCode();
77
- },
78
- bgColor() {
79
- this.renderQRCode();
80
- },
81
- color() {
82
- this.renderQRCode();
60
+ },
61
+ icon() {
62
+ this.renderQRCode();
63
+ },
64
+ size() {
65
+ let interval = 0;
66
+ // #ifdef APP-PLUS
67
+ interval = 33;
68
+ // #endif
69
+ setTimeout(() => {
70
+ this.renderQRCode();
71
+ }, interval);
72
+ },
73
+ iconSize() {
74
+ this.renderQRCode();
75
+ },
76
+ level() {
77
+ this.renderQRCode();
78
+ },
79
+ bgColor() {
80
+ this.renderQRCode();
81
+ },
82
+ color() {
83
+ this.renderQRCode();
84
+ },
83
85
  },
84
- },
85
- created() {
86
+ created() {
86
87
  // 小程序不能使用响应式的this.canvas
87
- this._canvas = null;
88
- this._ctx = null;
89
- },
90
- mounted() {
88
+ this._canvas = null;
89
+ this._ctx = null;
90
+ },
91
+ mounted() {
91
92
  // 判断是否为小程序环境,否则默认为 H5
92
93
  // #ifdef MP
93
- this.isWeb = false;
94
- // #endif
95
-
96
- // #ifndef MP
97
- this.isWeb = true;
98
- // #endif
99
-
100
- this.initCanvas();
101
- },
102
- methods: {
103
- async initCanvas() {
104
- await nextTick();
105
-
106
- // #ifndef H5
107
- this.initMiniProgramCanvas();
94
+ this.isWeb = false;
108
95
  // #endif
109
96
 
110
- // #ifdef H5
111
- this.initH5Canvas();
97
+ // #ifndef MP
98
+ this.isWeb = true;
112
99
  // #endif
113
- },
114
-
115
- // H5 环境初始化
116
- async initH5Canvas() {
117
- // 在 uniapp H5 环境中,canvas 会被包裹在 uni-canvas 内
118
- const uniCanvasElement = document.querySelector(`#${this.canvasId}`);
119
- let canvasElement = null;
120
-
121
- // 如果获取到的是 uni-canvas,需要找到内部的 canvas
122
- if (uniCanvasElement && uniCanvasElement.tagName === 'UNI-CANVAS') {
123
- canvasElement = uniCanvasElement.querySelector('canvas');
124
-
125
- // 设置 uni-canvas 的样式
126
- uniCanvasElement.style.width = `${this.size}px`;
127
- uniCanvasElement.style.height = `${this.size}px`;
128
- uniCanvasElement.style.overflow = 'visible';
129
-
130
- // 设置 wrapper 的样式
131
- const wrapper = uniCanvasElement.parentElement;
132
- if (wrapper) {
133
- wrapper.style.width = `${this.size}px`;
134
- wrapper.style.height = `${this.size}px`;
135
- wrapper.style.overflow = 'visible';
136
- }
137
- } else {
138
- canvasElement = uniCanvasElement;
139
- }
140
-
141
- if (canvasElement) {
142
- // 在初始化时设置 Canvas 的物理尺寸和显示尺寸
143
- const pixelRatio = window.devicePixelRatio || 1;
144
- const canvasSize = this.size * pixelRatio;
145
-
146
- // 设置物理尺寸(实际像素)
147
- canvasElement.width = canvasSize;
148
- canvasElement.height = canvasSize;
149
-
150
- // 设置显示尺寸(CSS 像素)
151
- canvasElement.style.width = `${this.size}px`;
152
- canvasElement.style.height = `${this.size}px`;
153
-
154
- // 添加 willReadFrequently 属性以优化性能并消除警告
155
- const ctx = canvasElement.getContext('2d', { willReadFrequently: true });
156
- this.canvas = canvasElement;
157
- this.ctx = ctx;
158
- await this.renderQRCode();
159
- } else {
160
- console.error('无法获取 canvas 元素');
161
- }
162
- },
163
-
164
- // 小程序环境初始化
165
- async initMiniProgramCanvas() {
166
- if (typeof uni !== 'undefined' && uni.createSelectorQuery) {
167
- const query = uni.createSelectorQuery().in(this);
168
- query
169
- .select(`#${this.canvasId}`)
170
- .fields({ node: true, size: true })
171
- .exec(async (res) => {
172
- if (!res || !res[0] || !res[0].node) {
173
- console.error('获取 canvas 节点失败');
174
- return;
175
- }
176
-
177
- const canvas = res[0].node;
178
- // 小程序环境也添加 willReadFrequently 属性
179
- try {
180
- let ctx;
181
- // #ifdef MP
182
- ctx = canvas.getContext('2d', { willReadFrequently: true });
183
- // #endif
184
- if (!ctx) {
185
- ctx = uni.createCanvasContext(this.canvasId, this);
186
- }
187
- this._canvas = canvas;
188
- this._ctx = ctx;
189
- } catch (e) {
190
- console.warn('获取 ctx 失败', e);
191
- }
192
100
 
193
-
194
- await this.renderQRCode();
195
- });
196
- }
101
+ this.initCanvas();
197
102
  },
198
-
199
- async renderQRCode() {
200
- const canvas = this._canvas || this.canvas;
201
- const ctx = this._ctx || this.ctx;
202
- if (!canvas || !ctx) {
203
- return;
204
- }
205
-
206
- const sizeProp = this.getSizeProp(this.iconSize);
207
-
208
- try {
209
- const qrData = useQRCode({
210
- value: this.value,
211
- level: this.level,
212
- minVersion: DEFAULT_MINVERSION,
213
- includeMargin: this.includeMargin,
214
- marginSize: this.marginSize,
215
- size: this.size,
216
- imageSettings: this.icon
217
- ? {
218
- src: this.icon,
219
- width: sizeProp.width,
220
- height: sizeProp.height,
221
- excavate: true,
222
- }
223
- : undefined,
224
- });
225
-
226
- // 获取设备像素比
227
- let pixelRatio = 1;
228
- let canvasSize;
229
- let scale;
103
+ methods: {
104
+ async initCanvas() {
105
+ await nextTick();
230
106
 
231
107
  // #ifndef H5
232
- // 小程序环境:获取真实的设备像素比并设置 Canvas 尺寸
233
- // 使用 getWindowInfo 替代已废弃的 getSystemInfoSync
234
- const windowInfo = getWindowInfo();
235
- pixelRatio = windowInfo.pixelRatio || 1;
236
- canvasSize = this.size * pixelRatio;
237
- canvas.width = canvasSize;
238
- canvas.height = canvasSize;
239
- // 小程序环境:scale 计算方式(参考 TS 实现)
240
- scale = canvasSize / qrData.numCells;
241
- // #ifdef APP-PLUS
242
- scale /= pixelRatio;
243
- // #endif
108
+ this.initMiniProgramCanvas();
244
109
  // #endif
245
110
 
246
111
  // #ifdef H5
247
- // H5 环境:每次渲染时重新设置 Canvas 尺寸(因为 size 可能变化)
248
- pixelRatio = window.devicePixelRatio || 1;
249
- canvasSize = this.size * pixelRatio;
112
+ this.initH5Canvas();
113
+ // #endif
114
+ },
250
115
 
251
- // 重新设置 Canvas 物理尺寸(会重置 canvas 状态)
252
- canvas.width = canvasSize;
253
- canvas.height = canvasSize;
116
+ // H5 环境初始化
117
+ async initH5Canvas() {
118
+ // 在 uniapp H5 环境中,canvas 会被包裹在 uni-canvas 内
119
+ const uniCanvasElement = document.querySelector(`#${this.canvasId}`);
120
+ let canvasElement = null;
121
+
122
+ // 如果获取到的是 uni-canvas,需要找到内部的 canvas
123
+ if (uniCanvasElement && uniCanvasElement.tagName === 'UNI-CANVAS') {
124
+ canvasElement = uniCanvasElement.querySelector('canvas');
125
+
126
+ // 设置 uni-canvas 的样式
127
+ uniCanvasElement.style.width = `${this.size}px`;
128
+ uniCanvasElement.style.height = `${this.size}px`;
129
+ uniCanvasElement.style.overflow = 'visible';
130
+
131
+ // 设置 wrapper 的样式
132
+ const wrapper = uniCanvasElement.parentElement;
133
+ if (wrapper) {
134
+ wrapper.style.width = `${this.size}px`;
135
+ wrapper.style.height = `${this.size}px`;
136
+ wrapper.style.overflow = 'visible';
137
+ }
138
+ } else {
139
+ canvasElement = uniCanvasElement;
140
+ }
254
141
 
255
- // 重新设置 Canvas 显示尺寸
256
- canvas.style.width = `${this.size}px`;
257
- canvas.style.height = `${this.size}px`;
142
+ if (canvasElement) {
143
+ // 在初始化时设置 Canvas 的物理尺寸和显示尺寸
144
+ const pixelRatio = window.devicePixelRatio || 1;
145
+ const canvasSize = this.size * pixelRatio;
146
+
147
+ // 设置物理尺寸(实际像素)
148
+ canvasElement.width = canvasSize;
149
+ canvasElement.height = canvasSize;
150
+
151
+ // 设置显示尺寸(CSS 像素)
152
+ canvasElement.style.width = `${this.size}px`;
153
+ canvasElement.style.height = `${this.size}px`;
154
+
155
+ // 添加 willReadFrequently 属性以优化性能并消除警告
156
+ const ctx = canvasElement.getContext('2d', { willReadFrequently: true });
157
+ this.canvas = canvasElement;
158
+ this.ctx = ctx;
159
+ await this.renderQRCode();
160
+ } else {
161
+ console.error('无法获取 canvas 元素');
162
+ }
163
+ },
258
164
 
259
- // H5 环境:scale 计算方式(基于物理尺寸)
260
- scale = canvasSize / qrData.numCells;
261
- // #endif
165
+ // 小程序环境初始化
166
+ async initMiniProgramCanvas() {
167
+ if (typeof uni !== 'undefined' && uni.createSelectorQuery) {
168
+ const query = uni.createSelectorQuery().in(this);
169
+ query
170
+ .select(`#${this.canvasId}`)
171
+ .fields({ node: true, size: true })
172
+ .exec(async (res) => {
173
+ if (!res || !res[0] || !res[0].node) {
174
+ console.error('获取 canvas 节点失败');
175
+ return;
176
+ }
262
177
 
263
- // 重置变换矩阵并应用缩放
264
- ctx.setTransform(1, 0, 0, 1, 0, 0);
265
- ctx.scale(scale, scale);
178
+ const canvas = res[0].node;
179
+ // 小程序环境也添加 willReadFrequently 属性
180
+ try {
181
+ let ctx;
182
+ // #ifdef MP
183
+ ctx = canvas.getContext('2d', { willReadFrequently: true });
184
+ // #endif
185
+ if (!ctx) {
186
+ ctx = uni.createCanvasContext(this.canvasId, this);
187
+ }
188
+ this._canvas = canvas;
189
+ this._ctx = ctx;
190
+ } catch (e) {
191
+ console.warn('获取 ctx 失败', e);
192
+ }
266
193
 
267
- // 绘制背景
268
- ctx.fillStyle = this.actualBgColor;
269
- ctx.fillRect(0, 0, qrData.numCells, qrData.numCells);
270
194
 
271
- // 处理需要挖空的区域(如果有图标)
272
- let cellsToDraw = qrData.cells;
273
- if (this.icon && qrData.calculatedImageSettings?.excavation) {
274
- cellsToDraw = excavateModules(qrData.cells, qrData.calculatedImageSettings.excavation);
195
+ await this.renderQRCode();
196
+ });
275
197
  }
198
+ },
276
199
 
277
- // 绘制二维码
278
- ctx.fillStyle = this.actualColor;
200
+ async renderQRCode() {
201
+ const canvas = this._canvas || this.canvas;
202
+ const ctx = this._ctx || this.ctx;
203
+ if (!canvas || !ctx) {
204
+ return;
205
+ }
279
206
 
280
- // Web 环境优先使用 Path2D(性能更好)
281
- if (this.isWeb && isSupportPath2d) {
282
- ctx.fill(new Path2D(generatePath(cellsToDraw, qrData.margin)));
283
- } else {
284
- // 小程序环境或不支持 Path2D 时使用逐个绘制
285
- cellsToDraw.forEach((row, y) => {
286
- row.forEach((cell, x) => {
287
- if (cell) {
288
- ctx.fillRect(x + qrData.margin, y + qrData.margin, 1, 1);
207
+ const sizeProp = this.getSizeProp(this.iconSize);
208
+
209
+ try {
210
+ const qrData = useQRCode({
211
+ value: this.value,
212
+ level: this.level,
213
+ minVersion: DEFAULT_MINVERSION,
214
+ includeMargin: this.includeMargin,
215
+ marginSize: this.marginSize,
216
+ size: this.size,
217
+ imageSettings: this.icon
218
+ ? {
219
+ src: this.icon,
220
+ width: sizeProp.width,
221
+ height: sizeProp.height,
222
+ excavate: true,
289
223
  }
290
- });
224
+ : undefined,
291
225
  });
292
- }
293
-
294
- // 绘制中心图标
295
- if (this.icon && qrData.calculatedImageSettings) {
296
- await this.drawIcon(qrData, pixelRatio);
297
- }
298
226
 
299
- // #ifdef APP-PLUS
300
- ctx.draw();
301
- // #endif
227
+ // 获取设备像素比
228
+ let pixelRatio = 1;
229
+ let canvasSize;
230
+ let scale;
231
+
232
+ // #ifndef H5
233
+ // 小程序环境:获取真实的设备像素比并设置 Canvas 尺寸
234
+ // 使用 getWindowInfo 替代已废弃的 getSystemInfoSync
235
+ const windowInfo = getWindowInfo();
236
+ pixelRatio = windowInfo.pixelRatio || 1;
237
+ canvasSize = this.size * pixelRatio;
238
+ canvas.width = canvasSize;
239
+ canvas.height = canvasSize;
240
+ // 小程序环境:scale 计算方式(参考 TS 实现)
241
+ scale = canvasSize / qrData.numCells;
242
+ // #ifdef APP-PLUS
243
+ scale /= pixelRatio;
244
+ // #endif
245
+ // #endif
246
+
247
+ // #ifdef H5
248
+ // H5 环境:每次渲染时重新设置 Canvas 尺寸(因为 size 可能变化)
249
+ pixelRatio = window.devicePixelRatio || 1;
250
+ canvasSize = this.size * pixelRatio;
251
+
252
+ // 重新设置 Canvas 物理尺寸(会重置 canvas 状态)
253
+ canvas.width = canvasSize;
254
+ canvas.height = canvasSize;
255
+
256
+ // 重新设置 Canvas 显示尺寸
257
+ canvas.style.width = `${this.size}px`;
258
+ canvas.style.height = `${this.size}px`;
259
+
260
+ // H5 环境:scale 计算方式(基于物理尺寸)
261
+ scale = canvasSize / qrData.numCells;
262
+ // #endif
263
+
264
+ // 重置变换矩阵并应用缩放
265
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
266
+ ctx.scale(scale, scale);
267
+
268
+ // 绘制背景
269
+ ctx.fillStyle = this.actualBgColor;
270
+ ctx.fillRect(0, 0, qrData.numCells, qrData.numCells);
271
+
272
+ // 处理需要挖空的区域(如果有图标)
273
+ let cellsToDraw = qrData.cells;
274
+ if (this.icon && qrData.calculatedImageSettings?.excavation) {
275
+ cellsToDraw = excavateModules(qrData.cells, qrData.calculatedImageSettings.excavation);
276
+ }
277
+
278
+ // 绘制二维码
279
+ ctx.fillStyle = this.actualColor;
280
+
281
+ // Web 环境优先使用 Path2D(性能更好)
282
+ if (this.isWeb && isSupportPath2d) {
283
+ ctx.fill(new Path2D(generatePath(cellsToDraw, qrData.margin)));
284
+ } else {
285
+ // 小程序环境或不支持 Path2D 时使用逐个绘制
286
+ cellsToDraw.forEach((row, y) => {
287
+ row.forEach((cell, x) => {
288
+ if (cell) {
289
+ ctx.fillRect(x + qrData.margin, y + qrData.margin, 1, 1);
290
+ }
291
+ });
292
+ });
293
+ }
302
294
 
303
- this.$emit('drawCompleted');
304
- } catch (err) {
305
- console.error('二维码绘制失败:', err);
306
- this.$emit('drawError', { error: err });
307
- }
308
- },
295
+ // 绘制中心图标
296
+ if (this.icon && qrData.calculatedImageSettings) {
297
+ await this.drawIcon(qrData, pixelRatio);
298
+ }
309
299
 
310
- async drawIcon(qrData, pixelRatio) {
311
- const ctx = this._ctx || this.ctx;
312
- const { calculatedImageSettings, margin } = qrData;
300
+ // #ifdef APP-PLUS
301
+ ctx.draw();
302
+ // #endif
313
303
 
314
- if (!calculatedImageSettings) {
315
- return;
316
- }
304
+ this.$emit('drawCompleted');
305
+ } catch (err) {
306
+ console.error('二维码绘制失败:', err);
307
+ this.$emit('drawError', { error: err });
308
+ }
309
+ },
317
310
 
318
- try {
319
- // 加载图标图片
320
- const img = await this.loadIconImage();
311
+ async drawIcon(qrData, pixelRatio) {
312
+ const ctx = this._ctx || this.ctx;
313
+ const { calculatedImageSettings, margin } = qrData;
321
314
 
322
- if (!img) {
323
- console.error('无法加载图标图片');
315
+ if (!calculatedImageSettings) {
324
316
  return;
325
317
  }
326
318
 
327
- const drawX = calculatedImageSettings.x + margin;
328
- const drawY = calculatedImageSettings.y + margin;
329
-
330
- // 设置透明度
331
- if (calculatedImageSettings.opacity !== null && calculatedImageSettings.opacity !== undefined) {
332
- ctx.globalAlpha = calculatedImageSettings.opacity;
319
+ try {
320
+ // 加载图标图片
321
+ const img = await this.loadIconImage();
322
+
323
+ if (!img) {
324
+ console.error('无法加载图标图片');
325
+ return;
326
+ }
327
+
328
+ const drawX = calculatedImageSettings.x + margin;
329
+ const drawY = calculatedImageSettings.y + margin;
330
+
331
+ // 设置透明度
332
+ if (calculatedImageSettings.opacity !== null && calculatedImageSettings.opacity !== undefined) {
333
+ ctx.globalAlpha = calculatedImageSettings.opacity;
334
+ }
335
+
336
+ // #ifdef H5
337
+ ctx.scale(1 / pixelRatio, 1 / pixelRatio); // H5 环境:需要调整缩放
338
+ // #endif
339
+
340
+ // 绘制图标
341
+ ctx.drawImage(
342
+ img,
343
+ drawX,
344
+ drawY,
345
+ calculatedImageSettings.w,
346
+ calculatedImageSettings.h,
347
+ );
348
+
349
+ // 恢复透明度
350
+ ctx.globalAlpha = 1;
351
+ } catch (err) {
352
+ console.error('图标绘制失败:', err);
353
+ }
354
+ },
355
+
356
+ // 加载图标图片
357
+ // 参考 TSX (H5) 和 TS (小程序) 的实现
358
+ async loadIconImage() {
359
+ const canvas = this._canvas || this.canvas;
360
+ if (!this.icon || !canvas) {
361
+ return null;
362
+ }
363
+ return loadImage({
364
+ canvas,
365
+ src: this.icon,
366
+ });
367
+ },
368
+
369
+ getSizeProp(iconSize) {
370
+ if (!iconSize) return { width: 0, height: 0 };
371
+ if (typeof iconSize === 'number') {
372
+ return {
373
+ width: iconSize,
374
+ height: iconSize,
375
+ };
333
376
  }
334
-
335
- // #ifdef H5
336
- ctx.scale(1 / pixelRatio, 1 / pixelRatio); // H5 环境:需要调整缩放
337
- // #endif
338
-
339
- // 绘制图标
340
- ctx.drawImage(
341
- img,
342
- drawX,
343
- drawY,
344
- calculatedImageSettings.w,
345
- calculatedImageSettings.h,
346
- );
347
-
348
- // 恢复透明度
349
- ctx.globalAlpha = 1;
350
- } catch (err) {
351
- console.error('图标绘制失败:', err);
352
- }
353
- },
354
-
355
- // 加载图标图片
356
- // 参考 TSX (H5) 和 TS (小程序) 的实现
357
- async loadIconImage() {
358
- const canvas = this._canvas || this.canvas;
359
- if (!this.icon || !canvas) {
360
- return null;
361
- }
362
- return loadImage({
363
- canvas,
364
- src: this.icon,
365
- });
366
- },
367
-
368
- getSizeProp(iconSize) {
369
- if (!iconSize) return { width: 0, height: 0 };
370
- if (typeof iconSize === 'number') {
371
377
  return {
372
- width: iconSize,
373
- height: iconSize,
378
+ width: iconSize.width,
379
+ height: iconSize.height,
374
380
  };
375
- }
376
- return {
377
- width: iconSize.width,
378
- height: iconSize.height,
379
- };
380
- },
381
+ },
381
382
 
382
- // 暴露 canvas 节点给父组件
383
- getCanvasNode() {
384
- let result;
385
- // #ifndef H5
386
- result = new Promise((resolve) => {
387
- if (typeof uni !== 'undefined' && uni.createSelectorQuery) {
388
- const query = uni.createSelectorQuery().in(this);
389
- query
390
- .select(`#${this.canvasId}`)
391
- .fields({ node: true, size: true })
392
- .exec((res) => {
393
- resolve(res[0]?.node);
394
- });
395
- } else {
396
- resolve(null);
397
- }
398
- });
399
- // #endif
383
+ // 暴露 canvas 节点给父组件
384
+ getCanvasNode() {
385
+ let result;
386
+ // #ifndef H5
387
+ result = new Promise((resolve) => {
388
+ if (typeof uni !== 'undefined' && uni.createSelectorQuery) {
389
+ const query = uni.createSelectorQuery().in(this);
390
+ query
391
+ .select(`#${this.canvasId}`)
392
+ .fields({ node: true, size: true })
393
+ .exec((res) => {
394
+ resolve(res[0]?.node);
395
+ });
396
+ } else {
397
+ resolve(null);
398
+ }
399
+ });
400
+ // #endif
400
401
 
401
- // #ifdef H5
402
- result = Promise.resolve(document.querySelector(`#${this.canvasId}`));
403
- // #endif
402
+ // #ifdef H5
403
+ result = Promise.resolve(document.querySelector(`#${this.canvasId}`));
404
+ // #endif
404
405
 
405
- return result;
406
+ return result;
407
+ },
406
408
  },
407
- },
408
- });
409
+ }),
410
+ };
409
411
  </script>
410
412
 
411
413
  <style scoped src="./qrcode-canvas.css"></style>