uview-plus 3.4.19 → 3.4.22

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,29 @@
1
+ ## 3.4.22(2025-04-22)
2
+ fix: 修复自动上传偶发的success被覆盖为uploading
3
+
4
+ fix: float-button缺少key #677
5
+
6
+ fix: upload组件完善优化(感谢@semdy)
7
+
8
+ fix: toolbar组件confirmColor属性默认改为空,以便默认使用主题色、标题字体加粗(感谢@semdy)
9
+
10
+ ## 3.4.21(2025-04-21)
11
+ feat: subsection分段器支持双向绑定current
12
+
13
+ feat: select组件支持maxHeight属性
14
+
15
+ feat: datetime-picker支持inputBorder属性
16
+
17
+ ## 3.4.20(2025-04-17)
18
+ fix: 修复navbar-mini提示border不存在
19
+
20
+ feat: status-bar支持对外暴露状态栏高度值
21
+
22
+ feat: upload支持自定义自动上传后处理逻辑便于对接不同规范后端
23
+
24
+ feat: 优化tag组件插槽
25
+
26
+
1
27
  ## 3.4.19(2025-04-14)
2
28
  fix: 修复model组件增加contentStyle带来的语法问题
3
29
 
@@ -5,7 +5,11 @@ export const props = defineMixin({
5
5
  // 是否显示input
6
6
  hasInput: {
7
7
  type: Boolean,
8
- default: () => false
8
+ default: false
9
+ },
10
+ inputBorder: {
11
+ type: String,
12
+ default: 'surround'
9
13
  },
10
14
  disabled: {
11
15
  type: Boolean,
@@ -7,7 +7,7 @@
7
7
  <up-input
8
8
  :placeholder="placeholder"
9
9
  :readonly="!!showByClickInput"
10
- border="surround"
10
+ :border="inputBorder"
11
11
  v-model="inputValue"
12
12
  :disabled="disabled"
13
13
  :disabledColor="disabledColor"
@@ -25,7 +25,7 @@
25
25
  bottom: height
26
26
  }">
27
27
  <slot name="list">
28
- <template v-for="item in list">
28
+ <template :key="index" v-for="(item, index) in list">
29
29
  <view class="u-float-button__item" :style="{
30
30
  backgroundColor: item?.backgroundColor ? item?.backgroundColor : backgroundColor,
31
31
  color: item?.color ? item?.color : color,
@@ -6,7 +6,6 @@
6
6
  ></u-status-bar>
7
7
  <view
8
8
  class="u-navbar-mini__content"
9
- :class="[border && 'u-border-bottom']"
10
9
  :style="{
11
10
  height: addUnit(height),
12
11
  backgroundColor: bgColor,
@@ -22,9 +22,10 @@
22
22
  :duration="duration + 50"
23
23
  :customStyle="overlayStyle"
24
24
  :opacity="overlayOpacity"
25
+ @touchmove.stop.prevent="noop"
25
26
  ></u-overlay>
26
27
  <view class="u-select__options__wrap"
27
- :style="{ zIndex: zIndex + 1, left: optionsWrapLeft, right: optionsWrapRight }">
28
+ :style="{ overflowY: 'auto', zIndex: zIndex + 1, left: optionsWrapLeft, right: optionsWrapRight, maxHeight: maxHeight}">
28
29
  <view class="u-select__options" v-if="isOpen">
29
30
  <slot name="options">
30
31
  <view class="u-select__options_item"
@@ -50,6 +51,10 @@ export default {
50
51
  name:"up-select",
51
52
  emits: ['update:current', 'select'],
52
53
  props: {
54
+ maxHeight: {
55
+ type: String,
56
+ default: '90vh'
57
+ },
53
58
  overlay: {
54
59
  type: Boolean,
55
60
  default: true
@@ -5,6 +5,11 @@ export const props = defineMixin({
5
5
  bgColor: {
6
6
  type: String,
7
7
  default: () => defProps.statusBar.bgColor
8
- }
8
+ },
9
+ // 状态栏获取得高度
10
+ height: {
11
+ type: Number,
12
+ default: () => defProps.statusBar.height
13
+ }
9
14
  }
10
15
  })
@@ -10,6 +10,7 @@
10
10
  export default {
11
11
  // statusBar
12
12
  statusBar: {
13
- bgColor: 'transparent'
13
+ bgColor: 'transparent',
14
+ height: 0
14
15
  }
15
16
  }
@@ -34,11 +34,13 @@
34
34
  this.isH5 = true
35
35
  // #endif
36
36
  },
37
+ emits: ['update:height'],
37
38
  computed: {
38
39
  style() {
39
40
  const style = {}
40
41
  // 状态栏高度,由于某些安卓和微信开发工具无法识别css的顶部状态栏变量,所以使用js获取的方式
41
42
  let sheight = getWindowInfo().statusBarHeight
43
+ this.$emit('update:height', sheight)
42
44
  if (sheight == 0) {
43
45
  this.isH5 = true
44
46
  } else {
@@ -189,7 +189,7 @@ export default {
189
189
  beforeUnmount() {
190
190
  uni.offWindowResize(this.windowResizeCallback)
191
191
  },
192
- emits: ["change"],
192
+ emits: ["change", "update:current"],
193
193
  methods: {
194
194
  addStyle,
195
195
  init() {
@@ -217,7 +217,8 @@ export default {
217
217
  // #endif
218
218
  },
219
219
  clickHandler(index) {
220
- this.innerCurrent = index
220
+ this.innerCurrent = index;
221
+ this.$emit('update:current', index);
221
222
  this.$emit("change", index);
222
223
  },
223
224
  },
@@ -32,15 +32,29 @@
32
32
  ></u-icon>
33
33
  </view>
34
34
  </slot>
35
- <text
36
- class="u-tag__text"
37
- :style="[textColor]"
38
- :class="[`u-tag__text--${type}`, plain && `u-tag__text--${type}--plain`, `u-tag__text--${size}`]"
39
- >
40
- <slot>
41
- {{ text }}
35
+ <view class="u-tag__content">
36
+ <slot name="content">
42
37
  </slot>
43
- </text>
38
+ <template v-if="!$slots.content">
39
+ <text
40
+ v-if="!$slots.default && !$slots.$default"
41
+ class="u-tag__text"
42
+ :style="[textColor]"
43
+ :class="[`u-tag__text--${type}`, plain && `u-tag__text--${type}--plain`, `u-tag__text--${size}`]"
44
+ >
45
+ {{ text }}
46
+ </text>
47
+ <text
48
+ v-else
49
+ class="u-tag__text"
50
+ :style="[textColor]"
51
+ :class="[`u-tag__text--${type}`, plain && `u-tag__text--${type}--plain`, `u-tag__text--${size}`]"
52
+ >
53
+ <slot>
54
+ </slot>
55
+ </text>
56
+ </template>
57
+ </view>
44
58
  </view>
45
59
  <view
46
60
  class="u-tag__close"
@@ -14,7 +14,7 @@ export default {
14
14
  cancelText: '取消',
15
15
  confirmText: '确认',
16
16
  cancelColor: '#909193',
17
- confirmColor: '#3c9cff',
17
+ confirmColor: '',
18
18
  title: ''
19
19
  }
20
20
 
@@ -106,6 +106,7 @@
106
106
  color: $u-main-color;
107
107
  padding: 0 60rpx;
108
108
  font-size: 16px;
109
+ font-weight: bold;
109
110
  flex: 1;
110
111
  text-align: center;
111
112
  }
@@ -163,5 +163,10 @@ export const props = defineMixin({
163
163
  type: Boolean,
164
164
  default: () => defProps.upload.getVideoThumb
165
165
  },
166
+ // 自定义自动上传后处理
167
+ customAfterAutoUpload: {
168
+ type: Boolean,
169
+ default: () => defProps.upload.customAfterAutoUpload
170
+ },
166
171
  }
167
172
  })
@@ -39,7 +39,7 @@
39
39
  v-else
40
40
  color="#80CBF9"
41
41
  size="26"
42
- :name="item.isVideo || (item.type && item.type === 'video') ? 'movie' : 'folder'"
42
+ :name="item.isVideo || (item.type && item.type === 'video') ? 'movie' : 'file-text'"
43
43
  ></u-icon>
44
44
  <view v-if="item.status === 'success'"
45
45
  class="u-upload__wrap__play"
@@ -54,6 +54,10 @@
54
54
  v-else
55
55
  class="u-upload__wrap__preview__other"
56
56
  @tap="onClickPreview(item, index)"
57
+ :style="[{
58
+ width: addUnit(width),
59
+ height: addUnit(height)
60
+ }]"
57
61
  >
58
62
  <u-icon
59
63
  color="#80CBF9"
@@ -61,7 +65,7 @@
61
65
  :name="item.isVideo || (item.type && item.type === 'video') ? 'movie' : 'folder'"
62
66
  ></u-icon>
63
67
  <text class="u-upload__wrap__preview__other__text">
64
- {{item.isVideo || (item.type && item.type === 'video') ? '视频' : '文件'}}
68
+ {{item.isVideo || (item.type && item.type === 'video') ? item.name || '视频' : item.name || '文件'}}
65
69
  </text>
66
70
  </view>
67
71
  <view
@@ -78,7 +82,6 @@
78
82
  <u-loading-icon
79
83
  size="22"
80
84
  mode="circle"
81
- color="#ffffff"
82
85
  v-else
83
86
  />
84
87
  </view>
@@ -263,7 +266,7 @@
263
266
  }
264
267
  },
