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.
- package/README.md +13 -0
- package/changelog.md +39 -0
- package/components/u-calendar/month.vue +71 -35
- package/components/u-calendar/u-calendar.vue +67 -31
- package/components/u-car-keyboard/u-car-keyboard.vue +21 -8
- package/components/u-cate-tab/u-cate-tab.vue +56 -11
- package/components/u-cell/u-cell.vue +20 -16
- package/components/u-collapse-item/u-collapse-item.vue +34 -11
- package/components/u-datetime-picker/u-datetime-picker.vue +16 -0
- package/components/u-dropdown/u-dropdown.vue +15 -0
- package/components/u-grid/u-grid.vue +9 -0
- package/components/u-grid-item/u-grid-item.vue +8 -0
- package/components/u-icon/util.js +16 -1
- package/components/u-list/u-list.vue +59 -8
- package/components/u-list-item/u-list-item.vue +13 -3
- package/components/u-number-keyboard/u-number-keyboard.vue +18 -6
- package/components/u-parse/parser.js +4 -3
- package/components/u-picker/u-picker.vue +6 -3
- package/components/u-search/u-search.vue +15 -5
- package/components/u-select/u-select.vue +206 -31
- package/components/u-slider/u-slider.vue +21 -12
- package/components/u-steps-item/u-steps-item.vue +9 -0
- package/components/u-tabbar-item/u-tabbar-item.vue +12 -1
- package/components/u-tabs/u-tabs.vue +5 -1
- package/components/u-text/u-text.vue +34 -32
- package/components/u-textarea/props.js +5 -0
- package/components/u-textarea/textarea.js +4 -1
- package/components/u-textarea/u-textarea.vue +3 -1
- package/components/u-upload/u-upload.vue +15 -4
- package/components/u-upload/utils.js +4 -1
- package/components/u-video/danmaku.js +97 -0
- package/components/u-video/props.js +205 -0
- package/components/u-video/u-video.vue +1427 -0
- package/components/u-video/video-danmaku.vue +273 -0
- package/components/u-video/video-slider.vue +179 -0
- package/components/u-video/video-utils.js +98 -0
- package/components/u-video/video.js +72 -0
- package/libs/config/props.js +2 -1
- package/libs/function/test.js +2 -0
- package/libs/i18n/locales/de.js +14 -1
- package/libs/i18n/locales/en.js +14 -1
- package/libs/i18n/locales/es.js +14 -1
- package/libs/i18n/locales/fr.js +14 -1
- package/libs/i18n/locales/ja.js +14 -1
- package/libs/i18n/locales/ko.js +14 -1
- package/libs/i18n/locales/ru.js +14 -1
- package/libs/i18n/locales/th.js +14 -1
- package/libs/i18n/locales/zh-Hans.js +14 -1
- package/libs/i18n/locales/zh-Hant.js +14 -1
- package/libs/mixin/mixin.js +12 -7
- package/package.json +1 -1
- package/skills/README.md +66 -0
- package/skills/uview-plus/SKILL.md +95 -0
- package/skills/uview-plus/references/api.md +105 -0
- package/skills/uview-plus/references/components.md +156 -0
- package/skills/uview-plus/references/icons.md +223 -0
- package/skills/uview-plus/references/pitfalls.md +77 -0
- package/skills/uview-plus/references/setup.md +168 -0
- package/types/comps/form.d.ts +44 -8
- package/types/comps/list.d.ts +1 -1
- package/types/comps/listItem.d.ts +1 -1
- package/types/comps/numberBox.d.ts +85 -21
- package/types/comps/pagination.d.ts +96 -0
- package/types/comps/swipeActionItem.d.ts +3 -4
- package/types/comps/textarea.d.ts +9 -3
- package/types/comps.d.ts +1 -0
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|