uview-plus 3.8.126 → 3.8.128

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 (67) hide show
  1. package/README.md +13 -0
  2. package/changelog.md +39 -0
  3. package/components/u-calendar/month.vue +71 -35
  4. package/components/u-calendar/u-calendar.vue +67 -31
  5. package/components/u-car-keyboard/u-car-keyboard.vue +21 -8
  6. package/components/u-cate-tab/u-cate-tab.vue +56 -11
  7. package/components/u-cell/u-cell.vue +20 -16
  8. package/components/u-collapse-item/u-collapse-item.vue +34 -11
  9. package/components/u-datetime-picker/u-datetime-picker.vue +16 -0
  10. package/components/u-dropdown/u-dropdown.vue +15 -0
  11. package/components/u-grid/u-grid.vue +9 -0
  12. package/components/u-grid-item/u-grid-item.vue +8 -0
  13. package/components/u-icon/util.js +16 -1
  14. package/components/u-list/u-list.vue +59 -8
  15. package/components/u-list-item/u-list-item.vue +13 -3
  16. package/components/u-number-keyboard/u-number-keyboard.vue +18 -6
  17. package/components/u-parse/parser.js +4 -3
  18. package/components/u-picker/u-picker.vue +6 -3
  19. package/components/u-search/u-search.vue +15 -5
  20. package/components/u-select/u-select.vue +206 -31
  21. package/components/u-slider/u-slider.vue +21 -12
  22. package/components/u-steps-item/u-steps-item.vue +9 -0
  23. package/components/u-tabbar-item/u-tabbar-item.vue +12 -1
  24. package/components/u-tabs/u-tabs.vue +5 -1
  25. package/components/u-text/u-text.vue +34 -32
  26. package/components/u-textarea/props.js +5 -0
  27. package/components/u-textarea/textarea.js +4 -1
  28. package/components/u-textarea/u-textarea.vue +3 -1
  29. package/components/u-upload/u-upload.vue +15 -4
  30. package/components/u-upload/utils.js +4 -1
  31. package/components/u-video/danmaku.js +97 -0
  32. package/components/u-video/props.js +205 -0
  33. package/components/u-video/u-video.vue +1427 -0
  34. package/components/u-video/video-danmaku.vue +273 -0
  35. package/components/u-video/video-slider.vue +179 -0
  36. package/components/u-video/video-utils.js +98 -0
  37. package/components/u-video/video.js +72 -0
  38. package/libs/config/props.js +2 -1
  39. package/libs/function/test.js +2 -0
  40. package/libs/i18n/locales/de.js +14 -1
  41. package/libs/i18n/locales/en.js +14 -1
  42. package/libs/i18n/locales/es.js +14 -1
  43. package/libs/i18n/locales/fr.js +14 -1
  44. package/libs/i18n/locales/ja.js +14 -1
  45. package/libs/i18n/locales/ko.js +14 -1
  46. package/libs/i18n/locales/ru.js +14 -1
  47. package/libs/i18n/locales/th.js +14 -1
  48. package/libs/i18n/locales/zh-Hans.js +14 -1
  49. package/libs/i18n/locales/zh-Hant.js +14 -1
  50. package/libs/mixin/mixin.js +12 -7
  51. package/package.json +1 -1
  52. package/skills/README.md +66 -0
  53. package/skills/uview-plus/SKILL.md +95 -0
  54. package/skills/uview-plus/references/api.md +105 -0
  55. package/skills/uview-plus/references/components.md +156 -0
  56. package/skills/uview-plus/references/icons.md +223 -0
  57. package/skills/uview-plus/references/pitfalls.md +77 -0
  58. package/skills/uview-plus/references/setup.md +168 -0
  59. package/types/comps/form.d.ts +44 -8
  60. package/types/comps/list.d.ts +1 -1
  61. package/types/comps/listItem.d.ts +1 -1
  62. package/types/comps/numberBox.d.ts +85 -21
  63. package/types/comps/pagination.d.ts +96 -0
  64. package/types/comps/swipeActionItem.d.ts +3 -4
  65. package/types/comps/textarea.d.ts +9 -3
  66. package/types/comps.d.ts +1 -0
  67. package/types/index.d.ts +5 -0
@@ -3,7 +3,7 @@
3
3
  <view :class="['u-select__content', disabled && 'disabled']">
