11<template >
22 <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+ >
410 <div class =" card-image" >
511 <img
612 v-if =" item.image"
713 :src =" item.image"
814 :alt =" item.title"
915 class =" image-content"
10- >
16+ / >
1117 <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)" />
1719 </div >
1820 </div >
1921 <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 >
2342 </div >
24-
43+ < div v-if = " !item.link " class = " built-in-label " >内置</ div >
2544 <a
26- v-if = " item.link "
45+ v-else
2746 :href =" item.link"
2847 class =" github-link no-external-icon"
2948 target =" _blank"
3049 rel =" noopener noreferrer"
3150 aria-label =" GitHub仓库"
51+ @click.stop
3252 >
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)" />
3454 </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- />
4755 </div >
4856 </div >
4957 </div >
5058 </div >
5159</template >
5260
5361<script setup lang="ts">
54- import VPIconify from ' vuepress-theme-plume/components/VPIconify.vue'
55-
5662
5763export interface PluginItem {
5864 icon: string
@@ -61,44 +67,58 @@ export interface PluginItem {
6167 tags: string []
6268 link? : string
6369 image? : string
70+ githubUser: string
6471}
6572
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+ )
7282
7383interface TagColors {
74- color? : string ;
75- bgColor ? : string ;
76- borderColor? : string ;
84+ color? : string
85+ backgroundColor ? : string
86+ borderColor? : string
7787}
7888
7989const getTagColors = (tag : string ): TagColors => {
8090 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)' }
8999 };
90100 return colors [tag ] || {
91101 color: ' var(--vp-c-text-2)' ,
92- bgColor : ' var(--vp-c-bg-soft)' ,
102+ backgroundColor : ' var(--vp-c-bg-soft)' ,
93103 borderColor: ' var(--vp-c-divider-light)'
94104 };
95105}
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+ };
96116 </script >
97117
98118<style scoped>
99119.plugin-card-container {
100120 display : grid ;
101- gap : 1.5 rem ;
121+ gap : 1 rem ;
102122 grid-template-columns : repeat (1 , 1fr );
103123}
104124
@@ -107,19 +127,23 @@ const getTagColors = (tag: string): TagColors => {
107127 flex-direction : column ;
108128 background-color : var (--vp-c-bg );
109129 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 );
111131 overflow : hidden ;
112132 transition : all 0.3s cubic-bezier (0.25 , 0.8 , 0.25 , 1 );
113133 height : 100% ;
114134 border : 1px solid var (--vp-c-divider );
115135}
116136
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 );
120140 border-color : var (--vp-c-brand );
121141}
122142
143+ .plugin-card.clickable {
144+ cursor : pointer ;
145+ }
146+
123147.card-image {
124148 width : 100% ;
125149 height : 160px ;
@@ -129,14 +153,20 @@ const getTagColors = (tag: string): TagColors => {
129153 display : flex ;
130154 align-items : center ;
131155 justify-content : center ;
156+ border-bottom : 1px solid var (--vp-c-divider );
132157}
133158
134159.image-content {
160+ display : block ;
161+ max-width : 100% ;
162+ max-height : 100% ;
163+ object-fit : contain ;
135164 transition : transform 0.5s ease ;
165+ pointer-events : none ;
136166}
137167
138- .plugin-card :hover .image-content {
139- transform : scale (1.05 );
168+ .plugin-card.clickable :hover .image-content {
169+ transform : scale (1.02 );
140170}
141171
142172.image-placeholder {
@@ -149,109 +179,121 @@ const getTagColors = (tag: string): TagColors => {
149179}
150180
151181.card-content {
152- padding : 1.25 rem ;
182+ padding : 1 rem ;
153183 flex-grow : 1 ;
154184 display : flex ;
155185 flex-direction : column ;
186+ justify-content : space-between ;
156187}
157188
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 {
159227 display : flex ;
160228 justify-content : space-between ;
161229 align-items : center ;
162- margin-bottom : 0.75rem ;
163230 gap : 0.5rem ;
164231}
165232
166- .card-title-group {
233+ .item-developer-info {
167234 display : flex ;
168235 align-items : center ;
169- gap : 0.5 rem ;
236+ gap : 0.4 rem ;
170237 flex-grow : 1 ;
171238 min-width : 0 ;
172239}
173240
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 );
180253 overflow : hidden ;
181254 text-overflow : ellipsis ;
182255 white-space : nowrap ;
183256}
184257
185258.github-link {
186259 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 ;
207261}
208262
209263.no-external-icon ::after {
210264 content : none !important ;
211265}
212266
213267.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 ;
219272 flex-shrink : 0 ;
220273 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 ;
236276}
237277
238278@media (min-width : 768px ) {
239279 .plugin-card-container {
240280 grid-template-columns : repeat (2 , 1fr );
281+ gap : 1.2rem ;
241282 }
242283
243284 .card-image {
244- height : 180 px ;
285+ height : 160 px ;
245286 }
246287}
247288
248289@media (min-width : 960px ) {
249290 .plugin-card-container {
250291 grid-template-columns : repeat (v-bind( ' props.columns' ), 1fr );
292+ gap : 1.5rem ;
251293 }
252294
253295 .card-image {
254- height : 200 px ;
296+ height : 180 px ;
255297 }
256298}
257299 </style >
0 commit comments