v-uni-app-ui 1.0.0 → 1.0.4

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 (86) hide show
  1. package/README.md +147 -0
  2. package/components/config/css/basic.scss +19 -0
  3. package/components/config/interface/basic-type.js +16 -0
  4. package/components/config/interface/components-interface.ts +0 -0
  5. package/components/config/interface/monitor/components/input-monitor.js +0 -0
  6. package/components/config/interface/monitor/property-monitor.ts +136 -0
  7. package/components/config/interface/props/basic-props.ts +88 -0
  8. package/components/config/interface/props/components/button-props.ts +85 -0
  9. package/components/config/interface/props/components/input-props.ts +69 -0
  10. package/components/config/interface/props/props-tools.ts +64 -0
  11. package/components/config/style/basic.js +346 -0
  12. package/components/config/style/component-registry.js +142 -0
  13. package/components/config/style/components/button-style.js +160 -0
  14. package/components/config/style/components/input-style.js +98 -0
  15. package/components/config/style/components-style.js +622 -0
  16. package/components/config/style/property-mapper.js +377 -0
  17. package/components/config/style/pseudo-processor.js +213 -0
  18. package/components/config.js +123 -0
  19. package/components/icon/iconfont.css +87 -0
  20. package/components/icon/iconfont.js +1 -0
  21. package/components/icon/iconfont.json +135 -0
  22. package/components/icon/iconfont.ttf +0 -0
  23. package/components/icon/iconfont.woff +0 -0
  24. package/components/icon/iconfont.woff2 +0 -0
  25. package/components/layout/v-card/v-card.vue +108 -0
  26. package/components/layout/v-grid/v-grid.vue +162 -0
  27. package/components/layout/v-icon-grid/v-icon-grid.vue +195 -0
  28. package/components/layout/v-infinite-scroll/v-infinite-scroll.vue +172 -0
  29. package/components/layout/v-list/v-list.vue +43 -0
  30. package/components/layout/v-row/v-row.vue +142 -0
  31. package/components/layout/v-waterfall/v-waterfall.vue +79 -0
  32. package/components/model/compound/v-checkbox-group/v-checkbox-group.vue +96 -0
  33. package/components/model/compound/v-console/v-console.js +20 -0
  34. package/components/model/compound/v-console/v-console.vue +299 -0
  35. package/components/model/compound/v-date-time/v-date-time.vue +261 -0
  36. package/components/model/compound/v-dialog/v-dialog.vue +178 -0
  37. package/components/model/compound/v-drum-select-picker/v-drum-select-picker.vue +83 -0
  38. package/components/model/compound/v-form/v-form.vue +226 -0
  39. package/components/model/compound/v-form-item/v-form-item.vue +255 -0
  40. package/components/model/compound/v-image/v-image.vue +357 -0
  41. package/components/model/compound/v-input-desensitize/v-input-desensitize.vue +101 -0
  42. package/components/model/compound/v-page/v-page.vue +11 -0
  43. package/components/model/compound/v-pages/v-pages.vue +141 -0
  44. package/components/model/compound/v-picker-list/v-picker-list.vue +109 -0
  45. package/components/model/compound/v-popup/v-popup.vue +151 -0
  46. package/components/model/compound/v-radio-group/v-radio-group.vue +86 -0
  47. package/components/model/compound/v-select-picker/v-select-picker.vue +202 -0
  48. package/components/model/compound/v-series-picker-list/v-series-picker-list.vue +221 -0
  49. package/components/model/compound/v-series-select-picker/v-series-select-picker.vue +203 -0
  50. package/components/model/compound/v-switch/v-switch.vue +136 -0
  51. package/components/model/compound/v-tabs-page/v-tabs-page.vue +138 -0
  52. package/components/model/native/v-badge/v-badge.vue +143 -0
  53. package/components/model/native/v-button/v-button.vue +81 -0
  54. package/components/model/native/v-carousel/v-carousel.vue +138 -0
  55. package/components/model/native/v-checkbox/v-checkbox.vue +215 -0
  56. package/components/model/native/v-collapse/v-collapse.vue +190 -0
  57. package/components/model/native/v-header-navigation-bar/v-header-navigation-bar.vue +92 -0
  58. package/components/model/native/v-input/v-input.vue +163 -0
  59. package/components/model/native/v-input-code/v-input-code.vue +146 -0
  60. package/components/model/native/v-loading/v-loading.vue +206 -0
  61. package/components/model/native/v-menu/v-menu.vue +222 -0
  62. package/components/model/native/v-menu-slide/v-menu-slide.vue +364 -0
  63. package/components/model/native/v-min-loading/v-min-loading.vue +80 -0
  64. package/components/model/native/v-null/v-null.vue +97 -0
  65. package/components/model/native/v-overlay/v-overlay.vue +96 -0
  66. package/components/model/native/v-pull-up-refresh/v-pull-up-refresh.vue +157 -0
  67. package/components/model/native/v-radio/v-radio.vue +138 -0
  68. package/components/model/native/v-scroll-list/v-scroll-list.vue +169 -0
  69. package/components/model/native/v-steps/v-steps.vue +253 -0
  70. package/components/model/native/v-table/v-table.vue +203 -0
  71. package/components/model/native/v-tabs/v-tabs.vue +235 -0
  72. package/components/model/native/v-tag/v-tag.vue +206 -0
  73. package/components/model/native/v-text/v-text.vue +187 -0
  74. package/components/model/native/v-textarea/v-textarea.vue +178 -0
  75. package/components/model/native/v-title/v-title.vue +91 -0
  76. package/components/model/native/v-toast/info.png +0 -0
  77. package/components/model/native/v-toast/success.png +0 -0
  78. package/components/model/native/v-toast/v-toast.vue +198 -0
  79. package/components/model/native/v-toast/warn.png +0 -0
  80. package/components/model/native/v-upload-file-button/v-upload-file-button.vue +296 -0
  81. package/components/model/native/v-video/v-video.vue +175 -0
  82. package/components/model/native/v-window/v-window.vue +158 -0
  83. package/components/utils/event-modifiers.ts +139 -0
  84. package/components/utils/validator.ts +451 -0
  85. package/index.js +372 -0
  86. package/package.json +25 -93