4
4
  <view :class="['u-select__label', border && 'u-select__label--border']" :style="selectLabelStyle" @click="labelClick">
5
5
  <slot name="text" :currentLabel="currentLabel">
6
- <text class="u-select__text" :style="{ color: resolvedTextColor }" v-if="showOptionsLabel">
6
+ <text class="u-select__text" :style="{ color: resolvedTextColor }" v-if="showOptionsLabel && currentLabel">
7
7
  {{ currentLabel }}
8
8
  </text>
9
9
  <text class="u-select__text" :style="{ color: resolvedTextColor }" v-else>
@@ -16,17 +16,25 @@
16
16
  </view>
17
17
  <up-overlay :show="isOpen" @click="overlayClick" v-if="overlay" :zIndex="zIndex" :duration="duration + 50"
18
18
  :customStyle="overlayStyle" :opacity="overlayOpacity" @touchmove.stop.prevent="noop"></up-overlay>
19
+ <view class="u-select__arrow" :class="'u-select__arrow--' + optionsPlacement" :style="arrowStyle"
20
+ v-if="isOpen && arrow">
21
+ <view class="u-select__arrow__outer"></view>
22
+ <view class="u-select__arrow__inner"></view>
23
+ </view>
19
24
  <view class="u-select__options__wrap" :style="optionsWrapStyle">
20
25
  <view class="u-select__options" :style="optionsStyle" v-if="isOpen">
21
26
  <slot name="options">
22
- <view class="u-select__options_item" :class="current == item[keyName] ? 'active': ''"
23
- :style="{ color: resolvedItemColor }"
27
+ <view class="u-select__options_item" :class="isSelected(item) ? 'active': ''"
28
+ :style="{ color: itemTextColor(item) }"
24
29
  :key="index" v-for="(item, index) in options" @click="selectItem(item)">
25
- <slot name="optionItem" :item="item">
26
- <text class="u-select__item_text" :style="{color: resolvedItemColor}">
27
- {{item[labelName]}}
28
- </text>
29
- </slot>
30
+ <view class="u-select__options_item__body">
31
+ <slot name="optionItem" :item="item" :selected="isSelected(item)">
32
+ <text class="u-select__item_text" :style="{color: itemTextColor(item)}">
33
+ {{item[labelName]}}
34
+ </text>
35
+ </slot>
36
+ </view>
37
+ <up-icon v-if="isSelected(item)" name="checkbox-mark" size="15px" :color="resolvedActiveColor"></up-icon>
30
38
  </view>
31
39
  </slot>
32
40
  </view>
@@ -99,8 +107,9 @@
99
107
  type: Boolean,
100
108
  default: false
101
109
  },
110
+ // 当前选中值,multiple 时传数组
102
111
  current: {
103
- type: [String, Number],
112
+ type: [String, Number, Array],
104
113
  default: ''
105
114
  },
106
115
  zIndex: {
@@ -133,19 +142,45 @@
133
142
  optionsWidth: {
134
143
  type: [String, Number],
135
144
  default: ''
145
+ },
146
+ // 下拉面板展开方向:auto 按上下剩余空间自动选择,bottom 固定向下,top 固定向上
147
+ placement: {
148
+ type: String,
149
+ default: 'auto'
150
+ },
151
+ // 是否显示下拉面板指向触发区的三角形指示器
152
+ arrow: {
153
+ type: Boolean,
154
+ default: true
155
+ },
156
+ // 是否多选,多选时 current 为数组且选中后面板不关闭
157
+ multiple: {
158
+ type: Boolean,
159
+ default: false
160
+ },
161
+ // 选中项的文字与勾选图标颜色,默认取主题主色
162
+ activeColor: {
163
+ type: String,
164
+ default: ''
136
165
  }
137
166
  },
138
167
  data() {
139
168
  return {
140
169
  isOpen: false,
141
170
  optionsWrapLeft: 'auto',
142
- optionsWrapRight: 'auto'
171
+ optionsWrapRight: 'auto',
172
+ // 展开方向与空间不足时的限高,都在打开时按剩余空间重新计算
173
+ optionsPlacement: 'bottom',
174
+ optionsWrapMaxHeight: ''
143
175
  }
144
176
  },
