1
1
<template >
2
2
<div class =" plugin-card-container" >
3
- <div v-for =" (item, index) in items" :key =" index" class =" plugin-card" >
3
+ <div
4
+ v-for =" (item, index) in items"
5
+ :key =" index"
6
+ class =" plugin-card"
7
+ :class =" { 'clickable': item.link }"
8
+ @click =" handleCardClick(item)"
9
+ >
4
10
<div class =" card-image" >
5
11
<img
6
12
v-if =" item.image"
7
13
:src =" item.image"
8
14
:alt =" item.title"
9
15
class =" image-content"
10
- >
16
+ / >
11
17
<div v-else class =" image-placeholder" >
12
- <VPIconify
13
- :name =" item.icon"
14
- size =" 3em"
15
- color =" var(--vp-c-brand)"
16
- />
18
+ <Icon :name =" item.icon" size =" 3em" color =" var(--vp-c-brand)" />
17
19
</div >
18
20
</div >
19
21
<div class =" card-content" >
20
- <div class =" card-header" >
21
- <div class =" card-title-group" >
22
- <h3 class =" card-title" >{{ item.title }}</h3 >
22
+ <h3 class =" card-title" >{{ item.title }}</h3 >
23
+ <p class =" card-description" >{{ item.description }}</p >
24
+ <div class =" card-tags" >
25
+ <span
26
+ v-for =" (tag, tagIndex) in item.tags"
27
+ :key =" tagIndex"
28
+ class =" badge"
29
+ :style =" getTagColors(tag)"
30
+ >{{ tag }}</span >
31
+ </div >
32
+ <div class =" card-footer" >
33
+ <div class =" item-developer-info" >
34
+ <img
35
+ :src =" getGithubAvatarUrl(item.githubUser)"
36
+ alt =" Developer Avatar"
37
+ class =" developer-avatar"
38
+ />
39
+ <span class =" developer-name" >
40
+ {{ item.githubUser }}
41
+ </span >
23
42
</div >
24
-
43
+ < div v-if = " !item.link " class = " built-in-label " >内置</ div >
25
44
<a
26
- v-if = " item.link "
45
+ v-else
27
46
:href =" item.link"
28
47
class =" github-link no-external-icon"
29
48
target =" _blank"
30
49
rel =" noopener noreferrer"
31
50
aria-label =" GitHub仓库"
51
+ @click.stop
32
52
>
33
- <VPIconify name =" mdi:github" size =" 2em " color =" var(--vp-c-text-2)" />
53
+ <Icon name =" mdi:github" size =" 1.5em " color =" var(--vp-c-text-2)" />
34
54
</a >
35
- <div v-else class =" built-in-label" >
36
- 内置
37
- </div >
38
- </div >
39
- <p class =" card-description" >{{ item.description }}</p >
40
- <div class =" card-tags" >
41
- <Badge
42
- v-for =" (tag, tagIndex) in item.tags"
43
- :key =" tagIndex"
44
- :text =" tag"
45
- v-bind =" getTagColors(tag)"
46
- />
47
55
</div >
48
56
</div >
49
57
</div >
50
58
</div >
51
59
</template >
52
60
53
61
<script setup lang="ts">
54
- import VPIconify from ' vuepress-theme-plume/components/VPIconify.vue'
55
-
56
62
57
63
export interface PluginItem {
58
64
icon: string
@@ -61,44 +67,58 @@ export interface PluginItem {
61
67
tags: string []
62
68
link? : string
63
69
image? : string
70
+ githubUser: string
64
71
}
65
72
66
- const props = withDefaults (defineProps <{
67
- items: PluginItem []
68
- columns? : number
69
- }>(), {
70
- columns: 3
71
- })
73
+ const props = withDefaults (
74
+ defineProps <{
75
+ items: PluginItem []
76
+ columns? : number
77
+ }>(),
78
+ {
79
+ columns: 3
80
+ }
81
+ )
72
82
73
83
interface TagColors {
74
- color? : string ;
75
- bgColor ? : string ;
76
- borderColor? : string ;
84
+ color? : string
85
+ backgroundColor ? : string
86
+ borderColor? : string
77
87
}
78
88
79
89
const getTagColors = (tag : string ): TagColors => {
80
90
const colors: Record <string , TagColors > = {
81
- ' MySQL' : { color: ' #006484' , bgColor : ' rgba(0, 100, 132, 0.1)' , borderColor: ' rgba(0, 100, 132, 0.2)' },
82
- ' PostgreSQL' : { color: ' #336699' , bgColor : ' rgba(51, 102, 153, 0.1)' , borderColor: ' rgba(51, 102, 153, 0.2)' },
83
- ' fba' : { color: ' #8b5cf6' , bgColor : ' rgba(139, 92, 246, 0.1)' , borderColor: ' rgba(139, 92, 246, 0.2)' },
84
- ' fba_ui' : { color: ' #a855f7' , bgColor : ' rgba(168, 85, 247, 0.1)' , borderColor: ' rgba(168, 85, 247, 0.2)' },
85
- ' app' : { color: ' #f97316' , bgColor : ' rgba(249, 115, 22, 0.1)' , borderColor: ' rgba(249, 115, 22, 0.2)' },
86
- ' extra' : { color: ' #64748b' , bgColor : ' rgba(100, 116, 139, 0.1)' , borderColor: ' rgba(100, 116, 139, 0.2)' },
87
- ' pay' : { color: ' #ef4444' , bgColor : ' rgba(239, 68, 68, 0.1)' , borderColor: ' rgba(239, 68, 68, 0.2)' },
88
- ' free' : { color: ' #10b981' , bgColor : ' rgba(16, 185, 129, 0.1)' , borderColor: ' rgba(16, 185, 129, 0.2)' }
91
+ ' MySQL' : { color: ' #006484' , backgroundColor : ' rgba(0, 100, 132, 0.1)' , borderColor: ' rgba(0, 100, 132, 0.2)' },
92
+ ' PostgreSQL' : { color: ' #336699' , backgroundColor : ' rgba(51, 102, 153, 0.1)' , borderColor: ' rgba(51, 102, 153, 0.2)' },
93
+ ' fba' : { color: ' #8b5cf6' , backgroundColor : ' rgba(139, 92, 246, 0.1)' , borderColor: ' rgba(139, 92, 246, 0.2)' },
94
+ ' fba_ui' : { color: ' #a855f7' , backgroundColor : ' rgba(168, 85, 247, 0.1)' , borderColor: ' rgba(168, 85, 247, 0.2)' },
95
+ ' app' : { color: ' #f97316' , backgroundColor : ' rgba(249, 115, 22, 0.1)' , borderColor: ' rgba(249, 115, 22, 0.2)' },
96
+ ' extra' : { color: ' #64748b' , backgroundColor : ' rgba(100, 116, 139, 0.1)' , borderColor: ' rgba(100, 116, 139, 0.2)' },
97
+ ' pay' : { color: ' #ef4444' , backgroundColor : ' rgba(239, 68, 68, 0.1)' , borderColor: ' rgba(239, 68, 68, 0.2)' },
98
+ ' free' : { color: ' #10b981' , backgroundColor : ' rgba(16, 185, 129, 0.1)' , borderColor: ' rgba(16, 185, 129, 0.2)' }
89
99
};
90
100
return colors [tag ] || {
91
101
color: ' var(--vp-c-text-2)' ,
92
- bgColor : ' var(--vp-c-bg-soft)' ,
102
+ backgroundColor : ' var(--vp-c-bg-soft)' ,
93
103
borderColor: ' var(--vp-c-divider-light)'
94
104
};
95
105
}
106
+
107
+ const getGithubAvatarUrl = (username : string ) => {
108
+ return ` https://github.com/${username }.png?size=32 ` ;
109
+ };
110
+
111
+ const handleCardClick = (item : PluginItem ) => {
112
+ if (item .link ) {
113
+ window .open (item .link , ' _blank' );
114
+ }
115
+ };
96
116
</script >
97
117
98
118
<style scoped>
99
119
.plugin-card-container {
100
120
display : grid ;
101
- gap : 1.5 rem ;
121
+ gap : 1 rem ;
102
122
grid-template-columns : repeat (1 , 1fr );
103
123
}
104
124
@@ -107,19 +127,23 @@ const getTagColors = (tag: string): TagColors => {
107
127
flex-direction : column ;
108
128
background-color : var (--vp-c-bg );
109
129
border-radius : 8px ;
110
- box-shadow : 0 4 px 8 px rgba (0 , 0 , 0 , 0.05 );
130
+ box-shadow : 0 2 px 4 px rgba (0 , 0 , 0 , 0.05 );
111
131
overflow : hidden ;
112
132
transition : all 0.3s cubic-bezier (0.25 , 0.8 , 0.25 , 1 );
113
133
height : 100% ;
114
134
border : 1px solid var (--vp-c-divider );
115
135
}
116
136
117
- .plugin-card :hover {
118
- transform : translateY (-4 px );
119
- box-shadow : 0 8 px 24 px rgba (0 , 0 , 0 , 0.1 );
137
+ .plugin-card.clickable :hover {
138
+ transform : translateY (-2 px );
139
+ box-shadow : 0 6 px 16 px rgba (0 , 0 , 0 , 0.08 );
120
140
border-color : var (--vp-c-brand );
121
141
}
122
142
143
+ .plugin-card.clickable {
144
+ cursor : pointer ;
145
+ }
146
+
123
147
.card-image {
124
148
width : 100% ;
125
149
height : 160px ;
@@ -129,14 +153,20 @@ const getTagColors = (tag: string): TagColors => {
129
153
display : flex ;
130
154
align-items : center ;
131
155
justify-content : center ;
156
+ border-bottom : 1px solid var (--vp-c-divider );
132
157
}
133
158
134
159
.image-content {
160
+ display : block ;
161
+ max-width : 100% ;
162
+ max-height : 100% ;
163
+ object-fit : contain ;
135
164
transition : transform 0.5s ease ;
165
+ pointer-events : none ;
136
166
}
137
167
138
- .plugin-card :hover .image-content {
139
- transform : scale (1.05 );
168
+ .plugin-card.clickable :hover .image-content {
169
+ transform : scale (1.02 );
140
170
}
141
171
142
172
.image-placeholder {
@@ -149,109 +179,121 @@ const getTagColors = (tag: string): TagColors => {
149
179
}
150
180
151
181
.card-content {
152
- padding : 1.25 rem ;
182
+ padding : 1 rem ;
153
183
flex-grow : 1 ;
154
184
display : flex ;
155
185
flex-direction : column ;
186
+ justify-content : space-between ;
156
187
}
157
188
158
- .card-header {
189
+ .card-title {
190
+ font-size : 1rem ;
191
+ font-weight : 600 ;
192
+ color : var (--vp-c-text-1 );
193
+ margin : 0 0 0.4rem 0 ;
194
+ line-height : 1.4 ;
195
+ }
196
+
197
+ .card-description {
198
+ color : var (--vp-c-text-2 );
199
+ font-size : 0.85rem ;
200
+ line-height : 1.5 ;
201
+ margin : 0 0 0.8rem 0 ;
202
+ flex-grow : 1 ;
203
+ }
204
+
205
+ .card-tags {
206
+ display : flex ;
207
+ flex-wrap : wrap ;
208
+ align-items : center ;
209
+ gap : 0.4rem ;
210
+ margin-bottom : 0.8rem ;
211
+ }
212
+
213
+ .badge {
214
+ display : inline-flex ;
215
+ align-items : center ;
216
+ justify-content : center ;
217
+ padding : 0.1rem 0.5rem ;
218
+ border-radius : 4px ;
219
+ font-size : 0.75rem ;
220
+ line-height : 1 ;
221
+ font-weight : 500 ;
222
+ white-space : nowrap ;
223
+ border : 1px solid transparent ;
224
+ }
225
+
226
+ .card-footer {
159
227
display : flex ;
160
228
justify-content : space-between ;
161
229
align-items : center ;
162
- margin-bottom : 0.75rem ;
163
230
gap : 0.5rem ;
164
231
}
165
232
166
- .card-title-group {
233
+ .item-developer-info {
167
234
display : flex ;
168
235
align-items : center ;
169
- gap : 0.5 rem ;
236
+ gap : 0.4 rem ;
170
237
flex-grow : 1 ;
171
238
min-width : 0 ;
172
239
}
173
240
174
- .card-title {
175
- font-size : 1.05rem ;
176
- font-weight : 600 ;
177
- color : var (--vp-c-text-1 );
178
- margin : 0 ;
179
- line-height : 1.4 ;
241
+ .developer-avatar {
242
+ width : 20px ;
243
+ height : 20px ;
244
+ border-radius : 50% ;
245
+ object-fit : cover ;
246
+ cursor : default ;
247
+ pointer-events : none ;
248
+ }
249
+
250
+ .developer-name {
251
+ font-size : 0.85rem ;
252
+ color : var (--vp-c-text-2 );
180
253
overflow : hidden ;
181
254
text-overflow : ellipsis ;
182
255
white-space : nowrap ;
183
256
}
184
257
185
258
.github-link {
186
259
transition : all 0.2s ease ;
187
- display : flex ;
188
- align-items : center ;
189
- justify-content : center ;
190
- padding : 0.1rem ;
191
- border-radius : 6px ;
192
- flex-shrink : 0 ;
193
- }
194
-
195
- .github-link :hover {
196
- background-color : var (--vp-c-bg-soft );
197
- }
198
-
199
- .github-link :hover :deep(.iconify-svg ) {
200
- color : var (--vp-c-brand );
201
- transform : scale (1.1 );
202
- }
203
-
204
- .no-external-icon {
205
- display : inline-flex ;
206
- align-items : center ;
260
+ pointer-events : none ;
207
261
}
208
262
209
263
.no-external-icon ::after {
210
264
content : none !important ;
211
265
}
212
266
213
267
.built-in-label {
214
- font-size : 0.875rem ;
215
- color : var (--vp-c-text-3 );
216
- background-color : var (--vp-c-bg-soft );
217
- padding : 0.25rem 0.5rem ;
218
- border-radius : 4px ;
268
+ font-size : 0.85rem ;
269
+ color : var (--vp-c-text-2 );
270
+ padding : 0 ;
271
+ border-radius : 0 ;
219
272
flex-shrink : 0 ;
220
273
margin-left : auto ;
221
- }
222
-
223
- .card-description {
224
- color : var (--vp-c-text-2 );
225
- font-size : 0.875rem ;
226
- line-height : 1.5 ;
227
- margin : 0.5rem 0 1rem ;
228
- flex-grow : 1 ;
229
- }
230
-
231
- .card-tags {
232
- display : flex ;
233
- flex-wrap : wrap ;
234
- gap : 0.5rem ;
235
- margin-top : auto ;
274
+ white-space : nowrap ;
275
+ font-weight : 500 ;
236
276
}
237
277
238
278
@media (min-width : 768px ) {
239
279
.plugin-card-container {
240
280
grid-template-columns : repeat (2 , 1fr );
281
+ gap : 1.2rem ;
241
282
}
242
283
243
284
.card-image {
244
- height : 180 px ;
285
+ height : 160 px ;
245
286
}
246
287
}
247
288
248
289
@media (min-width : 960px ) {
249
290
.plugin-card-container {
250
291
grid-template-columns : repeat (v-bind( ' props.columns' ), 1fr );
292
+ gap : 1.5rem ;
251
293
}
252
294
253
295
.card-image {
254
- height : 200 px ;
296
+ height : 180 px ;
255
297
}
256
298
}
257
299
</style >
0 commit comments