uview-plus 3.4.85 → 3.4.91

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,21 @@
1
+ ## 3.4.91(2025-08-14)
2
+ feat: 新增agreement弹窗协议组件
3
+
4
+ ## 3.4.90(2025-08-13)
5
+ feat: datetimepicker新增datehour类型
6
+
7
+ ## 3.4.89(2025-08-13)
8
+ fix: 修复u-form-item组件缺少对labelPosition的判断
9
+
10
+ ## 3.4.88(2025-08-13)
11
+ fix: subsection组件的disabled属性应为Boolean类型
12
+
13
+ ## 3.4.87(2025-08-12)
14
+ improvment: 签名组件示例前缀改为up
15
+
16
+ ## 3.4.86(2025-08-12)
17
+ feat: 新增signature签名签字组件
18
+
1
19
  ## 3.4.85(2025-08-11)
2
20
  feat: 完善图片裁剪功能支持props及JS两种方式
3
21
 
@@ -0,0 +1,76 @@
1
+ <style scoped lang="scss">
2
+ .agreement-content {
3
+ width: 100%;;
4
+ display: inline-block;
5
+ flex-direction: column;
6
+ .agreement-url {
7
+ display: inline-block;
8
+ color: blue;
9
+ // #ifdef H5
10
+ cursor: pointer;
11
+ // #endif
12
+ }
13
+ }
14
+ </style>
15
+
16
+ <template>
17
+ <view class="up-agreement">
18
+ <up-modal v-model:show="show" showCancelButton @confirm="confirm" @cancel="close" confirmText="阅读并同意">
19
+ <view class="agreement-content">
20
+ <slot>
21
+ 我们非常重视您的个人信息和隐私保护。为了更好地保障您的个人权益,在您使用我们的产品前,
22
+ 请务必审慎阅读《<text class="agreement-url" @click="urlClick('urlProtocol')">用户协议</text>》
23
+ 和《<text class="agreement-url" @click="urlClick('urlPrivacy')">隐私政策</text>》内的所有条款,
24
+ 尤其是:1.我们对您的个人信息的收集/保存/使用/对外提供/保护等规则条款,以及您的用户权利等条款;2. 约定我们的限制责任、免责
25
+ 条款;3.其他以颜色或加粗进行标识的重要条款。如您对以上协议有任何疑问,请先不要同意,您点击“同意并继续”的行为即表示您已阅读
26
+ 完毕并同意以上协议的全部内容。
27
+ </slot>
28
+ </view>
29
+ </up-modal>
30
+ </view>
31
+ </template>
32
+
33
+ <script>
34
+ export default {
35
+ name: 'up-agreement',
36
+ props: {
37
+ urlProtocol: {
38
+ type: String,
39
+ default: '/pages/user_agreement/agreement/info?title=用户协议'
40
+ },
41
+ urlPrivacy: {
42
+ type: String,
43
+ default: '/pages/user_agreement/agreement/info?title=隐私政策'
44
+ },
45
+ },
46
+ emits: ['confirm'],
47
+ data() {
48
+ return {
49
+ show: false
50
+ }
51
+ },
52
+ methods: {
53
+ close() {
54
+ // #ifdef H5
55
+ window.opener = null;
56
+ window.close();
57
+ // #endif
58
+ // #ifdef APP-PLUS
59
+ plus.runtime.quit();
60
+ // #endif
61
+ },
62
+ confirm() {
63
+ this.show = false;
64
+ this.$emit('confirm', 1);
65
+ },
66
+ showModal() {
67
+ this.show = true;
68
+ },
69
+ urlClick(type) {
70
+ uni.navigateTo({
71
+ url: this[type]
72
+ });
73
+ }
74
+ }
75
+ }
76
+ </script>
@@ -176,6 +176,9 @@
176
176
  case 'year-month':
177
177
  format = 'YYYY-MM'
178
178
  break;
179
+ case 'datehour':
180
+ format = 'YYYY-MM-DD HH'
181
+ break;
179
182
  case 'datetime':
180
183
  format = 'YYYY-MM-DD HH:mm'
181
184
  break;
@@ -15,7 +15,7 @@
15
15
  v-if="required || leftIcon || label"
16
16
  :style="{
17
17
  width: addUnit(labelWidth || parentData.labelWidth),
18
- marginBottom: parentData.labelPosition === 'left' ? 0 : '5px',
18
+ marginBottom: (labelPosition || parentData.labelPosition) === 'left' ? 0 : '5px',
19
19
  }"
20
20
  >
