@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.
Files changed (93) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/LICENSE +21 -0
  3. package/README.md +127 -3
  4. package/components/uv-badge/props.ts +83 -0
  5. package/components/uv-badge/uv-badge.vue +186 -0
  6. package/components/uv-button/props.ts +148 -0
  7. package/components/uv-button/uv-button.vue +565 -0
  8. package/components/uv-checkbox/props.ts +83 -0
  9. package/components/uv-checkbox/uv-checkbox.vue +403 -0
  10. package/components/uv-checkbox-group/props.ts +97 -0
  11. package/components/uv-checkbox-group/uv-checkbox-group.vue +163 -0
  12. package/components/uv-code/props.ts +48 -0
  13. package/components/uv-code/uv-code.vue +156 -0
  14. package/components/uv-dialog/props.ts +56 -0
  15. package/components/uv-dialog/uv-dialog.vue +492 -0
  16. package/components/uv-form/props.ts +59 -0
  17. package/components/uv-form/uv-form.vue +256 -0
  18. package/components/uv-form/valid.ts +1344 -0
  19. package/components/uv-form-item/props.ts +62 -0
  20. package/components/uv-form-item/uv-form-item.vue +335 -0
  21. package/components/uv-icon/icons.ts +161 -0
  22. package/components/uv-icon/props.ts +91 -0
  23. package/components/uv-icon/uv-icon.vue +204 -0
  24. package/components/uv-icon/uvicons.ttf +0 -0
  25. package/components/uv-input/props.ts +185 -0
  26. package/components/uv-input/uv-input.vue +431 -0
  27. package/components/uv-line/props.ts +42 -0
  28. package/components/uv-line/uv-line.vue +75 -0
  29. package/components/uv-line-progress/props.ts +43 -0
  30. package/components/uv-line-progress/uv-line-progress.vue +166 -0
  31. package/components/uv-loading-icon/props.ts +78 -0
  32. package/components/uv-loading-icon/uv-loading-icon.vue +226 -0
  33. package/components/uv-overlay/props.ts +38 -0
  34. package/components/uv-overlay/uv-overlay.vue +120 -0
  35. package/components/uv-popup/props.ts +101 -0
  36. package/components/uv-popup/uv-popup.vue +371 -0
  37. package/components/uv-safe-bottom/props.ts +18 -0
  38. package/components/uv-safe-bottom/uv-safe-bottom.vue +120 -0
  39. package/components/uv-status-bar/props.ts +22 -0
  40. package/components/uv-status-bar/uv-status-bar.vue +121 -0
  41. package/components/uv-tabs/props.ts +84 -0
  42. package/components/uv-tabs/uv-tabs.vue +432 -0
  43. package/components/uv-transition/props.ts +38 -0
  44. package/components/uv-transition/uv-transition.vue +170 -0
  45. package/hooks/index.ts +5 -0
  46. package/hooks/useDebounce.ts +35 -0
  47. package/hooks/useI18n.ts +95 -0
  48. package/hooks/useThrottle.ts +38 -0
  49. package/index.ts +23 -0
  50. package/libs/dayjs/index.ts +217 -0
  51. package/libs/index.ts +4 -0
  52. package/libs/luch-request/adapters/index.ts +133 -0
  53. package/libs/luch-request/core/InterceptorManager.ts +52 -0
  54. package/libs/luch-request/core/Request.ts +202 -0
  55. package/libs/luch-request/core/buildFullPath.ts +21 -0
  56. package/libs/luch-request/core/defaults.ts +34 -0
  57. package/libs/luch-request/core/dispatchRequest.ts +7 -0
  58. package/libs/luch-request/core/mergeConfig.ts +127 -0
  59. package/libs/luch-request/core/settle.ts +17 -0
  60. package/libs/luch-request/helpers/buildURL.ts +65 -0
  61. package/libs/luch-request/helpers/combineURLs.ts +15 -0
  62. package/libs/luch-request/helpers/isAbsoluteURL.ts +15 -0
  63. package/libs/luch-request/index.d.ts +197 -0
  64. package/libs/luch-request/index.ts +3 -0
  65. package/libs/luch-request/utils/clone.ts +265 -0
  66. package/libs/luch-request/utils.ts +136 -0
  67. package/locales/en.ts +78 -0
  68. package/locales/index.ts +169 -0
  69. package/locales/zh-Hans.ts +78 -0
  70. package/package.json +42 -4
  71. package/styles/color.scss +33 -0
  72. package/styles/common.scss +134 -0
  73. package/styles/components.scss +19 -0
  74. package/styles/index.scss +7 -0
  75. package/styles/theme.scss +44 -0
  76. package/styles/var.module.scss +32 -0
  77. package/styles/vue.scss +39 -0
  78. package/theme.scss +2 -0
  79. package/types/index.d.ts +66 -0
  80. package/utils/colorGradient.ts +139 -0
  81. package/utils/compat.ts +165 -0
  82. package/utils/compress.ts +34 -0
  83. package/utils/config.ts +95 -0
  84. package/utils/debounce.ts +7 -0
  85. package/utils/digit.ts +157 -0
  86. package/utils/function.ts +771 -0
  87. package/utils/http.ts +5 -0
  88. package/utils/index.ts +57 -0
  89. package/utils/locale.ts +296 -0
  90. package/utils/platform.ts +71 -0
  91. package/utils/route.ts +142 -0
  92. package/utils/test.ts +256 -0
  93. 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;