145
177
  computed: {
146
178
  resolvedItemColor() {
147
179
  return this.itemColor || this.upThemeVar('--up-main-color', '#303133');
148
180
  },
181
+ resolvedActiveColor() {
182
+ return this.activeColor || this.upThemeVar('--up-primary', '#3c9cff');
183
+ },
149
184
  resolvedTextColor() {
150
185
  return this.upThemeVar('--up-main-color', '#303133');
151
186
  },
@@ -174,14 +209,25 @@
174
209
  }
175
210
  return style;
176
211
  },
212
+ panelGap() {
213
+ // 显示指示器时把间距留成三角形的高度,让它正好顶住面板边框
214
+ return this.arrow ? 6 : 4;
215
+ },
177
216
  optionsWrapStyle() {
178
217
  const style = {
179
218
  overflowY: 'auto',
180
219
  zIndex: this.zIndex + 1,
181
220
  left: this.optionsWrapLeft,
182
221
  right: this.optionsWrapRight,
183
- maxHeight: this.maxHeight
222
+ maxHeight: this.optionsWrapMaxHeight || this.maxHeight
184
223
  };
224
+ if (this.optionsPlacement === 'top') {
225
+ style.top = 'auto';
226
+ style.bottom = `calc(100% + ${this.panelGap}px)`;
227
+ } else {
228
+ style.top = `calc(100% + ${this.panelGap}px)`;
229
+ style.bottom = 'auto';
230
+ }
185
231
  if (this.normalizedOptionsWidth) {
186
232
  style.width = this.normalizedOptionsWidth;
187
233
  }
@@ -195,15 +241,36 @@
195
241
  }
196
242
  return style;
197
243
  },
244
+ arrowStyle() {
245
+ // 指示器画在滚动容器外面,否则会被面板自身的 overflow 裁掉
246
+ const style = {
247
+ zIndex: this.zIndex + 2,
248
+ left: this.optionsWrapLeft === 'auto' ? 'auto' : '12px',
249
+ right: this.optionsWrapLeft === 'auto' ? '12px' : 'auto'
250
+ };
251
+ if (this.optionsPlacement === 'top') {
252
+ style.top = 'auto';
253
+ style.bottom = '100%';
254
+ } else {
255
+ style.top = '100%';
256
+ style.bottom = 'auto';
257
+ }
258
+ return style;
259
+ },
260
+ currentList() {
261
+ if (Array.isArray(this.current)) return this.current;
262
+ if (this.current === '' || this.current === null || typeof this.current === 'undefined') return [];
263
+ return [this.current];
264
+ },
198
265
  currentLabel() {
199
- let name = '';
266
+ const names = [];
200
267
  this.options.forEach((ele) => {
201
- if (ele[this.keyName] === this.current) {
202
- name = ele[this.labelName];
203
- }
204
- });
205
- return name;
206
- }
268
+ if (this.isSelected(ele)) {
269
+ names.push(ele[this.labelName]);
270
+ }
271
+ });
272
+ return names.join('、');
273
+ }
207
274
  },
