@coffic/cosy-ui 0.8.26 → 0.8.28
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/index-astro.ts +1 -0
- package/dist/index-vue.ts +18 -0
- package/dist/src-astro/badge/Badge.astro +67 -0
- package/dist/src-astro/badge/index.ts +1 -0
- package/dist/src-astro/code-container/CodeContainer.astro +4 -2
- package/dist/src-astro/team-member/TeamMember.astro +36 -4
- package/dist/src-astro/team-member/index.ts +3 -26
- package/dist/src-vue/badge/Badge.vue +66 -0
- package/dist/src-vue/badge/index.ts +1 -0
- package/dist/src-vue/buttons/ButtonFolder.vue +98 -0
- package/dist/src-vue/buttons/ButtonRefresh.vue +163 -0
- package/dist/src-vue/buttons/index.ts +2 -0
- package/dist/src-vue/card/Card.vue +52 -0
- package/dist/src-vue/card/CardCourse.vue +34 -0
- package/dist/src-vue/card/index.ts +2 -0
- package/dist/src-vue/key-catcher/KeyCatcher.vue +121 -0
- package/dist/src-vue/key-catcher/index.ts +1 -0
- package/dist/src-vue/progress/Progress.vue +68 -0
- package/dist/src-vue/progress/index.ts +1 -0
- package/dist/src-vue/status-bar/StatusBar.vue +101 -0
- package/dist/src-vue/status-bar/StatusBarItem.vue +97 -0
- package/dist/src-vue/status-bar/index.ts +2 -0
- package/dist/src-vue/tool-bar/ToolBar.vue +95 -0
- package/dist/src-vue/tool-bar/index.ts +1 -0
- package/package.json +1 -1
- package/dist/src-astro/code-container/CodePanel.astro +0 -14
- package/dist/src-astro/team-member/TeamMemberBasic.astro +0 -25
- package/dist/src-astro/team-member/TeamMemberCustomStyle.astro +0 -26
- package/dist/src-astro/team-member/TeamMemberGroup.astro +0 -60
- package/dist/src-astro/team-member/TeamMemberWithSocial.astro +0 -30
package/dist/index-astro.ts
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
export * from './src-astro/alert';
|
3
3
|
export * from './src-astro/article';
|
4
4
|
export * from './src-astro/banner';
|
5
|
+
export * from './src-astro/badge';
|
5
6
|
export * from './src-astro/button/index_astro';
|
6
7
|
export * from './src-astro/card';
|
7
8
|
export * from './src-astro/code-block';
|
package/dist/index-vue.ts
CHANGED
@@ -4,12 +4,18 @@ export * from './src-vue/alert-dialog/index';
|
|
4
4
|
// Alert
|
5
5
|
export * from './src-vue/alert/index';
|
6
6
|
|
7
|
+
// Badge
|
8
|
+
export * from './src-vue/badge/index';
|
9
|
+
|
7
10
|
// Banner
|
8
11
|
export * from './src-vue/banner-box/index';
|
9
12
|
|
10
13
|
// BlogList
|
11
14
|
export * from './src-vue/blog/index';
|
12
15
|
|
16
|
+
// Card
|
17
|
+
export * from './src-vue/card/index';
|
18
|
+
|
13
19
|
// Container
|
14
20
|
export * from './src-vue/container/index';
|
15
21
|
|
@@ -25,9 +31,21 @@ export * from './src-vue/buttons/index';
|
|
25
31
|
// Icons
|
26
32
|
export * from './src-vue/icons/index';
|
27
33
|
|
34
|
+
// KeyCatcher
|
35
|
+
export * from './src-vue/key-catcher/index';
|
36
|
+
|
28
37
|
// List
|
29
38
|
export * from './src-vue/list/index';
|
30
39
|
|
40
|
+
// Progress
|
41
|
+
export * from './src-vue/progress/index';
|
42
|
+
|
43
|
+
// StatusBar
|
44
|
+
export * from './src-vue/status-bar/index';
|
45
|
+
|
46
|
+
// ToolBar
|
47
|
+
export * from './src-vue/tool-bar/index';
|
48
|
+
|
31
49
|
// Windows
|
32
50
|
export * from './src-vue/mac-window/index';
|
33
51
|
export * from './src-vue/iPhone/index';
|
@@ -0,0 +1,67 @@
|
|
1
|
+
---
|
2
|
+
/**
|
3
|
+
* @component Badge
|
4
|
+
* @description 一个用于高亮信息的小组件。
|
5
|
+
* @usage
|
6
|
+
* ```astro
|
7
|
+
* <Badge>默认</Badge>
|
8
|
+
* <Badge variant="primary">Primary</Badge>
|
9
|
+
* ```
|
10
|
+
* @props
|
11
|
+
* @prop {('primary'|'secondary'|'accent'|'ghost'|'info'|'success'|'warning'|'error')} [variant] - 徽章的颜色变体。
|
12
|
+
* @prop {('xs'|'sm'|'md'|'lg')} [size] - 徽章的尺寸。
|
13
|
+
* @prop {boolean} [outline=false] - 徽章是否为描边样式。
|
14
|
+
* @prop {string} [class] - 自定义 CSS 类。
|
15
|
+
* @slots
|
16
|
+
* @slot default - 徽章内容。
|
17
|
+
*/
|
18
|
+
import '../../style.ts';
|
19
|
+
|
20
|
+
interface Props {
|
21
|
+
variant?:
|
22
|
+
| 'primary'
|
23
|
+
| 'secondary'
|
24
|
+
| 'accent'
|
25
|
+
| 'ghost'
|
26
|
+
| 'info'
|
27
|
+
| 'success'
|
28
|
+
| 'warning'
|
29
|
+
| 'error';
|
30
|
+
size?: 'xs' | 'sm' | 'md' | 'lg';
|
31
|
+
outline?: boolean;
|
32
|
+
class?: string;
|
33
|
+
}
|
34
|
+
|
35
|
+
const { variant, size, outline = false, class: className } = Astro.props;
|
36
|
+
|
37
|
+
function getVariantClass(variant: Props['variant']) {
|
38
|
+
if (variant === 'primary') return 'cosy:badge-primary';
|
39
|
+
if (variant === 'secondary') return 'cosy:badge-secondary';
|
40
|
+
if (variant === 'accent') return 'cosy:badge-accent';
|
41
|
+
if (variant === 'ghost') return 'cosy:badge-ghost';
|
42
|
+
if (variant === 'info') return 'cosy:badge-info';
|
43
|
+
if (variant === 'success') return 'cosy:badge-success';
|
44
|
+
if (variant === 'warning') return 'cosy:badge-warning';
|
45
|
+
if (variant === 'error') return 'cosy:badge-error';
|
46
|
+
return '';
|
47
|
+
}
|
48
|
+
|
49
|
+
function getSizeClass(size: Props['size']) {
|
50
|
+
if (size === 'xs') return 'cosy:badge-xs';
|
51
|
+
if (size === 'sm') return 'cosy:badge-sm';
|
52
|
+
if (size === 'md') return 'cosy:badge-md';
|
53
|
+
if (size === 'lg') return 'cosy:badge-lg';
|
54
|
+
return '';
|
55
|
+
}
|
56
|
+
---
|
57
|
+
|
58
|
+
<div
|
59
|
+
class:list={[
|
60
|
+
'cosy:badge',
|
61
|
+
getVariantClass(variant),
|
62
|
+
getSizeClass(size),
|
63
|
+
outline ? 'cosy:badge-outline' : '',
|
64
|
+
className,
|
65
|
+
]}>
|
66
|
+
<slot />
|
67
|
+
</div>
|
@@ -0,0 +1 @@
|
|
1
|
+
export { default as Badge } from './Badge.astro';
|
@@ -37,7 +37,7 @@
|
|
37
37
|
import CodeToolbar from './CodeToolbar.astro';
|
38
38
|
import Description from './Description.astro';
|
39
39
|
import Preview from './Preview.astro';
|
40
|
-
import CodePanel from '
|
40
|
+
import { CodePanel } from '../../index-astro';
|
41
41
|
import '../../style.ts';
|
42
42
|
|
43
43
|
interface Props {
|
@@ -92,7 +92,9 @@ const {
|
|
92
92
|
</div>
|
93
93
|
</Preview>
|
94
94
|
|
95
|
-
<
|
95
|
+
<div class="cosy:hidden cosy:code-example-panel" data-panel="code">
|
96
|
+
<CodePanel code={code} />
|
97
|
+
</div>
|
96
98
|
</div>
|
97
99
|
</div>
|
98
100
|
))
|
@@ -19,7 +19,7 @@
|
|
19
19
|
* <TeamMember
|
20
20
|
* name="张三"
|
21
21
|
* role="前端开发工程师"
|
22
|
-
* avatar="/images/avatars/
|
22
|
+
* avatar="/images/avatars/jack.jpg"
|
23
23
|
* bio="拥有5年前端开发经验,专注于React和Vue生态系统。"
|
24
24
|
* />
|
25
25
|
* ```
|
@@ -29,7 +29,7 @@
|
|
29
29
|
* <TeamMember
|
30
30
|
* name="李四"
|
31
31
|
* role="UI设计师"
|
32
|
-
* avatar="/images/avatars/
|
32
|
+
* avatar="/images/avatars/mary.jpg"
|
33
33
|
* bio="专注于用户体验和界面设计,热爱创造直观易用的产品。"
|
34
34
|
* socialLinks={[
|
35
35
|
* { platform: 'github', url: 'https://github.com/lisi' },
|
@@ -80,6 +80,16 @@ export interface Props {
|
|
80
80
|
* 自定义类名
|
81
81
|
*/
|
82
82
|
class?: string;
|
83
|
+
/**
|
84
|
+
* 卡片阴影强度
|
85
|
+
* @default 'md'
|
86
|
+
*/
|
87
|
+
shadow?: 'sm' | 'md' | 'lg' | 'xl';
|
88
|
+
/**
|
89
|
+
* 悬停时卡片阴影强度
|
90
|
+
* @default 'lg'
|
91
|
+
*/
|
92
|
+
hoverShadow?: 'sm' | 'md' | 'lg' | 'xl';
|
83
93
|
}
|
84
94
|
|
85
95
|
const {
|
@@ -89,13 +99,35 @@ const {
|
|
89
99
|
bio,
|
90
100
|
socialLinks,
|
91
101
|
class: className = '',
|
102
|
+
shadow = 'md',
|
103
|
+
hoverShadow = 'lg',
|
92
104
|
} = Astro.props;
|
105
|
+
|
106
|
+
const shadowMap = {
|
107
|
+
sm: 'cosy:shadow-sm',
|
108
|
+
md: 'cosy:shadow-md',
|
109
|
+
lg: 'cosy:shadow-lg',
|
110
|
+
xl: 'cosy:shadow-xl',
|
111
|
+
};
|
112
|
+
const hoverShadowMap = {
|
113
|
+
sm: 'cosy:hover:shadow-sm',
|
114
|
+
md: 'cosy:hover:shadow-md',
|
115
|
+
lg: 'cosy:hover:shadow-lg',
|
116
|
+
xl: 'cosy:hover:shadow-xl',
|
117
|
+
};
|
118
|
+
|
119
|
+
const shadowClass = shadowMap[shadow] || shadowMap['md'];
|
120
|
+
const hoverShadowClass = hoverShadowMap[hoverShadow] || hoverShadowMap['lg'];
|
93
121
|
---
|
94
122
|
|
95
123
|
<div
|
96
124
|
class:list={[
|
97
|
-
|
98
|
-
|
125
|
+
[
|
126
|
+
'cosy:card cosy:bg-base-100 cosy:transition-shadow cosy:duration-300',
|
127
|
+
shadowClass,
|
128
|
+
hoverShadowClass,
|
129
|
+
className,
|
130
|
+
],
|
99
131
|
]}>
|
100
132
|
<figure class="cosy:flex cosy:justify-center cosy:p-4">
|
101
133
|
<Image
|
@@ -1,30 +1,7 @@
|
|
1
1
|
import TeamMember from './TeamMember.astro';
|
2
|
-
import
|
3
|
-
import TeamMemberWithSocial from './TeamMemberWithSocial.astro';
|
4
|
-
import TeamMemberCustomStyle from './TeamMemberCustomStyle.astro';
|
5
|
-
import TeamMemberGroup from './TeamMemberGroup.astro';
|
6
|
-
import TeamMemebrs from './TeamMembers.astro';
|
7
|
-
|
8
|
-
import BasicSourceCode from './TeamMemberBasic.astro?raw';
|
9
|
-
import WithSocialSourceCode from './TeamMemberWithSocial.astro?raw';
|
10
|
-
import CustomStyleSourceCode from './TeamMemberCustomStyle.astro?raw';
|
11
|
-
import GroupSourceCode from './TeamMemberGroup.astro?raw';
|
12
|
-
|
13
|
-
import { extractSimpleExample } from '../../src/utils/component';
|
2
|
+
import TeamMembers from './TeamMembers.astro';
|
14
3
|
|
15
4
|
export {
|
16
|
-
|
17
|
-
|
18
|
-
TeamMemberWithSocial,
|
19
|
-
TeamMemberCustomStyle,
|
20
|
-
TeamMemberGroup,
|
21
|
-
TeamMemebrs,
|
22
|
-
};
|
23
|
-
|
24
|
-
// Export example codes
|
25
|
-
export const TeamMemberExampleCodes = {
|
26
|
-
Basic: extractSimpleExample(BasicSourceCode, 'TeamMember'),
|
27
|
-
WithSocial: extractSimpleExample(WithSocialSourceCode, 'TeamMember'),
|
28
|
-
CustomStyle: extractSimpleExample(CustomStyleSourceCode, 'TeamMember'),
|
29
|
-
Group: extractSimpleExample(GroupSourceCode, 'TeamMember'),
|
5
|
+
TeamMember,
|
6
|
+
TeamMembers,
|
30
7
|
};
|
@@ -0,0 +1,66 @@
|
|
1
|
+
<!--
|
2
|
+
@component Badge
|
3
|
+
@description 一个用于高亮信息的小组件。
|
4
|
+
@usage
|
5
|
+
```vue
|
6
|
+
<Badge>默认</Badge>
|
7
|
+
<Badge variant="primary">Primary</Badge>
|
8
|
+
```
|
9
|
+
@props
|
10
|
+
@prop {('primary'|'secondary'|'accent'|'ghost'|'info'|'success'|'warning'|'error')} [variant] - 徽章的颜色变体。
|
11
|
+
@prop {('xs'|'sm'|'md'|'lg')} [size] - 徽章的尺寸。
|
12
|
+
@prop {boolean} [outline=false] - 徽章是否为描边样式。
|
13
|
+
@prop {string} [class] - 自定义 CSS 类。
|
14
|
+
@slots
|
15
|
+
@slot default - 徽章内容。
|
16
|
+
-->
|
17
|
+
<script setup lang="ts">
|
18
|
+
import '../../style';
|
19
|
+
import { computed } from 'vue';
|
20
|
+
|
21
|
+
const props = defineProps({
|
22
|
+
variant: {
|
23
|
+
type: String,
|
24
|
+
default: undefined,
|
25
|
+
validator: (v: string) => ['primary', 'secondary', 'accent', 'ghost', 'info', 'success', 'warning', 'error'].includes(v)
|
26
|
+
},
|
27
|
+
size: {
|
28
|
+
type: String,
|
29
|
+
default: undefined,
|
30
|
+
validator: (v: string) => ['xs', 'sm', 'md', 'lg'].includes(v)
|
31
|
+
},
|
32
|
+
outline: Boolean,
|
33
|
+
class: String
|
34
|
+
});
|
35
|
+
|
36
|
+
const variantClass = computed(() => {
|
37
|
+
if (props.variant === 'primary') return 'cosy:badge-primary';
|
38
|
+
if (props.variant === 'secondary') return 'cosy:badge-secondary';
|
39
|
+
if (props.variant === 'accent') return 'cosy:badge-accent';
|
40
|
+
if (props.variant === 'ghost') return 'cosy:badge-ghost';
|
41
|
+
if (props.variant === 'info') return 'cosy:badge-info';
|
42
|
+
if (props.variant === 'success') return 'cosy:badge-success';
|
43
|
+
if (props.variant === 'warning') return 'cosy:badge-warning';
|
44
|
+
if (props.variant === 'error') return 'cosy:badge-error';
|
45
|
+
return '';
|
46
|
+
});
|
47
|
+
const sizeClass = computed(() => {
|
48
|
+
if (props.size === 'xs') return 'cosy:badge-xs';
|
49
|
+
if (props.size === 'sm') return 'cosy:badge-sm';
|
50
|
+
if (props.size === 'md') return 'cosy:badge-md';
|
51
|
+
if (props.size === 'lg') return 'cosy:badge-lg';
|
52
|
+
return '';
|
53
|
+
});
|
54
|
+
</script>
|
55
|
+
|
56
|
+
<template>
|
57
|
+
<span :class="[
|
58
|
+
'cosy:badge',
|
59
|
+
variantClass,
|
60
|
+
sizeClass,
|
61
|
+
props.outline ? 'cosy:badge-outline' : '',
|
62
|
+
props.class
|
63
|
+
]">
|
64
|
+
<slot />
|
65
|
+
</span>
|
66
|
+
</template>
|
@@ -0,0 +1 @@
|
|
1
|
+
export { default as Badge } from './Badge.vue';
|
@@ -0,0 +1,98 @@
|
|
1
|
+
<!--
|
2
|
+
ButtonFolder 组件
|
3
|
+
|
4
|
+
一个专门用于文件夹操作的按钮组件,封装了 Button 组件,并添加了文件夹图标。
|
5
|
+
|
6
|
+
使用示例:
|
7
|
+
```vue
|
8
|
+
<ButtonFolder @click="openFolder" />
|
9
|
+
<ButtonFolder style="ghost" size="sm" @click="openFolder" tooltip="打开文件夹" />
|
10
|
+
```
|
11
|
+
|
12
|
+
属性:
|
13
|
+
- 继承 Button 组件的所有属性
|
14
|
+
- 默认使用 ghost 样式
|
15
|
+
|
16
|
+
事件:
|
17
|
+
- click: 点击按钮时触发(disabled状态下不触发)
|
18
|
+
-->
|
19
|
+
|
20
|
+
<script setup lang="ts">
|
21
|
+
import { computed } from 'vue';
|
22
|
+
import { Button } from '@coffic/cosy-ui/vue';
|
23
|
+
import { RiFolderOpenLine } from '@remixicon/vue';
|
24
|
+
|
25
|
+
interface Props {
|
26
|
+
// 按钮颜色
|
27
|
+
color?:
|
28
|
+
| 'neutral'
|
29
|
+
| 'primary'
|
30
|
+
| 'secondary'
|
31
|
+
| 'accent'
|
32
|
+
| 'info'
|
33
|
+
| 'success'
|
34
|
+
| 'warning'
|
35
|
+
| 'error';
|
36
|
+
// 按钮样式
|
37
|
+
style?: 'outline' | 'dash' | 'soft' | 'ghost' | 'link';
|
38
|
+
// 按钮大小
|
39
|
+
size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
40
|
+
// 按钮形状
|
41
|
+
shape?: 'square' | 'circle';
|
42
|
+
// 是否禁用
|
43
|
+
disabled?: boolean;
|
44
|
+
// 是否激活
|
45
|
+
active?: boolean;
|
46
|
+
// 提示文本
|
47
|
+
tooltip?: string;
|
48
|
+
}
|
49
|
+
|
50
|
+
const props = withDefaults(defineProps<Props>(), {
|
51
|
+
style: 'ghost',
|
52
|
+
size: 'md',
|
53
|
+
disabled: false,
|
54
|
+
active: false,
|
55
|
+
});
|
56
|
+
|
57
|
+
const emit = defineEmits<{
|
58
|
+
(e: 'click', event: MouseEvent): void;
|
59
|
+
}>();
|
60
|
+
|
61
|
+
// 处理点击事件
|
62
|
+
const handleClick = (event: MouseEvent) => {
|
63
|
+
// 如果按钮已经处于禁用状态,不触发事件
|
64
|
+
if (props.disabled) return;
|
65
|
+
|
66
|
+
// 触发点击事件
|
67
|
+
emit('click', event);
|
68
|
+
};
|
69
|
+
|
70
|
+
// 计算图标大小类名
|
71
|
+
const iconSizeClass = computed(() => {
|
72
|
+
if (props.size === 'xs' || props.size === 'sm') return 'cosy:w-4 cosy:h-4';
|
73
|
+
if (props.size === 'lg' || props.size === 'xl') return 'cosy:w-6 cosy:h-6';
|
74
|
+
return 'cosy:w-5 cosy:h-5'; // 默认中等大小
|
75
|
+
});
|
76
|
+
</script>
|
77
|
+
|
78
|
+
<template>
|
79
|
+
<div
|
80
|
+
class="cosy:relative cosy:inline-block"
|
81
|
+
:data-tip="tooltip"
|
82
|
+
:class="{ tooltip: tooltip }"
|
83
|
+
>
|
84
|
+
<Button
|
85
|
+
:color="color"
|
86
|
+
:style="style"
|
87
|
+
:shape="shape"
|
88
|
+
:disabled="disabled"
|
89
|
+
:active="active"
|
90
|
+
@click="handleClick"
|
91
|
+
>
|
92
|
+
<RiFolderOpenLine
|
93
|
+
:class="[iconSizeClass, 'cosy:transition-all cosy:duration-300']"
|
94
|
+
/>
|
95
|
+
<slot></slot>
|
96
|
+
</Button>
|
97
|
+
</div>
|
98
|
+
</template>
|
@@ -0,0 +1,163 @@
|
|
1
|
+
<!--
|
2
|
+
ButtonRefresh 组件
|
3
|
+
|
4
|
+
一个专门用于刷新操作的按钮组件,封装了 Button 组件,并添加了动画效果。
|
5
|
+
当处于加载状态时,图标会旋转,提供视觉反馈。
|
6
|
+
|
7
|
+
使用示例:
|
8
|
+
```vue
|
9
|
+
<ButtonRefresh @click="refreshData" :loading="isLoading" />
|
10
|
+
<ButtonRefresh style="ghost" size="sm" @click="refreshData" />
|
11
|
+
```
|
12
|
+
|
13
|
+
属性:
|
14
|
+
- 继承 Button 组件的所有属性
|
15
|
+
- 默认使用 ghost 样式
|
16
|
+
|
17
|
+
事件:
|
18
|
+
- click: 点击按钮时触发(disabled或loading状态下不触发)
|
19
|
+
-->
|
20
|
+
|
21
|
+
<script setup lang="ts">
|
22
|
+
import { computed, ref, watch } from 'vue';
|
23
|
+
import { Button } from '@coffic/cosy-ui/vue';
|
24
|
+
import { RiRefreshLine } from '@remixicon/vue';
|
25
|
+
|
26
|
+
interface Props {
|
27
|
+
// 按钮颜色
|
28
|
+
color?:
|
29
|
+
| 'neutral'
|
30
|
+
| 'primary'
|
31
|
+
| 'secondary'
|
32
|
+
| 'accent'
|
33
|
+
| 'info'
|
34
|
+
| 'success'
|
35
|
+
| 'warning'
|
36
|
+
| 'error';
|
37
|
+
// 按钮样式
|
38
|
+
style?: 'outline' | 'dash' | 'soft' | 'ghost' | 'link';
|
39
|
+
// 按钮大小
|
40
|
+
size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
41
|
+
// 按钮形状
|
42
|
+
shape?: 'square' | 'circle';
|
43
|
+
// 是否显示加载状态
|
44
|
+
loading?: boolean;
|
45
|
+
// 是否禁用
|
46
|
+
disabled?: boolean;
|
47
|
+
// 是否激活
|
48
|
+
active?: boolean;
|
49
|
+
// 提示文本
|
50
|
+
tooltip?: string;
|
51
|
+
// 最小动画时间(毫秒)
|
52
|
+
minAnimationTime?: number;
|
53
|
+
}
|
54
|
+
|
55
|
+
const props = withDefaults(defineProps<Props>(), {
|
56
|
+
style: 'ghost',
|
57
|
+
size: 'md',
|
58
|
+
loading: false,
|
59
|
+
disabled: false,
|
60
|
+
active: false,
|
61
|
+
minAnimationTime: 800, // 默认最小动画时间为 800 毫秒
|
62
|
+
});
|
63
|
+
|
64
|
+
const emit = defineEmits<{
|
65
|
+
(e: 'click', event: MouseEvent): void;
|
66
|
+
}>();
|
67
|
+
|
68
|
+
// 处理点击事件
|
69
|
+
const handleClick = async (event: MouseEvent) => {
|
70
|
+
// 如果按钮已经处于加载或禁用状态,不触发事件
|
71
|
+
if (props.disabled || internalLoading.value) return;
|
72
|
+
|
73
|
+
// 触发点击事件
|
74
|
+
emit('click', event);
|
75
|
+
};
|
76
|
+
|
77
|
+
// 内部加载状态
|
78
|
+
const internalLoading = ref(false);
|
79
|
+
|
80
|
+
// 记录动画开始时间
|
81
|
+
const animationStartTime = ref(0);
|
82
|
+
|
83
|
+
// 监听外部加载状态
|
84
|
+
watch(
|
85
|
+
() => props.loading,
|
86
|
+
(newVal, oldVal) => {
|
87
|
+
if (newVal === true) {
|
88
|
+
// 开始加载,记录当前时间
|
89
|
+
animationStartTime.value = Date.now();
|
90
|
+
internalLoading.value = true;
|
91
|
+
} else if (oldVal === true && newVal === false) {
|
92
|
+
// 加载结束,计算已经经过的时间
|
93
|
+
const elapsedTime = Date.now() - animationStartTime.value;
|
94
|
+
const remainingTime = Math.max(0, props.minAnimationTime - elapsedTime);
|
95
|
+
|
96
|
+
if (remainingTime > 0) {
|
97
|
+
// 如果还没有达到最小动画时间,延迟关闭加载状态
|
98
|
+
setTimeout(() => {
|
99
|
+
internalLoading.value = false;
|
100
|
+
}, remainingTime);
|
101
|
+
} else {
|
102
|
+
// 已经超过最小动画时间,直接关闭
|
103
|
+
internalLoading.value = false;
|
104
|
+
}
|
105
|
+
}
|
106
|
+
},
|
107
|
+
{ immediate: true }
|
108
|
+
);
|
109
|
+
|
110
|
+
// 计算内容类名
|
111
|
+
const contentClass = computed(() => {
|
112
|
+
return {
|
113
|
+
hidden: internalLoading.value,
|
114
|
+
};
|
115
|
+
});
|
116
|
+
|
117
|
+
// 计算图标大小类名
|
118
|
+
const iconSizeClass = computed(() => {
|
119
|
+
if (props.size === 'xs' || props.size === 'sm') return 'cosy:w-4 cosy:h-4';
|
120
|
+
if (props.size === 'lg' || props.size === 'xl') return 'cosy:w-6 cosy:h-6';
|
121
|
+
return 'cosy:w-5 cosy:h-5'; // 默认中等大小
|
122
|
+
});
|
123
|
+
|
124
|
+
// 计算加载器类名
|
125
|
+
const loadingClass = computed(() => {
|
126
|
+
return [
|
127
|
+
'loading',
|
128
|
+
'loading-spinner',
|
129
|
+
props.size === 'xs' || props.size === 'sm'
|
130
|
+
? 'loading-xs'
|
131
|
+
: props.size === 'lg' || props.size === 'xl'
|
132
|
+
? 'loading-lg'
|
133
|
+
: 'loading-md',
|
134
|
+
iconSizeClass.value, // 使用相同的大小类
|
135
|
+
{ hidden: !internalLoading.value },
|
136
|
+
];
|
137
|
+
});
|
138
|
+
</script>
|
139
|
+
|
140
|
+
<template>
|
141
|
+
<div
|
142
|
+
class="cosy:relative cosy:inline-block"
|
143
|
+
:data-tip="tooltip"
|
144
|
+
:class="{ tooltip: tooltip }"
|
145
|
+
>
|
146
|
+
<Button
|
147
|
+
:color="color"
|
148
|
+
:style="style"
|
149
|
+
:shape="shape"
|
150
|
+
:disabled="disabled || internalLoading"
|
151
|
+
:active="active"
|
152
|
+
@click="handleClick"
|
153
|
+
>
|
154
|
+
<span :class="loadingClass"></span>
|
155
|
+
<span :class="contentClass">
|
156
|
+
<RiRefreshLine
|
157
|
+
:class="[iconSizeClass, 'cosy:transition-all cosy:duration-300']"
|
158
|
+
/>
|
159
|
+
<slot></slot>
|
160
|
+
</span>
|
161
|
+
</Button>
|
162
|
+
</div>
|
163
|
+
</template>
|
@@ -0,0 +1,52 @@
|
|
1
|
+
<script setup lang="ts">
|
2
|
+
import { computed } from 'vue';
|
3
|
+
|
4
|
+
defineOptions({
|
5
|
+
name: 'Card',
|
6
|
+
});
|
7
|
+
|
8
|
+
const props = defineProps({
|
9
|
+
title: { type: String, required: true },
|
10
|
+
subtitle: String,
|
11
|
+
imageUrl: String,
|
12
|
+
href: String,
|
13
|
+
compact: Boolean,
|
14
|
+
class: String,
|
15
|
+
});
|
16
|
+
|
17
|
+
const cardClasses = computed(() => [
|
18
|
+
'cosy:card',
|
19
|
+
'cosy:w-full',
|
20
|
+
'cosy:bg-base-100',
|
21
|
+
'cosy:shadow-xl',
|
22
|
+
'cosy:hover:shadow-2xl',
|
23
|
+
'cosy:transition-all',
|
24
|
+
'cosy:duration-300',
|
25
|
+
'cosy:ease-in-out',
|
26
|
+
props.compact ? 'cosy:card-compact' : '',
|
27
|
+
props.href ? 'cosy:cursor-pointer cosy:hover:scale-105 cosy:transform cosy:no-underline' : '',
|
28
|
+
props.class,
|
29
|
+
].filter(Boolean).join(' '));
|
30
|
+
|
31
|
+
const contentPadding = computed(() => props.compact ? 'cosy:p-4' : 'cosy:p-6');
|
32
|
+
</script>
|
33
|
+
|
34
|
+
<template>
|
35
|
+
<component :is="props.href ? 'a' : 'article'" :href="props.href" :class="cardClasses">
|
36
|
+
<template v-if="props.imageUrl">
|
37
|
+
<figure class="not-prose cosy:m-0 cosy:p-0">
|
38
|
+
<img :src="props.imageUrl" :alt="props.title"
|
39
|
+
class="cosy:w-full cosy:h-48 cosy:object-cover cosy:rounded-t-lg" />
|
40
|
+
</figure>
|
41
|
+
</template>
|
42
|
+
<div :class="['cosy:card-body', contentPadding]">
|
43
|
+
<h2 class="cosy:card-title cosy:text-xl cosy:font-bold">{{ props.title }}</h2>
|
44
|
+
<p v-if="props.subtitle" class="cosy:text-base-content/70 cosy:text-sm cosy:leading-relaxed">{{
|
45
|
+
props.subtitle }}
|
46
|
+
</p>
|
47
|
+
<div v-if="$slots.default" class="cosy:mt-4">
|
48
|
+
<slot />
|
49
|
+
</div>
|
50
|
+
</div>
|
51
|
+
</component>
|
52
|
+
</template>
|
@@ -0,0 +1,34 @@
|
|
1
|
+
<script setup lang="ts">
|
2
|
+
defineOptions({
|
3
|
+
name: 'CardCourse',
|
4
|
+
});
|
5
|
+
|
6
|
+
const props = defineProps({
|
7
|
+
title: { type: String, required: true },
|
8
|
+
link: { type: String, required: true },
|
9
|
+
});
|
10
|
+
</script>
|
11
|
+
|
12
|
+
<template>
|
13
|
+
<div>
|
14
|
+
<a :href="props.link" class="cosy:justify-center cosy:flex cosy:no-underline cosy:text-base-content">
|
15
|
+
<div class="cosy:w-56 cosy:h-80">
|
16
|
+
<div
|
17
|
+
class="cosy:bg-gradient-to-br cosy:w-full cosy:h-full cosy:from-accent/50 cosy:to-primary/30 cosy:rounded-3xl cosy:shadow-lg cosy:backdrop-blur-sm">
|
18
|
+
<div
|
19
|
+
class="cosy:bg-base-100/60 cosy:w-full cosy:h-full cosy:rounded-3xl cosy:border cosy:border-base-content/30 hover:cosy:scale-105 hover:cosy:shadow-2xl cosy:transform cosy:duration-200 cosy:backdrop-filter cosy:backdrop-blur-sm">
|
20
|
+
<div class="card-body cosy:p-1 cosy:h-full">
|
21
|
+
<div
|
22
|
+
class="cosy:h-3/5 cosy:w-full cosy:flex cosy:items-center cosy:justify-center cosy:text-6xl cosy:text-primary">
|
23
|
+
📚
|
24
|
+
</div>
|
25
|
+
<div>
|
26
|
+
<h2 class="cosy:text-lg cosy:text-center cosy:font-medium">{{ props.title }}</h2>
|
27
|
+
</div>
|
28
|
+
</div>
|
29
|
+
</div>
|
30
|
+
</div>
|
31
|
+
</div>
|
32
|
+
</a>
|
33
|
+
</div>
|
34
|
+
</template>
|