uview-plus 3.7.0 → 3.7.13

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,43 @@
1
+ ## 3.7.13(2026-02-07)
2
+ add: 新增up-canvas组件
3
+
4
+ ## 3.7.12(2026-02-07)
5
+ improvment: 优化签名组件兼容性修复微信小程序支持
6
+
7
+ ## 3.7.11(2026-02-06)
8
+ add: action-sheet组件新增nameKey和subnameKey属性
9
+
10
+ ## 3.7.10(2026-02-06)
11
+ add: checkbox-group组件的change事件增加第二个返回参数为当前触发变化的checkbox组件name信息等
12
+
13
+ ## 3.7.9(2026-02-06)
14
+ fix: 修复align-self属性值缺少d导致样式不生效的问题
15
+
16
+ ## 3.7.8(2026-02-05)
17
+ improvment: 优化card组件示例
18
+
19
+ ## 3.7.7(2026-02-05)
20
+ add: select组件新增closeSelect方法用于自定义控制关闭
21
+
22
+
23
+ ## 3.7.6(2026-02-04)
24
+ add: picker组件示例onLoad后加载数据
25
+
26
+ ## 3.7.5(2026-02-03)
27
+ add: up-slider组件新增垂直模式
28
+
29
+ ## 3.7.4(2026-02-03)
30
+ add: box组件props优化及新增leftIcon/leftTitle/rightTopIcon/rightTopTitle/rightBottomIcon/rightBottomTitle参数
31
+
32
+ ## 3.7.3(2026-02-03)
33
+ improvment: city-locate组件示例改为组合式API
34
+
35
+ ## 3.7.2(2026-02-02)
36
+ improvment: color-picker组件示例改为组合式API
37
+
38
+ ## 3.7.1(2026-02-02)
39
+ fix:修复qrcode缺失的preview定义emit
40
+
1
41
  ## 3.7.0(2026-02-01)
2
42
  fix: 修复up-qrcode二维码在微信小程序报错
3
43
 
