@zfqh/uview-x 0.0.0-stage → 0.0.1
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 +8 -0
- package/LICENSE +21 -0
- package/README.md +127 -3
- package/components/uv-badge/props.ts +83 -0
- package/components/uv-badge/uv-badge.vue +186 -0
- package/components/uv-button/props.ts +148 -0
- package/components/uv-button/uv-button.vue +565 -0
- package/components/uv-checkbox/props.ts +83 -0
- package/components/uv-checkbox/uv-checkbox.vue +403 -0
- package/components/uv-checkbox-group/props.ts +97 -0
- package/components/uv-checkbox-group/uv-checkbox-group.vue +163 -0
- package/components/uv-code/props.ts +48 -0
- package/components/uv-code/uv-code.vue +156 -0
- package/components/uv-dialog/props.ts +56 -0
- package/components/uv-dialog/uv-dialog.vue +492 -0
- package/components/uv-form/props.ts +59 -0
- package/components/uv-form/uv-form.vue +256 -0
- package/components/uv-form/valid.ts +1344 -0
- package/components/uv-form-item/props.ts +62 -0
- package/components/uv-form-item/uv-form-item.vue +335 -0
- package/components/uv-icon/icons.ts +161 -0
- package/components/uv-icon/props.ts +91 -0
- package/components/uv-icon/uv-icon.vue +204 -0
- package/components/uv-icon/uvicons.ttf +0 -0
- package/components/uv-input/props.ts +185 -0
- package/components/uv-input/uv-input.vue +431 -0
- package/components/uv-line/props.ts +42 -0
- package/components/uv-line/uv-line.vue +75 -0
- package/components/uv-line-progress/props.ts +43 -0
- package/components/uv-line-progress/uv-line-progress.vue +166 -0
- package/components/uv-loading-icon/props.ts +78 -0
- package/components/uv-loading-icon/uv-loading-icon.vue +226 -0
- package/components/uv-overlay/props.ts +38 -0
- package/components/uv-overlay/uv-overlay.vue +120 -0
- package/components/uv-popup/props.ts +101 -0
- package/components/uv-popup/uv-popup.vue +371 -0
- package/components/uv-safe-bottom/props.ts +18 -0
- package/components/uv-safe-bottom/uv-safe-bottom.vue +120 -0
- package/components/uv-status-bar/props.ts +22 -0
- package/components/uv-status-bar/uv-status-bar.vue +121 -0
- package/components/uv-tabs/props.ts +84 -0
- package/components/uv-tabs/uv-tabs.vue +432 -0
- package/components/uv-transition/props.ts +38 -0
- package/components/uv-transition/uv-transition.vue +170 -0
- package/hooks/index.ts +5 -0
- package/hooks/useDebounce.ts +35 -0
- package/hooks/useI18n.ts +95 -0
- package/hooks/useThrottle.ts +38 -0
- package/index.ts +23 -0
- package/libs/dayjs/index.ts +217 -0
- package/libs/index.ts +4 -0
- package/libs/luch-request/adapters/index.ts +133 -0
- package/libs/luch-request/core/InterceptorManager.ts +52 -0
- package/libs/luch-request/core/Request.ts +202 -0
- package/libs/luch-request/core/buildFullPath.ts +21 -0
- package/libs/luch-request/core/defaults.ts +34 -0
- package/libs/luch-request/core/dispatchRequest.ts +7 -0
- package/libs/luch-request/core/mergeConfig.ts +127 -0
- package/libs/luch-request/core/settle.ts +17 -0
- package/libs/luch-request/helpers/buildURL.ts +65 -0
- package/libs/luch-request/helpers/combineURLs.ts +15 -0
- package/libs/luch-request/helpers/isAbsoluteURL.ts +15 -0
- package/libs/luch-request/index.d.ts +197 -0
- package/libs/luch-request/index.ts +3 -0
- package/libs/luch-request/utils/clone.ts +265 -0
- package/libs/luch-request/utils.ts +136 -0
- package/locales/en.ts +78 -0
- package/locales/index.ts +169 -0
- package/locales/zh-Hans.ts +78 -0
- package/package.json +42 -4
- package/styles/color.scss +33 -0
- package/styles/common.scss +134 -0
- package/styles/components.scss +19 -0
- package/styles/index.scss +7 -0
- package/styles/theme.scss +44 -0
- package/styles/var.module.scss +32 -0
- package/styles/vue.scss +39 -0
- package/theme.scss +2 -0
- package/types/index.d.ts +66 -0
- package/utils/colorGradient.ts +139 -0
- package/utils/compat.ts +165 -0
- package/utils/compress.ts +34 -0
- package/utils/config.ts +95 -0
- package/utils/debounce.ts +7 -0
- package/utils/digit.ts +157 -0
- package/utils/function.ts +771 -0
- package/utils/http.ts +5 -0
- package/utils/index.ts +57 -0
- package/utils/locale.ts +296 -0
- package/utils/platform.ts +71 -0
- package/utils/route.ts +142 -0
- package/utils/test.ts +256 -0
- package/utils/throttle.ts +7 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
|
|
3
|
+
import config from '../../utils/config';
|
|
4
|
+
|
|
5
|
+
const props = {
|
|
6
|
+
// 弹出层类型,可选值,top: 顶部弹出层;bottom:底部弹出层;center:全屏弹出层
|
|
7
|
+
// message: 消息提示 ; dialog : 对话框
|
|
8
|
+
mode: {
|
|
9
|
+
type: String,
|
|
10
|
+
default: 'center',
|
|
11
|
+
},
|
|
12
|
+
// 动画时长,单位ms
|
|
13
|
+
duration: {
|
|
14
|
+
type: [String, Number],
|
|
15
|
+
default: 300,
|
|
16
|
+
},
|
|
17
|
+
// 层级(H5 与上游一致使用 997,弹层位于 H5 导航栏之下;App/小程序使用 10075)
|
|
18
|
+
zIndex: {
|
|
19
|
+
type: [String, Number],
|
|
20
|
+
default: 10075,
|
|
21
|
+
},
|
|
22
|
+
bgColor: {
|
|
23
|
+
type: String,
|
|
24
|
+
default: '#ffffff',
|
|
25
|
+
},
|
|
26
|
+
safeArea: {
|
|
27
|
+
type: Boolean,
|
|
28
|
+
default: true,
|
|
29
|
+
},
|
|
30
|
+
// 是否显示遮罩
|
|
31
|
+
overlay: {
|
|
32
|
+
type: Boolean,
|
|
33
|
+
default: true,
|
|
34
|
+
},
|
|
35
|
+
// 点击遮罩是否关闭弹窗
|
|
36
|
+
closeOnClickOverlay: {
|
|
37
|
+
type: Boolean,
|
|
38
|
+
default: true,
|
|
39
|
+
},
|
|
40
|
+
// 遮罩的透明度,0-1之间
|
|
41
|
+
overlayOpacity: {
|
|
42
|
+
type: [Number, String],
|
|
43
|
+
default: 0.4,
|
|
44
|
+
},
|
|
45
|
+
// 自定义遮罩的样式
|
|
46
|
+
overlayStyle: {
|
|
47
|
+
type: [Object, String],
|
|
48
|
+
default: '',
|
|
49
|
+
},
|
|
50
|
+
// 是否为iPhoneX留出底部安全距离
|
|
51
|
+
safeAreaInsetBottom: {
|
|
52
|
+
type: Boolean,
|
|
53
|
+
default: true,
|
|
54
|
+
},
|
|
55
|
+
// 是否留出顶部安全距离(状态栏高度)
|
|
56
|
+
safeAreaInsetTop: {
|
|
57
|
+
type: Boolean,
|
|
58
|
+
default: false,
|
|
59
|
+
},
|
|
60
|
+
// 是否显示关闭图标
|
|
61
|
+
closeable: {
|
|
62
|
+
type: Boolean,
|
|
63
|
+
default: false,
|
|
64
|
+
},
|
|
65
|
+
// 自定义关闭图标位置,top-left为左上角,top-right为右上角,bottom-left为左下角,bottom-right为右下角
|
|
66
|
+
closeIconPos: {
|
|
67
|
+
type: String,
|
|
68
|
+
default: 'top-right',
|
|
69
|
+
},
|
|
70
|
+
// 关闭图标颜色
|
|
71
|
+
closeIconColor: {
|
|
72
|
+
type: String,
|
|
73
|
+
default: '#909399',
|
|
74
|
+
},
|
|
75
|
+
// 关闭图标大小
|
|
76
|
+
closeIconSize: {
|
|
77
|
+
type: [String, Number],
|
|
78
|
+
default: 18,
|
|
79
|
+
},
|
|
80
|
+
// mode=center,也即中部弹出时,是否使用缩放模式
|
|
81
|
+
zoom: {
|
|
82
|
+
type: Boolean,
|
|
83
|
+
default: true,
|
|
84
|
+
},
|
|
85
|
+
round: {
|
|
86
|
+
type: [Number, String],
|
|
87
|
+
default: 8,
|
|
88
|
+
},
|
|
89
|
+
customStyle: {
|
|
90
|
+
type: [Object, String],
|
|
91
|
+
default: () => ({}),
|
|
92
|
+
},
|
|
93
|
+
customClass: {
|
|
94
|
+
type: String,
|
|
95
|
+
default: '',
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
...config.props.popup,
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export default props;
|
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<view
|
|
3
|
+
v-if="showPopup"
|
|
4
|
+
class="uv-popup"
|
|
5
|
+
:style="[{ zIndex: p.zIndex }]"
|
|
6
|
+
@touchmove="noop"
|
|
7
|
+
>
|
|
8
|
+
<view @touchstart="touchstart">
|
|
9
|
+
<!-- 遮罩层 -->
|
|
10
|
+
<uv-overlay
|
|
11
|
+
v-if="maskShow && p.overlay"
|
|
12
|
+
:show="showTrans"
|
|
13
|
+
:duration="p.duration"
|
|
14
|
+
:custom-style="p.overlayStyle"
|
|
15
|
+
:opacity="p.overlayOpacity"
|
|
16
|
+
:z-index="p.zIndex"
|
|
17
|
+
@click="onTap"
|
|
18
|
+
></uv-overlay>
|
|
19
|
+
<uv-transition
|
|
20
|
+
:mode="ani"
|
|
21
|
+
:custom-style="transitionStyle"
|
|
22
|
+
:duration="p.duration"
|
|
23
|
+
:show="showTrans"
|
|
24
|
+
@click="onTap"
|
|
25
|
+
>
|
|
26
|
+
<view
|
|
27
|
+
class="uv-popup__content"
|
|
28
|
+
:class="['uv-popup__content--' + layoutMode, p.customClass]"
|
|
29
|
+
:style="[contentStyle]"
|
|
30
|
+
@click="clear"
|
|
31
|
+
>
|
|
32
|
+
<uv-status-bar v-if="p.safeAreaInsetTop"></uv-status-bar>
|
|
33
|
+
<slot />
|
|
34
|
+
<uv-safe-bottom v-if="p.safeAreaInsetBottom"></uv-safe-bottom>
|
|
35
|
+
<view
|
|
36
|
+
v-if="p.closeable"
|
|
37
|
+
class="uv-popup__close"
|
|
38
|
+
:class="['uv-popup__close--' + p.closeIconPos]"
|
|
39
|
+
hover-class="uv-popup__close--hover"
|
|
40
|
+
:hover-stay-time="150"
|
|
41
|
+
@tap="close"
|
|
42
|
+
>
|
|
43
|
+
<slot name="close">
|
|
44
|
+
<uv-icon
|
|
45
|
+
name="close"
|
|
46
|
+
:color="p.closeIconColor"
|
|
47
|
+
:size="p.closeIconSize"
|
|
48
|
+
:bold="true"
|
|
49
|
+
></uv-icon>
|
|
50
|
+
</slot>
|
|
51
|
+
</view>
|
|
52
|
+
</view>
|
|
53
|
+
</uv-transition>
|
|
54
|
+
</view>
|
|
55
|
+
</view>
|
|
56
|
+
</template>
|
|
57
|
+
|
|
58
|
+
<script setup lang="ts">
|
|
59
|
+
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
|
60
|
+
import { $uv, platform } from '../../utils'
|
|
61
|
+
import props from './props'
|
|
62
|
+
import { preventEvent } from '../../utils/compat'
|
|
63
|
+
|
|
64
|
+
defineOptions({
|
|
65
|
+
name: 'uv-popup',
|
|
66
|
+
// #ifdef UNI-APP-X
|
|
67
|
+
styleIsolation: 'app-and-page',
|
|
68
|
+
// #endif
|
|
69
|
+
virtualHost: true,
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* PopUp 弹出层
|
|
74
|
+
* @description 弹出层组件,用于解决遮罩弹层的问题,通过 ref 调用 open()/close() 控制显隐
|
|
75
|
+
* @tutorial https://www.uvui.cn/components/popup.html
|
|
76
|
+
* @property {String} mode 弹出方式:top / bottom / center / left / right(默认 center)
|
|
77
|
+
* @property {Number} duration 动画时长,默认 300
|
|
78
|
+
* @property {Boolean} overlay 是否显示遮罩,默认 true
|
|
79
|
+
* @property {Boolean} overlayOpacity 遮罩透明度,默认 0.4
|
|
80
|
+
* @property {Object} overlayStyle 遮罩自定义样式
|
|
81
|
+
* @property {Boolean} closeOnClickOverlay 蒙版点击是否关闭弹窗,默认 true
|
|
82
|
+
* @property {Number | String} zIndex 弹出层的层级
|
|
83
|
+
* @property {Boolean} safeAreaInsetTop 是否留出顶部安全区(状态栏高度),默认 false
|
|
84
|
+
* @property {Boolean} safeAreaInsetBottom 是否为留出底部安全区适配,默认 true
|
|
85
|
+
* @property {Boolean} closeable 是否显示关闭图标,默认 false
|
|
86
|
+
* @property {String} closeIconPos 自定义关闭图标位置:top-left / top-right / bottom-left / bottom-right
|
|
87
|
+
* @property {String} closeIconColor 关闭图标颜色,默认 #909399
|
|
88
|
+
* @property {String | Number} closeIconSize 关闭图标大小,默认 18
|
|
89
|
+
* @property {Boolean} zoom mode=center 时是否开启缩放,默认 true
|
|
90
|
+
* @property {String} bgColor 主窗口背景色,默认 #ffffff
|
|
91
|
+
* @property {Number | String} round 圆角值,仅对 mode=top/bottom/center 有效
|
|
92
|
+
* @property {Object} customStyle 内容区自定义样式
|
|
93
|
+
* @event {Function} change 打开/关闭弹窗触发,e={show: true | false, type}
|
|
94
|
+
* @event {Function} maskClick 点击遮罩触发
|
|
95
|
+
* @event {Function} close 关闭弹窗触发
|
|
96
|
+
* @example <uv-popup ref="popup" mode="bottom"><text>内容</text></uv-popup>
|
|
97
|
+
*/
|
|
98
|
+
const p = defineProps(props)
|
|
99
|
+
|
|
100
|
+
const emit = defineEmits(['change', 'maskClick', 'close'])
|
|
101
|
+
|
|
102
|
+
// 兼容旧版的 type 别名(message->top、dialog->center、share->bottom)
|
|
103
|
+
const innerType = ['top', 'center', 'bottom', 'left', 'right', 'message', 'dialog', 'share']
|
|
104
|
+
const getLayoutMode = (mode: string): string => {
|
|
105
|
+
if (mode === 'message') return 'top'
|
|
106
|
+
if (mode === 'dialog') return 'center'
|
|
107
|
+
if (mode === 'share') return 'bottom'
|
|
108
|
+
return mode
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const showPopup = ref(false)
|
|
112
|
+
const showTrans = ref(false)
|
|
113
|
+
const maskShow = ref(true)
|
|
114
|
+
const layoutMode = ref('center')
|
|
115
|
+
const clearPropagation = ref(false)
|
|
116
|
+
let closeTimer: any = null
|
|
117
|
+
|
|
118
|
+
const bg = computed(() => {
|
|
119
|
+
if (p.bgColor === '' || p.bgColor === 'none' || $uv.getPx(p.round) > 0) {
|
|
120
|
+
return 'transparent'
|
|
121
|
+
}
|
|
122
|
+
return p.bgColor
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
const contentStyle = computed(() => {
|
|
126
|
+
const style: any = {}
|
|
127
|
+
if (p.bgColor) {
|
|
128
|
+
style.backgroundColor = bg.value
|
|
129
|
+
}
|
|
130
|
+
if (p.round) {
|
|
131
|
+
const value = $uv.addUnit(p.round)
|
|
132
|
+
const mode = layoutMode.value
|
|
133
|
+
style.backgroundColor = p.bgColor
|
|
134
|
+
if (mode === 'top') {
|
|
135
|
+
style.borderBottomLeftRadius = value
|
|
136
|
+
style.borderBottomRightRadius = value
|
|
137
|
+
} else if (mode === 'bottom') {
|
|
138
|
+
style.borderTopLeftRadius = value
|
|
139
|
+
style.borderTopRightRadius = value
|
|
140
|
+
} else if (mode === 'center') {
|
|
141
|
+
style.borderTopLeftRadius = value
|
|
142
|
+
style.borderTopRightRadius = value
|
|
143
|
+
style.borderBottomLeftRadius = value
|
|
144
|
+
style.borderBottomRightRadius = value
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return $uv.deepMerge(style, $uv.addStyle(p.customStyle))
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
const ani = computed<string[]>(() => {
|
|
151
|
+
const mode = layoutMode.value
|
|
152
|
+
if (mode === 'top') return ['slide-top']
|
|
153
|
+
if (mode === 'bottom') return ['slide-bottom']
|
|
154
|
+
if (mode === 'left') return ['slide-left']
|
|
155
|
+
if (mode === 'right') return ['slide-right']
|
|
156
|
+
return p.zoom ? ['zoom-in', 'fade'] : ['fade']
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
/** Web 端使用安全区 CSS 变量避开模拟导航栏/底部 tabbar;App/小程序由原生页面区域约束 */
|
|
160
|
+
const webOffset = (dir: string): any => {
|
|
161
|
+
let value: any = 0
|
|
162
|
+
// #ifdef WEB || H5
|
|
163
|
+
value = 'var(--uni-safe-area-inset-' + dir + ', 0px)'
|
|
164
|
+
// #endif
|
|
165
|
+
return value
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const transitionStyle = computed(() => {
|
|
169
|
+
const mode = layoutMode.value
|
|
170
|
+
const style: any = {
|
|
171
|
+
position: 'fixed',
|
|
172
|
+
zIndex: p.zIndex
|
|
173
|
+
}
|
|
174
|
+
if (mode === 'top') {
|
|
175
|
+
style.left = webOffset('left')
|
|
176
|
+
style.right = webOffset('right')
|
|
177
|
+
style.top = webOffset('top')
|
|
178
|
+
style.backgroundColor = bg.value
|
|
179
|
+
} else if (mode === 'bottom') {
|
|
180
|
+
style.left = webOffset('left')
|
|
181
|
+
style.right = webOffset('right')
|
|
182
|
+
style.bottom = webOffset('bottom')
|
|
183
|
+
style.backgroundColor = bg.value
|
|
184
|
+
} else if (mode === 'center') {
|
|
185
|
+
style.left = webOffset('left')
|
|
186
|
+
style.right = webOffset('right')
|
|
187
|
+
style.top = webOffset('top')
|
|
188
|
+
style.bottom = webOffset('bottom')
|
|
189
|
+
style.display = 'flex'
|
|
190
|
+
style.flexDirection = 'column'
|
|
191
|
+
style.justifyContent = 'center'
|
|
192
|
+
style.alignItems = 'center'
|
|
193
|
+
} else if (mode === 'left') {
|
|
194
|
+
style.left = webOffset('left')
|
|
195
|
+
style.top = webOffset('top')
|
|
196
|
+
style.bottom = webOffset('bottom')
|
|
197
|
+
style.backgroundColor = bg.value
|
|
198
|
+
style.display = 'flex'
|
|
199
|
+
style.flexDirection = 'column'
|
|
200
|
+
} else if (mode === 'right') {
|
|
201
|
+
style.right = webOffset('right')
|
|
202
|
+
style.top = webOffset('top')
|
|
203
|
+
style.bottom = webOffset('bottom')
|
|
204
|
+
style.backgroundColor = bg.value
|
|
205
|
+
style.display = 'flex'
|
|
206
|
+
style.flexDirection = 'column'
|
|
207
|
+
}
|
|
208
|
+
return style
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
const noop = () => {}
|
|
212
|
+
|
|
213
|
+
const touchstart = () => {
|
|
214
|
+
clearPropagation.value = false
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const clear = (e: any) => {
|
|
218
|
+
preventEvent(e)
|
|
219
|
+
clearPropagation.value = true
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// #ifdef WEB || H5
|
|
223
|
+
const setBodyOverflow = (hidden: boolean) => {
|
|
224
|
+
const body = document.body
|
|
225
|
+
if (body && body.style) {
|
|
226
|
+
body.style.overflow = hidden ? 'hidden' : 'visible'
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const keypressHandler = (event: any) => {
|
|
231
|
+
if (!showPopup.value || !maskShow.value) return
|
|
232
|
+
const key = event ? String(event.key || '') : ''
|
|
233
|
+
if (key === 'Escape' || key === 'Esc') {
|
|
234
|
+
onTap()
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
onMounted(() => {
|
|
239
|
+
document.addEventListener('keyup', keypressHandler)
|
|
240
|
+
})
|
|
241
|
+
// #endif
|
|
242
|
+
|
|
243
|
+
const clearCloseTimer = () => {
|
|
244
|
+
if (closeTimer) {
|
|
245
|
+
clearTimeout(closeTimer)
|
|
246
|
+
closeTimer = null
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const close = () => {
|
|
251
|
+
if (!showTrans.value) return
|
|
252
|
+
showTrans.value = false
|
|
253
|
+
emit('close')
|
|
254
|
+
emit('change', {
|
|
255
|
+
show: false,
|
|
256
|
+
type: p.mode
|
|
257
|
+
})
|
|
258
|
+
clearCloseTimer()
|
|
259
|
+
const duration = Number(p.duration) || 300
|
|
260
|
+
closeTimer = setTimeout(() => {
|
|
261
|
+
showPopup.value = false
|
|
262
|
+
closeTimer = null
|
|
263
|
+
}, duration)
|
|
264
|
+
// #ifdef WEB || H5
|
|
265
|
+
setBodyOverflow(false)
|
|
266
|
+
// #endif
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const open = (type?: string) => {
|
|
270
|
+
// 处理快速打开关闭的情况
|
|
271
|
+
if (showPopup.value) return
|
|
272
|
+
let direction: string = type || ''
|
|
273
|
+
if (!(direction && innerType.indexOf(direction) > -1)) {
|
|
274
|
+
direction = p.mode
|
|
275
|
+
}
|
|
276
|
+
if (innerType.indexOf(direction) === -1) {
|
|
277
|
+
$uv.error('缺少类型:' + direction)
|
|
278
|
+
return
|
|
279
|
+
}
|
|
280
|
+
layoutMode.value = getLayoutMode(direction)
|
|
281
|
+
showPopup.value = true
|
|
282
|
+
showTrans.value = true
|
|
283
|
+
// #ifdef WEB || H5
|
|
284
|
+
setBodyOverflow(true)
|
|
285
|
+
// #endif
|
|
286
|
+
emit('change', {
|
|
287
|
+
show: true,
|
|
288
|
+
type: direction
|
|
289
|
+
})
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const closeMask = () => {
|
|
293
|
+
maskShow.value = false
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const onTap = () => {
|
|
297
|
+
if (clearPropagation.value) {
|
|
298
|
+
// 处理事件冒泡导致的误关闭
|
|
299
|
+
clearPropagation.value = false
|
|
300
|
+
return
|
|
301
|
+
}
|
|
302
|
+
emit('maskClick')
|
|
303
|
+
if (!p.closeOnClickOverlay) return
|
|
304
|
+
close()
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
onUnmounted(() => {
|
|
308
|
+
clearCloseTimer()
|
|
309
|
+
// #ifdef WEB || H5
|
|
310
|
+
document.removeEventListener('keyup', keypressHandler)
|
|
311
|
+
setBodyOverflow(false)
|
|
312
|
+
// #endif
|
|
313
|
+
})
|
|
314
|
+
|
|
315
|
+
defineExpose({
|
|
316
|
+
open,
|
|
317
|
+
close,
|
|
318
|
+
closeMask
|
|
319
|
+
})
|
|
320
|
+
</script>
|
|
321
|
+
|
|
322
|
+
<style lang="scss">
|
|
323
|
+
@import '../../styles/components.scss';
|
|
324
|
+
|
|
325
|
+
.uv-popup {
|
|
326
|
+
position: fixed;
|
|
327
|
+
/* #ifndef APP-NVUE */
|
|
328
|
+
z-index: 99;
|
|
329
|
+
/* #endif */
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
.uv-popup__content {
|
|
333
|
+
/* #ifndef APP-NVUE */
|
|
334
|
+
overflow: hidden;
|
|
335
|
+
/* #endif */
|
|
336
|
+
position: relative;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
.uv-popup__content--left,
|
|
340
|
+
.uv-popup__content--right {
|
|
341
|
+
flex: 1;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.uv-popup__close {
|
|
345
|
+
position: absolute;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
.uv-popup__close--hover {
|
|
349
|
+
opacity: 0.4;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
.uv-popup__close--top-left {
|
|
353
|
+
top: 15px;
|
|
354
|
+
left: 15px;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
.uv-popup__close--top-right {
|
|
358
|
+
top: 15px;
|
|
359
|
+
right: 15px;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
.uv-popup__close--bottom-left {
|
|
363
|
+
bottom: 15px;
|
|
364
|
+
left: 15px;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
.uv-popup__close--bottom-right {
|
|
368
|
+
right: 15px;
|
|
369
|
+
bottom: 15px;
|
|
370
|
+
}
|
|
371
|
+
</style>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
|
|
3
|
+
import config from '../../utils/config';
|
|
4
|
+
|
|
5
|
+
const props = {
|
|
6
|
+
customStyle: {
|
|
7
|
+
type: [Object, String],
|
|
8
|
+
default: () => ({}),
|
|
9
|
+
},
|
|
10
|
+
customClass: {
|
|
11
|
+
type: String,
|
|
12
|
+
default: '',
|
|
13
|
+
},
|
|
14
|
+
|
|
15
|
+
...config.props.safeBottom,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export default props;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<view
|
|
3
|
+
class="uv-safe-bottom"
|
|
4
|
+
:class="[p.customClass]"
|
|
5
|
+
:style="[safeBottomStyle]"
|
|
6
|
+
></view>
|
|
7
|
+
</template>
|
|
8
|
+
|
|
9
|
+
<script setup lang="ts">
|
|
10
|
+
import { computed, onMounted, ref } from 'vue'
|
|
11
|
+
import { $uv } from '../../utils'
|
|
12
|
+
import props from './props'
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
defineOptions({
|
|
16
|
+
name: 'uv-safe-bottom',
|
|
17
|
+
// #ifdef UNI-APP-X
|
|
18
|
+
styleIsolation: 'app-and-page',
|
|
19
|
+
// #endif
|
|
20
|
+
virtualHost: true,
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* SafeBottom 底部安全区
|
|
25
|
+
* @description 针对 iPhone X 等底部带指示条的机型做底部安全区适配。
|
|
26
|
+
* App 蒸汽模式使用 uni.getWindowInfo() 的 safeAreaInsets 计算占位高度,
|
|
27
|
+
* Web / 小程序的 env() 作为兜底。
|
|
28
|
+
* @tutorial https://www.uvui.cn/components/safeAreaInset.html
|
|
29
|
+
* @property {Object} customStyle 定义需要用到的外部样式
|
|
30
|
+
* @property {String} customClass 自定义类名
|
|
31
|
+
* @example <uv-safe-bottom></uv-safe-bottom>
|
|
32
|
+
*/
|
|
33
|
+
const p = defineProps(props)
|
|
34
|
+
|
|
35
|
+
// uni-app x:窗口信息用 getWindowInfo()、设备信息用 getDeviceInfo();
|
|
36
|
+
// 旧基座缺字段时回退 getSystemInfoSync(),兼容 safeArea 坐标与 safeAreaInsets 的字段差异。
|
|
37
|
+
const systemInfo = ref<any>({})
|
|
38
|
+
const readSystemInfo = () => {
|
|
39
|
+
const info: any = {}
|
|
40
|
+
try {
|
|
41
|
+
const windowInfo: any = uni.getWindowInfo()
|
|
42
|
+
info.safeAreaInsets = windowInfo.safeAreaInsets
|
|
43
|
+
info.safeArea = windowInfo.safeArea
|
|
44
|
+
info.screenHeight = windowInfo.screenHeight
|
|
45
|
+
info.statusBarHeight = windowInfo.statusBarHeight
|
|
46
|
+
} catch (e) {}
|
|
47
|
+
try {
|
|
48
|
+
const deviceInfo: any = uni.getDeviceInfo()
|
|
49
|
+
info.osName = deviceInfo.osName
|
|
50
|
+
info.deviceType = deviceInfo.deviceType
|
|
51
|
+
} catch (e) {}
|
|
52
|
+
if (info.safeAreaInsets === undefined || info.safeArea === undefined) {
|
|
53
|
+
try {
|
|
54
|
+
const legacyInfo: any = uni.getSystemInfoSync()
|
|
55
|
+
if (info.safeAreaInsets === undefined) info.safeAreaInsets = legacyInfo.safeAreaInsets
|
|
56
|
+
if (info.safeArea === undefined) info.safeArea = legacyInfo.safeArea
|
|
57
|
+
if (info.screenHeight === undefined) info.screenHeight = legacyInfo.screenHeight
|
|
58
|
+
if (info.statusBarHeight === undefined) info.statusBarHeight = legacyInfo.statusBarHeight
|
|
59
|
+
} catch (e) {}
|
|
60
|
+
}
|
|
61
|
+
systemInfo.value = info
|
|
62
|
+
}
|
|
63
|
+
readSystemInfo()
|
|
64
|
+
|
|
65
|
+
onMounted(() => {
|
|
66
|
+
readSystemInfo()
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
/** 底部安全区高度:优先 safeAreaInsets.bottom,App 端用 safeArea 坐标换算兜底 */
|
|
70
|
+
const safeBottomHeight = computed(() => {
|
|
71
|
+
const info = systemInfo.value || {}
|
|
72
|
+
const insets = info.safeAreaInsets
|
|
73
|
+
if (insets && typeof insets.bottom === 'number' && insets.bottom > 0) {
|
|
74
|
+
return insets.bottom
|
|
75
|
+
}
|
|
76
|
+
// #ifdef APP || MP
|
|
77
|
+
// 旧 API 的 safeArea 是屏幕坐标,需要 screenHeight - safeArea.bottom 换算成安全区高度
|
|
78
|
+
const safeArea = info.safeArea
|
|
79
|
+
const screenHeight = Number(info.screenHeight)
|
|
80
|
+
if (safeArea && screenHeight > 0 && safeArea.bottom > 0) {
|
|
81
|
+
const bottom = screenHeight - Number(safeArea.bottom)
|
|
82
|
+
if (bottom > 0) return bottom
|
|
83
|
+
}
|
|
84
|
+
// #endif
|
|
85
|
+
return 0
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
const safeBottomStyle = computed(() => {
|
|
89
|
+
const style: any = {}
|
|
90
|
+
const height = safeBottomHeight.value
|
|
91
|
+
if (height > 0) {
|
|
92
|
+
style.height = $uv.addUnit(height, 'px')
|
|
93
|
+
}
|
|
94
|
+
return $uv.deepMerge(style, $uv.addStyle(p.customStyle))
|
|
95
|
+
})
|
|
96
|
+
</script>
|
|
97
|
+
|
|
98
|
+
<style lang="scss">
|
|
99
|
+
@import '../../styles/components.scss';
|
|
100
|
+
|
|
101
|
+
.uv-safe-bottom {
|
|
102
|
+
/* #ifndef APP-NVUE */
|
|
103
|
+
width: 100%;
|
|
104
|
+
flex-shrink: 0;
|
|
105
|
+
/* #endif */
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/* #ifdef APP && !APP-NVUE */
|
|
109
|
+
.uv-safe-bottom {
|
|
110
|
+
height: var(--uni-safe-area-inset-bottom, 0px);
|
|
111
|
+
}
|
|
112
|
+
/* #endif */
|
|
113
|
+
|
|
114
|
+
/* #ifdef WEB || H5 || MP */
|
|
115
|
+
.uv-safe-bottom {
|
|
116
|
+
height: constant(safe-area-inset-bottom);
|
|
117
|
+
height: env(safe-area-inset-bottom);
|
|
118
|
+
}
|
|
119
|
+
/* #endif */
|
|
120
|
+
</style>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
|
|
3
|
+
import config from '../../utils/config';
|
|
4
|
+
|
|
5
|
+
const props = {
|
|
6
|
+
bgColor: {
|
|
7
|
+
type: String,
|
|
8
|
+
default: 'transparent',
|
|
9
|
+
},
|
|
10
|
+
customStyle: {
|
|
11
|
+
type: [Object, String],
|
|
12
|
+
default: () => ({}),
|
|
13
|
+
},
|
|
14
|
+
customClass: {
|
|
15
|
+
type: String,
|
|
16
|
+
default: '',
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
...config.props.statusBar,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export default props;
|