@@ -0,0 +1,364 @@
1
+ <template>
2
+ <view class="v-menu-slide" :class="[`v-menu-slide--${direction}`]">
3
+ <!-- 菜单标题区域 -->
4
+ <view :class="['menu-header', `menu-header--${direction}`]">
5
+ <view
6
+ v-for="(titleItem, index) in processedTitleItems"
7
+ :key="index"
8
+ :class="['menu-item', { 'menu-item--active': activeIndex === index }]"
9
+ @click="selectMenuItem(index)"
10
+ >
11
+ <slot name="title" :item="titleItem" :index="index" :active="activeIndex === index" :direction="direction">
12
+ <view class="menu-item__content" :class="[`menu-item__content--${direction}`]">
13
+ <text class="menu-item__text">{{ titleItem.title }}</text>
14
+ </view>
15
+ </slot>
16
+ </view>
17
+ </view>
18
+
19
+ <!-- 内容区域 -->
20
+ <view class="menu-content">
21
+ <swiper :current="activeIndex" :vertical="direction === 'y'" :duration="transitionDuration" @change="onSwiperChange" class="menu-content-swiper">
22
+ <swiper-item v-for="(contentItem, index) in processedContentItems" :key="index">
23
+ <view class="content-item">
24
+ <slot name="content" :item="contentItem" :index="index">
25
+ <text class="content-default-text">{{ contentItem.content || `内容 ${index + 1}` }}</text>
26
+ </slot>
27
+ </view>
28
+ </swiper-item>
29
+ </swiper>
30
+ </view>
31
+ </view>
32
+ </template>
33
+
34
+ <script setup>
35
+ import { ref, watch, onMounted, computed } from 'vue';
36
+
37
+ const props = defineProps({
38
+ // 菜单项数据(合并格式)
39
+ items: {
40
+ type: Array,
41
+ default: () => []
42
+ },
43
+ // 菜单标题项数据(分开格式)
44
+ titleItems: {
45
+ type: Array,
46
+ default: () => []
47
+ },
48
+ // 菜单内容项数据(分开格式)
49
+ contentItems: {
50
+ type: Array,
51
+ default: () => []
52
+ },
53
+ // 排列方向:x(水平)或 y(垂直)
54
+ direction: {
55
+ type: String,
56
+ default: 'x',
57
+ validator: (value) => ['x', 'y'].includes(value)
58
+ },
59
+ // 默认激活的菜单项索引
60
+ defaultActive: {
61
+ type: Number,
62
+ default: 0
63
+ },
64
+ // 过渡动画时长(毫秒)
65
+ transitionDuration: {
66
+ type: Number,
67
+ default: 300
68
+ },
69
+ // 标题数据的关联键名
70
+ titleKey: {
71
+ type: String,
72
+ default: null
73
+ },
74
+ // 内容数据的关联键名
75
+ contentKey: {
76
+ type: String,
77
+ default: null
78
+ },
79
+ // 向后兼容的关联键名(如果设置了,会同时设置 titleKey 和 contentKey)
80
+ relationKey: {
81
+ type: String,
82
+ default: null
83
+ }
84
+ });
85
+
86
+ const emit = defineEmits(['update:active', 'change']);
87
+
88
+ // 当前激活的菜单项索引
89
+ const activeIndex = ref(props.defaultActive);
90
+
91
+ // 计算实际使用的键名
92
+ const effectiveTitleKey = computed(() => {
93
+ if (props.titleKey) return props.titleKey;
94
+ if (props.relationKey) return props.relationKey;
95
+ return null; // 默认按索引匹配
96
+ });
97
+
98
+ const effectiveContentKey = computed(() => {
99
+ if (props.contentKey) return props.contentKey;
100
+ if (props.relationKey) return props.relationKey;
101
+ return null; // 默认按索引匹配
102
+ });
103
+
104
+ // 检测数据格式
105
+ const isSeparateFormat = computed(() => {
106
+ return props.titleItems.length > 0 && props.contentItems.length > 0;
107
+ });
108
+
109
+ // 处理标题项数据
110
+ const processedTitleItems = computed(() => {
111
+ if (isSeparateFormat.value) {
112
+ return props.titleItems;
113
+ }
114
+ return props.items;
115
+ });
116
+
117
+ // 处理内容项数据
118
+ const processedContentItems = computed(() => {
119
+ if (isSeparateFormat.value) {
120
+ // 分开格式:根据关联键匹配内容
121
+ if (effectiveTitleKey.value && effectiveContentKey.value && props.titleItems.length > 0 && props.contentItems.length > 0) {
122
+ return props.titleItems.map((titleItem) => {
123
+ const titleValue = titleItem[effectiveTitleKey.value];
124
+ const matchedContent = props.contentItems.find((contentItem) => contentItem[effectiveContentKey.value] === titleValue);
125
+ return matchedContent || { content: '暂无内容' };
126
+ });
127
+ }
128
+
129
+ // 分开格式:按索引匹配
130
+ return props.contentItems.length > 0 ? props.contentItems : props.titleItems.map(() => ({}));
131
+ }
132
+
133
+ // 合并格式:直接使用 items
134
+ return props.items;
135
+ });
136
+
137
+ // 选择菜单项
138
+ const selectMenuItem = (index) => {
139
+ if (activeIndex.value === index) return;
140
+
141
+ activeIndex.value = index;
142
+ emit('update:active', index);
143
+ emit('change', index);
144
+ };
145
+
146
+ // Swiper 切换回调
147
+ const onSwiperChange = (e) => {
148
+ const index = e.detail.current;
149
+ if (activeIndex.value === index) return;
150
+
151
+ activeIndex.value = index;
152
+ emit('update:active', index);
153
+ emit('change', index);
154
+ };
155
+
156
+ // 监听外部 active 变化
157
+ watch(
158
+ () => props.defaultActive,
159
+ (newVal) => {
160
+ if (activeIndex.value !== newVal) {
161
+ activeIndex.value = newVal;
162
+ }
163
+ }
164
+ );
165
+
166
+ // 监听 items 变化
167
+ watch(
168
+ () => [props.items, props.titleItems, props.contentItems],
169
+ () => {
170
+ // 数据变化时重置激活索引(可选)
171
+ const maxIndex = isSeparateFormat.value ? Math.min(props.titleItems.length, props.contentItems.length) - 1 : props.items.length - 1;
172
+
173
+ if (activeIndex.value > maxIndex) {
174
+ activeIndex.value = Math.max(0, maxIndex);
175
+ }
176
+ },
177
+ { deep: true }
178
+ );
179
+
180
+ // 初始化
181
+ onMounted(() => {
182
+ if (props.items.length === 0 && props.titleItems.length === 0) {
183
+ console.warn('v-menu-slide: 请提供菜单数据(items 或 titleItems/contentItems)');
184
+ }
185
+
186
+ // 数据格式检查
187
+ if (props.items.length > 0 && (props.titleItems.length > 0 || props.contentItems.length > 0)) {
188
+ console.warn('v-menu-slide: 检测到同时提供了 items 和 titleItems/contentItems,将优先使用 titleItems/contentItems');
189
+ }
190
+
191
+ // 键名检查
192
+ if (isSeparateFormat.value && effectiveTitleKey.value && effectiveContentKey.value) {
193
+ // 检查标题数据中是否包含指定的键
194
+ if (props.titleItems.length > 0 && !props.titleItems[0].hasOwnProperty(effectiveTitleKey.value)) {
195
+ console.warn(`v-menu-slide: 标题数据中未找到键 "${effectiveTitleKey.value}",将按索引匹配`);
196
+ }
197
+
198
+ // 检查内容数据中是否包含指定的键
199
+ if (props.contentItems.length > 0 && !props.contentItems[0].hasOwnProperty(effectiveContentKey.value)) {
200
+ console.warn(`v-menu-slide: 内容数据中未找到键 "${effectiveContentKey.value}",将按索引匹配`);
201
+ }
202
+ }
203
+ });
204
+ </script>
205
+
206
+ <style lang="scss" scoped>
207
+ /* 样式保持不变,与之前相同 */
208
+ .v-menu-slide {
209
+ width: 100%;
210
+ height: 100%;
211
+ display: flex;
212
+
213
+ // 默认X轴方向
214
+ flex-direction: column;
215
+
216
+ // Y轴方向布局
217
+ &--y {
218
+ flex-direction: row;
219
+ }
220
+ }
221
+
222
+ // 菜单标题区域
223
+ .menu-header {
224
+ display: flex;
225
+ background-color: #fff;
226
+ z-index: 10;
227
+
228
+ &--x {
229
+ flex-direction: row;
230
+ overflow-x: auto;
231
+ white-space: nowrap;
232
+ // 隐藏滚动条(H5和App兼容)
233
+ &::-webkit-scrollbar {
234
+ display: none;
235
+ width: 0;
236
+ height: 0;
237
+ color: transparent;
238
+ }
239
+ }
240
+
241
+ &--y {
242
+ flex-direction: column;
243
+ width: 200rpx; // Y轴方向时固定宽度
244
+ flex-shrink: 0; // 防止被压缩
245
+ overflow-y: auto;
246
+ // 隐藏滚动条(H5和App兼容)
247
+ &::-webkit-scrollbar {
248
+ display: none;
249
+ width: 0;
250
+ height: 0;
251
+ color: transparent;
252
+ }
253
+ }
254
+ }
255
+
256
+ // 菜单项
257
+ .menu-item {
258
+ display: flex;
259
+ align-items: center;
260
+ justify-content: center;
261
+ padding: 20rpx 30rpx;
262
+ position: relative;
263
+ transition: all 0.3s ease;
264
+ flex-shrink: 0; // 防止在x方向排列时被压缩
265
+
266
+ // Y轴方向时,菜单项内容横向排列
267
+ .menu-header--y & {
268
+ justify-content: flex-start;
269
+ }
270
+
271
+ &--active {
272
+ color: #007aff; // 激活状态颜色
273
+
274
+ &::after {
275
+ content: '';
276
+ position: absolute;
277
+ background-color: #007aff;
278
+ transition: all 0.3s ease;
279
+ }
280
+ }
281
+
282
+ // X方向激活指示器
283
+ .menu-header--x &--active::after {
284
+ bottom: 0;
285
+ left: 20%;
286
+ right: 20%;
287
+ height: 4rpx;
288
+ }
289
+
290
+ // Y方向激活指示器
291
+ .menu-header--y &--active::after {
292
+ top: 20%;
293
+ bottom: 20%;
294
+ left: 0;
295
+ width: 4rpx;
296
+ }
297
+ }
298
+
299
+ // 菜单项内容容器
300
+ .menu-item__content {
301
+ display: flex;
302
+ align-items: center;
303
+
304
+ // Y轴方向时,内容横向排列
305
+ &--y {
306
+ flex-direction: row;
307
+ }
308
+ }
309
+
310
+ .menu-item__text {
311
+ font-size: 28rpx;
312
+ font-weight: 500;
313
+ }
314
+
315
+ // 内容区域
316
+ .menu-content {
317
+ flex: 1;
318
+ position: relative;
319
+ overflow: hidden;
320
+ }
321
+
322
+ .menu-content-swiper {
323
+ width: 100%;
324
+ height: 100%;
325
+ }
326
+
327
+ .content-item {
328
+ width: 100%;
329
+ height: 100%;
330
+ padding: 20rpx;
331
+ box-sizing: border-box;
332
+ /* #ifdef H5 */
333
+ overflow: auto;
334
+ /* #endif */
335
+ // 隐藏滚动条(H5和App兼容)
336
+ &::-webkit-scrollbar {
337
+ display: none;
338
+ width: 0;
339
+ height: 0;
340
+ color: transparent;
341
+ }
342
+ }
343
+
344
+ .content-default-text {
345
+ font-size: 28rpx;
346
+ color: #666;
347
+ }
348
+
349
+ /* App和H5兼容性处理 */
350
+ /* #ifdef APP-PLUS */
351
+ .menu-header--x {
352
+ -webkit-overflow-scrolling: touch;
353
+ }
354
+ /* #endif */
355
+
356
+ /* #ifdef H5 */
357
+ // H5特定样式
358
+ @media (hover: hover) {
359
+ .menu-item:hover {
360
+ background-color: #f5f5f5;
361
+ }
362
+ }
363
+ /* #endif */
364
+ </style>
@@ -0,0 +1,80 @@
1
+ <template>
2
+ <view v-show="value" :class="['spinner', `spinner--type--${props.type}`]"></view>
3
+ </template>
4
+
5
+ <script lang="ts" setup>
6
+ import { inject } from 'vue';
7
+
8
+ const props = defineProps({
9
+ value: {
10
+ type: Boolean,
11
+ default: false
12
+ },
13
+ type: {
14
+ type: String,
15
+ default: 'default'
16
+ },
17
+ customizeColor: {
18
+ type: String,
19
+ default: null
20
+ },
21
+ vacancyColor: {
22
+ type: String,
23
+ default: '#fff'
24
+ },
25
+ opacity: {
26
+ type: Number,
27
+ default: 0.5
28
+ }
29
+ });
30
+
31
+ const config = inject<any>('config');
32
+ const type = props.customizeColor === null ? '1px solid transparent' : `1px solid ${props.customizeColor}`;
33
+ </script>
34
+
35
+ <style lang="scss" scoped>
36
+ .spinner {
37
+ width: 20rpx;
38
+ height: 20rpx;
39
+ border-radius: 50%;
40
+ border-top-color: v-bind('props.vacancyColor') !important;
41
+ animation: spin 1s ease-in-out infinite;
42
+ margin: auto;
43
+ opacity: v-bind('props.opacity');
44
+
45
+ &--type--default {
46
+ border: 1rpx solid ;
47
+ border-color: v-bind("config.border.default");
48
+ }
49
+
50
+ &--type--warn {
51
+ border: 1rpx solid ;
52
+ border-color: v-bind("config.border.warn");
53
+ }
54
+
55
+ &--type--info {
56
+ border: 1rpx solid ;
57
+ border-color: v-bind("config.border.info");
58
+ }
59
+
60
+ &--type--delete {
61
+ border: 1rpx solid ;
62
+ border-color: v-bind("config.border.delete");
63
+ }
64
+
65
+ &--type--succeed {
66
+ border: 1rpx solid ;
67
+ border-color: v-bind("config.border.succeed");
68
+ }
69
+
70
+ &--type--customize {
71
+ border: v-bind('type');
72
+ }
73
+ }
74
+
75
+ @keyframes spin {
76
+ to {
77
+ transform: rotate(360deg);
78
+ }
79
+ }
80
+ </style>
@@ -0,0 +1,97 @@
1
+ <template>
2
+ <view class="v-null">
3
+ <view class="v-null-content">
4
+ <slot>
5
+ <view class="v-null-icon" v-if="showIcon">
6
+ <slot name="icon">
7
+ <view class="v-null-default-icon"></view>
8
+ </slot>
9
+ </view>
10
+ <view class="v-null-text">{{ text }}</view>
11
+ </slot>
12
+ </view>
13
+ </view>
14
+ </template>
15
+
16
+ <script lang="ts" setup>
17
+ import { computed, inject } from 'vue';
18
+
19
+ const props = defineProps({
20
+ text: {
21
+ type: String,
22
+ default: '暂无数据'
23
+ },
24
+ showIcon: {
25
+ type: Boolean,
26
+ default: true
27
+ },
28
+ iconSize: {
29
+ type: Number,
30
+ default: 40
31
+ },
32
+ textColor: {
33
+ type: String,
34
+ default: '#999999'
35
+ },
36
+ iconColor: {
37
+ type: String,
38
+ default: '#cccccc'
39
+ }
40
+ });
41
+
42
+ const config = inject<any>('config');
43
+ const iconSizeStyle = computed(() => ({
44
+ width: `${props.iconSize}rpx`,
45
+ height: `${props.iconSize}rpx`
46
+ }));
47
+ </script>
48
+
49
+ <style lang="scss" scoped>
50
+ .v-null {
51
+ display: flex;
52
+ justify-content: center;
53
+ align-items: center;
54
+ height: 100%;
55
+ text-align: center;
56
+ font-size: v-bind("config.fontSize.smallText");
57
+ color: v-bind('props.textColor');
58
+ }
59
+
60
+ .v-null-content {
61
+ display: flex;
62
+ flex-direction: column;
63
+ align-items: center;
64
+ }
65
+
66
+ .v-null-icon {
67
+ display: flex;
68
+ justify-content: center;
69
+ align-items: center;
70
+ margin-bottom: 12rpx;
71
+ }
72
+
73
+ .v-null-default-icon {
74
+ width: v-bind('iconSizeStyle.width');
75
+ height: v-bind('iconSizeStyle.height');
76
+ background-color: v-bind('props.iconColor');
77
+ border-radius: 50%;
78
+ position: relative;
79
+ overflow: hidden;
80
+ }
81
+
82
+ .v-null-default-icon::before {
83
+ content: '';
84
+ position: absolute;
85
+ top: 50%;
86
+ left: 50%;
87
+ width: 60%;
88
+ height: 60%;
89
+ background-color: #fff;
90
+ border-radius: 50%;
91
+ transform: translate(-50%, -50%);
92
+ }
93
+
94
+ .v-null-text {
95
+ color: v-bind('props.textColor');
96
+ }
97
+ </style>
@@ -0,0 +1,96 @@
1
+ <template>
2
+ <view class="v-overlay" :class="['v-overlay-open']" @click="handleOverlayClick">
3
+ <slot></slot>
4
+ </view>
5
+ </template>
6
+
7
+ <script lang="ts" setup>
8
+ import { ref,computed,watch,inject } from 'vue';
9
+
10
+ /**
11
+ * v-overlay 遮罩层
12
+ * value 是否显示遮罩层 默认值:false 可选值true显示、false隐藏
13
+ * zIndex 遮罩层z-index值 默认值:999
14
+ * opacity 遮罩层透明度 默认值:0.5
15
+ * closeOnClick 是否支持点击遮罩层关闭 默认值:true 可选值true支持关闭、false不支持关闭
16
+ * animation 遮罩层动画效果 默认值:'fade' 可选值'fade'淡入淡出等
17
+ */
18
+ const props = defineProps({
19
+ value: {
20
+ type: Boolean,
21
+ default: false
22
+ },
23
+ zIndex: {
24
+ type: Number,
25
+ default: 999
26
+ },
27
+ opacity: {
28
+ type: Number,
29
+ default: 0.5
30
+ },
31
+ closeOnClick: {
32
+ type: Boolean,
33
+ default: true
34
+ },
35
+ animation: {
36
+ type: String,
37
+ default: 'fade'
38
+ }
39
+ });
40
+ const emits = defineEmits(['update:value','click']);
41
+
42
+ const config = inject<any>('config');
43
+ const isVisible = ref(props.value)
44
+ const visible = ref(isVisible.value ? "flex" : "none");
45
+ const backgroundColor = `rgba(0, 0, 0, ${props.opacity})`
46
+
47
+
48
+ watch(()=>props.value,(newValue)=>{
49
+ isVisible.value = newValue
50
+ visible.value = newValue ? "flex" : "none"
51
+ })
52
+ watch(()=>isVisible.value,(newValue)=>{
53
+ emits('update:value', newValue)
54
+ })
55
+
56
+ const handleOverlayClick = () => {
57
+ if (props.closeOnClick) {
58
+ isVisible.value = false;
59
+ visible.value = "none"
60
+ emits('click', false);
61
+ }
62
+ };
63
+
64
+ </script>
65
+
66
+ <style lang="scss" scoped>
67
+ .v-overlay {
68
+ position: fixed;
69
+ top: 0;
70
+ left: 0;
71
+ width: 100%;
72
+ height: 100%;
73
+ background-color: v-bind("backgroundColor");
74
+ display: v-bind("visible");
75
+ justify-content: center;
76
+ align-items: center;
77
+ transition: opacity 0.3s ease;
78
+ opacity: v-bind("config.opacity.mask");
79
+ pointer-events: none;
80
+ z-index: v-bind("props.zIndex");
81
+ }
82
+
83
+ .v-overlay-open {
84
+ opacity: v-bind("backgroundColor");
85
+ pointer-events: all;
86
+ }
87
+
88
+ .v-overlay.fade {
89
+ opacity: v-bind("config.opacity.mask");
90
+ transition: opacity 0.3s ease;
91
+ }
92
+
93
+ .v-overlay.fade.v-overlay-open {
94
+ opacity: v-bind("backgroundColor");
95
+ }
96
+ </style>