11<template >
2+ <h1 class =" market-title" >插件市场</h1 >
3+ <div class =" search-container" >
4+ <input
5+ v-model =" searchQuery"
6+ type =" text"
7+ placeholder =" 🔍 搜索插件..."
8+ class =" search-input"
9+ @input =" filterPlugins"
10+ />
11+ </div >
212 <div class =" plugin-card-container" >
313 <div
4- v-for =" (item, index) in items "
14+ v-for =" (item, index) in filteredItems "
515 :key =" index"
616 class =" plugin-card"
717 :class =" { 'clickable': item.link }"
2030 <div v-if =" item.priceLabel" class =" price-corner-tag" :class =" { 'paid': item.priceLabel === '付费' }" >
2131 <span class =" price-corner-text" >{{ item.priceLabel }}</span >
2232 </div >
23- <div class =" image-footer" >
24- <div class =" item-developer-info" >
25- <img
26- :src =" getGithubAvatarUrl(item.githubUser)"
27- alt =" Developer Avatar"
28- class =" developer-avatar"
29- />
30- <span class =" developer-name" >
31- {{ item.githubUser }}
32- </span >
33- </div >
34- <a
35- v-if =" item.link"
36- :href =" item.link"
37- class =" no-external-icon"
38- target =" _blank"
39- rel =" noopener noreferrer"
40- aria-label =" GitHub仓库"
41- @click.stop
42- >
43- <Icon name =" mdi:github" color =" var(--vp-c-text-2)" />
44- </a >
45- <span v-else class =" built-in-label-inline" >内置</span >
46- </div >
4733 </div >
4834 <div class =" card-content" >
49- <h3 class =" card-title" >{{ item.title }}</h3 >
35+ <div class =" card-title-row" >
36+ <h3 class =" card-title" >{{ item.title }}</h3 >
37+ <div class =" card-title-link" >
38+ <a
39+ v-if =" item.link"
40+ :href =" item.link"
41+ class =" no-external-icon"
42+ target =" _blank"
43+ rel =" noopener noreferrer"
44+ aria-label =" GitHub仓库"
45+ @click.stop
46+ >
47+ <Icon name =" mdi:github" color =" var(--vp-c-text-2)" />
48+ </a >
49+ <span v-else class =" built-in-label-inline" >内置</span >
50+ </div >
51+ </div >
5052 <p class =" card-description" >{{ item.description }}</p >
5153 <div class =" card-tags" >
5254 <Badge
6264</template >
6365
6466<script setup lang="ts">
67+ import { ref , computed } from ' vue'
68+
6569export interface PluginItem {
6670 icon: string
6771 title: string
6872 description: string
6973 tags: string []
7074 link? : string
7175 image? : string
72- githubUser: string
7376 priceLabel? : ' 免费' | ' 付费'
7477}
7578
@@ -79,10 +82,23 @@ const props = withDefaults(
7982 columns? : number
8083 }>(),
8184 {
82- columns: 3
85+ columns: 5
8386 }
8487)
8588
89+ const searchQuery = ref (' ' )
90+ const filteredItems = computed (() => {
91+ if (! searchQuery .value .trim ()) {
92+ return props .items
93+ }
94+ const query = searchQuery .value .toLowerCase ().trim ()
95+ return props .items .filter (item =>
96+ item .title .toLowerCase ().includes (query ) ||
97+ item .description .toLowerCase ().includes (query ) ||
98+ item .tags .some (tag => tag .toLowerCase ().includes (query ))
99+ )
100+ })
101+
86102const colors: Record <string , TagColors > = {
87103 ' MySQL' : { color: ' #006484' , backgroundColor: ' rgba(0, 100, 132, 0.1)' , borderColor: ' rgba(0, 100, 132, 0.2)' },
88104 ' PostgreSQL' : {
@@ -92,24 +108,31 @@ const colors: Record<string, TagColors> = {
92108 },
93109 ' 后端' : { color: ' #009485' , backgroundColor: ' rgba(0,148,133,0.1)' , borderColor: ' rgba(0,148,133,0.2)' },
94110 ' 前端' : { color: ' #a855f7' , backgroundColor: ' rgba(168, 85, 247, 0.1)' , borderColor: ' rgba(168, 85, 247, 0.2)' },
95- };
111+ }
96112
97113const getGithubAvatarUrl = (username : string ) => {
98- return ` https://github.com/${ username }.png?size=32 ` ;
99- };
114+ return ` https://github.com/${username }.png?size=32 `
115+ }
100116
101117const handleCardClick = (item : PluginItem ) => {
102118 if (item .link ) {
103- window .open (item .link , ' _blank' );
119+ window .open (item .link , ' _blank' )
104120 }
105- };
121+ }
106122 </script >
107123
108124<style scoped>
125+ /* 保留所有原始样式 */
109126.plugin-card-container {
110127 display : grid ;
111- gap : 1 rem ;
128+ padding : 2 rem 3 rem ;
112129 grid-template-columns : repeat (1 , 1fr );
130+ font-family : ' Segoe UI' , Tahoma , Geneva, Verdana , sans-serif ;
131+ }
132+
133+ .market-title {
134+ text-align : center ;
135+ margin : 5rem 0 3rem ;
113136}
114137
115138.plugin-card {
@@ -169,44 +192,40 @@ const handleCardClick = (item: PluginItem) => {
169192 background : var (--vp-c-bg-soft );
170193}
171194
172- .image-footer {
173- position : absolute ;
174- bottom : 0 ;
175- left : 0 ;
176- right : 0 ;
195+ .card-content {
196+ padding : 0.75rem ;
197+ flex-grow : 1 ;
177198 display : flex ;
178- justify-content : space-between ;
179- align-items : center ;
180- padding : 0.5rem ;
181- background : rgba (0 , 0 , 0 , 0.05 );
182- color : #fff ;
199+ flex-direction : column ;
183200}
184201
185- .item-developer-info {
202+ .card-title-row {
186203 display : flex ;
187204 align-items : center ;
188- gap : 0.4rem ;
189- flex-grow : 1 ;
190- min-width : 0 ;
191- }
192-
193- .developer-avatar {
194- width : 20px ;
195- height : 20px ;
196- border-radius : 50% ;
197- object-fit : cover ;
198- cursor : default ;
199- pointer-events : none ;
205+ gap : 0.5rem ;
206+ margin-bottom : 0.5rem ;
207+ flex-wrap : nowrap ;
208+ width : 100% ;
200209}
201210
202- .developer-name {
203- font-size : 0.85rem ;
204- color : var (--vp-c-text-2 );
211+ .card-title {
212+ font-size : 1.25rem ;
213+ font-weight : 600 ;
214+ color : var (--vp-c-text-1 );
215+ margin : 0 ;
216+ line-height : 1.4 ;
217+ flex-grow : 1 ;
205218 overflow : hidden ;
206219 text-overflow : ellipsis ;
207220 white-space : nowrap ;
208221}
209222
223+ .card-title-link {
224+ flex-shrink : 0 ;
225+ display : flex ;
226+ font-size : 1.5rem
227+ }
228+
210229.no-external-icon ::after {
211230 content : none !important ;
212231}
@@ -218,31 +237,14 @@ const handleCardClick = (item: PluginItem) => {
218237 border-radius : 4px ;
219238 border : 1px solid var (--vp-c-border );
220239 background : rgba (255 , 255 , 255 , 0.1 );
221- flex-shrink : 0 ;
222- margin-left : 0.5rem ;
223240 white-space : nowrap ;
224241 font-weight : 500 ;
225242}
226243
227- .card-content {
228- padding : 0.75rem ;
229- flex-grow : 1 ;
230- display : flex ;
231- flex-direction : column ;
232- }
233-
234- .card-title {
235- font-size : 1.25rem ;
236- font-weight : 600 ;
237- color : var (--vp-c-text-1 );
238- margin : 0 0 0.5rem 0 ;
239- line-height : 1.4 ;
240- }
241-
242244.card-description {
243245 color : var (--vp-c-text-2 );
244246 font-size : 0.75rem ;
245- line-height : 1.5 ;
247+ line-height : 1.8 ;
246248 margin : 0 0 1rem ;
247249 flex-grow : 1 ;
248250}
@@ -280,6 +282,28 @@ const handleCardClick = (item: PluginItem) => {
280282 text-transform : uppercase ;
281283}
282284
285+ .search-container {
286+ position : relative ;
287+ max-width : 30% ;
288+ margin : 0 auto 2rem ;
289+ }
290+
291+ .search-input {
292+ width : 100% ;
293+ padding : 0.5rem 2rem 0.5rem 0.75rem ;
294+ border : 1px solid var (--vp-c-border );
295+ border-radius : 4px ;
296+ font-size : 0.875rem ;
297+ background : var (--vp-c-bg );
298+ color : var (--vp-c-text-1 );
299+ font-family : ' Segoe UI' , Tahoma , Geneva, Verdana , sans-serif ;
300+ }
301+
302+ .search-input :focus {
303+ outline : none ;
304+ border-color : var (--vp-c-brand );
305+ }
306+
283307@media (min-width : 768px ) {
284308 .plugin-card-container {
285309 grid-template-columns : repeat (2 , 1fr );
0 commit comments