21
21
  <!-- 为了块对齐 -->
@@ -62,7 +62,7 @@
62
62
  v-if="!!message && parentData.errorType === 'message'"
63
63
  class="u-form-item__body__right__message"
64
64
  :style="{
65
- marginLeft: addUnit(parentData.labelPosition === 'top' ? 0 : (labelWidth || parentData.labelWidth))
65
+ marginLeft: addUnit((labelPosition || parentData.labelPosition) === 'top' ? 0 : (labelWidth || parentData.labelWidth))
66
66
  }"
67
67
  >{{ message }}</text>
68
68
  </slot>
@@ -0,0 +1,498 @@
1
+ <template>
2
+ <view class="u-signature">
3
+ <view class="u-signature__canvas-wrap">
4
+ <canvas
5
+ class="u-signature__canvas"
6
+ :canvas-id="canvasId"
7
+ :disable-scroll="true"
8
+ @touchstart="touchStart"
9
+ @touchmove="touchMove"
10
+ @touchend="touchEnd"
11
+ :style="{
12
+ width: canvasWidth + 'px',
13
+ height: canvasHeight + 'px',
14
+ background: bgColor
15
+ }"
16
+ ></canvas>
17
+ </view>
18
+
19
+ <view v-if="showToolbar" class="u-signature__toolbar">
20
+ <view class="u-signature__toolbar-icons u-flex u-flex-x">
21
+ <view class="u-signature__toolbar-icon" @click="undo">
22
+ <up-icon name="arrow-left" size="22" :color="pathStack.length === 0 ? '#ccc' : '#999'"></up-icon>
23
+ </view>
24
+ <view class="u-signature__toolbar-icon" @click="clear">
25
+ <up-icon name="trash" size="25" color="#999"></up-icon>
26
+ </view>
27
+ <view class="u-signature__toolbar-icon" @click="toggleBrushSettings">
28
+ <up-icon name="edit-pen" size="25" color="#999"></up-icon>
29
+ </view>
30
+ <view class="u-signature__toolbar-icon" @click="toggleColorSettings">
31
+ <up-icon name="grid" size="24" color="#999"></up-icon>
32
+ </view>
33
+ <view class="u-signature__toolbar-icon" @click="exportSignature">
34
+ <up-icon name="checkmark" size="25" :color="isEmpty ? '#ccc' : '#999'"></up-icon>
35
+ </view>
36
+ </view>
37
+
38
+ <!-- 笔画设置 -->
39
+ <view v-if="showBrushSettings" class="u-signature__brush-settings">
40
+ <view class="u-signature__progress">
41
+ <text class="u-signature__progress-label">笔画大小:</text>
42
+ <up-slider
43
+ v-model="lineWidth"
44
+ :min="1"
45
+ :max="20"
46
+ :step="1"
47
+ @show-value="true"
48
+ :value-show="(lineWidth)"
49
+ ></up-slider>
50
+ </view>
51
+ </view>
52
+
53
+ <!-- 颜色设置 -->
54
+ <view v-if="showColorSettings" class="u-signature__color-settings">
55
+ <view class="u-signature__color-picker">
56
+ <text class="u-signature__color-label">笔画颜色:</text>
57
+ <view class="u-signature__colors">
58
+ <view
59
+ v-for="(color, index) in presetColors"
60
+ :key="index"
61
+ class="u-signature__color-item"
62
+ :class="{'u-signature__color-item--active': lineColor === color}"
63
+ :style="{ backgroundColor: color }"
64
+ @click="selectColor(color)"
65
+ ></view>
66
+ </view>
67
+ </view>
68
+ </view>
69
+ </view>
70
+ </view>
71
+ </template>
72
+
73
+ <script>
74
+ export default {
75
+ name: 'u-signature',
76
+ props: {
77
+ // 画布宽度
78
+ width: {
79
+ type: [String, Number],
80
+ default: 300
81
+ },
82
+ // 画布高度
83
+ height: {
84
+ type: [String, Number],
85
+ default: 200
86
+ },
87
+ // 背景颜色
88
+ bgColor: {
89
+ type: String,
90
+ default: '#ffffff'
91
+ },
92
+ // 默认笔画颜色
93
+ color: {
94
+ type: String,
95
+ default: '#000000'
96
+ },
97
+ // 默认笔画粗细
98
+ thickness: {
99
+ type: [String, Number],
100
+ default: 3
101
+ },
102
+ // 是否显示工具栏
103
+ showToolbar: {
104
+ type: Boolean,
105
+ default: true
106
+ }
107
+ },
108
+ data() {
109
+ return {
110
+ canvasId: 'u-signature-' + Math.random().toString(36).substr(2, 9),
111
+ canvasWidth: 300,
112
+ canvasHeight: 200,
113
+ lineColor: '#000000',
114
+ lineWidth: 3,
115
+ isDrawing: false,
116
+ pathStack: [], // 存储绘制路径用于回退
117
+ currentPath: [], // 当前绘制路径
118
+ ctx: null,
119
+ isEmpty: true,
120
+ presetColors: [
121
+ '#000000', // 黑色
122
+ '#ff0000', // 红色
123
+ '#00ff00', // 绿色
124
+ '#0000ff', // 蓝色
125
+ '#ffff00', // 黄色
126
+ '#00ffff', // 青色
127
+ '#ff00ff', // 紫色
128
+ '#ffffff' // 白色
129
+ ],
130
+ showBrushSettings: false,
131
+ showColorSettings: false,
132
+ lastPoint: null // 保存上一个点的坐标
133
+ }
134
+ },
135
+ mounted() {
136
+ this.initCanvas()
137
+ },
138
+ watch: {
139
+ width: {
140
+ handler(newVal) {
141
+ this.canvasWidth = Number(newVal)
142
+ },
143
+ immediate: true
144
+ },
145
+ height: {
146
+ handler(newVal) {
147
+ this.canvasHeight = Number(newVal)
148
+ },
149
+ immediate: true
150
+ },
151
+ color: {
152
+ handler(newVal) {
153
+ this.lineColor = newVal
154
+ },
155
+ immediate: true
156
+ },
157
+ thickness: {
158
+ handler(newVal) {
159
+ this.lineWidth = Number(newVal)
160
+ },
161
+ immediate: true
162
+ }
163
+ },
164
+ methods: {
165
+ initCanvas() {
166
+ // #ifndef APP-NVUE
167
+ const ctx = uni.createCanvasContext(this.canvasId, this)
168
+ this.ctx = ctx
169
+ this.clearCanvas()
170
+ // #endif
171
+
172
+ // #ifdef APP-NVUE
173
+ // NVUE环境下的处理
174
+ // #endif
175
+ },
176
+
177
+ touchStart(e) {
178
+ if (!this.ctx) return
179
+
180
+ this.isDrawing = true
181
+ this.isEmpty = false
182
+ this.currentPath = []
183
+
184
+ const { x, y } = this.getCanvasPoint(e)
185
+ this.ctx.beginPath()
186
+ this.ctx.moveTo(x, y)
187
+ this.ctx.setLineCap('round')
188
+ this.ctx.setLineJoin('round')
189
+ this.ctx.setStrokeStyle(this.lineColor)
190
+ this.ctx.setLineWidth(this.lineWidth)
191
+
192
+ // 记录起始点
193
+ this.currentPath.push({
194
+ x,
195
+ y,
196
+ type: 'start',
197
+ color: this.lineColor,
198
+ width: this.lineWidth
199
+ })
200
+
201
+ // 保存上一个点
202
+ this.lastPoint = { x, y }
203
+
204
+ // 阻止默认事件以提高性能
205
+ e.preventDefault()
206
+ },
207
+
208
+ touchMove(e) {
209
+ if (!this.isDrawing || !this.ctx) return
210
+
211
+ // 阻止默认事件以提高性能
212
+ e.preventDefault()
213
+
214
+ const { x, y } = this.getCanvasPoint(e)
215
+
216
+ // 使用更密集的点采样确保线条连贯性
217
+ if (this.lastPoint) {
218
+ // 计算两点间距离
219
+ const distance = Math.sqrt(Math.pow(x - this.lastPoint.x, 2) + Math.pow(y - this.lastPoint.y, 2))
220
+ // 根据距离确定插值点数量,确保点间距不超过1像素以获得更平滑的线条
221
+ const steps = Math.max(1, Math.floor(distance / 1))
222
+
223
+ // 在两点间插入插值点
224
+ for (let i = 1; i <= steps; i++) {
225
+ const t = i / steps
226
+ const midX = this.lastPoint.x + (x - this.lastPoint.x) * t
227
+ const midY = this.lastPoint.y + (y - this.lastPoint.y) * t
228
+
229
+ this.ctx.lineTo(midX, midY)
230
+ this.ctx.stroke()
231
+ this.currentPath.push({
232
+ x: midX,
233
+ y: midY,
234
+ type: 'move'
235
+ })
236
+ }
237
+ } else {
238
+ this.ctx.lineTo(x, y)
239
+ this.ctx.stroke()
240
+ this.currentPath.push({
241
+ x,
242
+ y,
243
+ type: 'move'
244
+ })
245
+ }
246
+
247
+ this.ctx.draw(true)
248
+
249
+ // 更新上一个点
250
+ this.lastPoint = { x, y }
251
+ },
252
+
253
+ touchEnd(e) {
254
+ if (!this.isDrawing || !this.ctx) return
255
+
256
+ this.isDrawing = false
257
+ this.ctx.closePath()
258
+ this.lastPoint = null
259
+
260
+ // 将当前路径加入栈中用于回退
261
+ if (this.currentPath.length > 0) {
262
+ this.pathStack.push([...this.currentPath])
263
+ }
264
+ },
265
+
266
+ // 获取相对于canvas的坐标点
267
+ getCanvasPoint(e) {
268
+ const touch = e.touches[0]
269
+ const rect = uni.createSelectorQuery().in(this).select('.u-signature__canvas').boundingClientRect()
270
+
271
+ return new Promise((resolve) => {
272
+ rect.boundingClientRect(data => {
273
+ const x = touch.x - data.left
274
+ const y = touch.y - data.top
275
+ resolve({ x, y })
276
+ }).exec()
277
+ })
278
+ },
279
+
280
+ // 同步获取canvas坐标点(兼容处理)
281
+ getCanvasPoint(e) {
282
+ const touch = e.touches[0]
283
+ const canvas = uni.createSelectorQuery().in(this).select('.u-signature__canvas')
284
+
285
+ // 返回一个包含坐标的对象
286
+ return {
287
+ x: touch.x,
288
+ y: touch.y
289
+ }
290
+ },
291
+
292
+ // 选择颜色
293
+ selectColor(color) {
294
+ this.lineColor = color
295
+ },
296
+
297
+ // 回退操作
298
+ undo() {
299
+ if (this.pathStack.length === 0) return
300
+
301
+ // 弹出最后一个路径
302
+ this.pathStack.pop()
303
+
304
+ // 重新绘制
305
+ this.redraw()
306
+ },
307
+
308
+ // 重新绘制所有路径
309
+ redraw() {
310
+ this.clearCanvas()
311
+
312
+ if (this.pathStack.length === 0) {
313
+ this.isEmpty = true
314
+ return
315
+ }
316
+
317
+ this.isEmpty = false
318
+
319
+ // #ifndef APP-NVUE
320
+ this.pathStack.forEach(path => {
321
+ if (path.length === 0) return
322
+
323
+ this.ctx.beginPath()
324
+ this.ctx.setLineCap('round')
325
+ this.ctx.setLineJoin('round')
326
+
327
+ let lastPoint = null
328
+ path.forEach((point, index) => {
329
+ if (index === 0 && point.type === 'start') {
330
+ // 设置起始点样式
331
+ this.ctx.setStrokeStyle(point.color)
332
+ this.ctx.setLineWidth(point.width)
333
+ this.ctx.moveTo(point.x, point.y)
334
+ lastPoint = { x: point.x, y: point.y }
335
+ } else if (point.type === 'move') {
336
+ this.ctx.lineTo(point.x, point.y)
337
+ lastPoint = { x: point.x, y: point.y }
338
+ }
339
+ })
340
+
341
+ this.ctx.stroke()
342
+ this.ctx.draw(true)
343
+ })
344
+ // #endif
345
+ },
346
+
347
+ // 清空画布
348
+ clear() {
349
+ this.pathStack = []
350
+ this.currentPath = []
351
+ this.isEmpty = true
352
+ this.lastPoint = null
353
+ this.clearCanvas()
354
+ },
355
+
356
+ // 清空画布内容
357
+ clearCanvas() {
358
+ if (!this.ctx) return
359
+
360
+ // #ifndef APP-NVUE
361
+ this.ctx.setFillStyle(this.bgColor)
362
+ this.ctx.fillRect(0, 0, this.canvasWidth, this.canvasHeight)
363
+ this.ctx.draw()
364
+ // #endif
365
+ },
366
+
367
+ // 导出签名图片
368
+ exportSignature() {
369
+ if (this.isEmpty) return
370
+
371
+ // #ifndef APP-NVUE
372
+ uni.canvasToTempFilePath({
373
+ canvasId: this.canvasId,
374
+ fileType: 'png',
375
+ quality: 1,
376
+ success: (res) => {
377
+ this.$emit('confirm', res.tempFilePath)
378
+ },
379
+ fail: (err) => {
380
+ this.$emit('error', err)
381
+ }
382
+ }, this)
383
+ // #endif
384
+
385
+ // #ifdef APP-NVUE
386
+ // NVUE环境下可能需要特殊处理
387
+ // #endif
388
+ },
389
+
390
+ // 切换笔画设置显示
391
+ toggleBrushSettings() {
392
+ this.showBrushSettings = !this.showBrushSettings;
393
+ if (this.showBrushSettings) {
394
+ this.showColorSettings = false;
395
+ }
396
+ },
397
+
398
+ // 切换颜色设置显示
399
+ toggleColorSettings() {
400
+ this.showColorSettings = !this.showColorSettings;
401
+ if (this.showColorSettings) {
402
+ this.showBrushSettings = false;
403
+ }
404
+ },
405
+ }
406
+ }
407
+ </script>
408
+
409
+ <style lang="scss" scoped>
410
+ .u-signature {
411
+ display: flex;
412
+ flex-direction: column;
413
+
414
+ &__canvas-wrap {
415
+ border: 1px solid #e0e0e0;
416
+ border-radius: 4px;
417
+ overflow: hidden;
418
+ }
419
+
420
+ &__canvas {
421
+ width: 100%;
422
+ height: 100%;
423
+ }
424
+
425
+ &__toolbar {
426
+ margin-top: 5px;
427
+ background-color: #fff;
428
+ }
429
+
430
+ &__toolbar-icons {
431
+ display: flex;
432
+ justify-content: space-between;
433
+ align-items: center;
434
+ padding: 1px 0;
435
+ // border: 1px solid #e0e0e0;
436
+ border-radius: 4px;
437
+ }
438
+
439
+ &__toolbar-icon {
440
+ padding: 5px;
441
+ }
442
+
443
+ &__brush-settings,
444
+ &__color-settings {
445
+ margin-top: 15px;
446
+ padding: 1px;
447
+ // border: 1px solid #e0e0e0;
448
+ border-radius: 4px;
449
+ }
450
+
451
+ &__progress {
452
+ &-label {
453
+ display: block;
454
+ margin-bottom: 10px;
455
+ font-size: 14px;
456
+ color: #999;
457
+ }
458
+ }
459
+
460
+ &__color-picker {
461
+ margin-bottom: 10px;
462
+ }
463
+
464
+ &__color-label {
465
+ display: block;
466
+ margin-bottom: 10px;
467
+ font-size: 14px;
468
+ color: #999;
469
+ }
470
+
471
+ &__colors {
472
+ display: flex;
473
+ flex-direction: row;
474
+ flex-wrap: wrap;
475
+ gap: 10px;
476
+ }
477
+
478
+ &__color-item {
479
+ width: 30px;
480
+ height: 30px;
481
+ border-radius: 50%;
482
+ border: 2px solid #f0f0f0;
483
+ cursor: pointer;
484
+
485
+ &--active {
486
+ border-color: #2979ff;
487
+ transform: scale(1.1);
488
+ }
489
+ }
490
+
491
+ &__actions {
492
+ display: flex;
493
+ flex-direction: row;
494
+ gap: 10px;
495
+ justify-content: center;
496
+ }
497
+ }
498
+ </style>
@@ -60,7 +60,7 @@ export const props = defineMixin({
60
60
  },
61
61
  // 是否禁用
62
62
  disabled: {
63
- type: String,
63
+ type: Boolean,
64
64
  default: () => defProps.subsection.disabled
65
65
  }
66
66
  }
package/package.json CHANGED
@@ -2,8 +2,8 @@
2
2
  "id": "uview-plus",
3
3
  "name": "uview-plus",
4
4
  "displayName": "零云®uview-plus3.0重磅发布,全面的Vue3鸿蒙跨端移动组件库,组件丰富维护更新稳定。",
5
- "version": "3.4.85",
6
- "description": "零云®uview-plus已兼容vue3,100+全面的组件和便捷的工具会让您信手拈来。近期新增拖动排序、条码、图片裁剪、下拉刷新、虚拟列表等。",
5
+ "version": "3.4.91",
6
+ "description": "零云®uview-plus已兼容vue3,100+全面的组件和便捷的工具会让您信手拈来。近期新增拖动排序、条码、图片裁剪、下拉刷新、虚拟列表、签名等。",
7
7
  "keywords": [
8
8
  "uview",
9
9
  "uview-plus",