208
275
  methods: {
209
276
  labelClick() {
@@ -230,24 +297,76 @@
230
297
  if (!this.closeOnClickOverlay) return;
231
298
  this.closeSelect();
232
299
  },
300
+ isSelected(item) {
301
+ const value = item[this.keyName];
302
+ if (this.multiple) {
303
+ return this.currentList.some((ele) => ele == value);
304
+ }
305
+ // 没有选中值时不能走宽松比较,否则 0 == '' 会把 key 为 0 的项误标成选中
306
+ if (this.current === '' || this.current === null || typeof this.current === 'undefined') {
307
+ return false;
308
+ }
309
+ return this.current == value;
310
+ },
311
+ itemTextColor(item) {
312
+ // 选项颜色是行内绑定的,选中态必须走同一个绑定,否则会被行内样式盖掉
313
+ return this.isSelected(item) ? this.resolvedActiveColor : this.resolvedItemColor;
314
+ },
233
315
  selectItem(item) {
316
+ const value = item[this.keyName];
317
+ if (this.multiple) {
318
+ // 多选时保持面板展开,方便连续勾选
319
+ const list = this.currentList.slice();
320
+ const index = list.findIndex((ele) => ele == value);
321
+ if (index > -1) {
322
+ list.splice(index, 1);
323
+ } else {
324
+ list.push(value);
325
+ }
326
+ this.$emit('update:current', list);
327
+ this.$emit('select', item, list);
328
+ return;
329
+ }
234
330
  this.isOpen = false;
235
- this.$emit('update:current', item[this.keyName]);
331
+ this.$emit('update:current', value);
236
332
  this.$emit('select', item);
237
333
  },
238
334
  adjustOptionsWrapPosition() {
335
+ // 每次打开都从"左对齐、向下、不限高"重新算,避免上一次的结果影响这次的测量
239
336
  this.optionsWrapLeft = '0px';
240
337
  this.optionsWrapRight = 'auto';
241
- let wi = getWindowInfo();
242
- let windowWidth = wi.windowWidth;
243
- this.$uGetRect('.u-select__options__wrap').then(rect => {
244
- if (rect.left + rect.width > windowWidth) {
245
- // 如果右侧被遮挡,则调整到左侧
246
- this.optionsWrapLeft = 'auto';
247
- this.optionsWrapRight = `0px`;
248
- }
249
- });
250
- }
338
+ this.optionsWrapMaxHeight = '';
339
+ this.optionsPlacement = this.placement === 'top' ? 'top' : 'bottom';
340
+ this.$nextTick(() => {
341
+ if (!this.isOpen) return;
342
+ let wi = getWindowInfo();
343
+ let windowWidth = wi.windowWidth;
344
+ let windowHeight = wi.windowHeight;
345
+ Promise.all([
346
+ this.$uGetRect('.u-select__label'),
347
+ this.$uGetRect('.u-select__options__wrap')
348
+ ]).then(([labelRect, wrapRect]) => {
349
+ if (!this.isOpen || !labelRect || !wrapRect) return;
350
+ if (wrapRect.left + wrapRect.width > windowWidth) {
351
+ // 如果右侧被遮挡,则调整到左侧
352
+ this.optionsWrapLeft = 'auto';
353
+ this.optionsWrapRight = `0px`;
354
+ }
355
+ // boundingClientRect 的坐标以窗口可用区域为原点,直接和 windowHeight 比即可
356
+ const spaceBelow = windowHeight - labelRect.bottom - this.panelGap;
357
+ const spaceAbove = labelRect.top - this.panelGap;
358
+ if (this.placement === 'auto' && wrapRect.height > spaceBelow && spaceAbove > spaceBelow) {
359
+ // 下方放不下且上方更宽裕时翻到触发区上方
360
+ this.optionsPlacement = 'top';
361
+ }
362
+ // 选定方向后仍放不下就限高滚动,否则面板会溢出窗口,并把页面撑高
363
+ const space = this.optionsPlacement === 'top' ? spaceAbove : spaceBelow;
364
+ if (space > 0 && wrapRect.height > space) {
365
+ this.optionsWrapMaxHeight = `${Math.floor(space)}px`;
366
+ }
367
+ });
368
+ });
369
+ }
251
370
  }
252
371
  }
253
372
  </script>
@@ -287,10 +406,56 @@
287
406
  pointer-events: none;
288
407
  }
289
408
 