265
268
  // #ifdef VUE3
266
- emits: ['error', 'beforeRead', 'oversize', 'afterRead', 'delete', 'clickPreview', 'update:fileList'],
269
+ emits: ['error', 'beforeRead', 'oversize', 'afterRead', 'delete', 'clickPreview', 'update:fileList', 'afterAutoUpload'],
267
270
  // #endif
268
271
  methods: {
269
272
  addUnit,
@@ -327,18 +330,19 @@
327
330
  const {
328
331
  fileList = [], maxCount
329
332
  } = this;
330
- const lists = fileList.map((item) =>
331
- Object.assign(Object.assign({}, item), {
333
+ const lists = fileList.map((item) => {
334
+ const name = item.name || item.url || item.thumb
335
+ return Object.assign(Object.assign({}, item), {
332
336
  // 如果item.url为本地选择的blob文件的话,无法判断其为video还是image,此处优先通过accept做判断处理
333
- isImage: this.accept === 'image' || test.image(item.url || item.thumb),
334
- isVideo: this.accept === 'video' || test.video(item.url || item.thumb),
335
- deletable: typeof(item.deletable) === 'boolean' ? item.deletable : this.deletable,
337
+ isImage: name ? test.image(name) : (this.accept === 'image' || test.image(name)),
338
+ isVideo: name ? test.video(name) : (this.accept === 'video' || test.video(name)),
339
+ deletable: typeof item.deletable === 'boolean' ? item.deletable : this.deletable,
336
340
  })
337
- );
341
+ });
338
342
  this.lists = lists
339
343
  this.isInCount = lists.length < maxCount
340
344
  },
341
- chooseFile() {
345
+ chooseFile(params) {
342
346
  const {
343
347
  maxCount,
344
348
  multiple,
@@ -358,13 +362,14 @@
358
362
  accept: this.accept,
359
363
  extension: this.extension,
360
364
  multiple: this.multiple,
361
- capture: capture,
365
+ capture: this.capture,
362
366
  compressed: this.compressed,
363
367
  maxDuration: this.maxDuration,
364
368
  sizeType: this.sizeType,
365
369
  camera: this.camera,
366
370
  }, {
367
371
  maxCount: maxCount - lists.length,
372
+ ...params
368
373
  })
369
374
  )
370
375
  .then((res) => {
@@ -406,7 +411,7 @@
406
411
  if (test.promise(res)) {
407
412
  res.then((data) => this.onAfterRead(data || file));
408
413
  } else {
409
- this.onAfterRead(file);
414
+ this.onAfterRead(res);
410
415
  }
411
416
  },
412
417
  getDetail(index) {
@@ -491,12 +496,29 @@
491
496
  // header: header,
492
497
  formData: formData,
493
498
  success: (uploadFileRes) => {
494
- result = res0.data.params.host + '/' + res0.data.params.key;
495
499
  let thumb = '';
496
- if (this.accept === 'video' || test.video(result)) {
497
- thumb = result + '?x-oss-process=video/snapshot,t_10000,m_fast';
500
+ let afterPromise = '';
501
+ if (that.customAfterAutoUpload) {
502
+ afterPromise = new Promise((resolve, reject) => {
503
+ that.$emit(
504
+ 'afterAutoUpload',
505
+ Object.assign(res0, {
506
+ callback: (r) => {
507
+ r.url ? resolve(r) : reject();
508
+ },
509
+ })
510
+ );
511
+ });
512
+ }
513
+ if (test.promise(afterPromise)) {
514
+ afterPromise.then((data) => that.succcessUpload(len + j, data.url, data.thumb));
515
+ } else {
516
+ result = res0.data.params.host + '/' + res0.data.params.key;
517
+ if (that.accept === 'video' || test.video(result)) {
518
+ thumb = result + '?x-oss-process=video/snapshot,t_10000,m_fast';
519
+ }
520
+ that.succcessUpload(len + j, result, thumb);
498
521
  }
499
- that.succcessUpload(len + j, result, thumb);
500
522
  }
501
523
  });
502
524
  uploadTask.onProgressUpdate((res) => {
@@ -517,9 +539,33 @@
517
539
  name: 'file',
518
540
  // fileType: 'video', // 仅支付宝小程序,且必填。
519
541
  header: this.autoUploadHeader,
520
- success: (r) => {
521
- result = res0.data.params.host + '/' + res0.data.params.key;
522
- that.succcessUpload(len + j, result);
542
+ success: (uploadFileRes) => {
543
+ let res0 = uploadFileRes.data;
544
+ let afterPromise = '';
545
+ if (that.customAfterAutoUpload) {
546
+ afterPromise = new Promise((resolve, reject) => {
547
+ that.$emit(
548
+ 'afterAutoUpload',
549
+ Object.assign(res0, {
550
+ callback: (r) => {
551
+ r.url ? resolve(r) : reject();
552
+ }
553
+ })
554
+ );
555
+ });
556
+ }
557
+ if (test.promise(afterPromise)) {
558
+ afterPromise.then((data) => that.succcessUpload(len + j, data.url));
559
+ } else {
560
+ if (res0.code != 200) {
561
+ uni.showToast({
562
+ title: res0.msg
563
+ });
564
+ } else {
565
+ result = res0.data.url;
566
+ that.succcessUpload(len + j, result);
567
+ }
568
+ }
523
569
  }
524
570
  });
525
571
  uploadTask.onProgressUpdate((res) => {
@@ -546,7 +592,8 @@
546
592
  let item = this.fileList[index];
547
593
  this.fileList.splice(index, 1, {
548
594
  ...item,
549
- status: 'uploading',
595
+ // 注意这里不判断会出现succcessUpload先执行又被覆盖的问题
596
+ status: param.progress == 100 ? 'success' : 'uploading',
550
597
  message: '',
551
598
  progress: param.progress
552
599
  });
@@ -39,6 +39,7 @@ export default {
39
39
  autoUploadAuthUrl: '',
40
40
  autoUploadDriver: '',
41
41
  autoUploadHeader: {},
42
- getVideoThumb: false
42
+ getVideoThumb: false,
43
+ customAfterAutoUpload: false
43
44
  }
44
45
  }
@@ -89,6 +89,11 @@ export function chooseFile({
89
89
  maxCount,
90
90
  extension
91
91
  }) {
92
+ try {
93
+ capture = test.array(capture) ? capture : capture.split(',');
94
+ } catch(e) {
95
+ capture = [];
96
+ }
92
97
  return new Promise((resolve, reject) => {
93
98
  switch (accept) {
94
99
  case 'image':
package/index.js CHANGED
@@ -84,7 +84,15 @@ export const mount$u = function() {
84
84
  uni.$u = $u
85
85
  }
86
86
 
87
- // #ifdef H5
87
+ function toCamelCase(str) {
88
+ return str.replace(/-([a-z])/g, function(match, group1) {
89
+ return group1.toUpperCase();
90
+ }).replace(/^[a-z]/, function(match) {
91
+ return match.toUpperCase();
92
+ });
93
+ }
94
+
95
+ // #ifdef APP || H5
88
96
  const importFn = import.meta.glob('./components/u-*/u-*.vue', { eager: true })
89
97
  let components = [];
90
98
 
@@ -102,18 +110,13 @@ for (const key in importFn) {
102
110
  }
103
111
  // #endif
104
112
 
105
- function toCamelCase(str) {
106
- return str.replace(/-([a-z])/g, function(match, group1) {
107
- return group1.toUpperCase();
108
- }).replace(/^[a-z]/, function(match) {
109
- return match.toUpperCase();
110
- });
111
- }
112
-
113
113
  const install = (Vue, upuiParams = '') => {
114
- // #ifdef H5
114
+ // #ifdef APP || H5
115
115
  components.forEach(function(component) {
116
116
  const name = component.name.replace(/u-([a-zA-Z0-9-_]+)/g, 'up-$1');
117
+ if (name != component.name) {
118
+ Vue.component(component.name, component);
119
+ }
117
120
  Vue.component(name, component);
118
121
  });
119
122
  // #endif
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "id": "uview-plus",
3
3
  "name": "uview-plus",
4
4
  "displayName": "零云®uview-plus3.0重磅发布,全面的Vue3鸿蒙移动组件库。",
5
- "version": "3.4.19",
5
+ "version": "3.4.22",
6
6
  "description": "零云®uview-plus已兼容vue3,全面的组件和便捷的工具会让您信手拈来,如鱼得水。",
7
7
  "keywords": [
8
8
  "uview",