@coffic/cosy-ui 0.8.24 → 0.8.26
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/dist/app.css +1 -1
- package/dist/src-astro/alert/Alert.astro +45 -5
- package/dist/src-astro/list/ListItem.astro +64 -38
- package/dist/src-astro/list/ListItemBreath.astro +60 -0
- package/dist/src-astro/list/ListItemGlow.astro +65 -0
- package/dist/src-astro/list/ListItemIconLeft.astro +53 -0
- package/dist/src-astro/list/ListItemIconRight.astro +53 -0
- package/dist/src-astro/list/ListItemPulse.astro +61 -0
- package/dist/src-astro/list/ListItemRing.astro +59 -0
- package/dist/src-astro/list/index.ts +7 -1
- package/dist/src-vue/alert/Alert.vue +66 -40
- package/dist/src-vue/alert-dialog/Multilang.vue +1 -1
- package/dist/src-vue/buttons/Button.vue +20 -14
- package/dist/src-vue/container/Container.vue +1 -1
- package/dist/src-vue/iPhone/StatusBarContent.vue +88 -76
- package/dist/src-vue/iPhone/iPhoneWindow.vue +149 -129
- package/dist/src-vue/list/ListItem.vue +77 -32
- package/dist/src-vue/list/ListItemBreath.vue +70 -0
- package/dist/src-vue/list/ListItemGlow.vue +75 -0
- package/dist/src-vue/list/ListItemIconLeft.vue +63 -0
- package/dist/src-vue/list/ListItemIconRight.vue +63 -0
- package/dist/src-vue/list/ListItemPulse.vue +71 -0
- package/dist/src-vue/list/ListItemRing.vue +71 -0
- package/dist/src-vue/mac-window/MacWindow.vue +160 -139
- package/package.json +1 -1
@@ -63,8 +63,8 @@ iPhoneWindow 组件模拟 iPhone 设备的外观,包含状态栏、时间显
|
|
63
63
|
@emits
|
64
64
|
-->
|
65
65
|
<script lang="ts">
|
66
|
-
import '../../style
|
67
|
-
import { AlertDialog } from '../../index-vue
|
66
|
+
import '../../style';
|
67
|
+
import { AlertDialog } from '../../index-vue';
|
68
68
|
import { ref, defineComponent } from 'vue';
|
69
69
|
import iphoneFrame from './assets/iPhone 14 Pro - Deep Purple - Portrait.png';
|
70
70
|
import StatusBarContent from './StatusBarContent.vue';
|
@@ -83,148 +83,168 @@ const mainContentWidthAspectRatio = 1179 / iphoneFrameWidth;
|
|
83
83
|
// 比例-总高度
|
84
84
|
const mainContentHeightAspectRatio = 2556 / iphoneFrameHeight;
|
85
85
|
// 比例-状态栏高度
|
86
|
-
const iphoneFrameStatusBarHeightAspectRatio =
|
86
|
+
const iphoneFrameStatusBarHeightAspectRatio =
|
87
|
+
iphoneFrameStatusBarHeight / iphoneFrameHeight;
|
87
88
|
// 比例-状态栏离上边框的距离
|
88
|
-
const iphoneFrameStatusBarTopAspectRatio =
|
89
|
+
const iphoneFrameStatusBarTopAspectRatio =
|
90
|
+
iphoneFrameStatusBarTop / iphoneFrameHeight;
|
89
91
|
|
90
92
|
// 预定义的高度选项
|
91
93
|
type HeightOption = 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | '4xl' | '5xl';
|
92
94
|
|
93
95
|
const heightClasses: Record<HeightOption, string> = {
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
96
|
+
sm: 'cosy:h-64', // 256px
|
97
|
+
md: 'cosy:h-80', // 320px
|
98
|
+
lg: 'cosy:h-96', // 384px
|
99
|
+
xl: 'cosy:h-[480px]', // 480px
|
100
|
+
'2xl': 'cosy:h-[560px]', // 560px
|
101
|
+
'3xl': 'cosy:h-[640px]', // 640px
|
102
|
+
'4xl': 'cosy:h-[720px]', // 720px
|
103
|
+
'5xl': 'cosy:h-[800px]', // 800px
|
102
104
|
};
|
103
105
|
|
104
106
|
export default defineComponent({
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
107
|
+
name: 'iPhoneWindow',
|
108
|
+
components: {
|
109
|
+
AlertDialog,
|
110
|
+
StatusBarContent,
|
111
|
+
},
|
112
|
+
props: {
|
113
|
+
height: {
|
114
|
+
type: String as () => HeightOption,
|
115
|
+
default: 'lg',
|
116
|
+
validator: (value: string) => {
|
117
|
+
return Object.keys(heightClasses).includes(value);
|
118
|
+
},
|
109
119
|
},
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
default: 'lg',
|
114
|
-
validator: (value: string) => {
|
115
|
-
return Object.keys(heightClasses).includes(value);
|
116
|
-
},
|
117
|
-
},
|
118
|
-
debug: {
|
119
|
-
type: Boolean,
|
120
|
-
default: false,
|
121
|
-
},
|
122
|
-
title: {
|
123
|
-
type: String,
|
124
|
-
default: '',
|
125
|
-
},
|
126
|
-
statusBarButtons: {
|
127
|
-
type: Array,
|
128
|
-
default: () => [],
|
129
|
-
},
|
130
|
-
withShadow: {
|
131
|
-
type: Boolean,
|
132
|
-
default: true,
|
133
|
-
},
|
134
|
-
showFrame: {
|
135
|
-
type: Boolean,
|
136
|
-
default: true,
|
137
|
-
},
|
138
|
-
backgroundColor: {
|
139
|
-
type: String,
|
140
|
-
default: '',
|
141
|
-
},
|
120
|
+
debug: {
|
121
|
+
type: Boolean,
|
122
|
+
default: false,
|
142
123
|
},
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
// 计算当前高度的缩放比例
|
148
|
-
const getScaleRatio = () => {
|
149
|
-
const heightValues = {
|
150
|
-
sm: 256,
|
151
|
-
md: 320,
|
152
|
-
lg: 384,
|
153
|
-
xl: 480,
|
154
|
-
'2xl': 560,
|
155
|
-
'3xl': 640,
|
156
|
-
'4xl': 720,
|
157
|
-
'5xl': 800,
|
158
|
-
};
|
159
|
-
const currentHeight = heightValues[props.height];
|
160
|
-
// 基于特定高度计算缩放比例
|
161
|
-
return currentHeight / 500;
|
162
|
-
};
|
163
|
-
|
164
|
-
return {
|
165
|
-
showAlertDialog,
|
166
|
-
alertMessage,
|
167
|
-
iphoneFrame: (iphoneFrame as any).src || iphoneFrame,
|
168
|
-
heightClasses,
|
169
|
-
mainContentWidthAspectRatio,
|
170
|
-
mainContentHeightAspectRatio,
|
171
|
-
iphoneFrameWidth,
|
172
|
-
iphoneFrameHeight,
|
173
|
-
iphoneFrameStatusBarTop,
|
174
|
-
iphoneFrameStatusBarHeight,
|
175
|
-
iphoneFrameStatusBarHeightAspectRatio,
|
176
|
-
iphoneFrameStatusBarTopAspectRatio,
|
177
|
-
getScaleRatio,
|
178
|
-
};
|
124
|
+
title: {
|
125
|
+
type: String,
|
126
|
+
default: '',
|
179
127
|
},
|
128
|
+
statusBarButtons: {
|
129
|
+
type: Array,
|
130
|
+
default: () => [],
|
131
|
+
},
|
132
|
+
withShadow: {
|
133
|
+
type: Boolean,
|
134
|
+
default: true,
|
135
|
+
},
|
136
|
+
showFrame: {
|
137
|
+
type: Boolean,
|
138
|
+
default: true,
|
139
|
+
},
|
140
|
+
backgroundColor: {
|
141
|
+
type: String,
|
142
|
+
default: '',
|
143
|
+
},
|
144
|
+
},
|
145
|
+
setup(props) {
|
146
|
+
const showAlertDialog = ref(false);
|
147
|
+
const alertMessage = ref('');
|
148
|
+
|
149
|
+
// 计算当前高度的缩放比例
|
150
|
+
const getScaleRatio = () => {
|
151
|
+
const heightValues = {
|
152
|
+
sm: 256,
|
153
|
+
md: 320,
|
154
|
+
lg: 384,
|
155
|
+
xl: 480,
|
156
|
+
'2xl': 560,
|
157
|
+
'3xl': 640,
|
158
|
+
'4xl': 720,
|
159
|
+
'5xl': 800,
|
160
|
+
};
|
161
|
+
const currentHeight = heightValues[props.height];
|
162
|
+
// 基于特定高度计算缩放比例
|
163
|
+
return currentHeight / 500;
|
164
|
+
};
|
165
|
+
|
166
|
+
return {
|
167
|
+
showAlertDialog,
|
168
|
+
alertMessage,
|
169
|
+
iphoneFrame: (iphoneFrame as any).src || iphoneFrame,
|
170
|
+
heightClasses,
|
171
|
+
mainContentWidthAspectRatio,
|
172
|
+
mainContentHeightAspectRatio,
|
173
|
+
iphoneFrameWidth,
|
174
|
+
iphoneFrameHeight,
|
175
|
+
iphoneFrameStatusBarTop,
|
176
|
+
iphoneFrameStatusBarHeight,
|
177
|
+
iphoneFrameStatusBarHeightAspectRatio,
|
178
|
+
iphoneFrameStatusBarTopAspectRatio,
|
179
|
+
getScaleRatio,
|
180
|
+
};
|
181
|
+
},
|
180
182
|
});
|
181
183
|
</script>
|
182
184
|
|
183
185
|
<template>
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
186
|
+
<div
|
187
|
+
:class="['cosy:relative', heightClasses[height]]"
|
188
|
+
:style="{
|
189
|
+
aspectRatio: `${iphoneFrameWidth}/${iphoneFrameHeight}`,
|
190
|
+
// 调试模式,背景色为半透明的黄色
|
191
|
+
backgroundColor: debug ? 'rgba(255, 255, 0, 0.3)' : 'transparent',
|
192
|
+
}"
|
193
|
+
>
|
194
|
+
<!-- iPhone 边框 -->
|
195
|
+
<img
|
196
|
+
v-if="showFrame"
|
197
|
+
style="max-width: 100%; max-height: 100%"
|
198
|
+
:src="iphoneFrame"
|
199
|
+
alt="iPhone frame"
|
200
|
+
/>
|
201
|
+
|
202
|
+
<!-- 顶部状态栏 -->
|
203
|
+
<div
|
204
|
+
:style="{
|
205
|
+
position: 'absolute',
|
206
|
+
top: iphoneFrameStatusBarTopAspectRatio * 100 + '%',
|
207
|
+
height: iphoneFrameStatusBarHeightAspectRatio * 100 + '%',
|
208
|
+
width: mainContentWidthAspectRatio * 100 + '%',
|
209
|
+
left: '50%',
|
210
|
+
transform: 'translate(-50%, 0)',
|
211
|
+
paddingLeft: '5%',
|
212
|
+
paddingRight: '5%',
|
213
|
+
// 调试模式,背景色为半透明的红色
|
214
|
+
backgroundColor: debug ? 'rgba(255, 0, 0, 0.3)' : 'transparent',
|
215
|
+
zIndex: 10,
|
216
|
+
}"
|
217
|
+
>
|
218
|
+
<StatusBarContent :scaleRatio="getScaleRatio()" />
|
219
|
+
</div>
|
220
|
+
|
221
|
+
<!-- 内容区域 -->
|
222
|
+
<div
|
223
|
+
class="cosy:inset-0 cosy:h-full"
|
224
|
+
:style="{
|
225
|
+
width: mainContentWidthAspectRatio * 100 + '%',
|
226
|
+
height: mainContentHeightAspectRatio * 100 + '%',
|
227
|
+
// 水平居中
|
228
|
+
left: '50%',
|
229
|
+
// 垂直居中
|
230
|
+
top: '50%',
|
231
|
+
transform: 'translate(-50%, -50%)',
|
232
|
+
position: 'absolute',
|
233
|
+
// 调试模式,背景色为半透明的蓝色
|
234
|
+
backgroundColor: debug ? 'rgba(0, 0, 255, 0.3)' : 'transparent',
|
235
|
+
zIndex: 10,
|
236
|
+
}"
|
237
|
+
>
|
238
|
+
<div
|
239
|
+
:class="[
|
240
|
+
debug ? 'cosy:bg-green-300/50' : '',
|
241
|
+
'cosy:h-full cosy:w-full cosy:overflow-hidden',
|
242
|
+
]"
|
243
|
+
>
|
244
|
+
<slot />
|
245
|
+
</div>
|
227
246
|
</div>
|
247
|
+
</div>
|
228
248
|
|
229
|
-
|
230
|
-
</template>
|
249
|
+
<AlertDialog v-model="showAlertDialog" :message="alertMessage" />
|
250
|
+
</template>
|
@@ -1,50 +1,95 @@
|
|
1
1
|
<template>
|
2
|
-
<
|
3
|
-
|
2
|
+
<ListItemRing
|
3
|
+
v-if="animationType === 'ring'"
|
4
|
+
:loading="loading"
|
5
|
+
:duration="duration"
|
4
6
|
@click="$emit('click')"
|
5
7
|
>
|
6
|
-
<
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
</
|
8
|
+
<slot />
|
9
|
+
</ListItemRing>
|
10
|
+
|
11
|
+
<ListItemIconLeft
|
12
|
+
v-else-if="animationType === 'icon-left'"
|
13
|
+
:loading="loading"
|
14
|
+
:duration="duration"
|
15
|
+
@click="$emit('click')"
|
16
|
+
>
|
17
|
+
<slot />
|
18
|
+
</ListItemIconLeft>
|
19
|
+
|
20
|
+
<ListItemIconRight
|
21
|
+
v-else-if="animationType === 'icon-right'"
|
22
|
+
:loading="loading"
|
23
|
+
:duration="duration"
|
24
|
+
@click="$emit('click')"
|
25
|
+
>
|
26
|
+
<slot />
|
27
|
+
</ListItemIconRight>
|
28
|
+
|
29
|
+
<ListItemBreath
|
30
|
+
v-else-if="animationType === 'breath'"
|
31
|
+
:loading="loading"
|
32
|
+
:duration="duration"
|
33
|
+
@click="$emit('click')"
|
34
|
+
>
|
35
|
+
<slot />
|
36
|
+
</ListItemBreath>
|
37
|
+
|
38
|
+
<ListItemPulse
|
39
|
+
v-else-if="animationType === 'pulse'"
|
40
|
+
:loading="loading"
|
41
|
+
:duration="duration"
|
42
|
+
@click="$emit('click')"
|
43
|
+
>
|
44
|
+
<slot />
|
45
|
+
</ListItemPulse>
|
46
|
+
|
47
|
+
<ListItemGlow
|
48
|
+
v-else-if="animationType === 'glow'"
|
49
|
+
:loading="loading"
|
50
|
+
:duration="duration"
|
51
|
+
@click="$emit('click')"
|
52
|
+
>
|
53
|
+
<slot />
|
54
|
+
</ListItemGlow>
|
55
|
+
|
56
|
+
<!-- 默认使用 ring 动画 -->
|
57
|
+
<ListItemRing
|
58
|
+
v-else
|
59
|
+
:loading="loading"
|
60
|
+
:duration="duration"
|
61
|
+
@click="$emit('click')"
|
62
|
+
>
|
63
|
+
<slot />
|
64
|
+
</ListItemRing>
|
17
65
|
</template>
|
18
66
|
|
19
67
|
<script setup lang="ts">
|
20
|
-
import
|
68
|
+
import ListItemRing from './ListItemRing.vue';
|
69
|
+
import ListItemIconLeft from './ListItemIconLeft.vue';
|
70
|
+
import ListItemIconRight from './ListItemIconRight.vue';
|
71
|
+
import ListItemBreath from './ListItemBreath.vue';
|
72
|
+
import ListItemPulse from './ListItemPulse.vue';
|
73
|
+
import ListItemGlow from './ListItemGlow.vue';
|
21
74
|
|
22
75
|
withDefaults(
|
23
76
|
defineProps<{
|
24
77
|
loading?: boolean;
|
25
|
-
duration?: number;
|
78
|
+
duration?: number;
|
79
|
+
animationType?:
|
80
|
+
| 'ring'
|
81
|
+
| 'icon-left'
|
82
|
+
| 'icon-right'
|
83
|
+
| 'breath'
|
84
|
+
| 'pulse'
|
85
|
+
| 'glow';
|
26
86
|
}>(),
|
27
87
|
{
|
28
88
|
loading: false,
|
29
|
-
duration:
|
89
|
+
duration: undefined,
|
90
|
+
animationType: 'ring',
|
30
91
|
}
|
31
92
|
);
|
32
93
|
|
33
94
|
defineEmits(['click']);
|
34
95
|
</script>
|
35
|
-
|
36
|
-
<style scoped>
|
37
|
-
.loading-bar {
|
38
|
-
width: 0%;
|
39
|
-
height: 100%;
|
40
|
-
animation: loading-bar-anim linear forwards;
|
41
|
-
}
|
42
|
-
@keyframes loading-bar-anim {
|
43
|
-
0% {
|
44
|
-
width: 0%;
|
45
|
-
}
|
46
|
-
100% {
|
47
|
-
width: 100%;
|
48
|
-
}
|
49
|
-
}
|
50
|
-
</style>
|
@@ -0,0 +1,70 @@
|
|
1
|
+
<template>
|
2
|
+
<li
|
3
|
+
:class="[
|
4
|
+
'cosy:mb-2 cosy:rounded-md cosy:bg-base-300 cosy:p-2 cosy:flex cosy:items-center cosy:gap-3 cosy:hover:bg-accent/10 cosy:relative cosy:overflow-hidden',
|
5
|
+
loading && !duration ? 'breath-anim' : '',
|
6
|
+
]"
|
7
|
+
@click="$emit('click')"
|
8
|
+
>
|
9
|
+
<div v-if="loading">
|
10
|
+
<template v-if="duration">
|
11
|
+
<div
|
12
|
+
class="cosy:absolute cosy:left-0 cosy:top-0 cosy:h-full cosy:bg-accent/40 cosy:z-0 loading-bar"
|
13
|
+
:style="{ animationDuration: duration + 'ms' }"
|
14
|
+
></div>
|
15
|
+
</template>
|
16
|
+
</div>
|
17
|
+
<div
|
18
|
+
class="cosy:relative cosy:z-10 cosy:w-full cosy:flex cosy:items-center cosy:gap-3"
|
19
|
+
>
|
20
|
+
<slot />
|
21
|
+
</div>
|
22
|
+
</li>
|
23
|
+
</template>
|
24
|
+
|
25
|
+
<script setup lang="ts">
|
26
|
+
withDefaults(
|
27
|
+
defineProps<{
|
28
|
+
loading?: boolean;
|
29
|
+
duration?: number;
|
30
|
+
}>(),
|
31
|
+
{
|
32
|
+
loading: false,
|
33
|
+
duration: undefined,
|
34
|
+
}
|
35
|
+
);
|
36
|
+
|
37
|
+
defineEmits(['click']);
|
38
|
+
</script>
|
39
|
+
|
40
|
+
<style scoped>
|
41
|
+
/* 背景色呼吸动画 */
|
42
|
+
@keyframes breath {
|
43
|
+
0%,
|
44
|
+
100% {
|
45
|
+
background-color: rgb(55 65 81); /* base-300 等效颜色 */
|
46
|
+
}
|
47
|
+
50% {
|
48
|
+
background-color: rgb(139 92 246 / 0.3); /* accent 色彩 */
|
49
|
+
}
|
50
|
+
}
|
51
|
+
.breath-anim {
|
52
|
+
animation: breath 2s ease-in-out infinite;
|
53
|
+
background-color: rgb(55 65 81); /* 确保有初始背景色 */
|
54
|
+
}
|
55
|
+
|
56
|
+
/* 进度条动画 */
|
57
|
+
.loading-bar {
|
58
|
+
width: 0%;
|
59
|
+
height: 100%;
|
60
|
+
animation: loading-bar-anim linear forwards;
|
61
|
+
}
|
62
|
+
@keyframes loading-bar-anim {
|
63
|
+
0% {
|
64
|
+
width: 0%;
|
65
|
+
}
|
66
|
+
100% {
|
67
|
+
width: 100%;
|
68
|
+
}
|
69
|
+
}
|
70
|
+
</style>
|
@@ -0,0 +1,75 @@
|
|
1
|
+
<template>
|
2
|
+
<li
|
3
|
+
:class="[
|
4
|
+
'cosy:mb-2 cosy:rounded-md cosy:bg-base-300 cosy:p-2 cosy:flex cosy:items-center cosy:gap-3 cosy:hover:bg-accent/10 cosy:relative cosy:overflow-hidden',
|
5
|
+
loading && !duration ? 'glow-anim' : '',
|
6
|
+
]"
|
7
|
+
@click="$emit('click')"
|
8
|
+
>
|
9
|
+
<div v-if="loading">
|
10
|
+
<template v-if="duration">
|
11
|
+
<div
|
12
|
+
class="cosy:absolute cosy:left-0 cosy:top-0 cosy:h-full cosy:bg-accent/40 cosy:z-0 loading-bar"
|
13
|
+
:style="{ animationDuration: duration + 'ms' }"
|
14
|
+
></div>
|
15
|
+
</template>
|
16
|
+
</div>
|
17
|
+
<div
|
18
|
+
class="cosy:relative cosy:z-10 cosy:w-full cosy:flex cosy:items-center cosy:gap-3"
|
19
|
+
>
|
20
|
+
<slot />
|
21
|
+
</div>
|
22
|
+
</li>
|
23
|
+
</template>
|
24
|
+
|
25
|
+
<script setup lang="ts">
|
26
|
+
withDefaults(
|
27
|
+
defineProps<{
|
28
|
+
loading?: boolean;
|
29
|
+
duration?: number;
|
30
|
+
}>(),
|
31
|
+
{
|
32
|
+
loading: false,
|
33
|
+
duration: undefined,
|
34
|
+
}
|
35
|
+
);
|
36
|
+
|
37
|
+
defineEmits(['click']);
|
38
|
+
</script>
|
39
|
+
|
40
|
+
<style scoped>
|
41
|
+
/* 发光边框动画 */
|
42
|
+
@keyframes glow {
|
43
|
+
0%,
|
44
|
+
100% {
|
45
|
+
border: 1px solid rgb(139 92 246 / 0.3);
|
46
|
+
box-shadow: 0 0 5px rgb(139 92 246 / 0.3);
|
47
|
+
}
|
48
|
+
50% {
|
49
|
+
border: 1px solid rgb(139 92 246 / 0.8);
|
50
|
+
box-shadow:
|
51
|
+
0 0 20px rgb(139 92 246 / 0.6),
|
52
|
+
0 0 30px rgb(139 92 246 / 0.4),
|
53
|
+
inset 0 0 10px rgb(139 92 246 / 0.2);
|
54
|
+
}
|
55
|
+
}
|
56
|
+
.glow-anim {
|
57
|
+
animation: glow 2s ease-in-out infinite;
|
58
|
+
border: 1px solid rgb(139 92 246 / 0.3); /* 确保有初始边框 */
|
59
|
+
}
|
60
|
+
|
61
|
+
/* 进度条动画 */
|
62
|
+
.loading-bar {
|
63
|
+
width: 0%;
|
64
|
+
height: 100%;
|
65
|
+
animation: loading-bar-anim linear forwards;
|
66
|
+
}
|
67
|
+
@keyframes loading-bar-anim {
|
68
|
+
0% {
|
69
|
+
width: 0%;
|
70
|
+
}
|
71
|
+
100% {
|
72
|
+
width: 100%;
|
73
|
+
}
|
74
|
+
}
|
75
|
+
</style>
|
@@ -0,0 +1,63 @@
|
|
1
|
+
<template>
|
2
|
+
<li
|
3
|
+
class="cosy:mb-2 cosy:rounded-md cosy:bg-base-300 cosy:p-2 cosy:flex cosy:items-center cosy:gap-3 cosy:hover:bg-accent/10 cosy:relative cosy:overflow-hidden"
|
4
|
+
@click="$emit('click')"
|
5
|
+
>
|
6
|
+
<div v-if="loading">
|
7
|
+
<template v-if="duration">
|
8
|
+
<div
|
9
|
+
class="cosy:absolute cosy:left-0 cosy:top-0 cosy:h-full cosy:bg-accent/40 cosy:z-0 loading-bar"
|
10
|
+
:style="{ animationDuration: duration + 'ms' }"
|
11
|
+
></div>
|
12
|
+
</template>
|
13
|
+
</div>
|
14
|
+
<div
|
15
|
+
class="cosy:relative cosy:z-10 cosy:w-full cosy:flex cosy:items-center cosy:gap-3"
|
16
|
+
>
|
17
|
+
<!-- 前置 loading 图标 -->
|
18
|
+
<div
|
19
|
+
v-if="loading && !duration"
|
20
|
+
class="cosy:flex-shrink-0 cosy:transform-none"
|
21
|
+
style="transform: none !important"
|
22
|
+
>
|
23
|
+
<div
|
24
|
+
class="cosy:loading cosy:loading-spinner cosy:loading-sm cosy:text-accent"
|
25
|
+
></div>
|
26
|
+
</div>
|
27
|
+
|
28
|
+
<slot />
|
29
|
+
</div>
|
30
|
+
</li>
|
31
|
+
</template>
|
32
|
+
|
33
|
+
<script setup lang="ts">
|
34
|
+
withDefaults(
|
35
|
+
defineProps<{
|
36
|
+
loading?: boolean;
|
37
|
+
duration?: number;
|
38
|
+
}>(),
|
39
|
+
{
|
40
|
+
loading: false,
|
41
|
+
duration: undefined,
|
42
|
+
}
|
43
|
+
);
|
44
|
+
|
45
|
+
defineEmits(['click']);
|
46
|
+
</script>
|
47
|
+
|
48
|
+
<style scoped>
|
49
|
+
/* 进度条动画 */
|
50
|
+
.loading-bar {
|
51
|
+
width: 0%;
|
52
|
+
height: 100%;
|
53
|
+
animation: loading-bar-anim linear forwards;
|
54
|
+
}
|
55
|
+
@keyframes loading-bar-anim {
|
56
|
+
0% {
|
57
|
+
width: 0%;
|
58
|
+
}
|
59
|
+
100% {
|
60
|
+
width: 100%;
|
61
|
+
}
|
62
|
+
}
|
63
|
+
</style>
|