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
@@ -441,6 +441,19 @@
441
441
  index: index == null ? this.fileList.length : index,
442
442
  };
443
443
  },
444
+ // 组装uni.uploadFile的文件参数。
445
+ // H5下选择结果里带的是真实File对象,直接交给uni.uploadFile;否则它只能拿blob:
446
+ // 地址再读回二进制,而部分webview(如微信内置浏览器)读blob:会拿到空内容,
447
+ // 上传就会失败(uploadFile:fail file error)。#801
448
+ uploadFileParams(item, name = 'file') {
449
+ // #ifdef H5
450
+ const raw = item.file || item.tempFile
451
+ if (typeof Blob !== 'undefined' && raw instanceof Blob) {
452
+ return { name, files: [{ name, file: raw }] }
453
+ }
454
+ // #endif
455
+ return { name, filePath: item.url }
456
+ },
444
457
  async onAfterRead(file) {
445
458
  const {
446
459
  maxSize,
@@ -511,8 +524,7 @@
511
524
  }
512
525
  var uploadTask = uni.uploadFile({
513
526
  url: res0.data.params.host,
514
- filePath: lists[j].url,
515
- name: 'file',
527
+ ...this.uploadFileParams(lists[j]),
516
528
  // fileType: 'video', // 仅支付宝小程序,且必填。
517
529
  // header: header,
518
530
  formData: formData,
@@ -556,8 +568,7 @@
556
568
  // 服务器本机上传
557
569
  var uploadTask = uni.uploadFile({
558
570
  url: this.autoUploadApi,
559
- filePath: lists[j].url,
560
- name: 'file',
571
+ ...this.uploadFileParams(lists[j]),
561
572
  // fileType: 'video', // 仅支付宝小程序,且必填。
562
573
  header: this.autoUploadHeader,
563
574
  success: (uploadFileRes) => {
@@ -42,7 +42,10 @@ function formatVideo(res) {
42
42
  height: res.height || 0, // APP 2.1.0+、H5、微信小程序、京东小程序
43
43
  // #ifdef H5
44
44
  name: res.name,
45
- file: res
45
+ // chooseVideo 在H5已经返回了真实的File对象(res.tempFile),必须把它透出来,
46
+ // 和formatImage/formatMedia/formatFile保持一致。否则调用方只剩blob:地址可用,
47
+ // 而部分webview(如微信内置浏览器)读blob:会拿到空内容,视频就传不上去。#801
48
+ file: res.tempFile || res
46
49
  // #endif
47
50
  // #ifndef H5
48
51
  name: res.tempFilePath.split('/').pop() + '.mp4',
@@ -0,0 +1,97 @@
1
+ /**
2
+ * u-video 弹幕引擎的纯逻辑部分(归一化、时间游标、轨道分配)。
3
+ * 与 video-utils.js 一样不引入 uni / vue,方便 Node 端直接做行为断言。
4
+ */
5
+
6
+ // 同一轨道上两条弹幕之间的最小像素间隔
7
+ const TRACK_GAP = 20
8
+
9
+ const DANMU_TYPES = ['scroll', 'top', 'bottom']
10
+
11
+ export function normalizeDanmuItem(item) {
12
+ const danmu = typeof item === 'string' ? { text: item } : (item || {})
13
+ const time = Number(danmu.time)
14
+ return {
15
+ text: danmu.text === undefined || danmu.text === null ? '' : String(danmu.text),
16
+ color: danmu.color || '',
17
+ time: Number.isFinite(time) && time > 0 ? time : 0,
18
+ type: DANMU_TYPES.includes(danmu.type) ? danmu.type : 'scroll'
19
+ }
20
+ }
21
+
22
+ // 按出现时间升序排列,播放过程中只需要向后推进游标
23
+ export function normalizeDanmuList(list) {
24
+ if (!Array.isArray(list)) return []
25
+ return list
26
+ .map(normalizeDanmuItem)
27
+ .filter((item) => item.text !== '')
28
+ .sort((a, b) => a.time - b.time)
29
+ }
30
+
31
+ // 二分查找第一条「时间点不早于 time」的弹幕,拖动进度条后用它重置游标
32
+ export function findDanmuCursor(list, time) {
33
+ if (!Array.isArray(list) || !list.length) return 0
34
+ const target = Number(time)
35
+ let low = 0
36
+ let high = list.length
37
+ while (low < high) {
38
+ const middle = (low + high) >> 1
39
+ if (list[middle].time < target) {
40
+ low = middle + 1
41
+ } else {
42
+ high = middle
43
+ }
44
+ }
45
+ return low
46
+ }
47
+
48
+ // 粗略估算弹幕宽度:中文按一个字宽,其余按 0.55 字宽
49
+ export function estimateDanmuWidth(text, fontSize) {
50
+ const size = Number(fontSize) > 0 ? Number(fontSize) : 14
51
+ const content = String(text === undefined || text === null ? '' : text)
52
+ let units = 0
53
+ for (const char of content) {
54
+ units += char.charCodeAt(0) > 0x2e80 ? 1 : 0.55
55
+ }
56
+ return Math.ceil(units * size) + size
57
+ }
58
+
59
+ export function createDanmuState() {
60
+ return { tracks: [] }
61
+ }
62
+
63
+ export function resetDanmuState(state) {
64
+ if (state) state.tracks = []
65
+ return state
66
+ }
67
+
68
+ /**
69
+ * 取一条空闲轨道,全部占用时返回 -1(该条弹幕直接丢弃)。
70
+ * 滚动弹幕在尾巴完全进场后即可复用轨道;hold 用于顶/底部固定弹幕。
71
+ */
72
+ export function acquireDanmuTrack(state, options = {}) {
73
+ const {
74
+ now = 0,
75
+ itemWidth = 0,
76
+ containerWidth = 0,
77
+ duration = 8,
78
+ trackCount = 1,
79
+ hold = 0
80
+ } = options
81
+ if (!state) return -1
82
+ const total = Math.max(1, Math.floor(trackCount))
83
+ let busyFor = Number(hold)
84
+ if (!Number.isFinite(busyFor) || busyFor <= 0) {
85
+ const distance = Number(containerWidth) + Number(itemWidth)
86
+ const speed = distance > 0 ? distance / (Number(duration) * 1000) : 0
87
+ busyFor = speed > 0 ? (Number(itemWidth) + TRACK_GAP) / speed : 0
88
+ }
89
+ for (let index = 0; index < total; index++) {
90
+ const freeAt = state.tracks[index]
91
+ if (freeAt === undefined || now >= freeAt) {
92
+ state.tracks[index] = now + busyFor
93
+ return index
94
+ }
95
+ }
96
+ return -1
97
+ }
@@ -0,0 +1,205 @@
1
+ import { defineMixin } from '../../libs/vue'
2
+ import { registerComponentProps } from '../../libs/config/props.js'
3
+ import VideoDefaultProps from './video'
4
+
5
+ const defProps = registerComponentProps(VideoDefaultProps)
6
+
7
+ export const props = defineMixin({
8
+ props: {
9
+ // 视频地址,传了 episodes 时作为兜底地址
10
+ src: {
11
+ type: String,
12
+ default: () => defProps.video.src
13
+ },
14
+ // 封面图
15
+ poster: {
16
+ type: String,
17
+ default: () => defProps.video.poster
18
+ },
19
+ title: {
20
+ type: String,
21
+ default: () => defProps.video.title
22
+ },
23
+ width: {
24
+ type: [String, Number],
25
+ default: () => defProps.video.width
26
+ },
27
+ height: {
28
+ type: [String, Number],
29
+ default: () => defProps.video.height
30
+ },
31
+ radius: {
32
+ type: [String, Number],
33
+ default: () => defProps.video.radius
34
+ },
35
+ objectFit: {
36
+ type: String,
37
+ default: () => defProps.video.objectFit
38
+ },
39
+ autoplay: {
40
+ type: Boolean,
41
+ default: () => defProps.video.autoplay
42
+ },
43
+ loop: {
44
+ type: Boolean,
45
+ default: () => defProps.video.loop
46
+ },
47
+ muted: {
48
+ type: Boolean,
49
+ default: () => defProps.video.muted
50
+ },
51
+ initialTime: {
52
+ type: [String, Number],
53
+ default: () => defProps.video.initialTime
54
+ },
55
+ // 自绘控制层总开关
56
+ controls: {
57
+ type: Boolean,
58
+ default: () => defProps.video.controls
59
+ },
60
+ showCenterPlayBtn: {
61
+ type: Boolean,
62
+ default: () => defProps.video.showCenterPlayBtn
63
+ },
64
+ showFullscreenBtn: {
65
+ type: Boolean,
66
+ default: () => defProps.video.showFullscreenBtn
67
+ },
68
+ showBack: {
69
+ type: Boolean,
70
+ default: () => defProps.video.showBack
71
+ },
72
+ backIcon: {
73
+ type: String,
74
+ default: () => defProps.video.backIcon
75
+ },
76
+ autoHide: {
77
+ type: [String, Number],
78
+ default: () => defProps.video.autoHide
79
+ },
80
+ showLock: {
81
+ type: Boolean,
82
+ default: () => defProps.video.showLock
83
+ },
84
+ // 倍速
85
+ showRate: {
86
+ type: Boolean,
87
+ default: () => defProps.video.showRate
88
+ },
89
+ rate: {
90
+ type: [String, Number],
91
+ default: () => defProps.video.rate
92
+ },
93
+ rateList: {
94
+ type: Array,
95
+ default: () => defProps.video.rateList
96
+ },
97
+ // 音量
98
+ showVolume: {
99
+ type: Boolean,
100
+ default: () => defProps.video.showVolume
101
+ },
102
+ volume: {
103
+ type: [String, Number],
104
+ default: () => defProps.video.volume
105
+ },
106
+ // 弹幕
107
+ enableDanmu: {
108
+ type: Boolean,
109
+ default: () => defProps.video.enableDanmu
110
+ },
111
+ danmuList: {
112
+ type: Array,
113
+ default: () => defProps.video.danmuList
114
+ },
115
+ danmuBtn: {
116
+ type: Boolean,
117
+ default: () => defProps.video.danmuBtn
118
+ },
119
+ danmuOpen: {
120
+ type: Boolean,
121
+ default: () => defProps.video.danmuOpen
122
+ },
123
+ danmuColor: {
124
+ type: String,
125
+ default: () => defProps.video.danmuColor
126
+ },
127
+ danmuFontSize: {
128
+ type: [String, Number],
129
+ default: () => defProps.video.danmuFontSize
130
+ },
131
+ danmuDuration: {
132
+ type: [String, Number],
133
+ default: () => defProps.video.danmuDuration
134
+ },
135
+ danmuArea: {
136
+ type: String,
137
+ default: () => defProps.video.danmuArea
138
+ },
139
+ danmuOpacity: {
140
+ type: [String, Number],
141
+ default: () => defProps.video.danmuOpacity
142
+ },
143
+ danmuMax: {
144
+ type: [String, Number],
145
+ default: () => defProps.video.danmuMax
146
+ },
147
+ // 选集
148
+ episodes: {
149
+ type: Array,
150
+ default: () => defProps.video.episodes
151
+ },
152
+ episodeIndex: {
153
+ type: [String, Number],
154
+ default: () => defProps.video.episodeIndex
155
+ },
156
+ episodeColumns: {
157
+ type: [String, Number],
158
+ default: () => defProps.video.episodeColumns
159
+ },
160
+ autoNext: {
161
+ type: Boolean,
162
+ default: () => defProps.video.autoNext
163
+ },
164
+ // 广告
165
+ ads: {
166
+ type: Array,
167
+ default: () => defProps.video.ads
168
+ },
169
+ // 原生手势
170
+ enableProgressGesture: {
171
+ type: Boolean,
172
+ default: () => defProps.video.enableProgressGesture
173
+ },
174
+ pageGesture: {
175
+ type: Boolean,
176
+ default: () => defProps.video.pageGesture
177
+ },
178
+ vslideGesture: {
179
+ type: Boolean,
180
+ default: () => defProps.video.vslideGesture
181
+ },
182
+ direction: {
183
+ type: [String, Number],
184
+ default: () => defProps.video.direction
185
+ },
186
+ autoPauseIfNavigate: {
187
+ type: Boolean,
188
+ default: () => defProps.video.autoPauseIfNavigate
189
+ },
190
+ autoPauseIfOpenNative: {
191
+ type: Boolean,
192
+ default: () => defProps.video.autoPauseIfOpenNative
193
+ },
194
+ videoId: {
195
+ type: String,
196
+ default: () => defProps.video.videoId
197
+ },
198
+ activeColor: {
199
+ type: String,
200
+ default: () => defProps.video.activeColor
201
+ }
202
+ }
203
+ })
204
+
205
+ export default props