409
+ .u-select__arrow {
410
+ position: absolute;
411
+ width: 0;
412
+ height: 0;
413
+ pointer-events: none;
414
+
415
+ .u-select__arrow__outer,
416
+ .u-select__arrow__inner {
417
+ position: absolute;
418
+ left: 0;
419
+ /* 三角形以锚点为中心,左右对齐时都不会贴到面板边角 */
420
+ margin-left: -6px;
421
+ width: 0;
422
+ height: 0;
423
+ border-style: solid;
424
+ border-color: transparent;
425
+ }
426
+
427
+ &--bottom {
428
+ .u-select__arrow__outer {
429
+ top: 0;
430
+ border-width: 0 6px 6px 6px;
431
+ border-bottom-color: var(--up-border-color, #f1f1f1);
432
+ }
433
+
434
+ .u-select__arrow__inner {
435
+ // 往面板里挪 1px,盖掉面板自身的边框,拼出缺口效果
436
+ top: 1px;
437
+ border-width: 0 6px 6px 6px;
438
+ border-bottom-color: var(--up-card-bg-color, #fff);
439
+ }
440
+ }
441
+
442
+ &--top {
443
+ .u-select__arrow__outer {
444
+ bottom: 0;
445
+ border-width: 6px 6px 0 6px;
446
+ border-top-color: var(--up-border-color, #f1f1f1);
447
+ }
448
+
449
+ .u-select__arrow__inner {
450
+ bottom: 1px;
451
+ border-width: 6px 6px 0 6px;
452
+ border-top-color: var(--up-card-bg-color, #fff);
453
+ }
454
+ }
455
+ }
456
+
290
457
  .u-select__options__wrap {
291
- margin-bottom: 46px;
292
458
  position: absolute;
293
- top: calc(100% + 4px);
294
459
  left: 0;
295
460
  }
296
461
 
@@ -303,6 +468,8 @@
303
468
 
304
469
  .u-select__options_item {
305
470
  padding: 10px 12px;
471
+ display: flex;
472
+ align-items: center;
306
473
  box-sizing: border-box;
307
474
  width: 100%;
308
475
  height: 100%;
@@ -310,6 +477,14 @@
310
477
  &:hover {
311
478
  background-color: var(--up-bg-color, #f7f7f7);
312
479
  }
480
+
481
+ &.active {
482
+ background-color: var(--up-primary-light, #ecf5ff);
483
+ }
484
+
485
+ .u-select__options_item__body {
486
+ flex: 1;
487
+ }
313
488
 
314
489
  /* #ifdef H5 */
315
490
  &:hover {
@@ -21,7 +21,6 @@
21
21
  >
22
22
  </view>
23
23
  <view
24
- @click="onClick"
25
24
  class="u-slider__gap"
26
25
  :style="[
27
26
  barStyle,
@@ -52,7 +51,7 @@
52
51
  <template v-if="isRange">
53
52
  <view class="u-slider__button-wrap u-slider__button-wrap-0" @touchstart="onTouchStart($event, 0)"
54
53
  @touchmove="onTouchMove($event, 0)" @touchend="onTouchEnd($event, 0)"
55
- @touchcancel="onTouchEnd($event, 0)" :style="touchButtonStyle(barStyle0)">
54
+ @touchcancel="onTouchEnd($event, 0)" @click.stop="() => {}" :style="touchButtonStyle(barStyle0)">
56
55
  <slot name="min" v-if="$slots.min || $slots.$min"/>
57
56
  <view v-else class="u-slider__button" :style="[blockStyle, {
58
57
  height: getPx(blockSize, true),
@@ -63,7 +62,7 @@
63
62
  </template>
64
63
  <view class="u-slider__button-wrap" @touchstart="onTouchStart"
65
64
  @touchmove="onTouchMove" @touchend="onTouchEnd"
66
- @touchcancel="onTouchEnd" :style="touchButtonStyle(barStyle)">
65
+ @touchcancel="onTouchEnd" @click.stop="() => {}" :style="touchButtonStyle(barStyle)">
67
66
  <slot name="max" v-if="isRange && ($slots.max || $slots.$max)"/>
68
67
  <slot v-else-if="$slots.default || $slots.$default"/>
69
68
  <view v-else class="u-slider__button" :style="[blockStyle, {
@@ -153,8 +152,7 @@
153
152
  modelValue(n) {
154
153
  // 只有在非滑动状态时,才可以通过value更新滑块值,这里监听,是为了让用户触发
155
154
  if (this.status == 'end') {
156
- const $crtFmtValue = this.updateValue(this.modelValue, false);
157
- this.$emit('change', $crtFmtValue);
155
+ this.updateValue(this.modelValue, false);
158
156
  }
159
157
  },
160
158
  // #endif
@@ -162,8 +160,7 @@
162
160
  value(n) {
163
161
  // 只有在非滑动状态时,才可以通过value更新滑块值,这里监听,是为了让用户触发
164
162
  if (this.status == 'end') {
165
- const $crtFmtValue = this.updateValue(this.value, false);
166
- this.$emit('change', $crtFmtValue);
163
+ this.updateValue(this.value, false);
167
164
  }
168
165
  },
169
166
  // #endif
@@ -172,7 +169,6 @@
172
169
  if (this.status == 'end') {
173
170
  this.updateValue(this.rangeValue[0], false, 0);
174
171
  this.updateValue(this.rangeValue[1], false, 1);
175
- this.$emit('change', this.rangeValue);
176
172
  }
177
173
  },
178
174
  deep:true
@@ -414,16 +410,24 @@
414
410
  // nvue下暂时无法获取坐标
415
411
  const min = this.toSliderNumber(this.min)
416
412
  const max = this.toSliderNumber(this.max, 100)
413
+ const detail = event && event.detail ? event.detail : {}
414
+ const position = Number(this.vertical ? detail.y : detail.x)
415
+ const sliderSize = this.vertical ? this.sliderRect.height : this.sliderRect.width
416
+ // 少数端的tap没有坐标,滑块在弹窗里未显示时也量不到长度,
417
+ // 这两种情况下算出来的只会是min,别当成用户点了min发出去
418
+ if (!Number.isFinite(position) || !sliderSize) return
417
419
  if (this.vertical) {
418
- let clientY = event.detail.y - this.sliderRect.top
420
+ let clientY = position - this.sliderRect.top
419
421
  // console.log(this.sliderRect.top, event.detail.y)
420
422
  this.newValue = ((clientY / this.sliderRect.height) * (max - min)) + min
421
- this.updateValue(this.newValue, false, 1)
422
423
  } else {
423
- let clientX = event.detail.x - this.sliderRect.left
424
+ let clientX = position - this.sliderRect.left
424
425
  this.newValue = ((clientX / this.sliderRect.width) * (max - min)) + min
425
- this.updateValue(this.newValue, false, 1)
426
426
  }
427
+ // 点击等同于一次完整的取值,需要和拖动结束一样对外发出change,
428
+ // 否则单向绑定(:modelValue)时外部拿不到点击后的值
429
+ const $crtFmtValue = this.updateValue(this.newValue, false, 1)
430
+ this.$emit('change', $crtFmtValue)
427
431
  // #endif
428
432
  },
429
433
  updateValue(value, drag, index = 1) {
@@ -572,6 +576,11 @@
572
576
  flex-direction: column;
573
577
  position: relative;
574
578
  border-radius: 999px;
579
+ /* #ifndef APP-NVUE */
580
+ // 高度按 blockSize 内联下发,须以内容盒为基准;否则应用层的
581
+ // view { box-sizing: border-box } 会把上下 padding 从内容高度中扣掉,滑块与滑道错位
582
+ box-sizing: content-box;
583
+ /* #endif */
575
584
  padding: 10px 18px;
576
585
  justify-content: center;
577
586
  }
@@ -187,6 +187,9 @@
187
187
  }
188
188
  },
189
189
  mounted() {
190
+ // 抖音小程序等平台子组件 created 可能在父组件 created 之前执行,
191
+ // 此时 parent.children 尚未初始化,init 被跳过,此处补偿重试
192
+ this.init()
190
193
  this.parent && this.parent.updateFromChild()
191
194
  sleep().then(() => {
192
195
  this.getStepsItemRect()
@@ -199,6 +202,12 @@
199
202
  if (!this.parent) {
200
203
  return error('u-steps-item必须要搭配u-steps组件使用')
201
204
  }
205
+ // 抖音小程序等平台子组件 created 可能在父组件 created 之前执行,
206
+ // 此时 parent.children 尚未初始化(u-steps 在 created 中才赋值 children=[]),
207
+ // 跳过本次初始化,由 mounted 钩子补偿重试
208
+ if (!this.parent.children) {
209
+ return
210
+ }
202
211
  this.index = this.parent.children.indexOf(this)
203
212
  this.childLength = this.parent.children.length
204
213
  },
@@ -239,6 +239,9 @@
239
239
  this.init()
240
240
  },
241
241
  mounted() {
242
+ // 抖音小程序等平台子组件 created 可能在父组件 created 之前执行,
243
+ // 此时 parent.children 尚未初始化,init 被跳过,此处补偿重试
244
+ this.init()
242
245
  this.scheduleMidButtonBorderMeasure()
243
246
  },
244
247
  watch: {
@@ -260,6 +263,13 @@
260
263
  this.updateParentData()
261
264
  if (!this.parent) {
262
265
  error('up-tabbar-item必须搭配up-tabbar组件使用')
266
+ return
267
+ }
268
+ // 抖音小程序等平台子组件 created 可能在父组件 created 之前执行,
269
+ // 此时 parent.children 尚未初始化(u-tabbar 在 created 中才赋值 children=[]),
270
+ // 跳过本次初始化,由 mounted 钩子补偿重试
271
+ if (!this.parent.children) {
272
+ return
263
273
  }
264
274
  // 本子组件在u-tabbar的children数组中的索引
265
275
  const index = this.parent.children.indexOf(this)
@@ -299,10 +309,11 @@
299
309
  },
300
310
  clickHandler() {
301
311
  this.$nextTick(() => {
312
+ if (!this.parent || !this.parent.children) return
302
313
  const index = this.parent.children.indexOf(this)
303
314
  const name = this.name || index
304
315
  // 点击的item为非激活的item才发出change事件
305
- if (name !== this.parent.value) {
316
+ if (this.parent && name !== this.parent.value) {
306
317
  this.parent.$emit('change', name)
307
318
  }
308
319
  this.$emit('click', name)
@@ -405,7 +405,11 @@
405
405
  }
406
406
 
407
407
  &__scroll-view {
408
- @include flex;
408
+ // 这里不能声明 display: flex:各端 scroll-view 都把内容包在内层容器里,flex 作用不到内容上,
409
+ // 反而会让微信小程序告警「设置 enable-flex 属性以使 flexbox 布局生效」(issue #650)
410
+ /* #ifdef APP-NVUE */
411
+ flex-direction: row;
412
+ /* #endif */
409
413
  flex: 1;
410
414
  }
411
415
 
@@ -44,6 +44,7 @@
44
44
  :send-message-img="sendMessageImg"
45
45
  :show-message-card="showMessageCard"
46
46
  :app-parameter="appParameter"
47
+ v-bind="$attrs"
47
48
  >
48
49
  {{ value }}
49
50
  </button>
@@ -73,22 +74,22 @@ import value from './value.js'
73
74
  import { mpMixin } from '../../libs/mixin/mpMixin';
74
75
  import { mixin } from '../../libs/mixin/mixin';
75
76
  import { buttonMixin } from '../../libs/mixin/button';
76
- import { openType } from '../../libs/mixin/openType';
77
- import { addStyle, addUnit, deepMerge } from '../../libs/function/index';
78
-
79
- function normalizeLineHeight(value) {
80
- const valueString = String(value).trim()
81
- const numericValue = Number(valueString)
82
- const isUnitless =
83
- /^[+-]?(?:\d+\.?\d*|\.\d+)$/.test(valueString) &&
84
- numericValue > 0 &&
85
- numericValue < 10
86
-
87
- return isUnitless ? valueString : addUnit(value)
88
- }
89
-
90
- /**
91
- * Text 文本
77
+ import { openType } from '../../libs/mixin/openType';
78
+ import { addStyle, addUnit, deepMerge } from '../../libs/function/index';
79
+
80
+ function normalizeLineHeight(value) {
81
+ const valueString = String(value).trim()
82
+ const numericValue = Number(valueString)
83
+ const isUnitless =
84
+ /^[+-]?(?:\d+\.?\d*|\.\d+)$/.test(valueString) &&
85
+ numericValue > 0 &&
86
+ numericValue < 10
87
+
88
+ return isUnitless ? valueString : addUnit(value)
89
+ }
90
+
91
+ /**
92
+ * Text 文本
92
93
  * @description 此组件集成了文本类在项目中的常用功能,包括状态,拨打电话,格式化日期,*替换,超链接...等功能。 您大可不必在使用特殊文本时自己定义,text组件几乎涵盖您能使用的大部分场景。
93
94
  * @tutorial https://uview-plus.jiangruyi.com/components/loading.html
94
95
  * @property {String} type 主题颜色
@@ -109,7 +110,7 @@ function normalizeLineHeight(value) {
109
110
  * @property {Object | String} iconStyle 图标的样式 (默认 {fontSize: '15px'} )
110
111
  * @property {String} decoration 文字装饰,下划线,中划线等,可选值 none|underline|line-through(默认 'none' )
111
112
  * @property {Object | String | Number} margin 外边距,对象、字符串,数值形式均可(默认 0 )
112
- * @property {String | Number} lineHeight 文本行高,支持带单位值或无单位倍数(如 1.2)
113
+ * @property {String | Number} lineHeight 文本行高,支持带单位值或无单位倍数(如 1.2)
113
114
  * @property {String} align 文本对齐方式,可选值left|center|right(默认 'left' )
114
115
  * @property {String} wordWrap 文字换行,可选值break-word|normal|anywhere(默认 'normal' )
115
116
  * @event {Function} click 点击触发事件
@@ -123,6 +124,7 @@ export default {
123
124
  // #ifndef MP
124
125
  mixins: [mpMixin, mixin, value, props],
125
126
  // #endif
127
+ inheritAttrs: false,
126
128
  emits: ['click'],
127
129
  computed: {
128
130
  wrapStyle() {
@@ -139,21 +141,21 @@ export default {
139
141
  }
140
142
  return style;
141
143
  },
142
- valueStyle() {
143
- const style = {
144
- textDecoration: this.decoration,
145
- fontWeight: this.bold ? 'bold' : 'normal',
146
- wordWrap: this.wordWrap,
147
- fontSize: addUnit(this.size)
148
- }
149
- if (!this.type) {
150
- style.color = this.color || this.upThemeVar('--up-content-color', this.$u?.color?.contentColor || '#606266')
151
- }
152
- this.isNvue && this.lines && (style.lines = this.lines)
153
- this.lineHeight &&
154
- (style.lineHeight = normalizeLineHeight(this.lineHeight))
155
- !this.isNvue && this.block && (style.display = 'block')
156
- return deepMerge(style, addStyle(this.customStyle))
144
+ valueStyle() {
145
+ const style = {
146
+ textDecoration: this.decoration,
147
+ fontWeight: this.bold ? 'bold' : 'normal',
148
+ wordWrap: this.wordWrap,
149
+ fontSize: addUnit(this.size)
150
+ }
151
+ if (!this.type) {
152
+ style.color = this.color || this.upThemeVar('--up-content-color', this.$u?.color?.contentColor || '#606266')
153
+ }
154
+ this.isNvue && this.lines && (style.lines = this.lines)
155
+ this.lineHeight &&
156
+ (style.lineHeight = normalizeLineHeight(this.lineHeight))
157
+ !this.isNvue && this.block && (style.display = 'block')
158
+ return deepMerge(style, addStyle(this.customStyle))
157
159
  },
158
160
  isNvue() {
159
161
  let nvue = false
@@ -40,6 +40,11 @@ export const props = defineMixin({
40
40
  type: String,
41
41
  default: () => defProps.textarea.confirmType
42
42
  },
43
+ // 点击键盘右下角按钮时是否保持键盘不收起,confirmType 非 return 时有效
44
+ confirmHold: {
45
+ type: Boolean,
46
+ default: () => defProps.textarea.confirmHold
47
+ },
43
48
  // 是否禁用
44
49
  disabled: {
45
50
  type: Boolean,
@@ -15,7 +15,10 @@ export default {
15
15
  placeholderClass: 'textarea-placeholder',
16
16
  placeholderStyle: '',
17
17
  height: 70,
18
- confirmType: 'done',
18
+ // 与 uni-app textarea 原生默认值保持一致:return 表示回车换行;
19
+ // 取 done/go/next/search/send 时回车会触发 confirm 且不换行
20
+ confirmType: 'return',
21
+ confirmHold: false,
19
22
  disabled: false,
20
23
  count: false,
21
24
  focus: false,
@@ -21,6 +21,7 @@
21
21
  :holdKeyboard="holdKeyboard"
22
22
  :maxlength="maxlength"
23
23
  :confirm-type="confirmType"
24
+ :confirm-hold="confirmHold"
24
25
  :ignoreCompositionEvent="ignoreCompositionEvent"
25
26
  @focus="onFocus"
26
27
  @blur="onBlur"
@@ -55,7 +56,8 @@ import { addStyle, addUnit, deepMerge, formValidate, os } from '../../libs/funct
55
56
  * @property {String} placeholderClass 指定placeholder的样式类,注意页面或组件的style中写了scoped时,需要在类名前写/deep/ ( 默认 'input-placeholder' )
56
57
  * @property {String | Object} placeholderStyle 指定placeholder的样式,字符串/对象形式,如"color: red;"
57
58
  * @property {String | Number} height 输入框高度(默认 70 )
58
- * @property {String} confirmType 设置键盘右下角按钮的文字,仅微信小程序,App-vue和H5有效(默认 'done' )
59
+ * @property {String} confirmType 设置键盘右下角按钮的文字,仅微信小程序,App-vue和H5有效。为 'return' 时回车换行,取 'done'、'go'、'next'、'search'、'send' 时回车触发 confirm 且不换行(默认 'return' )
60
+ * @property {Boolean} confirmHold 点击键盘右下角按钮时是否保持键盘不收起,confirmType 非 'return' 时有效(默认 false )
59
61
  * @property {Boolean} disabled 是否禁用(默认 false )
60
62
  * @property {Boolean} count 是否显示统计字数(默认 false )
61
63
  * @property {Boolean} focus 是否自动获取焦点,nvue不支持,H5取决于浏览器的实现(默认 false )