uview-plus 3.8.127 → 3.8.129
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 +38 -0
- package/components/u-barcode/u-barcode.vue +1 -1
- 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 +9 -3
- package/components/u-navbar/navbar.js +2 -1
- package/components/u-navbar/props.js +6 -1
- package/components/u-navbar/u-navbar.vue +20 -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-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 +11 -6
- 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/navbar.d.ts +6 -0
- 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
|
@@ -11,12 +11,18 @@
|
|
|
11
11
|
:custom-style="iconStyle"
|
|
12
12
|
:size="size === 'large' ? 22 : 18"></up-icon>
|
|
13
13
|
</view>
|
|
14
|
+
<!-- 必填星号用真实节点渲染,绝对定位的伪元素在nvue和小程序下不显示,且通栏布局时会跑到屏幕外 -->
|
|
15
|
+
<text
|
|
16
|
+
v-if="required"
|
|
17
|
+
class="u-cell__required"
|
|
18
|
+
:style="[cellRequiredDynamicStyle]"
|
|
19
|
+
>*</text>
|
|
14
20
|
<view class="u-cell__title">
|
|
15
21
|
<!-- 将slot与默认内容用if/else分开主要是因为微信小程序不支持slot嵌套传递,这样才能解决collapse组件的slot不失效问题,label暂时未用到。 -->
|
|
16
22
|
<slot name="title" v-if="$slots.title || !title">
|
|
17
23
|
</slot>
|
|
18
24
|
<text v-else class="u-cell__title-text" :style="[cellTitleDynamicStyle, titleTextStyle]"
|
|
19
|
-
:class="[
|
|
25
|
+
:class="[disabled && 'u-cell--disabled', size === 'large' && 'u-cell__title-text--large']">{{ title }}</text>
|
|
20
26
|
<slot name="label">
|
|
21
27
|
<text class="u-cell__label" v-if="label"
|
|
22
28
|
:style="[cellLabelDynamicStyle]"
|
|
@@ -102,6 +108,11 @@
|
|
|
102
108
|
cellDisabledColor() {
|
|
103
109
|
return this.upThemeVar('--up-disabled-color', '#c8c9cc')
|
|
104
110
|
},
|
|
111
|
+
cellRequiredDynamicStyle() {
|
|
112
|
+
return {
|
|
113
|
+
color: this.upThemeVar('--up-error', '#f56c6c')
|
|
114
|
+
}
|
|
115
|
+
},
|
|
105
116
|
cellTitleDynamicStyle() {
|
|
106
117
|
return {
|
|
107
118
|
color: this.upThemeVar('--up-main-color', '#303133')
|
|
@@ -150,6 +161,8 @@
|
|
|
150
161
|
$u-cell-title-font-size: 15px !default;
|
|
151
162
|
$u-cell-title-line-height: 22px !default;
|
|
152
163
|
$u-cell-title-color: $u-main-color !default;
|
|
164
|
+
$u-cell-required-font-size: 14px !default;
|
|
165
|
+
$u-cell-required-color: $u-error !default;
|
|
153
166
|
$u-cell-label-font-size: 12px !default;
|
|
154
167
|
$u-cell-label-color: $u-tips-color !default;
|
|
155
168
|
$u-cell-label-line-height: 18px !default;
|
|
@@ -258,23 +271,14 @@
|
|
|
258
271
|
}
|
|
259
272
|
}
|
|
260
273
|
|
|
261
|
-
|
|
262
|
-
/* #ifndef APP-NVUE */
|
|
263
|
-
overflow: visible;
|
|
264
|
-
/* #endif */
|
|
265
|
-
@include flex;
|
|
266
|
-
align-items: center;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
&--required:before {
|
|
270
|
-
position: absolute;
|
|
274
|
+
&__required {
|
|
271
275
|
/* #ifndef APP-NVUE */
|
|
272
|
-
|
|
276
|
+
align-self: flex-start;
|
|
273
277
|
/* #endif */
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
color: $u-
|
|
278
|
+
margin-right: 2px;
|
|
279
|
+
font-size: $u-cell-required-font-size;
|
|
280
|
+
line-height: $u-cell-title-line-height;
|
|
281
|
+
color: $u-cell-required-color;
|
|
278
282
|
}
|
|
279
283
|
|
|
280
284
|
&--clickable {
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
<view
|
|
46
46
|
class="u-collapse-item__content"
|
|
47
47
|
:animation="animationData"
|
|
48
|
+
:style="[contentStyle]"
|
|
48
49
|
ref="animation"
|
|
49
50
|
>
|
|
50
51
|
<view
|
|
@@ -93,6 +94,8 @@
|
|
|
93
94
|
elId: guid(),
|
|
94
95
|
// uni.createAnimation的导出数据
|
|
95
96
|
animationData: {},
|
|
97
|
+
// 展开动画结束后接管面板高度,交还给内容,避免内容后续变高被裁切
|
|
98
|
+
contentStyle: {},
|
|
96
99
|
// 是否展开状态
|
|
97
100
|
expanded: false,
|
|
98
101
|
// 根据expanded确定是否显示border,为了控制展开时,cell的下划线更好的显示效果,进行一定时间的延时
|
|
@@ -134,16 +137,20 @@
|
|
|
134
137
|
children = []
|
|
135
138
|
} = this.parent
|
|
136
139
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
+
// value为null(默认值)时面板并非受控,展开状态只存在于子组件自身的expanded上,
|
|
141
|
+
// 此时按value反推会把用户点开的面板强行收起,init()便只剩重算内容高度这一职责
|
|
142
|
+
if (value !== null && value !== undefined) {
|
|
143
|
+
if (accordion) {
|
|
144
|
+
if (test.array(value)) {
|
|
145
|
+
return error('手风琴模式下,u-collapse组件的value参数不能为数组')
|
|
146
|
+
}
|
|
147
|
+
this.expanded = this.name == value
|
|
148
|
+
} else {
|
|
149
|
+
if (!test.array(value)) {
|
|
150
|
+
return error('非手风琴模式下,u-collapse组件的value参数必须为数组')
|
|
151
|
+
}
|
|
152
|
+
this.expanded = value.some(item => item == this.name)
|
|
140
153
|
}
|
|
141
|
-
this.expanded = this.name == value
|
|
142
|
-
} else {
|
|
143
|
-
if (!test.array(value) && value !== null) {
|
|
144
|
-
return error('非手风琴模式下,u-collapse组件的value参数必须为数组')
|
|
145
|
-
}
|
|
146
|
-
this.expanded = (value || []).some(item => item == this.name)
|
|
147
154
|
}
|
|
148
155
|
// 设置组件的展开或收起状态
|
|
149
156
|
await nextTick()
|
|
@@ -158,6 +165,15 @@
|
|
|
158
165
|
// 好处是,父组件从服务端获取内容后,变更折叠面板后可以获得最新的高度
|
|
159
166
|
const rect = await this.queryRect()
|
|
160
167
|
const height = this.expanded ? rect.height : 0
|
|
168
|
+
// #ifndef APP-NVUE
|
|
169
|
+
// 展开状态下面板高度是auto,收起前先固定成当前实际高度,否则浏览器无法从auto过渡到0
|
|
170
|
+
if (!this.expanded && this.contentStyle.height === 'auto') {
|
|
171
|
+
this.contentStyle = { height: `${rect.height}px` }
|
|
172
|
+
await nextTick()
|
|
173
|
+
// 再查询一次节点,借这次布局读取确保上面的固定高度已经生效,收起动画才有起点
|
|
174
|
+
await this.queryRect()
|
|
175
|
+
}
|
|
176
|
+
// #endif
|
|
161
177
|
this.animating = true
|
|
162
178
|
// #ifdef APP-NVUE
|
|
163
179
|
const ref = this.$refs['animation'].ref
|
|
@@ -178,17 +194,24 @@
|
|
|
178
194
|
const animation = uni.createAnimation({
|
|
179
195
|
timingFunction: 'ease-in-out',
|
|
180
196
|
});
|
|
197
|
+
// 只保留一个step:uni的step会累积之前的animates,多余的空step会在动画结束时
|
|
198
|
+
// 把同一个像素高度再写一遍,把下面交还给内容的auto高度盖掉
|
|
181
199
|
animation
|
|
182
200
|
.height(height)
|
|
183
201
|
.step({
|
|
184
202
|
duration: this.duration,
|
|
185
203
|
})
|
|
186
|
-
.step()
|
|
187
204
|
// 导出动画数据给面板的animationData值
|
|
188
205
|
this.animationData = animation.export()
|
|
189
|
-
//
|
|
206
|
+
// 标识动画结束。快速连点时只认最后一次动画的回调,否则旧回调会打断新动画
|
|
207
|
+
const animateToken = (this.animateToken || 0) + 1
|
|
208
|
+
this.animateToken = animateToken
|
|
190
209
|
sleep(this.duration).then(() => {
|
|
210
|
+
if (this.animateToken !== animateToken) return
|
|
191
211
|
this.animating = false
|
|
212
|
+
// 展开动画结束后把高度交还给内容:内容里的图片、异步数据等在测量之后才变高时,
|
|
213
|
+
// 面板不会再停留在过时的测量值上被overflow:hidden裁掉一截
|
|
214
|
+
this.contentStyle = this.expanded ? { height: 'auto' } : {}
|
|
192
215
|
})
|
|
193
216
|
// #endif
|
|
194
217
|
},
|
|
@@ -197,6 +197,10 @@
|
|
|
197
197
|
emits: ['close', 'closed', 'cancel', 'confirm', 'change', 'update:modelValue'],
|
|
198
198
|
// #endif
|
|
199
199
|
methods: {
|
|
200
|
+
// 是否为“未选择/已清空”的空值。空值必须原样透传,不能被夹取成边界值
|
|
201
|
+
isEmptyValue(value) {
|
|
202
|
+
return value === '' || value === null || value === undefined
|
|
203
|
+
},
|
|
200
204
|
toInt(value, fallback = 0) {
|
|
201
205
|
const num = parseInt(value, 10)
|
|
202
206
|
return Number.isFinite(num) ? num : fallback
|
|
@@ -331,6 +335,14 @@
|
|
|
331
335
|
},
|
|
332
336
|
// 点击工具栏的确定按钮
|
|
333
337
|
confirm() {
|
|
338
|
+
// 值为空(从未选择或被外部清空)时,用户看到的是各列当前停留的位置,
|
|
339
|
+
// 直接上抛空值会“所见非所得”,故先按各列当前显示值取一次真实值
|
|
340
|
+
if (this.isEmptyValue(this.innerValue)) {
|
|
341
|
+
this.change({
|
|
342
|
+
indexs: this.innerDefaultIndex,
|
|
343
|
+
values: this.columns
|
|
344
|
+
})
|
|
345
|
+
}
|
|
334
346
|
// #ifdef VUE3
|
|
335
347
|
this.$emit('update:modelValue', this.innerValue)
|
|
336
348
|
// #endif
|
|
@@ -564,6 +576,10 @@
|
|
|
564
576
|
},
|
|
565
577
|
// 得出合法的时间
|
|
566
578
|
correctValue(value) {
|
|
579
|
+
// 空值代表“未选择/已清空”,直接返回空串。
|
|
580
|
+
// 否则日期模式会被夹取成minDate、时间模式会被夹取成minHour:minMinute,
|
|
581
|
+
// 导致外部清空绑定值后输入框仍然显示最小日期/最小时间
|
|
582
|
+
if (this.isEmptyValue(value)) return ''
|
|
567
583
|
const isDateMode = !['time', 'timesecond'].includes(this.mode)
|
|
568
584
|
if (isDateMode) {
|
|
569
585
|
// 日期类型统一解析成毫秒时间戳,没有设置合法的当前时间时才使用最小时间。
|
|
@@ -203,7 +203,11 @@
|
|
|
203
203
|
|
|
204
204
|
.u-dropdown {
|
|
205
205
|
flex: 1;
|
|
206
|
+
// nvue(weex)不支持百分比长度,width: 100%会被编译成width: 100(100px),
|
|
207
|
+
// 使整个下拉菜单被压缩到屏幕左侧一条窄列,nvue下交由flex拉伸撑满父级宽度
|
|
208
|
+
/* #ifndef APP-NVUE */
|
|
206
209
|
width: 100%;
|
|
210
|
+
/* #endif */
|
|
207
211
|
position: relative;
|
|
208
212
|
|
|
209
213
|
&__menu {
|
|
@@ -243,8 +247,14 @@
|
|
|
243
247
|
&__content {
|
|
244
248
|
position: absolute;
|
|
245
249
|
z-index: 8;
|
|
250
|
+
// 绝对定位元素在nvue下用left+right撑满父级宽度,替代无效的width: 100%
|
|
251
|
+
/* #ifndef APP-NVUE */
|
|
246
252
|
width: 100%;
|
|
253
|
+
/* #endif */
|
|
247
254
|
left: 0px;
|
|
255
|
+
/* #ifdef APP-NVUE */
|
|
256
|
+
right: 0px;
|
|
257
|
+
/* #endif */
|
|
248
258
|
bottom: 0;
|
|
249
259
|
overflow: hidden;
|
|
250
260
|
|
|
@@ -253,8 +263,13 @@
|
|
|
253
263
|
position: absolute;
|
|
254
264
|
z-index: 9;
|
|
255
265
|
background: rgba(0, 0, 0, .3);
|
|
266
|
+
/* #ifndef APP-NVUE */
|
|
256
267
|
width: 100%;
|
|
268
|
+
/* #endif */
|
|
257
269
|
left: 0;
|
|
270
|
+
/* #ifdef APP-NVUE */
|
|
271
|
+
right: 0;
|
|
272
|
+
/* #endif */
|
|
258
273
|
top: 0;
|
|
259
274
|
bottom: 0;
|
|
260
275
|
}
|
|
@@ -109,5 +109,14 @@
|
|
|
109
109
|
grid-gap: v-bind(gap);
|
|
110
110
|
grid-template-columns: repeat(v-bind(col), minmax(0, 1fr));
|
|
111
111
|
/* #endif */
|
|
112
|
+
// 安卓7自带的WebView是Chromium 52/55,CSS Grid要到Chromium 57才可用,上面那条display:grid会被整条丢弃,
|
|
113
|
+
// 这里显式退回上面的flex换行,并把列数透出给u-grid-item,让宫格自己按列数取宽度;
|
|
114
|
+
// 这类老内核同样不支持flex的gap,所以gap在兜底布局下画不出来。
|
|
115
|
+
/* #ifndef APP-NVUE || MP */
|
|
116
|
+
@supports not (display: grid) {
|
|
117
|
+
display: flex !important;
|
|
118
|
+
--up-grid-col: v-bind(col);
|
|
119
|
+
}
|
|
120
|
+
/* #endif */
|
|
112
121
|
}
|
|
113
122
|
</style>
|
|
@@ -197,6 +197,14 @@
|
|
|
197
197
|
margin-top:$u-grid-item-margin-top;
|
|
198
198
|
/* #endif */
|
|
199
199
|
|
|
200
|
+
// 老WebView(安卓7自带的是Chromium 52/55)没有CSS Grid,u-grid会退回flex换行,
|
|
201
|
+
// 内联的width:100%此时等于整行宽度,会让每个宫格独占一行,所以按u-grid透出的列数重新分配。
|
|
202
|
+
/* #ifndef APP-NVUE || MP */
|
|
203
|
+
@supports not (display: grid) {
|
|
204
|
+
width: calc(100% / var(--up-grid-col, 1)) !important;
|
|
205
|
+
}
|
|
206
|
+
/* #endif */
|
|
207
|
+
|
|
200
208
|
&--hover-class {
|
|
201
209
|
opacity:$u-grid-item-hover-class-opcatiy;
|
|
202
210
|
}
|
|
@@ -6,6 +6,13 @@ const appIconFontUrl = '_www/static/app-plus/uview-plus/upicon.ttf';
|
|
|
6
6
|
// 项目没有本地字体(未启用本地字体构建插件、或没拷贝 static/app-plus/uview-plus/upicon.ttf)时:
|
|
7
7
|
// App-Vue 在 uni.loadFontFace 失败后回退 config.iconUrl,App-nvue 先探测字体文件是否存在再决定字体地址。
|
|
8
8
|
const useAppStaticIconFont = true;
|
|
9
|
+
// 字体加载失败后的重试上限与重试基础间隔。重试只能由定时器驱动,不能由“新建u-icon”驱动:
|
|
10
|
+
// u-search、u-input的清除按钮仅在输入框聚焦时才创建,若在那一刻补发字体请求,
|
|
11
|
+
// 微信开发者工具里正在聚焦的input会立即失去焦点,表现为输入内容后再也无法聚焦、无法删除内容。
|
|
12
|
+
// https://github.com/ijry/uview-plus/issues/844
|
|
13
|
+
const maxFontRetryTimes = 3;
|
|
14
|
+
const fontRetryDelay = 2000;
|
|
15
|
+
let fontRetryTimes = 0;
|
|
9
16
|
|
|
10
17
|
let params = {
|
|
11
18
|
loaded: false,
|
|
@@ -176,7 +183,15 @@ const loadFont = () => {
|
|
|
176
183
|
// console.log('内置字体图标加载成功');
|
|
177
184
|
},
|
|
178
185
|
fail() {
|
|
179
|
-
|
|
186
|
+
// 失败后不立刻释放请求锁,改由定时器重试有限次数,
|
|
187
|
+
// 避免此后每创建一个u-icon都立刻补发一次字体请求。
|
|
188
|
+
if (fontRetryTimes < maxFontRetryTimes) {
|
|
189
|
+
fontRetryTimes++;
|
|
190
|
+
setTimeout(() => {
|
|
191
|
+
params.loading = false;
|
|
192
|
+
loadFont();
|
|
193
|
+
}, fontRetryDelay * fontRetryTimes);
|
|
194
|
+
}
|
|
180
195
|
// console.error('内置字体图标加载出错');
|
|
181
196
|
}
|
|
182
197
|
});
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
<!-- #ifndef APP-NVUE -->
|
|
17
17
|
<scroll-view
|
|
18
18
|
class="u-list"
|
|
19
|
-
:scroll-into-view="
|
|
19
|
+
:scroll-into-view="innerScrollIntoView"
|
|
20
20
|
:style="[listStyle]"
|
|
21
21
|
:scroll-y="scrollable"
|
|
22
22
|
:scroll-top="Number(scrollTop)"
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
* @property {Boolean} enableFlex 启用 flexbox 布局。开启后,当前节点声明了display: flex就会成为flex container,并作用于其孩子节点,仅微信小程序有效(默认 false )
|
|
67
67
|
* @property {Boolean} pagingEnabled 是否按分页模式显示List,(默认 false )
|
|
68
68
|
* @property {Boolean} scrollable 是否允许List滚动(默认 true )
|
|
69
|
-
* @property {String} scrollIntoView
|
|
69
|
+
* @property {String} scrollIntoView 滚动到指定位置,值为u-list-item的anchor,或item内某个子元素的id(id不能以数字开头)
|
|
70
70
|
* @property {Boolean} scrollWithAnimation 在设置滚动条位置时使用动画过渡 (默认 false )
|
|
71
71
|
* @property {Boolean} enableBackToTop iOS点击顶部状态栏、安卓双击标题栏时,滚动条返回顶部,只对微信小程序有效 (默认 false )
|
|
72
72
|
* @property {String | Number} height 列表的高度 (默认 0 )
|
|
@@ -88,6 +88,8 @@
|
|
|
88
88
|
return {
|
|
89
89
|
// 记录内部滚动的距离
|
|
90
90
|
innerScrollTop: 0,
|
|
91
|
+
// 非nvue下真正传给scroll-view的scroll-into-view值
|
|
92
|
+
innerScrollIntoView: '',
|
|
91
93
|
// vue下,scroll-view在上拉加载时的偏移值
|
|
92
94
|
offset: 0,
|
|
93
95
|
sys: getWindowInfo()
|
|
@@ -109,11 +111,17 @@
|
|
|
109
111
|
}
|
|
110
112
|
},
|
|
111
113
|
created() {
|
|
112
|
-
this.refs = []
|
|
113
114
|
this.children = []
|
|
114
115
|
this.anchors = []
|
|
116
|
+
// 最后一次请求滚动的目标,非响应式,仅用于丢弃过期的重试
|
|
117
|
+
this.scrollIntoViewTarget = ''
|
|
118
|
+
},
|
|
119
|
+
mounted() {
|
|
120
|
+
// 初始就设置了scroll-into-view时,子组件已挂载完成,此时才能匹配到anchor
|
|
121
|
+
if (this.scrollIntoView) {
|
|
122
|
+
this.scrollIntoViewById(this.scrollIntoView)
|
|
123
|
+
}
|
|
115
124
|
},
|
|
116
|
-
mounted() {},
|
|
117
125
|
emits: ["scroll", "scrolltolower", "scroll-to-lower", "scrolltoupper", "scroll-to-upper",
|
|
118
126
|
"refresherpulling", "refresherrefresh", "refresherrestore", "refresherabort"],
|
|
119
127
|
methods: {
|
|
@@ -131,15 +139,58 @@
|
|
|
131
139
|
this.innerScrollTop = scrollTop
|
|
132
140
|
this.$emit('scroll', scrollTop)
|
|
133
141
|
},
|
|
134
|
-
|
|
142
|
+
// 根据id找到设置了对应anchor的u-list-item,anchor与u-list-item-${anchor}两种写法都支持
|
|
143
|
+
getAnchorChild(id) {
|
|
144
|
+
const anchorId = String(id)
|
|
145
|
+
return this.children.find(child => {
|
|
146
|
+
if (child.anchor === '' || child.anchor === null || child.anchor === undefined) return false
|
|
147
|
+
const anchor = String(child.anchor)
|
|
148
|
+
return anchor === anchorId || `u-list-item-${anchor}` === anchorId
|
|
149
|
+
})
|
|
150
|
+
},
|
|
151
|
+
scrollIntoViewById(id, retry = true) {
|
|
152
|
+
if (id === '' || id === null || id === undefined) return
|
|
153
|
+
// 记录最后一次请求,避免过期的重试覆盖掉新值
|
|
154
|
+
this.scrollIntoViewTarget = id
|
|
155
|
+
// 根据id参数,找到所有u-list-item中匹配的节点
|
|
156
|
+
const child = this.getAnchorChild(id)
|
|
157
|
+
const scheduleRetry = () => {
|
|
158
|
+
if (!retry) return
|
|
159
|
+
this.$nextTick(() => {
|
|
160
|
+
if (this.scrollIntoViewTarget === id) this.scrollIntoViewById(id, false)
|
|
161
|
+
})
|
|
162
|
+
}
|
|
163
|
+
// #ifndef APP-NVUE
|
|
164
|
+
// 命中anchor时滚动到item的id;未命中时仍按原语义透传用户自己的子元素id
|
|
165
|
+
const target = child ? 'u-list-item-' + child.anchor : id
|
|
166
|
+
// scroll-into-view需要有一个值变动的过程,重复滚动同一节点时先置空
|
|
167
|
+
if (retry && this.innerScrollIntoView === target) {
|
|
168
|
+
this.innerScrollIntoView = ''
|
|
169
|
+
this.$nextTick(() => {
|
|
170
|
+
if (this.scrollIntoViewTarget === id) this.innerScrollIntoView = target
|
|
171
|
+
})
|
|
172
|
+
} else {
|
|
173
|
+
this.innerScrollIntoView = target
|
|
174
|
+
}
|
|
175
|
+
// #endif
|
|
135
176
|
// #ifdef APP-NVUE
|
|
136
|
-
//
|
|
137
|
-
const
|
|
138
|
-
|
|
177
|
+
// nvue下通过children和带前缀的ref查找节点,也兼容用户直接传入子节点ref名
|
|
178
|
+
const ref = child && child.$refs && child.$refs['u-list-item-' + child.anchor]
|
|
179
|
+
const matchedItem = ref ? null : this.children.find(item => item.$refs && item.$refs[id])
|
|
180
|
+
const targetRef = ref || (matchedItem && matchedItem.$refs[id])
|
|
181
|
+
if (!targetRef) {
|
|
182
|
+
scheduleRetry()
|
|
183
|
+
return
|
|
184
|
+
}
|
|
185
|
+
dom.scrollToElement(targetRef, {
|
|
139
186
|
// 是否需要滚动动画
|
|
140
187
|
animated: this.scrollWithAnimation
|
|
141
188
|
})
|
|
142
189
|
// #endif
|
|
190
|
+
// #ifndef APP-NVUE
|
|
191
|
+
// anchor可能和列表数据同一帧生成,未命中时下一帧再尝试一次
|
|
192
|
+
if (!child) scheduleRetry()
|
|
193
|
+
// #endif
|
|
143
194
|
},
|
|
144
195
|
// 滚动到底部触发事件
|
|
145
196
|
scrolltolower(e) {
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
<!-- #endif -->
|
|
5
5
|
<view
|
|
6
6
|
class="u-list-item"
|
|
7
|
+
:id="anchorId"
|
|
7
8
|
:ref="`u-list-item-${anchor}`"
|
|
8
9
|
:anchor="`u-list-item-${anchor}`"
|
|
9
10
|
:class="[`u-list-item-${anchor}`]"
|
|
@@ -27,8 +28,8 @@
|
|
|
27
28
|
* List 列表
|
|
28
29
|
* @description 该组件为高性能列表组件
|
|
29
30
|
* @tutorial https://uview-plus.jiangruyi.com/components/list.html
|
|
30
|
-
* @property {String | Number} anchor
|
|
31
|
-
* @example <u-list-
|
|
31
|
+
* @property {String | Number} anchor item的锚点,将u-list的scroll-into-view设置为该值即可滚动到此item
|
|
32
|
+
* @example <u-list-item v-for="(item, index) in indexList" :key="index" :anchor="`item-${index}`"></u-list-item>
|
|
32
33
|
*/
|
|
33
34
|
export default {
|
|
34
35
|
name: 'u-list-item',
|
|
@@ -43,7 +44,12 @@
|
|
|
43
44
|
}
|
|
44
45
|
},
|
|
45
46
|
computed: {
|
|
46
|
-
|
|
47
|
+
// scroll-view的scroll-into-view只认节点id,故由anchor加前缀生成;
|
|
48
|
+
// 未设置anchor时输出null,避免列表里的每一项都得到同一个空id。
|
|
49
|
+
anchorId() {
|
|
50
|
+
if (this.anchor === '' || this.anchor === null || this.anchor === undefined) return null
|
|
51
|
+
return 'u-list-item-' + this.anchor
|
|
52
|
+
}
|
|
47
53
|
},
|
|
48
54
|
inject: ['uList'],
|
|
49
55
|
watch: {
|
|
@@ -104,6 +104,11 @@ export const props = defineMixin({
|
|
|
104
104
|
scrollTop: {
|
|
105
105
|
type: [String, Number],
|
|
106
106
|
default: () => defProps.navbar.scrollTop
|
|
107
|
-
}
|
|
107
|
+
},
|
|
108
|
+
// 微信小程序端右侧区域是否避让右上角原生胶囊按钮
|
|
109
|
+
avoidCapsule: {
|
|
110
|
+
type: Boolean,
|
|
111
|
+
default: () => defProps.navbar.avoidCapsule
|
|
112
|
+
},
|
|
108
113
|
}
|
|
109
114
|
})
|
|
@@ -85,6 +85,7 @@
|
|
|
85
85
|
</view>
|
|
86
86
|
<view
|
|
87
87
|
class="u-navbar__content__right"
|
|
88
|
+
:style="[navbarRightStyle]"
|
|
88
89
|
v-if="$slots.right || rightIcon || rightText"
|
|
89
90
|
@tap="rightClick"
|
|
90
91
|
>
|
|
@@ -143,6 +144,7 @@
|
|
|
143
144
|
* @property {Object | String} titleStyle 标题的样式,对象或字符串
|
|
144
145
|
* @property {String} mode 导航栏模式,default-常规,ios-大标题磨砂模式(默认 'default' )
|
|
145
146
|
* @property {String | Number} scrollTop 页面滚动距离,仅 ios 模式使用,由页面 onPageScroll 传入(默认 0 )
|
|
147
|
+
* @property {Boolean} avoidCapsule 微信小程序端右侧区域是否避让右上角原生胶囊按钮(默认 true )
|
|
146
148
|
* @event {Function} leftClick 点击左侧区域
|
|
147
149
|
* @event {Function} rightClick 点击右侧区域
|
|
148
150
|
* @example <u-navbar title="剑未配妥,出门已是江湖" left-text="返回" right-text="帮助" @click-left="onClickBack" @click-right="onClickRight"></u-navbar>
|
|
@@ -221,9 +223,24 @@
|
|
|
221
223
|
if (this.leftIconColor) return this.leftIconColor
|
|
222
224
|
return this.upThemeVar('--up-main-color', this.$u.color.mainColor)
|
|
223
225
|
},
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
226
|
+
navbarRightColor() {
|
|
227
|
+
return this.upThemeVar('--up-main-color', this.$u.color.mainColor)
|
|
228
|
+
},
|
|
229
|
+
// 微信小程序端右侧区域避让右上角原生胶囊按钮:
|
|
230
|
+
// 将容器右边缘从屏幕右缘移到胶囊左缘,内容随容器自带 padding 自然留出间距。
|
|
231
|
+
navbarRightStyle() {
|
|
232
|
+
// #ifdef MP-WEIXIN
|
|
233
|
+
if (!this.avoidCapsule) return {}
|
|
234
|
+
const rect = uni.getMenuButtonBoundingClientRect()
|
|
235
|
+
const windowWidth = getWindowInfo().windowWidth
|
|
236
|
+
// 胶囊信息不可用(部分调试环境返回全 0)时维持原有布局
|
|
237
|
+
if (!rect || !rect.left || !windowWidth) return {}
|
|
238
|
+
return { right: `${windowWidth - rect.left}px` }
|
|
239
|
+
// #endif
|
|
240
|
+
// #ifndef MP-WEIXIN
|
|
241
|
+
return {}
|
|
242
|
+
// #endif
|
|
243
|
+
},
|
|
227
244
|
navbarInnerStyle() {
|
|
228
245
|
const style = {}
|
|
229
246
|
style.background = this.navbarBgColor
|
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
class="u-keyboard__button-wrapper__button u-keyboard__button-wrapper__button--gray"
|
|
26
26
|
hover-class="u-hover-class"
|
|
27
27
|
:hover-stay-time="200"
|
|
28
|
-
@
|
|
28
|
+
@tap="backspaceClick"
|
|
29
|
+
@touchstart.stop="backspaceTouchStart"
|
|
29
30
|
@touchend="clearTimer"
|
|
30
31
|
>
|
|
31
32
|
<up-icon
|
|
@@ -110,17 +111,28 @@
|
|
|
110
111
|
},
|
|
111
112
|
emits: ["backspace", "change"],
|
|
112
113
|
methods: {
|
|
113
|
-
//
|
|
114
|
+
// 点击退格键,PC端浏览器(如电脑版微信内置浏览器)只有鼠标事件不触发touch,故由tap触发删除
|
|
114
115
|
backspaceClick() {
|
|
115
116
|
this.$emit('backspace');
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
117
|
+
},
|
|
118
|
+
// 触摸退格键,一定时间后进入长按连续删除状态,短按时会在touchend前被清除
|
|
119
|
+
backspaceTouchStart() {
|
|
120
|
+
this.clearTimer(); //再次清空定时器,防止重复注册定时器
|
|
121
|
+
this.timer = setTimeout(() => {
|
|
122
|
+
this.$emit('backspace');
|
|
123
|
+
this.backspaceLongPress();
|
|
124
|
+
}, 600);
|
|
125
|
+
},
|
|
126
|
+
// 长按状态下每隔一段时间删除一次
|
|
127
|
+
backspaceLongPress() {
|
|
128
|
+
this.clearTimer();
|
|
129
|
+
this.timer = setTimeout(() => {
|
|
119
130
|
this.$emit('backspace');
|
|
131
|
+
this.backspaceLongPress();
|
|
120
132
|
}, 250);
|
|
121
133
|
},
|
|
122
134
|
clearTimer() {
|
|
123
|
-
|
|
135
|
+
clearTimeout(this.timer);
|
|
124
136
|
this.timer = null;
|
|
125
137
|
},
|
|
126
138
|
// 获取键盘显示的内容
|
|
@@ -783,9 +783,10 @@ Parser.prototype.popNode = function () {
|
|
|
783
783
|
}
|
|
784
784
|
// #endif
|
|
785
785
|
|
|
786
|
-
// 一些编辑器的自带 class
|
|
787
|
-
|
|
788
|
-
|
|
786
|
+
// 一些编辑器的自带 class(quill 等编辑器用 class 而非行内样式表示对齐,如 ql-align-right)
|
|
787
|
+
const alignClass = ['left', 'center', 'right', 'justify'].find(align => (attrs.class || '').includes('align-' + align))
|
|
788
|
+
if (alignClass) {
|
|
789
|
+
styleObj['text-align'] = alignClass
|
|
789
790
|
}
|
|
790
791
|
|
|
791
792
|
Object.assign(styleObj, this.parseStyle(node))
|
|
@@ -334,9 +334,13 @@ export default {
|
|
|
334
334
|
//记录用户选中但是还没确认的值
|
|
335
335
|
this.currentActiveValue = value;
|
|
336
336
|
// 通过对比前后两次的列索引,得出当前变化的是哪一列
|
|
337
|
+
// lastIndex记录的是"上一次确认"的值,滚动但未点确定时并不会更新,
|
|
338
|
+
// 用它做对比会导致滚动过上级列之后,columnIndex一直停留在上级列,
|
|
339
|
+
// 所以这里用innerIndex,即picker-view上一次所处的位置
|
|
340
|
+
const prevIndex = this.innerIndex
|
|
337
341
|
for (let i = 0; i < value.length; i++) {
|
|
338
342
|
let item = value[i]
|
|
339
|
-
if (item !== undefined && item !== (
|
|
343
|
+
if (item !== undefined && item !== (prevIndex[i] || 0)) { // 把undefined转为合法假值0
|
|
340
344
|
// 设置columnIndex为当前变化列的索引
|
|
341
345
|
columnIndex = i
|
|
342
346
|
// index则为变化列中的变化项的索引
|
|
@@ -346,8 +350,7 @@ export default {
|
|
|
346
350
|
}
|
|
347
351
|
this.columnIndex = columnIndex
|
|
348
352
|
const values = this.innerColumns
|
|
349
|
-
//
|
|
350
|
-
// this.setLastIndex(value)
|
|
353
|
+
// 记录picker-view当前的各列位置,作为下一次变化对比的基准
|
|
351
354
|
this.setIndexs(value)
|
|
352
355
|
//如果是非自带输入框才会在change时候触发v-model绑值的变化
|
|
353
356
|
//否则会非常的奇怪,用户未确认,值就变了
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
backgroundColor: resolvedBgColor,
|
|
14
14
|
borderRadius: shape == 'round' ? '100px' : '4px',
|
|
15
15
|
borderColor: resolvedBorderColor,
|
|
16
|
+
'--up-search-placeholder-color': resolvedPlaceholderColor,
|
|
16
17
|
}"
|
|
17
18
|
>
|
|
18
19
|
<template v-if="$slots.label || label !== null">
|
|
@@ -268,6 +269,20 @@
|
|
|
268
269
|
}
|
|
269
270
|
</script>
|
|
270
271
|
|
|
272
|
+
<style lang="scss">
|
|
273
|
+
/**
|
|
274
|
+
* placeholder-class 指定的类不能写在 scoped 中:小程序端 placeholder 节点由 input 内部渲染,
|
|
275
|
+
* 不会带上 scoped 的 data-v- 类,编译出的 .xxx--placeholder.data-v-xxx 永远匹配不到它,
|
|
276
|
+
* placeholderColor 就只剩内联 placeholder-style 一条通路,而真机上首次渲染并不总会应用它,
|
|
277
|
+
* 表现为"输入内容后颜色才生效"。颜色统一由 --up-search-placeholder-color 变量传入。
|
|
278
|
+
*/
|
|
279
|
+
$u-search-input-placeholder-color: $u-tips-color !default;
|
|
280
|
+
|
|
281
|
+
.u-search__content__input--placeholder {
|
|
282
|
+
color: var(--up-search-placeholder-color, #{$u-search-input-placeholder-color});
|
|
283
|
+
}
|
|
284
|
+
</style>
|
|
285
|
+
|
|
271
286
|
<style lang="scss" scoped>
|
|
272
287
|
$u-search-content-padding: 0 10px !default;
|
|
273
288
|
$u-search-label-color: $u-main-color !default;
|
|
@@ -280,7 +295,6 @@ $u-search-close-transform: scale(0.82) !default;
|
|
|
280
295
|
$u-search-input-font-size: 14px !default;
|
|
281
296
|
$u-search-input-margin: 0 5px !default;
|
|
282
297
|
$u-search-input-color: $u-main-color !default;
|
|
283
|
-
$u-search-input-placeholder-color: $u-tips-color !default;
|
|
284
298
|
$u-search-action-font-size: 14px !default;
|
|
285
299
|
$u-search-action-color: $u-main-color !default;
|
|
286
300
|
$u-search-action-width: 0 !default;
|
|
@@ -341,10 +355,6 @@ $u-search-action-margin-left: 5px !default;
|
|
|
341
355
|
line-height: 1;
|
|
342
356
|
margin: $u-search-input-margin;
|
|
343
357
|
color: $u-search-input-color;
|
|
344
|
-
|
|
345
|
-
&--placeholder {
|
|
346
|
-
color: $u-search-input-placeholder-color;
|
|
347
|
-
}
|
|
348
358
|
}
|
|
349
359
|
}
|
|
350
360
|
|