uview-plus 3.8.108 → 3.8.113
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 +63 -0
- package/components/u-action-sheet/u-action-sheet.vue +3 -1
- package/components/u-calendar/u-calendar.vue +3 -1
- package/components/u-cate-tab/u-cate-tab.vue +2 -2
- package/components/u-color-picker/u-color-picker.vue +2 -2
- package/components/u-cropper/u-cropper.vue +78 -70
- package/components/u-datetime-picker/u-datetime-picker.vue +3 -1
- package/components/u-goods-sku/u-goods-sku.vue +2 -1
- package/components/u-icon/util.js +2 -1
- package/components/u-keyboard/u-keyboard.vue +2 -1
- package/components/u-lazy-load/u-lazy-load.vue +5 -3
- package/components/u-navbar/navbar.js +3 -1
- package/components/u-navbar/props.js +10 -0
- package/components/u-navbar/theme-vars.scss +5 -0
- package/components/u-navbar/u-navbar.vue +155 -12
- package/components/u-picker/u-picker.vue +3 -1
- package/components/u-picker-data/u-picker-data.vue +3 -2
- package/components/u-popup/u-popup.vue +38 -5
- package/components/u-sticky/u-sticky.vue +2 -2
- package/libs/function/index.js +18 -0
- package/libs/root/root.js +3 -1
- package/libs/theme/runtime.js +4 -0
- package/libs/theme/theme.js +6 -2
- package/package.json +1 -1
- package/types/comps/actionSheet.d.ts +4 -0
- package/types/comps/calendar.d.ts +4 -0
- package/types/comps/datetimePicker.d.ts +4 -0
- package/types/comps/keyboard.d.ts +4 -0
- package/types/comps/navbar.d.ts +13 -0
- package/types/comps/picker.d.ts +4 -0
- package/types/comps/popup.d.ts +6 -2
- package/types/func.d.ts +11 -0
- package/types/index.d.ts +1 -0
package/changelog.md
CHANGED
|
@@ -1,3 +1,66 @@
|
|
|
1
|
+
## 3.8.113(2026-08-27)
|
|
2
|
+
fix: 修复启用uniUpRoot,在root组件存在components选项时组件插入错误
|
|
3
|
+
|
|
4
|
+
fix: 修复打包APK运行时icon组件未正确获取字体路径
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
## 3.8.112
|
|
8
|
+
feat: navbar 新增 iOS 大标题模式
|
|
9
|
+
|
|
10
|
+
新增 `mode` 属性,`ios` 值提供现代 iOS 系统应用的导航栏体验:进入页面时导航栏背景透明、标题以 34px 左对齐显示;向下滚动时大标题被压缩进导航栏,标题过渡为常规居中形态,同时出现毛玻璃磨砂背景。
|
|
11
|
+
|
|
12
|
+
- 新增 `mode` 属性,可选 `default`(默认)与 `ios`,非法值按 `default` 处理
|
|
13
|
+
- 新增 `scrollTop` 属性接收页面滚动距离,需由页面 `onPageScroll` 传入;不传时停留在大标题展开态
|
|
14
|
+
- 大标题渲染在 in-flow 层靠原生滚动位移,不经过 JS,小程序端不会因 setData 回程产生拖影
|
|
15
|
+
- 磨砂采用 0.82 不透明底色叠加 `backdrop-filter`:支持的端得到真实毛玻璃,不支持的端仍有 82% 不透明底色兜住文字可读性
|
|
16
|
+
- 玻璃与居中标题的透明度曲线分段递进(0-0.5 与 0.75-1),确保任何滚动位置都不出现两段标题文字互相透出
|
|
17
|
+
- 居中标题在淡入的同时由下方 12px 上浮就位,并带 0.15s 过渡补齐离散滚动事件之间的空隙
|
|
18
|
+
- 新增 `--up-navbar-glass-bg-color` 与 `--up-navbar-glass-blur` 主题变量,跟随明暗模式切换
|
|
19
|
+
- 新增 verify:navbar-ios-mode 校验脚本
|
|
20
|
+
|
|
21
|
+
已知边界:`ios` 模式下 `fixed` 与 `placeholder` 被忽略——固定层恒定固定,in-flow 层恒定渲染,因为该层承载的是大标题这一实际内容而非可选占位。nvue 端因缺少 filter 管线、页面非原生滚动容器、`onPageScroll` 不可靠三项约束,传入 `mode="ios"` 时整体降级为 `default`。
|
|
22
|
+
|
|
23
|
+
`default` 模式的结构、样式与属性语义完全不变。
|
|
24
|
+
|
|
25
|
+
## 3.8.111
|
|
26
|
+
fix: 修复小程序端使用交叉观察器组件时控制台报枚举实例键告警(#864)
|
|
27
|
+
|
|
28
|
+
小程序端 `uni.createIntersectionObserver(this)` 会把 Vue 实例代理直接透传给原生 API,原生内部的 `Object.keys(vm)` 命中 Vue 开发模式下的 ownKeys 拦截,从而输出告警:
|
|
29
|
+
`[Vue warn]: Avoid app logic that relies on enumerating keys on a component instance.`
|
|
30
|
+
根因是 uni-app 只为 `createSelectorQuery` 做了 `$scope` 解包,一直没有为 `createIntersectionObserver` 做同样处理。
|
|
31
|
+
|
|
32
|
+
- 新增 `upCreateIntersectionObserver(comp, options)` 公共方法:优先调用组件实例上的同名方法(内部已完成实例解包,不会外泄代理),APP 端实例上没有该方法时回退到全局 API
|
|
33
|
+
- u-lazy-load、u-sticky、u-cate-tab 三个组件改用该方法,小程序端不再产生告警
|
|
34
|
+
- u-sticky 的 `thresholds: [0.95, 0.98, 1]` 原样透传。注意不能改传 `comp.$scope`:APP 端页面的 `$scope` 仅为 `{ $getAppWebview }`,会被全局 API 误判成 options 参数,导致真实配置被静默丢弃
|
|
35
|
+
- u-parse/node 的观察器位于 `#ifdef H5 || APP-PLUS` 条件编译块内,不会编译进小程序,故保持不变
|
|
36
|
+
- 补充 `upCreateIntersectionObserver` 类型定义,新增 verify:up-create-intersection-observer 校验脚本
|
|
37
|
+
|
|
38
|
+
组件对外行为不变,APP 端调用路径与配置均保持原样。
|
|
39
|
+
|
|
40
|
+
## 3.8.110
|
|
41
|
+
feat: cropper 支持外部图片路径直接裁剪(#897)
|
|
42
|
+
|
|
43
|
+
- `chooseImage` 第二个参数新增 `imageSrc`,可传业务拍照或选图得到的本地临时路径并直接开始裁剪
|
|
44
|
+
- 有效 `imageSrc` 会跳过组件内置 `uni.chooseImage`;未传、空字符串或非字符串仍保持原选图行为
|
|
45
|
+
- 外部路径与系统选图复用同一套图片信息读取、裁剪框初始化和绘制流程
|
|
46
|
+
- “重选”按钮仍打开系统图片选择器;远程地址继续受目标平台读取能力和 H5 跨域限制
|
|
47
|
+
|
|
48
|
+
## 3.8.109
|
|
49
|
+
feat: popup 关闭事件补齐,新增 closed 事件(#902)
|
|
50
|
+
|
|
51
|
+
close 此前只在点击遮罩、点击关闭图标时发出。外部直接把 show 置为 false 时组件内部没有走过关闭动作,不会发出任何事件,业务侧就少了一个"弹窗已关闭"的统一监听点。
|
|
52
|
+
|
|
53
|
+
- u-popup: show 由 true 变 false 时补发 close,任意关闭方式都能被监听到。内部关闭走 emitClose(),它先同步置位去重标记再 emit,因此下游在 close 的同步 handler 里修改 show 数据源不会被误判成外部关闭而重复发出
|
|
54
|
+
- u-popup: 新增 closed 事件,接 u-transition 已有但一直无人监听的 afterLeave,在离场动画结束、弹窗真正消失后发出。close 是"开始关闭",closed 是"已关闭"
|
|
55
|
+
- pageInline 模式下 transition 的 show 恒为 true、不执行离场动画,收不到 afterLeave,改由 watch 补发 closed
|
|
56
|
+
- closed 透传到 u-picker、u-datetime-picker、u-action-sheet、u-keyboard、u-calendar、u-color-picker、u-picker-data、u-goods-sku。其中 u-datetime-picker、u-picker-data 包裹的是 u-picker,属二级透传;u-calendar 只从主体 popup 转发,关闭内部时间选择器不会误报
|
|
57
|
+
- 补充 popup/picker/datetimePicker/actionSheet/keyboard/calendar 的 onClosed 类型定义
|
|
58
|
+
- 新增 verify:popup-close-events 校验脚本
|
|
59
|
+
|
|
60
|
+
组件保持纯受控,渲染只依赖 show prop,未引入内部副本,老用户行为不变。
|
|
61
|
+
|
|
62
|
+
已知取舍:业务在 close 里跨 nextTick 才把 show 置为 false 时,close 会发两次。去重标记只在当次更新周期内有效是刻意的,否则残留标记会吞掉后续真实的关闭事件。这类异步确认场景应改用 closed,它只在弹窗真正消失时发出一次。
|
|
63
|
+
|
|
1
64
|
## 3.8.108(2026-08-18)
|
|
2
65
|
fix: 修复鸿蒙二维码不显示、海报文字不换行等绘图问题
|
|
3
66
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
:show="show"
|
|
4
4
|
mode="bottom"
|
|
5
5
|
@close="closeHandler"
|
|
6
|
+
@closed="$emit('closed')"
|
|
6
7
|
:safeAreaInsetBottom="safeAreaInsetBottom"
|
|
7
8
|
:round="round"
|
|
8
9
|
>
|
|
@@ -156,6 +157,7 @@
|
|
|
156
157
|
*
|
|
157
158
|
* @event {Function} select 点击ActionSheet列表项时触发
|
|
158
159
|
* @event {Function} close 点击取消按钮时触发
|
|
160
|
+
* @event {Function} closed 弹窗已关闭(离场动画结束、弹窗真正消失后)触发
|
|
159
161
|
* @event {Function} getuserinfo 用户点击该按钮时,会返回获取到的用户信息,回调的 detail 数据与 wx.getUserInfo 返回的一致,openType="getUserInfo"时有效
|
|
160
162
|
* @event {Function} contact 客服消息回调,openType="contact"时有效
|
|
161
163
|
* @event {Function} getphonenumber 获取用户手机号回调,openType="getPhoneNumber"时有效
|
|
@@ -224,7 +226,7 @@
|
|
|
224
226
|
})
|
|
225
227
|
}
|
|
226
228
|
},
|
|
227
|
-
emits: ["close", "select", "update:show"],
|
|
229
|
+
emits: ["close", "closed", "select", "update:show"],
|
|
228
230
|
methods: {
|
|
229
231
|
// 关闭操作菜单事件处理
|
|
230
232
|
closeHandler() {
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
:bgColor="bgColor"
|
|
13
13
|
:closeable="!pageInline"
|
|
14
14
|
@close="close"
|
|
15
|
+
@closed="$emit('closed')"
|
|
15
16
|
:round="round"
|
|
16
17
|
:pageInline="pageInline"
|
|
17
18
|
:closeOnClickOverlay="closeOnClickOverlay"
|
|
@@ -236,6 +237,7 @@ import { t } from '../../libs/i18n'
|
|
|
236
237
|
*
|
|
237
238
|
* @event {Function()} confirm 点击确定按钮时触发 选择日期相关的返回参数
|
|
238
239
|
* @event {Function()} close 日历关闭时触发 可定义页面关闭时的回调事件
|
|
240
|
+
* @event {Function()} closed 日历已关闭(离场动画结束、弹窗真正消失后)触发
|
|
239
241
|
* @example <u-calendar :defaultDate="defaultDateMultiple" :show="show" mode="multiple" @confirm="confirm">
|
|
240
242
|
</u-calendar>
|
|
241
243
|
* */
|
|
@@ -411,7 +413,7 @@ export default {
|
|
|
411
413
|
this.start = Date.now()
|
|
412
414
|
this.init()
|
|
413
415
|
},
|
|
414
|
-
emits: ["confirm", "close"],
|
|
416
|
+
emits: ["confirm", "close", "closed"],
|
|
415
417
|
methods: {
|
|
416
418
|
addUnit,
|
|
417
419
|
padTime(num) {
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
</view>
|
|
48
48
|
</template>
|
|
49
49
|
<script>
|
|
50
|
-
import { addUnit, sleep } from '../../libs/function/index';
|
|
50
|
+
import { addUnit, sleep, upCreateIntersectionObserver } from '../../libs/function/index';
|
|
51
51
|
export default {
|
|
52
52
|
name: 'up-cate-tab',
|
|
53
53
|
props: {
|
|
@@ -179,7 +179,7 @@
|
|
|
179
179
|
this._observerList = [];
|
|
180
180
|
|
|
181
181
|
this.tabList.map((val, index) => {
|
|
182
|
-
let observer =
|
|
182
|
+
let observer = upCreateIntersectionObserver(this);
|
|
183
183
|
this._observerList.push(observer);
|
|
184
184
|
// 检测相交状态
|
|
185
185
|
observer.relativeTo('.u-cate-tab__right-box', {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
:style="{backgroundColor: value}" >
|
|
5
5
|
<slot></slot>
|
|
6
6
|
</view>
|
|
7
|
-
<up-popup :show="show" mode="bottom" round="10" @close="close" :closeOnClickOverlay="true">
|
|
7
|
+
<up-popup :show="show" mode="bottom" round="10" @close="close" @closed="$emit('closed')" :closeOnClickOverlay="true">
|
|
8
8
|
<view class="up-color-picker__content">
|
|
9
9
|
<view class="up-color-picker__header">
|
|
10
10
|
<text class="up-color-picker__title">选择颜色</text>
|
|
@@ -267,7 +267,7 @@ export default {
|
|
|
267
267
|
mounted() {
|
|
268
268
|
this.initColor()
|
|
269
269
|
},
|
|
270
|
-
emits: ['update:modelValue', 'confirm', 'close'],
|
|
270
|
+
emits: ['update:modelValue', 'confirm', 'close', 'closed'],
|
|
271
271
|
methods: {
|
|
272
272
|
// 初始化颜色
|
|
273
273
|
initColor() {
|
|
@@ -221,68 +221,71 @@
|
|
|
221
221
|
}
|
|
222
222
|
this.hideImg();
|
|
223
223
|
},
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
this.
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
|
|
224
|
+
loadImage(path) {
|
|
225
|
+
uni.showLoading({ mask: true });
|
|
226
|
+
this.imgPath = path;
|
|
227
|
+
uni.getImageInfo({
|
|
228
|
+
src: path,
|
|
229
|
+
success: r => {
|
|
230
|
+
this.imgWidth = r.width;
|
|
231
|
+
this.imgHeight = r.height;
|
|
232
|
+
this.path = path;
|
|
233
|
+
if (!this.hasSel) {
|
|
234
|
+
let style = this.selStyle || {};
|
|
235
|
+
if (this.arWidth && this.arHeight) {
|
|
236
|
+
let areaWidth = this.arWidth.indexOf('rpx') >= 0 ? parseInt(this.arWidth) * this.pxRatio : parseInt(this.arWidth),
|
|
237
|
+
areaHeight = this.arHeight.indexOf('rpx') >= 0 ? parseInt(this.arHeight) * this.pxRatio : parseInt(this.arHeight);
|
|
238
|
+
style.width = areaWidth + 'px';
|
|
239
|
+
style.height = areaHeight + 'px';
|
|
240
|
+
style.top = (this.windowHeight - areaHeight - tabHeight) / 2 + 'px';
|
|
241
|
+
style.left = (this.windowWidth - areaWidth) / 2 + 'px';
|
|
242
|
+
} else {
|
|
243
|
+
uni.showModal({
|
|
244
|
+
title: t("up.cropper.emptyWidhtOrHeight"),
|
|
245
|
+
showCancel: false
|
|
246
|
+
})
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
this.selStyle = style;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (this.noBar) {
|
|
253
|
+
this.drawInit(true);
|
|
254
|
+
} else {
|
|
255
|
+
uni.hideTabBar({
|
|
256
|
+
complete: () => {
|
|
257
|
+
this.drawInit(true);
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
},
|
|
262
|
+
fail: () => {
|
|
263
|
+
uni.showToast({
|
|
264
|
+
title: "error3",
|
|
265
|
+
duration: 2000,
|
|
266
|
+
})
|
|
267
|
+
},
|
|
268
|
+
complete() {
|
|
269
|
+
uni.hideLoading();
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
},
|
|
273
|
+
select() {
|
|
274
|
+
if (this.fSelecting) return;
|
|
275
|
+
this.fSelecting = true;
|
|
276
|
+
setTimeout(() => { this.fSelecting = false; }, 500);
|
|
277
|
+
uni.chooseImage({
|
|
278
|
+
count: 1,
|
|
279
|
+
sizeType: ['original', 'compressed'],
|
|
280
|
+
sourceType: ['album', 'camera'],
|
|
281
|
+
success: (r) => {
|
|
282
|
+
this.loadImage(r.tempFilePaths[0]);
|
|
283
|
+
},
|
|
284
|
+
fail: () => {
|
|
285
|
+
this.$emit('cancel')
|
|
286
|
+
}
|
|
287
|
+
})
|
|
288
|
+
},
|
|
286
289
|
async confirm() {
|
|
287
290
|
if (this.fUploading) return;
|
|
288
291
|
this.fUploading = true;
|
|
@@ -725,9 +728,10 @@
|
|
|
725
728
|
uni.hideLoading();
|
|
726
729
|
}
|
|
727
730
|
});
|
|
728
|
-
},
|
|
729
|
-
chooseImage(index = undefined, params = undefined, data = undefined) {
|
|
730
|
-
|
|
731
|
+
},
|
|
732
|
+
chooseImage(index = undefined, params = undefined, data = undefined) {
|
|
733
|
+
const imageSrc = typeof params?.imageSrc === 'string' ? params.imageSrc.trim() : '';
|
|
734
|
+
if (params) {
|
|
731
735
|
console.log(params)
|
|
732
736
|
let areaWidth = params.areaWidth || this.areaWidth,
|
|
733
737
|
areaHeight = params.areaHeight || this.areaHeight,
|
|
@@ -774,11 +778,15 @@
|
|
|
774
778
|
// console.log(this.selStyle);
|
|
775
779
|
this.hasSel = true;
|
|
776
780
|
}
|
|
777
|
-
}
|
|
778
|
-
this.rtn = data;
|
|
779
|
-
this.indx = index;
|
|
780
|
-
|
|
781
|
-
|
|
781
|
+
}
|
|
782
|
+
this.rtn = data;
|
|
783
|
+
this.indx = index;
|
|
784
|
+
if (imageSrc) {
|
|
785
|
+
this.loadImage(imageSrc);
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
this.select();
|
|
789
|
+
},
|
|
782
790
|
rotate() {
|
|
783
791
|
// #ifdef APP-PLUS
|
|
784
792
|
if (this.platform === 'android') {
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
:maskClass="maskClass"
|
|
34
34
|
:maskStyle="resolvedMaskStyle"
|
|
35
35
|
@close="close"
|
|
36
|
+
@closed="$emit('closed')"
|
|
36
37
|
@cancel="cancel"
|
|
37
38
|
@confirm="confirm"
|
|
38
39
|
@change="change"
|
|
@@ -93,6 +94,7 @@
|
|
|
93
94
|
* @property {Boolean} closeOnClickOverlay 是否允许点击遮罩关闭选择器 ( 默认 false )
|
|
94
95
|
* @property {Array} defaultIndex 各列的默认索引
|
|
95
96
|
* @event {Function} close 关闭选择器时触发
|
|
97
|
+
* @event {Function} closed 选择器已关闭(离场动画结束、弹窗真正消失后)触发
|
|
96
98
|
* @event {Function} confirm 点击确定按钮,返回当前选择的值
|
|
97
99
|
* @event {Function} change 当选择值变化时触发
|
|
98
100
|
* @event {Function} cancel 点击取消按钮
|
|
@@ -191,7 +193,7 @@
|
|
|
191
193
|
}
|
|
192
194
|
},
|
|
193
195
|
// #ifdef VUE3
|
|
194
|
-
emits: ['close', 'cancel', 'confirm', 'change', 'update:modelValue'],
|
|
196
|
+
emits: ['close', 'closed', 'cancel', 'confirm', 'change', 'update:modelValue'],
|
|
195
197
|
// #endif
|
|
196
198
|
methods: {
|
|
197
199
|
toInt(value, fallback = 0) {
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
:pageInline="pageInline"
|
|
11
11
|
:border-radius="20"
|
|
12
12
|
@close="close"
|
|
13
|
+
@closed="$emit('closed')"
|
|
13
14
|
>
|
|
14
15
|
<view class="up-goods-sku-container" :style="{padding: pageInline ? '0px' : ''}">
|
|
15
16
|
<view class="up-goods-sku__header">
|
|
@@ -175,7 +176,7 @@
|
|
|
175
176
|
},
|
|
176
177
|
watch: {
|
|
177
178
|
},
|
|
178
|
-
emits: ['open', 'confirm', 'close'],
|
|
179
|
+
emits: ['open', 'confirm', 'close', 'closed'],
|
|
179
180
|
created() {
|
|
180
181
|
if (this.pageInline) {
|
|
181
182
|
this.show = true;
|
|
@@ -44,7 +44,8 @@ const getAppIconUrl = () => {
|
|
|
44
44
|
}
|
|
45
45
|
if (typeof plus !== 'undefined' && plus.io && typeof plus.io.convertLocalFileSystemURL === 'function') {
|
|
46
46
|
const iconFontPath = plus.io.convertLocalFileSystemURL(appIconFontUrl);
|
|
47
|
-
if (iconFontPath) return iconFontPath;
|
|
47
|
+
// if (iconFontPath) return iconFontPath;
|
|
48
|
+
return iconFontPath && iconFontPath.startsWith('file://') ? iconFontPath : 'file://' + iconFontPath;
|
|
48
49
|
}
|
|
49
50
|
return appIconFontUrl;
|
|
50
51
|
// #endif
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
:show="show"
|
|
8
8
|
:safeAreaInsetBottom="safeAreaInsetBottom"
|
|
9
9
|
@close="popupClose"
|
|
10
|
+
@closed="$emit('closed')"
|
|
10
11
|
:zIndex="zIndex"
|
|
11
12
|
:customStyle="popupStyle"
|
|
12
13
|
>
|
|
@@ -111,7 +112,7 @@
|
|
|
111
112
|
}
|
|
112
113
|
}
|
|
113
114
|
},
|
|
114
|
-
emits: ["change", "close", "confirm", "cancel", "backspace"],
|
|
115
|
+
emits: ["change", "close", "closed", "confirm", "cancel", "backspace"],
|
|
115
116
|
methods: {
|
|
116
117
|
change(e) {
|
|
117
118
|
this.$emit('change', e);
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
import {
|
|
21
21
|
addUnit,
|
|
22
22
|
guid,
|
|
23
|
-
rpx2px
|
|
23
|
+
rpx2px,
|
|
24
|
+
upCreateIntersectionObserver
|
|
24
25
|
} from '../../libs/function/index.js';
|
|
25
26
|
/**
|
|
26
27
|
* lazyLoad 懒加载
|
|
@@ -211,10 +212,11 @@
|
|
|
211
212
|
|
|
212
213
|
setTimeout(() => {
|
|
213
214
|
// #ifndef APP-NVUE
|
|
214
|
-
//
|
|
215
|
+
// 这里是组件内获取布局状态,不能直传Vue实例给uni.createIntersectionObserver,
|
|
216
|
+
// 否则小程序内部会Object.keys(vm)导致告警,统一走upCreateIntersectionObserver
|
|
215
217
|
// this.disconnectObserver('contentObserver');
|
|
216
218
|
// nvue 里不支持
|
|
217
|
-
const contentObserver =
|
|
219
|
+
const contentObserver = upCreateIntersectionObserver(this);
|
|
218
220
|
// 要理解这里怎么计算的,请看这个:
|
|
219
221
|
// https://blog.csdn.net/qq_25324335/article/details/83687695
|
|
220
222
|
contentObserver.relativeToViewport({
|
|
@@ -94,6 +94,16 @@ export const props = defineMixin({
|
|
|
94
94
|
titleStyle: {
|
|
95
95
|
type: [String, Object],
|
|
96
96
|
default: () => defProps.navbar.titleStyle
|
|
97
|
+
},
|
|
98
|
+
// 导航栏模式,default-常规,ios-大标题磨砂模式
|
|
99
|
+
mode: {
|
|
100
|
+
type: String,
|
|
101
|
+
default: () => defProps.navbar.mode
|
|
102
|
+
},
|
|
103
|
+
// 页面滚动距离,仅 ios 模式使用,由页面 onPageScroll 传入
|
|
104
|
+
scrollTop: {
|
|
105
|
+
type: [String, Number],
|
|
106
|
+
default: () => defProps.navbar.scrollTop
|
|
97
107
|
}
|
|
98
108
|
}
|
|
99
109
|
})
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
page,
|
|
3
3
|
body {
|
|
4
4
|
--up-navbar-bg-color: #ffffff;
|
|
5
|
+
--up-navbar-glass-bg-color: rgba(255, 255, 255, 0.82);
|
|
6
|
+
--up-navbar-glass-blur: 20px;
|
|
5
7
|
}
|
|
6
8
|
|
|
7
9
|
@media (prefers-color-scheme: dark) {
|
|
@@ -9,13 +11,16 @@ body {
|
|
|
9
11
|
page,
|
|
10
12
|
body {
|
|
11
13
|
--up-navbar-bg-color: #1c1c1e;
|
|
14
|
+
--up-navbar-glass-bg-color: rgba(28, 28, 30, 0.82);
|
|
12
15
|
}
|
|
13
16
|
}
|
|
14
17
|
|
|
15
18
|
[data-up-theme='light'] {
|
|
16
19
|
--up-navbar-bg-color: #ffffff;
|
|
20
|
+
--up-navbar-glass-bg-color: rgba(255, 255, 255, 0.82);
|
|
17
21
|
}
|
|
18
22
|
|
|
19
23
|
[data-up-theme='dark'] {
|
|
20
24
|
--up-navbar-bg-color: #1c1c1e;
|
|
25
|
+
--up-navbar-glass-bg-color: rgba(28, 28, 30, 0.82);
|
|
21
26
|
}
|
|
@@ -1,21 +1,47 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<view class="u-navbar" :class="[customClass]">
|
|
3
|
+
<!-- ios 模式:in-flow 层,恒定渲染,随页面原生滚动被带走 -->
|
|
4
|
+
<view
|
|
5
|
+
v-if="isIosMode"
|
|
6
|
+
class="u-navbar__flow"
|
|
7
|
+
>
|
|
8
|
+
<view :style="{ height: navbarFlowSpacerHeight }"></view>
|
|
9
|
+
<view
|
|
10
|
+
v-if="title"
|
|
11
|
+
class="u-navbar__large-title"
|
|
12
|
+
:style="{ height: addUnit(largeTitleHeight) }"
|
|
13
|
+
>
|
|
14
|
+
<text
|
|
15
|
+
class="u-line-1 u-navbar__large-title__text"
|
|
16
|
+
:style="[{ color: navbarTitleColor }, addStyle(titleStyle)]"
|
|
17
|
+
>{{ title }}</text>
|
|
18
|
+
</view>
|
|
19
|
+
</view>
|
|
20
|
+
<!-- default 模式:原有占位块,行为保持不变 -->
|
|
3
21
|
<view
|
|
4
22
|
class="u-navbar__placeholder"
|
|
5
|
-
v-if="fixed && placeholder"
|
|
23
|
+
v-if="!isIosMode && fixed && placeholder"
|
|
6
24
|
:style="{
|
|
7
25
|
height: addUnit(getPx(height) + getWindowInfo().statusBarHeight,'px'),
|
|
8
26
|
}"
|
|
9
27
|
></view>
|
|
10
28
|
<view
|
|
11
29
|
class="u-navbar__inner"
|
|
12
|
-
:class="[fixed && 'u-navbar--fixed']"
|
|
30
|
+
:class="[(isIosMode || fixed) && 'u-navbar--fixed']"
|
|
13
31
|
:style="[navbarInnerStyle]"
|
|
14
32
|
>
|
|
33
|
+
<view
|
|
34
|
+
v-if="isIosMode"
|
|
35
|
+
class="u-navbar__glass"
|
|
36
|
+
:style="{
|
|
37
|
+
opacity: navbarGlassOpacity,
|
|
38
|
+
background: navbarGlassBgColor,
|
|
39
|
+
}"
|
|
40
|
+
></view>
|
|
15
41
|
<u-status-bar v-if="safeAreaInsetTop"></u-status-bar>
|
|
16
42
|
<view
|
|
17
43
|
class="u-navbar__content"
|
|
18
|
-
:class="[border && 'u-border-bottom']"
|
|
44
|
+
:class="[border && !isIosMode && 'u-border-bottom']"
|
|
19
45
|
:style="{
|
|
20
46
|
height: addUnit(height),
|
|
21
47
|
backgroundColor: 'transparent',
|
|
@@ -43,15 +69,20 @@
|
|
|
43
69
|
>{{ leftText }}</text>
|
|
44
70
|
</slot>
|
|
45
71
|
</view>
|
|
46
|
-
<
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
72
|
+
<view
|
|
73
|
+
class="u-navbar__content__center"
|
|
74
|
+
:style="[navbarCenterStyle]"
|
|
75
|
+
>
|
|
76
|
+
<slot name="center">
|
|
77
|
+
<text
|
|
78
|
+
class="u-line-1 u-navbar__content__title"
|
|
79
|
+
:style="[{
|
|
80
|
+
width: addUnit(titleWidth),
|
|
81
|
+
color: navbarTitleColor,
|
|
82
|
+
}, addStyle(titleStyle)]"
|
|
83
|
+
>{{ title }}</text>
|
|
84
|
+
</slot>
|
|
85
|
+
</view>
|
|
55
86
|
<view
|
|
56
87
|
class="u-navbar__content__right"
|
|
57
88
|
v-if="$slots.right || rightIcon || rightText"
|
|
@@ -82,6 +113,12 @@
|
|
|
82
113
|
import { mixin } from '../../libs/mixin/mixin';
|
|
83
114
|
import config from '../../libs/config/config';
|
|
84
115
|
import { addUnit, addStyle, getPx, getWindowInfo } from '../../libs/function/index';
|
|
116
|
+
|
|
117
|
+
// iOS 大标题行高。它同时是 navbarProgress 的分母,
|
|
118
|
+
// 因此改动此值会同步改变压缩过程的滚动区间。
|
|
119
|
+
const LARGE_TITLE_HEIGHT = 52
|
|
120
|
+
// 居中标题上浮的起始偏移,与淡入同步归零
|
|
121
|
+
const CENTER_TITLE_RISE = 12
|
|
85
122
|
/**
|
|
86
123
|
* Navbar 自定义导航栏
|
|
87
124
|
* @description 此组件一般用于在特殊情况下,需要自定义导航栏的时候用到,一般建议使用uni-app带的导航栏。
|
|
@@ -104,6 +141,8 @@
|
|
|
104
141
|
* @property {String | Number} leftIconColor 左侧返回图标的颜色(默认 #303133 )
|
|
105
142
|
* @property {Boolean} autoBack 点击左侧区域(返回图标),是否自动返回上一页(默认 false )
|
|
106
143
|
* @property {Object | String} titleStyle 标题的样式,对象或字符串
|
|
144
|
+
* @property {String} mode 导航栏模式,default-常规,ios-大标题磨砂模式(默认 'default' )
|
|
145
|
+
* @property {String | Number} scrollTop 页面滚动距离,仅 ios 模式使用,由页面 onPageScroll 传入(默认 0 )
|
|
107
146
|
* @event {Function} leftClick 点击左侧区域
|
|
108
147
|
* @event {Function} rightClick 点击右侧区域
|
|
109
148
|
* @example <u-navbar title="剑未配妥,出门已是江湖" left-text="返回" right-text="帮助" @click-left="onClickBack" @click-right="onClickRight"></u-navbar>
|
|
@@ -116,6 +155,60 @@
|
|
|
116
155
|
}
|
|
117
156
|
},
|
|
118
157
|
computed: {
|
|
158
|
+
isIosMode() {
|
|
159
|
+
// nvue 缺少 filter 管线、页面非原生滚动容器、onPageScroll 不可靠,
|
|
160
|
+
// 三条约束任意一条都足以让 ios 模式失效,整体降级为 default。
|
|
161
|
+
// 注意 APP-NVUE 在 app-harmony 上为 false,harmony 走 ArkWeb 应获得完整 ios 模式。
|
|
162
|
+
// #ifdef APP-NVUE
|
|
163
|
+
return false
|
|
164
|
+
// #endif
|
|
165
|
+
// #ifndef APP-NVUE
|
|
166
|
+
return this.mode === 'ios'
|
|
167
|
+
// #endif
|
|
168
|
+
},
|
|
169
|
+
// 有效大标题行高:title 为空时不渲染大标题行,行高塌陷为 0
|
|
170
|
+
largeTitleHeight() {
|
|
171
|
+
return this.title ? LARGE_TITLE_HEIGHT : 0
|
|
172
|
+
},
|
|
173
|
+
// in-flow 层顶部让位块高度,为固定层腾出空间
|
|
174
|
+
navbarFlowSpacerHeight() {
|
|
175
|
+
const statusBarHeight = this.safeAreaInsetTop ? getWindowInfo().statusBarHeight : 0
|
|
176
|
+
return addUnit(getPx(this.height) + statusBarHeight, 'px')
|
|
177
|
+
},
|
|
178
|
+
// 压缩进度。分母取有效大标题行高,progress=1 即大标题恰好完全没入导航栏
|
|
179
|
+
navbarProgress() {
|
|
180
|
+
if (!this.isIosMode) return 1
|
|
181
|
+
const height = this.largeTitleHeight
|
|
182
|
+
if (height <= 0) return 1
|
|
183
|
+
const offset = getPx(this.scrollTop) || 0
|
|
184
|
+
return Math.min(Math.max(offset / height, 0), 1)
|
|
185
|
+
},
|
|
186
|
+
// 磨砂在前半段走完,为居中标题的出现铺好不透明底
|
|
187
|
+
navbarGlassOpacity() {
|
|
188
|
+
if (!this.isIosMode) return 0
|
|
189
|
+
return Math.min(Math.max(this.navbarProgress / 0.5, 0), 1)
|
|
190
|
+
},
|
|
191
|
+
// 居中标题在后段才启动,此时玻璃已满不透明,不会与大标题互相透出
|
|
192
|
+
navbarCenterOpacity() {
|
|
193
|
+
if (!this.isIosMode) return 1
|
|
194
|
+
return Math.min(Math.max((this.navbarProgress - 0.75) / 0.25, 0), 1)
|
|
195
|
+
},
|
|
196
|
+
// 居中标题由下往上浮现:与淡入同一段行程,位移随之归零
|
|
197
|
+
navbarCenterStyle() {
|
|
198
|
+
if (!this.isIosMode) return {}
|
|
199
|
+
const opacity = this.navbarCenterOpacity
|
|
200
|
+
return {
|
|
201
|
+
opacity,
|
|
202
|
+
transform: `translateY(${(1 - opacity) * CENTER_TITLE_RISE}px)`
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
navbarGlassBgColor() {
|
|
206
|
+
// 0.82 是可读性下限的承重值:backdrop-filter 不生效时,
|
|
207
|
+
// 仅靠这个不透明度也必须保证文字不与下方内容读串。
|
|
208
|
+
return this.bgColor
|
|
209
|
+
|| this.upThemeVar('--up-navbar-glass-bg-color',
|
|
210
|
+
this.upThemeIsDark ? 'rgba(28, 28, 30, 0.82)' : 'rgba(255, 255, 255, 0.82)')
|
|
211
|
+
},
|
|
119
212
|
navbarBgColor() {
|
|
120
213
|
if (this.bgColor) return this.bgColor
|
|
121
214
|
return this.upThemeVar('--up-navbar-bg-color', this.upThemeIsDark ? '#1c1c1e' : '#ffffff')
|
|
@@ -134,6 +227,11 @@
|
|
|
134
227
|
navbarInnerStyle() {
|
|
135
228
|
const style = {}
|
|
136
229
|
style.background = this.navbarBgColor
|
|
230
|
+
// ios 模式下固定层必须透明,否则 backdrop-filter 采样到自身底色,模糊不出内容。
|
|
231
|
+
// 背景由独立的 __glass 层承担,随滚动淡入。
|
|
232
|
+
if (this.isIosMode) {
|
|
233
|
+
style.background = 'transparent'
|
|
234
|
+
}
|
|
137
235
|
return style
|
|
138
236
|
}
|
|
139
237
|
},
|
|
@@ -179,12 +277,46 @@
|
|
|
179
277
|
z-index: 11;
|
|
180
278
|
}
|
|
181
279
|
|
|
280
|
+
&__flow {
|
|
281
|
+
/* #ifndef APP-NVUE */
|
|
282
|
+
width: 100%;
|
|
283
|
+
/* #endif */
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
&__large-title {
|
|
287
|
+
@include flex(row);
|
|
288
|
+
align-items: center;
|
|
289
|
+
padding: 0 13px;
|
|
290
|
+
|
|
291
|
+
&__text {
|
|
292
|
+
font-size: 34px;
|
|
293
|
+
font-weight: 700;
|
|
294
|
+
line-height: 1.2;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
&__glass {
|
|
299
|
+
position: absolute;
|
|
300
|
+
top: 0;
|
|
301
|
+
left: 0;
|
|
302
|
+
right: 0;
|
|
303
|
+
bottom: 0;
|
|
304
|
+
// 模糊半径写在静态 class 内而非 :style 绑定,使 -webkit- 前缀在编译期确定。
|
|
305
|
+
// iOS 16 之前的 WKWebView 只认前缀版本,两条都必须保留。
|
|
306
|
+
// CSS 变量不被支持时回落到字面量 20px。
|
|
307
|
+
-webkit-backdrop-filter: saturate(180%) blur(var(--up-navbar-glass-blur, 20px));
|
|
308
|
+
backdrop-filter: saturate(180%) blur(var(--up-navbar-glass-blur, 20px));
|
|
309
|
+
}
|
|
310
|
+
|
|
182
311
|
&__content {
|
|
183
312
|
@include flex(row);
|
|
184
313
|
align-items: center;
|
|
185
314
|
height: 44px;
|
|
186
315
|
background-color: $u-bg-color;
|
|
316
|
+
// 玻璃层是绝对定位的,会盖住同级的非定位元素。
|
|
317
|
+
// 缺少这两行的表现是整个导航栏内容不可见。
|
|
187
318
|
position: relative;
|
|
319
|
+
z-index: 1;
|
|
188
320
|
justify-content: center;
|
|
189
321
|
|
|
190
322
|
&__left,
|
|
@@ -211,6 +343,17 @@
|
|
|
211
343
|
}
|
|
212
344
|
}
|
|
213
345
|
|
|
346
|
+
&__center {
|
|
347
|
+
@include flex(row);
|
|
348
|
+
align-items: center;
|
|
349
|
+
justify-content: center;
|
|
350
|
+
// 滚动事件是离散到达的,补一段短过渡让上浮与淡入连续。
|
|
351
|
+
// 时长刻意短于一次滚动事件的间隔,避免落后于手指。
|
|
352
|
+
/* #ifndef APP-NVUE */
|
|
353
|
+
transition: opacity 0.15s linear, transform 0.15s ease-out;
|
|
354
|
+
/* #endif */
|
|
355
|
+
}
|
|
356
|
+
|
|
214
357
|
&__title {
|
|
215
358
|
text-align: center;
|
|
216
359
|
font-size: 16px;
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
:pageInline="pageInline"
|
|
24
24
|
:overlayOpacity="overlayOpacity"
|
|
25
25
|
@close="closeHandler"
|
|
26
|
+
@closed="$emit('closed')"
|
|
26
27
|
>
|
|
27
28
|
<view class="u-picker">
|
|
28
29
|
<u-toolbar
|
|
@@ -108,6 +109,7 @@
|
|
|
108
109
|
* @property {String | Number} duration 动画时长,单位ms (默认 300 )
|
|
109
110
|
* @property {String | Number} overlayDuration 遮罩层动画时长,单位ms (默认 350 )
|
|
110
111
|
* @event {Function} close 关闭选择器时触发
|
|
112
|
+
* @event {Function} closed 选择器已关闭(离场动画结束、弹窗真正消失后)触发
|
|
111
113
|
* @event {Function} cancel 点击取消按钮触发
|
|
112
114
|
* @event {Function} change 当选择值变化时触发
|
|
113
115
|
* @event {Function} confirm 点击确定按钮,返回当前选择的值
|
|
@@ -195,7 +197,7 @@ export default {
|
|
|
195
197
|
}
|
|
196
198
|
}
|
|
197
199
|
},
|
|
198
|
-
emits: ['close', 'cancel', 'confirm', 'change', 'update:modelValue', 'update:show'],
|
|
200
|
+
emits: ['close', 'closed', 'cancel', 'confirm', 'change', 'update:modelValue', 'update:show'],
|
|
199
201
|
computed: {
|
|
200
202
|
// input的props
|
|
201
203
|
inputPropsInner() {
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
:defaultIndex="defaultIndex"
|
|
21
21
|
@confirm="confirm"
|
|
22
22
|
@cancel="cancel"
|
|
23
|
-
@close="close"
|
|
23
|
+
@close="close"
|
|
24
|
+
@closed="$emit('closed')">
|
|
24
25
|
</up-picker>
|
|
25
26
|
</view>
|
|
26
27
|
</template>
|
|
@@ -95,7 +96,7 @@ export default {
|
|
|
95
96
|
return [this.options];
|
|
96
97
|
}
|
|
97
98
|
},
|
|
98
|
-
emits: ['update:modelValue', 'cancel', 'close', 'confirm'],
|
|
99
|
+
emits: ['update:modelValue', 'cancel', 'close', 'closed', 'confirm'],
|
|
99
100
|
methods: {
|
|
100
101
|
hideKeyboard() {
|
|
101
102
|
uni.hideKeyboard()
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
:mode="pageInline ? 'none' : position"
|
|
26
26
|
:duration="duration"
|
|
27
27
|
@afterEnter="afterEnter"
|
|
28
|
+
@afterLeave="afterLeave"
|
|
28
29
|
@click="clickHandler"
|
|
29
30
|
>
|
|
30
31
|
<!-- @click.stop不能去除,去除会导致居中模式下点击内容区域触发关闭弹窗 -->
|
|
@@ -99,8 +100,9 @@
|
|
|
99
100
|
* @property {String} minHeight 最小高度,单位任意,数值默认为px(默认 '200px' )
|
|
100
101
|
* @property {String} maxHeight 最大高度,单位任意,数值默认为px(默认 '80%' )
|
|
101
102
|
* @property {Object} customStyle 组件的样式,对象形式
|
|
102
|
-
* @event {Function} open
|
|
103
|
-
* @event {Function} close
|
|
103
|
+
* @event {Function} open 弹出层打开(进场动画结束后)
|
|
104
|
+
* @event {Function} close 弹出层收起(关闭动作发生时,离场动画开始前;外部直接将show置为false时也会触发)
|
|
105
|
+
* @event {Function} closed 弹出层已关闭(离场动画结束、弹窗真正消失后)
|
|
104
106
|
* @example <u-popup v-model:show="show"><text>出淤泥而不染,濯清涟而不妖</text></u-popup>
|
|
105
107
|
*/
|
|
106
108
|
export default {
|
|
@@ -109,6 +111,8 @@
|
|
|
109
111
|
data() {
|
|
110
112
|
return {
|
|
111
113
|
overlayDuration: this.duration + 50,
|
|
114
|
+
// close事件是否已由组件内部的关闭动作发出,避免show变化时重复发出
|
|
115
|
+
closeEmitted: false,
|
|
112
116
|
// 触摸相关数据
|
|
113
117
|
touchStartY: 0,
|
|
114
118
|
touchStartHeight: 0,
|
|
@@ -120,10 +124,25 @@
|
|
|
120
124
|
watch: {
|
|
121
125
|
show(newValue, oldValue) {
|
|
122
126
|
if (newValue === true) {
|
|
127
|
+
this.closeEmitted = false
|
|
123
128
|
// #ifdef MP-WEIXIN
|
|
124
129
|
const children = this.$children
|
|
125
130
|
this.retryComputedComponentRect(children)
|
|
126
131
|
// #endif
|
|
132
|
+
} else if (oldValue === true) {
|
|
133
|
+
// 外部直接将show置为false时,组件内部没有走过关闭动作,
|
|
134
|
+
// 此处补发close,保证任意关闭方式都能被监听到
|
|
135
|
+
if (this.closeEmitted) {
|
|
136
|
+
// 本次关闭已由内部动作发出过close,消费标记即可
|
|
137
|
+
this.closeEmitted = false
|
|
138
|
+
} else {
|
|
139
|
+
this.$emit('close')
|
|
140
|
+
}
|
|
141
|
+
// pageInline模式下transition的show恒为true,不会执行离场动画,
|
|
142
|
+
// 收不到afterLeave,此处按动画时长补发closed
|
|
143
|
+
if (this.pageInline) {
|
|
144
|
+
this.$emit('closed')
|
|
145
|
+
}
|
|
127
146
|
}
|
|
128
147
|
}
|
|
129
148
|
},
|
|
@@ -238,25 +257,39 @@
|
|
|
238
257
|
}
|
|
239
258
|
},
|
|
240
259
|
},
|
|
241
|
-
emits: ["open", "close", "click", "update:show"],
|
|
260
|
+
emits: ["open", "close", "closed", "click", "update:show"],
|
|
242
261
|
methods: {
|
|
262
|
+
// 发出close事件。标记仅在本次更新周期内有效,
|
|
263
|
+
// 用于抑制紧随其后的show变化导致watch重复补发close
|
|
264
|
+
emitClose() {
|
|
265
|
+
this.closeEmitted = true
|
|
266
|
+
this.$emit('close')
|
|
267
|
+
this.$nextTick(() => {
|
|
268
|
+
this.closeEmitted = false
|
|
269
|
+
})
|
|
270
|
+
},
|
|
243
271
|
// 点击遮罩
|
|
244
272
|
overlayClick() {
|
|
245
273
|
if (this.closeOnClickOverlay) {
|
|
246
274
|
this.$emit('update:show', false)
|
|
247
|
-
this
|
|
275
|
+
this.emitClose()
|
|
248
276
|
}
|
|
249
277
|
},
|
|
250
278
|
open(e) {
|
|
279
|
+
this.closeEmitted = false
|
|
251
280
|
this.$emit('update:show', true)
|
|
252
281
|
},
|
|
253
282
|
close(e) {
|
|
254
283
|
this.$emit('update:show', false)
|
|
255
|
-
this
|
|
284
|
+
this.emitClose()
|
|
256
285
|
},
|
|
257
286
|
afterEnter() {
|
|
258
287
|
this.$emit('open')
|
|
259
288
|
},
|
|
289
|
+
// 离场动画结束,弹窗已真正消失
|
|
290
|
+
afterLeave() {
|
|
291
|
+
this.$emit('closed')
|
|
292
|
+
},
|
|
260
293
|
clickHandler() {
|
|
261
294
|
// 由于中部弹出时,其u-transition占据了整个页面相当于遮罩,此时需要发出遮罩点击事件,是否无法通过点击遮罩关闭弹窗
|
|
262
295
|
if(this.mode === 'center') {
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
import { props } from './props';
|
|
18
18
|
import { mpMixin } from '../../libs/mixin/mpMixin';
|
|
19
19
|
import { mixin } from '../../libs/mixin/mixin';
|
|
20
|
-
import { addUnit, addStyle, deepMerge, getPx, guid, getDeviceInfo, os } from '../../libs/function/index';
|
|
20
|
+
import { addUnit, addStyle, deepMerge, getPx, guid, getDeviceInfo, os, upCreateIntersectionObserver } from '../../libs/function/index';
|
|
21
21
|
import zIndex from '../../libs/config/zIndex';
|
|
22
22
|
/**
|
|
23
23
|
* sticky 吸顶
|
|
@@ -119,7 +119,7 @@
|
|
|
119
119
|
observeContent() {
|
|
120
120
|
// 先断掉之前的观察
|
|
121
121
|
this.disconnectObserver('contentObserver')
|
|
122
|
-
const contentObserver =
|
|
122
|
+
const contentObserver = upCreateIntersectionObserver(this, {
|
|
123
123
|
// 检测的区间范围
|
|
124
124
|
thresholds: [0.95, 0.98, 1]
|
|
125
125
|
})
|
package/libs/function/index.js
CHANGED
|
@@ -70,6 +70,23 @@ export function upGetRect(selector, all = false, comp = null) {
|
|
|
70
70
|
})
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
// 创建交叉观察器
|
|
74
|
+
// 小程序端 uni.createIntersectionObserver(vm) 会把 Vue 实例代理直接透传给原生 API,
|
|
75
|
+
// 原生内部的 Object.keys(vm) 会触发告警:
|
|
76
|
+
// Avoid app logic that relies on enumerating keys on a component instance
|
|
77
|
+
// (uni-app 只给 createSelectorQuery 做了 $scope 解包,没给 createIntersectionObserver 做)
|
|
78
|
+
// 组件实例上的同名方法内部使用 $scope(原生组件实例),不会把代理外泄,因此优先使用
|
|
79
|
+
export function upCreateIntersectionObserver(comp, options) {
|
|
80
|
+
// 各家小程序与 H5 都在组件实例上暴露了该方法,内部已完成实例解包
|
|
81
|
+
if (comp && typeof comp.createIntersectionObserver === 'function') {
|
|
82
|
+
return options ? comp.createIntersectionObserver(options) : comp.createIntersectionObserver()
|
|
83
|
+
}
|
|
84
|
+
// APP 端未在实例上暴露该方法,退回全局 API,其内部会自行解析实例,本身不产生告警
|
|
85
|
+
// 注意:不能传 comp.$scope,APP 端页面的 $scope 仅为 { $getAppWebview },
|
|
86
|
+
// 会被全局 API 误判成 options 参数从而丢失真实配置
|
|
87
|
+
return options ? uni.createIntersectionObserver(comp, options) : uni.createIntersectionObserver(comp)
|
|
88
|
+
}
|
|
89
|
+
|
|
73
90
|
/**
|
|
74
91
|
* @description 如果value小于min,取min;如果value大于max,取max
|
|
75
92
|
* @param {number} min
|
|
@@ -907,6 +924,7 @@ function hslToHex(h, s, l) {
|
|
|
907
924
|
|
|
908
925
|
export default {
|
|
909
926
|
upGetRect,
|
|
927
|
+
upCreateIntersectionObserver,
|
|
910
928
|
range,
|
|
911
929
|
getPx,
|
|
912
930
|
sleep,
|
package/libs/root/root.js
CHANGED
|
@@ -7,7 +7,9 @@ function ensureRootToastHostComponent(ms, scriptContent) {
|
|
|
7
7
|
if (componentsMatch && /\bUpRootToastHost\b/.test(componentsMatch[1])) return
|
|
8
8
|
|
|
9
9
|
if (componentsMatch) {
|
|
10
|
-
ms.appendLeft(componentsMatch.index + componentsMatch[0].length, ' UpRootToastHost,')
|
|
10
|
+
// ms.appendLeft(componentsMatch.index + componentsMatch[0].length, ' UpRootToastHost,')
|
|
11
|
+
const openBraceIndex = componentsMatch.index + componentsMatch[0].indexOf('{')
|
|
12
|
+
ms.appendLeft(openBraceIndex + 1, ' UpRootToastHost,')
|
|
11
13
|
return
|
|
12
14
|
}
|
|
13
15
|
|
package/libs/theme/runtime.js
CHANGED
|
@@ -12,6 +12,8 @@ const FALLBACK_THEME_VARS = {
|
|
|
12
12
|
'--up-page-bg-color': '#f3f4f6',
|
|
13
13
|
'--up-card-bg-color': '#ffffff',
|
|
14
14
|
'--up-navbar-bg-color': '#ffffff',
|
|
15
|
+
'--up-navbar-glass-bg-color': 'rgba(255, 255, 255, 0.82)',
|
|
16
|
+
'--up-navbar-glass-blur': '20px',
|
|
15
17
|
'--up-table2-header-bg-color': '#f5f7fa',
|
|
16
18
|
'--up-table2-zebra-bg-color': '#fafafa',
|
|
17
19
|
'--up-table2-highlight-bg-color': '#f5f7fa',
|
|
@@ -54,6 +56,8 @@ const FALLBACK_THEME_VARS = {
|
|
|
54
56
|
'--up-page-bg-color': '#1f1f1f',
|
|
55
57
|
'--up-card-bg-color': '#1c1c1e',
|
|
56
58
|
'--up-navbar-bg-color': '#1c1c1e',
|
|
59
|
+
'--up-navbar-glass-bg-color': 'rgba(28, 28, 30, 0.82)',
|
|
60
|
+
'--up-navbar-glass-blur': '20px',
|
|
57
61
|
'--up-table2-header-bg-color': '#2a2d33',
|
|
58
62
|
'--up-table2-zebra-bg-color': '#23262b',
|
|
59
63
|
'--up-table2-highlight-bg-color': '#2f3440',
|
package/libs/theme/theme.js
CHANGED
|
@@ -73,7 +73,9 @@ const DEFAULT_THEME_EXTRA_VARS = Object.freeze({
|
|
|
73
73
|
'--up-skeleton-shimmer-color': '#e6e6e6',
|
|
74
74
|
'--up-swipe-action-button-bg-color': '#c7c6cd',
|
|
75
75
|
'--up-index-list-indicator-bg-color': '#c9c9c9',
|
|
76
|
-
'--up-calendar-month-mark-color': 'rgba(231, 232, 234, 0.83)'
|
|
76
|
+
'--up-calendar-month-mark-color': 'rgba(231, 232, 234, 0.83)',
|
|
77
|
+
'--up-navbar-glass-bg-color': 'rgba(255, 255, 255, 0.82)',
|
|
78
|
+
'--up-navbar-glass-blur': '20px'
|
|
77
79
|
}),
|
|
78
80
|
dark: Object.freeze({
|
|
79
81
|
'--up-table2-header-bg-color': '#2a2d33',
|
|
@@ -84,7 +86,9 @@ const DEFAULT_THEME_EXTRA_VARS = Object.freeze({
|
|
|
84
86
|
'--up-skeleton-shimmer-color': 'rgba(255, 255, 255, 0.12)',
|
|
85
87
|
'--up-swipe-action-button-bg-color': '#4b5563',
|
|
86
88
|
'--up-index-list-indicator-bg-color': '#4b5563',
|
|
87
|
-
'--up-calendar-month-mark-color': 'rgba(255, 255, 255, 0.04)'
|
|
89
|
+
'--up-calendar-month-mark-color': 'rgba(255, 255, 255, 0.04)',
|
|
90
|
+
'--up-navbar-glass-bg-color': 'rgba(28, 28, 30, 0.82)',
|
|
91
|
+
'--up-navbar-glass-blur': '20px'
|
|
88
92
|
})
|
|
89
93
|
})
|
|
90
94
|
|
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.8.
|
|
5
|
+
"version": "3.8.113",
|
|
6
6
|
"description": "零云®uview-plus已兼容vue3支持多语言,120+全面的组件和便捷的工具会让您信手拈来。近期新增拖动排序、条码、图片裁剪、下拉刷新、虚拟列表、签名、Markdown等。",
|
|
7
7
|
"keywords": [
|
|
8
8
|
"uview",
|
package/types/comps/navbar.d.ts
CHANGED
|
@@ -81,6 +81,19 @@ declare interface NavbarProps {
|
|
|
81
81
|
* 标题的样式
|
|
82
82
|
*/
|
|
83
83
|
titleStyle?: unknown
|
|
84
|
+
/**
|
|
85
|
+
* 导航栏模式。ios 模式下初始背景透明、标题左对齐大字号显示,
|
|
86
|
+
* 下滑压缩后过渡为居中标题并呈现磨砂背景。
|
|
87
|
+
* ios 模式下 fixed 与 placeholder 被忽略。nvue 端降级为 default。
|
|
88
|
+
* @default "default"
|
|
89
|
+
*/
|
|
90
|
+
mode?: 'default' | 'ios'
|
|
91
|
+
/**
|
|
92
|
+
* 页面滚动距离,仅 ios 模式使用,需由页面 onPageScroll 传入。
|
|
93
|
+
* 不传时导航栏停留在大标题展开态。
|
|
94
|
+
* @default 0
|
|
95
|
+
*/
|
|
96
|
+
scrollTop?: string | number
|
|
84
97
|
/**
|
|
85
98
|
* 点击左侧区域
|
|
86
99
|
*/
|
package/types/comps/picker.d.ts
CHANGED
package/types/comps/popup.d.ts
CHANGED
|
@@ -78,13 +78,17 @@ declare interface PopupProps {
|
|
|
78
78
|
*/
|
|
79
79
|
customStyle?: unknown
|
|
80
80
|
/**
|
|
81
|
-
*
|
|
81
|
+
* 弹出层打开(进场动画结束后)
|
|
82
82
|
*/
|
|
83
83
|
onOpen?: () => any
|
|
84
84
|
/**
|
|
85
|
-
*
|
|
85
|
+
* 弹出层收起(关闭动作发生时,离场动画开始前;外部直接将show置为false时也会触发)
|
|
86
86
|
*/
|
|
87
87
|
onClose?: () => any
|
|
88
|
+
/**
|
|
89
|
+
* 弹出层已关闭(离场动画结束、弹窗真正消失后)
|
|
90
|
+
*/
|
|
91
|
+
onClosed?: () => any
|
|
88
92
|
}
|
|
89
93
|
|
|
90
94
|
declare interface PopupSlots {
|
package/types/func.d.ts
CHANGED
|
@@ -124,6 +124,17 @@ export interface Func {
|
|
|
124
124
|
comp?: ComponentPublicInstance | ComponentInternalInstance | null
|
|
125
125
|
): Promise<UniNamespace.NodeInfo | UniNamespace.NodeInfo[]>;
|
|
126
126
|
|
|
127
|
+
/**
|
|
128
|
+
* 创建交叉观察器,优先使用组件实例上的同名方法,
|
|
129
|
+
* 避免小程序端因枚举 Vue 实例代理的键而产生告警
|
|
130
|
+
* @param comp 组件实例
|
|
131
|
+
* @param options 交叉观察器配置
|
|
132
|
+
*/
|
|
133
|
+
upCreateIntersectionObserver(
|
|
134
|
+
comp?: ComponentPublicInstance | ComponentInternalInstance | null,
|
|
135
|
+
options?: UniNamespace.CreateIntersectionObserverOptions
|
|
136
|
+
): UniNamespace.IntersectionObserver;
|
|
137
|
+
|
|
127
138
|
/**
|
|
128
139
|
* 进行延时,以达到可以简写代码的目的 比如: await uni.$u.sleep(20)将会阻塞20ms
|
|
129
140
|
* @param value 延时时间(ms),默认 30
|
package/types/index.d.ts
CHANGED
|
@@ -142,6 +142,7 @@ declare module 'uview-plus' {
|
|
|
142
142
|
export const getPx: $u['getPx']
|
|
143
143
|
export const rpx2px: $u['rpx2px']
|
|
144
144
|
export const upGetRect: $u['upGetRect']
|
|
145
|
+
export const upCreateIntersectionObserver: $u['upCreateIntersectionObserver']
|
|
145
146
|
export const sleep: $u['sleep']
|
|
146
147
|
export const os: $u['os']
|
|
147
148
|
export const sys: $u['sys']
|