@@ -14,6 +14,8 @@ export default {
14
14
  title: '',
15
15
  description: '',
16
16
  actions: [],
17
+ nameKey: 'name',
18
+ subnameKey: 'subnameKey',
17
19
  index: '',
18
20
  cancelText: '',
19
21
  closeOnClickAction: true,
@@ -31,6 +31,16 @@ export const props = defineMixin({
31
31
  type: Array,
32
32
  default: () => defProps.actionSheet.actions
33
33
  },
34
+ // 选项名称key
35
+ nameKey: {
36
+ type: String,
37
+ default: () => defProps.actionSheet.nameKey
38
+ },
39
+ // 选项子名称key
40
+ subnameKey: {
41
+ type: String,
42
+ default: () => defProps.actionSheet.subnameKey
43
+ },
34
44
  // 取消按钮的文字,不为空时显示按钮
35
45
  cancelText: {
36
46
  type: String,
@@ -71,11 +71,11 @@
71
71
  <text
72
72
  class="u-action-sheet__item-wrap__item__name"
73
73
  :style="[itemStyle(index)]"
74
- >{{ item.name }}</text>
74
+ >{{ item[nameKey] }}</text>
75
75
  <text
76
- v-if="item.subname"
76
+ v-if="item[subnameKey]"
77
77
  class="u-action-sheet__item-wrap__item__subname"
78
- >{{ item.subname }}</text>
78
+ >{{ item[subnameKey] }}</text>
79
79
  </template>
80
80
  <!-- 加载状态图标 -->
81
81
  <u-loading-icon
@@ -0,0 +1,24 @@
1
+ /*
2
+ * @Author : ly
3
+ * @Description :
4
+ * @version : 4.0
5
+ * @Date : 2026-02-02 23:54:58
6
+ * @LastAuthor : ly
7
+ * @lastTime : 2026-02-02 23:54:58
8
+ * @FilePath : /uview-ultra/components/up-box/box.js
9
+ */
10
+ export default {
11
+ // box 组件
12
+ box: {
13
+ bgColors: ['#EEFCFF', '#FCF8FF', '#FDF8F2'],
14
+ height: '160px',
15
+ borderRadius: '6px',
16
+ gap: '15px',
17
+ leftIcon: '',
18
+ leftTitle: '左',
19
+ rightTopIcon: '',
20
+ rightTopTitle: '右上',
21
+ rightBottomIcon: '',
22
+ rightBottomTitle: '右下'
23
+ }
24
+ }
@@ -1,27 +1,57 @@
1
- import { defineMixin } from '../../libs/vue'
2
- import defProps from '../../libs/config/props.js'
1
+ import { defineMixin } from '../../libs/vue.js'
2
+ import defProps from './box.js'
3
3
 
4
4
  export const propsBox = defineMixin({
5
5
  props: {
6
6
  // 背景色
7
7
  bgColors: {
8
8
  type: [Array],
9
- default: ['#EEFCFF', '#FCF8FF', '#FDF8F2']
9
+ default: () => defProps.box.bgColors
10
10
  },
11
11
  // 高度
12
12
  height: {
13
13
  type: [String],
14
- default: "160px"
14
+ default: () => defProps.box.height
15
15
  },
16
16
  // 圆角
17
17
  borderRadius: {
18
18
  type: [String],
19
- default: "6px"
19
+ default: () => defProps.box.borderRadius
20
20
  },
21
21
  // 间隔
22
22
  gap: {
23
23
  type: [String],
24
- default: "15px"
24
+ default: () => defProps.box.gap
25
+ },
26
+ // 左侧图标
27
+ leftIcon: {
28
+ type: [String],
29
+ default: () => defProps.box.leftIcon
30
+ },
31
+ // 左侧文案
32
+ leftTitle: {
33
+ type: [String],
34
+ default: () => defProps.box.leftTitle
35
+ },
36
+ // 右上图标
37
+ rightTopIcon: {
38
+ type: [String],
39
+ default: () => defProps.box.rightTopIcon
40
+ },
41
+ // 右上文案
42
+ rightTopTitle: {
43
+ type: [String],
44
+ default: () => defProps.box.rightTopTitle
45
+ },
46
+ // 右下图标
47
+ rightBottomIcon: {
48
+ type: [String],
49
+ default: () => defProps.box.rightBottomIcon
50
+ },
51
+ // 右下文案
52
+ rightBottomTitle: {
53
+ type: [String],
54
+ default: () => defProps.box.rightBottomTitle
25
55
  },
26
56
  }
27
- })
57
+ })
@@ -1,18 +1,33 @@
1
1
  <template>
2
- <view class="u-box" :style="[{height: height}, addStyle(customStyle)]">
3
- <view class="u-box__left" :style="{borderRadius: borderRadius, backgroundColor: bgColors[0]}">
4
- <slot name="left">左</slot>
5
- </view>
6
- <view class="u-box__gap" :style="{width: gap, height: height}"></view>
7
- <view class="u-box__right">
8
- <view class="u-box__right-top" :style="{borderRadius: borderRadius, backgroundColor: bgColors[1]}">
9
- <slot name="rightTop">右上</slot>
10
- </view>
11
- <view class="u-box__right-gap" :style="{height: gap}"></view>
12
- <view class="u-box__right-bottom" :style="{borderRadius: borderRadius, backgroundColor: bgColors[2]}">
13
- <slot name="rightBottom">右下</slot>
14
- </view>
15
- </view>
2
+ <view class="u-box" :style="[{height: height}, addStyle(customStyle)]">
3
+ <view class="u-box__left" :style="{borderRadius: borderRadius, backgroundColor: bgColors[0]}">
4
+ <slot name="left">
5
+ <view class="flex flex-row items-center justify-center">
6
+ <u-icon size="36" :name="leftIcon"></u-icon>
7
+ <text class="ml-2 text-16px">{{leftTitle}}</text>
8
+ </view>
9
+ </slot>
10
+ </view>
11
+ <view class="u-box__gap" :style="{width: gap, height: height}"></view>
12
+ <view class="u-box__right">
13
+ <view class="u-box__right-top" :style="{borderRadius: borderRadius, backgroundColor: bgColors[1]}">
14
+ <slot name="rightTop">
15
+ <view class="flex flex-row items-center justify-center">
16
+ <u-icon size="36" :name="rightTopIcon"></u-icon>
17
+ <text class="ml-2 text-15px">{{rightTopTitle}}</text>
18
+ </view>
19
+ </slot>
20
+ </view>
21
+ <view class="u-box__right-gap" :style="{height: gap}"></view>
22
+ <view class="u-box__right-bottom" :style="{borderRadius: borderRadius, backgroundColor: bgColors[2]}">
23
+ <slot name="rightBottom">
24
+ <view class="flex flex-row items-center justify-center">
25
+ <u-icon size="36" :name="rightBottomIcon"></u-icon>
26
+ <text class="ml-2 text-15px">{{rightBottomTitle}}</text>
27
+ </view>
28
+ </slot>
29
+ </view>
30
+ </view>
16
31
  </view>
17
32
  </template>
18
33
 
@@ -8,24 +8,34 @@
8
8
  <!-- #ifdef MP || H5 -->
9
9
  <canvas
10
10
  class="u-canvas__canvas"
11
- :id="cid"
12
- :canvas-id="cid"
11
+ :id="canvasId"
12
+ :canvas-id="canvasId"
13
13
  type="2d"
14
- :style="{ width: sizeLocal + unit, height: sizeLocal + unit }" />
14
+ :style="{ width: width + unit, height: height + unit }"
15
+ @touchstart="onTouchStart"
16
+ @touchmove="onTouchMove"
17
+ @touchend="onTouchEnd"/>
15
18
  <!-- #endif -->
16
19
 
17
20
  <!-- #ifdef APP-PLUS -->
18
21
  <canvas
19
22
  class="u-canvas__canvas"
20
- :id="cid"
21
- :canvas-id="cid"
22
- :style="{ width: width + unit, height: height + unit }" />
23
+ :id="canvasId"
24
+ :canvas-id="canvasId"
25
+ :style="{ width: width + unit, height: height + unit }"
26
+ @touchstart="onTouchStart"
27
+ @touchmove="onTouchMove"
28
+ @touchend="onTouchEnd"/>
23
29
  <!-- #endif -->
24
30
 
25
31
  <!-- #ifdef APP-NVUE -->
26
32
  <gcanvas class="u-canvas__canvas" ref="gcanvess"
27
- :style="{ width: width + unit, height: height + unit }">
33
+ :style="{ width: width + unit, height: height + unit }"
34
+ @touchstart="onTouchStart"
35
+ @touchmove="onTouchMove"
36
+ @touchend="onTouchEnd">
28
37
  </gcanvas>
38
+ <!-- #endif -->
29
39
  </view>
30
40
  </template>
31
41
 
@@ -38,15 +48,14 @@ import {
38
48
  Image as GImage
39
49
  } from '../../libs/util/gcanvas/index.js';
40
50
  // #endif
41
- // 20260201不能在data中存储canvas,否则会导致在微信小程序getContext获取不到canvas对象报错Cannot read property 'type'
42
- let canvas = null
51
+ let canvasNode = null;
43
52
  export default {
44
53
  name: "u-canvas",
45
54
  props: {
46
- cid: {
55
+ canvasId: {
47
56
  type: String,
48
57
  default: () => {
49
- return `u-qrcode-canvas${Math.floor(Math.random() * 1000000)}`
58
+ return `u-canvas${Math.floor(Math.random() * 1000000)}`
50
59
  }
51
60
  },
52
61
  width: {
@@ -61,6 +70,15 @@ export default {
61
70
  type: String,
62
71
  default: 'px'
63
72
  },
73
+ useRootHeightAndWidth: {
74
+ type: Boolean,
75
+ default: false
76
+ },
77
+ // 背景色
78
+ bgColor: {
79
+ type: String,
80
+ default: '#ffffff'
81
+ }
64
82
  },
65
83
  data() {
66
84
  return {
@@ -69,9 +87,30 @@ export default {
69
87
  canvasContext: null,
70
88
  widthLocal: this.width,
71
89
  heightLocal: this.height,
90
+ ctx: null
72
91
  };
73
92
  },
93
+ computed: {
94
+ // 计算实际画布尺寸
95
+ actualWidth() {
96
+ return this.useRootHeightAndWidth ? this.widthLocal : Number(this.width);
97
+ },
98
+ actualHeight() {
99
+ return this.useRootHeightAndWidth ? this.heightLocal : Number(this.height);
100
+ }
101
+ },
74
102
  methods: {
103
+ // 添加触摸事件处理方法
104
+ onTouchStart(e) {
105
+ this.$emit('touchstart', e);
106
+ },
107
+ onTouchMove(e) {
108
+ this.$emit('touchmove', e);
109
+ },
110
+ onTouchEnd(e) {
111
+ this.$emit('touchend', e);
112
+ },
113
+
75
114
  /**
76
115
  * 获取节点
77
116
  * @param id 节点id
@@ -117,14 +156,237 @@ export default {
117
156
  }
118
157
  })
119
158
  },
120
- getUPCanvasContext() {
121
- // #ifdef APP-PLUS
122
- return uni.createCanvasContext(this.cid, this);
123
- // #endif
124
- // #ifdef APP-PLUS-NVUE || MP || H5
125
- return canvas.getContext('2d');
126
- // #endif
127
- },
159
+
160
+ /**
161
+ * 获取Canvas上下文
162
+ */
163
+ getCanvasContext() {
164
+ // #ifdef APP-PLUS
165
+ return uni.createCanvasContext(this.canvasId, this);
166
+ // #endif
167
+ // #ifdef APP-PLUS-NVUE || MP || H5
168
+ return canvasNode.getContext('2d');
169
+ // #endif
170
+ },
171
+
172
+ /**
173
+ * 初始化Canvas
174
+ */
175
+ async initCanvas() {
176
+ try {
177
+ canvasNode = await this.getCanvasNode(this.canvasId);
178
+
179
+ // #ifdef MP-WEIXIN
180
+ // 在微信小程序中,为了提高清晰度,需要考虑设备像素比
181
+ const dpr = uni.getSystemInfoSync().pixelRatio;
182
+ if(canvasNode) {
183
+ // 设置canvas实际绘制尺寸为显示尺寸的dpr倍
184
+ canvasNode.width = this.actualWidth * dpr;
185
+ canvasNode.height = this.actualHeight * dpr;
186
+ }
187
+ // #endif
188
+
189
+ this.ctx = this.getCanvasContext();
190
+
191
+ // #ifdef MP-WEIXIN
192
+ if(this.ctx) {
193
+ this.ctx.scale(dpr, dpr);
194
+ }
195
+ // #endif
196
+
197
+ // 初始化背景,但不在微信小程序中调用draw
198
+ this.clearCanvas();
199
+ } catch (error) {
200
+ console.error("初始化Canvas失败:", error);
201
+ }
202
+ },
203
+
204
+ /**
205
+ * 清空画布
206
+ */
207
+ clearCanvas() {
208
+ if (!this.ctx) return;
209
+
210
+ this.clearRect(0, 0, this.actualWidth, this.actualHeight);
211
+
212
+ // 填充背景色
213
+ this.beginPath();
214
+ this.rect(0, 0, this.actualWidth, this.actualHeight);
215
+
216
+ this.setFillStyle(this.bgColor);
217
+ this.fill();
218
+
219
+ this.draw();
220
+ },
221
+ rect(x, y, width, height) {
222
+ if (!this.ctx) return;
223
+ this.ctx.rect(x, y, width, height);
224
+ },
225
+ clearRect(x, y, width, height) {
226
+ if (!this.ctx) return;
227
+ this.ctx.clearRect(x, y, width, height);
228
+ },
229
+ fill() {
230
+ if (!this.ctx) return;
231
+ this.ctx.fill();
232
+ },
233
+ setFillStyle(color) {
234
+ if (!this.ctx) return;
235
+
236
+ // #ifndef APP-PLUS-NVUE
237
+ if (this.ctx.setFillStyle) {
238
+ this.ctx.setFillStyle(color);
239
+ } else {
240
+ this.ctx.fillStyle = color;
241
+ }
242
+ // #endif
243
+ // #ifdef APP-PLUS-NVUE
244
+ this.ctx.setFillStyle(color);
245
+ // #endif
246
+ },
247
+
248
+ /**
249
+ * 设置线条样式
250
+ */
251
+ setLineStyle(lineColor, lineWidth) {
252
+ if (!this.ctx) return;
253
+ this.setLineCap('round');
254
+ this.setLineJoin('round');
255
+ this.setStrokeStyle(lineColor);
256
+ this.setLineWidth(lineWidth);
257
+ },
258
+ setLineCap(lineCap = 'round') {
259
+ if (!this.ctx) return;
260
+ if (this.ctx.setLineCap) {
261
+ this.ctx.setLineCap(lineCap);
262
+ } else {
263
+ this.ctx.lineCap = lineCap;
264
+ }
265
+ },
266
+ setLineJoin(lineJoin = 'round') {
267
+ if (!this.ctx) return;
268
+ if (this.ctx.setLineJoin) {
269
+ this.ctx.setLineJoin(lineJoin);
270
+ } else {
271
+ this.ctx.lineJoin = lineJoin;
272
+ }
273
+ },
274
+ setStrokeStyle(color) {
275
+ if (!this.ctx) return;
276
+ if (this.ctx.setStrokeStyle) {
277
+ this.ctx.setStrokeStyle(color);
278
+ } else {
279
+ this.ctx.strokeStyle = color;
280
+ }
281
+ },
282
+ setLineWidth(width) {
283
+ if (!this.ctx) return;
284
+ if (this.ctx.setLineWidth) {
285
+ this.ctx.setLineWidth(width);
286
+ } else {
287
+ this.ctx.lineWidth = width;
288
+ }
289
+ },
290
+
291
+ /**
292
+ * 开始路径
293
+ */
294
+ beginPath() {
295
+ if (!this.ctx) return;
296
+ this.ctx.beginPath();
297
+ },
298
+
299
+ /**
300
+ * 移动到某点
301
+ */
302
+ moveTo(x, y) {
303
+ if (!this.ctx) return;
304
+ this.ctx.moveTo(x, y);
305
+ },
306
+
307
+ /**
308
+ * 画线到某点
309
+ */
310
+ lineTo(x, y) {
311
+ if (!this.ctx) return;
312
+ this.ctx.lineTo(x, y);
313
+ },
314
+
315
+ /**
316
+ * 描边
317
+ */
318
+ stroke() {
319
+ if (!this.ctx) return;
320
+ this.ctx.stroke();
321
+ },
322
+
323
+ /**
324
+ * 关闭路径
325
+ */
326
+ closePath() {
327
+ if (!this.ctx) return;
328
+ this.ctx.closePath();
329
+ },
330
+
331
+ /**
332
+ * 绘制操作
333
+ */
334
+ draw(isLastDraw = false) {
335
+ // #ifndef MP-WEIXIN
336
+ if (this.ctx && typeof this.ctx.draw === 'function') {
337
+ this.ctx.draw(isLastDraw);
338
+ }
339
+ // #endif
340
+ },
341
+
342
+ /**
343
+ * 导出图片
344
+ */
345
+ exportImage(fileType = 'png', quality = 1) {
346
+ return new Promise((resolve, reject) => {
347
+ // #ifdef MP-WEIXIN
348
+ // 微信小程序中需要先完成绘制,然后导出图片
349
+ setTimeout(() => {
350
+ uni.canvasToTempFilePath({
351
+ x: 0,
352
+ y: 0,
353
+ width: this.actualWidth,
354
+ height: this.actualHeight,
355
+ destWidth: this.actualWidth * 2, // 使用双倍尺寸以提高清晰度
356
+ destHeight: this.actualHeight * 2,
357
+ canvas: canvasNode, // 2d必须
358
+ canvasId: this.canvasId,
359
+ fileType: fileType,
360
+ quality: quality,
361
+ success: (res) => {
362
+ resolve(res.tempFilePath);
363
+ },
364
+ fail: (err) => {
365
+ console.error('导出图片失败:', err);
366
+ reject(err);
367
+ }
368
+ }, this);
369
+ }, 50); // 等待50毫秒确保绘制完成
370
+ // #endif
371
+
372
+ // #ifndef MP-WEIXIN
373
+ uni.canvasToTempFilePath({
374
+ canvas: canvasNode, // 2d必须
375
+ canvasId: this.canvasId,
376
+ fileType: fileType,
377
+ quality: quality,
378
+ success: (res) => {
379
+ resolve(res.tempFilePath);
380
+ },
381
+ fail: (err) => {
382
+ console.error('导出图片失败:', err);
383
+ reject(err);
384
+ }
385
+ }, this);
386
+ // #endif
387
+ });
388
+ },
389
+
128
390
  /**
129
391
  * 使用根节点宽高 设置新的size
130
392
  * @return {Promise<void>}
@@ -132,15 +394,30 @@ export default {
132
394
  async setNewSize(){
133
395
  const rootNode = await this.getCanvasNode(this.rootId, false);
134
396
  const { width , height } = rootNode;
135
- this.widthLocal = height
136
- this.heightLocal = width
137
- },
397
+ this.widthLocal = height;
398
+ this.heightLocal = width;
399
+ }
138
400
  },
401
+
139
402
  async mounted() {
140
403
  // 如果使用根节点的宽高 则 重新设置 size
141
404
  if(this.useRootHeightAndWidth){
142
- await this.setNewSize()
405
+ await this.setNewSize();
143
406
  }
407
+
408
+ // 初始化Canvas
409
+ await this.initCanvas();
144
410
  }
145
411
  };
146
- </script>
412
+ </script>
413
+
414
+ <style lang="scss" scoped>
415
+ .u-canvas {
416
+ position: relative;
417
+ overflow: hidden;
418
+ }
419
+
420
+ .u-canvas__canvas {
421
+ display: block;
422
+ }
423
+ </style>
@@ -109,7 +109,10 @@
109
109
  this.$emit("input", values);
110
110
  // #endif
111
111
  // 放在最后更新,否则change事件传出去的values不会更新
112
- this.$emit('change', values)
112
+ this.$emit('change', values, {
113
+ isChecked: childInstance.$data['isChecked'],
114
+ name: childInstance.$props['name'] // 当前变动的checkbox的name值
115
+ })
113
116
  },
114
117
  }
115
118
  }
@@ -135,7 +135,7 @@ export default {
135
135
  default: () => false
136
136
  },
137
137
  },
138
- emits: ['result', 'longpressCallback'],
138
+ emits: ['preview', 'result', 'longpressCallback'],
139
139
  data() {
140
140
  return {
141
141
  loading: false,
@@ -146,6 +146,9 @@ export default {
146
146
  }
147
147
  });
148
148
  },
149
+ closeSelect() {
150
+ this.isOpen = false;
151
+ },
149
152
  overlayClick() {
150
153
  this.isOpen = false;
151
154
  },