1+ <!-- https://github.com/pengzhanbo/vuepress-theme-plume/blob/e87ae4fc16cd32c9a341539933e45cb05829f359/docs/.vuepress/themes/components/Demos.vue -->
2+
3+ <script setup lang="ts">
4+ import { NIcon , NImage } from ' naive-ui'
5+ import { ImageOutline as ImageOutlineIcon } from ' @vicons/ionicons5'
6+
7+ interface Demo {
8+ name: string
9+ desc: string
10+ logo: string
11+ repo: string
12+ url: string
13+ preview: string
14+ }
15+
16+ defineProps <{
17+ list: Demo []
18+ }>()
19+
20+ // 检查是否有预览图
21+ const hasPreview = (preview : string ) => {
22+ return preview && preview .trim () !== ' '
23+ }
24+ </script >
25+
26+ <template >
27+ <div class =" demos" >
28+ <div v-for =" demo in list" :key =" demo.url" class =" demo-item" >
29+ <div class =" demo-img-container" >
30+ <a :href =" demo.url" target =" _blank" rel =" noopener noreferrer" >
31+ <!-- 无预览图的情况 -->
32+ <div v-if =" !hasPreview(demo.preview)" class =" no-preview-container" >
33+ <div class =" no-preview-content" >
34+ <n-icon :size =" 48" color =" lightGrey" >
35+ <ImageOutlineIcon />
36+ </n-icon >
37+ <div class =" no-preview-text" >暂无预览图</div >
38+ </div >
39+ </div >
40+
41+ <!-- 有预览图的情况 -->
42+ <n-image
43+ v-else
44+ lazy
45+ :src =" demo.preview"
46+ :alt =" demo.name"
47+ object-fit =" cover"
48+ preview-disabled
49+ class =" demo-image"
50+ :intersection-observer-options =" {
51+ root: null
52+ }"
53+ >
54+ <template #error >
55+ <div class =" error-container" >
56+ <n-icon :size =" 48" color =" lightGrey" >
57+ <ImageOutlineIcon />
58+ </n-icon >
59+ </div >
60+ </template >
61+ </n-image >
62+ </a >
63+ </div >
64+ <div class =" demo-content" >
65+ <h3 class =" demo-title" >
66+ <span v-if =" demo.logo" class =" logo" :style =" `background-image: url(${demo.logo})`" />
67+ <span class =" title" >
68+ <a :href =" demo.url" target =" _blank" rel =" noopener noreferrer" :aria-label =" demo.name" :title =" demo.name" >{{ demo.name }}</a >
69+ </span >
70+ <a v-if =" demo.repo" :href =" demo.repo" class =" github" target =" _blank" rel =" noopener noreferrer" :aria-label =" `Link to GitHub: ${demo.name}`" >
71+ <svg width =" 20" height =" 20" viewBox =" 0 0 24 24" fill =" currentColor" >
72+ <path d =" M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
73+ </svg >
74+ </a >
75+ </h3 >
76+ <p :title =" demo.desc" >
77+ {{ demo.desc }}
78+ </p >
79+ </div >
80+ </div >
81+ </div >
82+ </template >
83+
84+ <style scoped>
85+ .demos {
86+ display : grid ;
87+ grid-template-columns : repeat (1 , minmax (0 , 1fr ));
88+ gap : 20px 16px ;
89+ width : 100% ;
90+ }
91+
92+ @media (min-width : 768px ) {
93+ .demos {
94+ grid-template-columns : repeat (2 , minmax (0 , 1fr ));
95+ }
96+ }
97+
98+ .demo-item {
99+ overflow : hidden ;
100+ border : solid 1px var (--vp-c-divider );
101+ border-radius : 8px ;
102+ box-shadow : var (--vp-shadow-1 );
103+ transition : var (--vp-t-color );
104+ transition-property : border ;
105+ display : flex ;
106+ flex-direction : column ;
107+ height : 100% ;
108+ }
109+
110+ .demo-item :hover {
111+ box-shadow : var (--vp-shadow-3 );
112+ }
113+
114+ .demo-img-container {
115+ position : relative ;
116+ width : 100% ;
117+ height : 200px ;
118+ overflow : hidden ;
119+ background-color : var (--vp-c-bg-soft );
120+ flex-shrink : 0 ;
121+ }
122+
123+ :deep(.demo-image ) {
124+ width : 100% ;
125+ height : 100% ;
126+ }
127+
128+ :deep(.demo-image .n-image ) {
129+ width : 100% ;
130+ height : 100% ;
131+ }
132+
133+ :deep(.demo-image .n-image img ) {
134+ width : 100% ;
135+ height : 100% ;
136+ object-fit : cover ;
137+ transition : transform 1s cubic-bezier (0.19 , 1 , 0.22 , 1 );
138+ transform : scale (1 );
139+ }
140+
141+ .demo-item :hover :deep(.demo-image .n-image img ) {
142+ transform : scale (1.05 );
143+ }
144+
145+ .demo-content {
146+ padding : 16px ;
147+ flex-grow : 1 ;
148+ display : flex ;
149+ flex-direction : column ;
150+ }
151+
152+ .demo-title {
153+ display : flex ;
154+ align-items : center ;
155+ justify-content : space-between ;
156+ margin : 0 0 8px 0 ;
157+ font-size : 16px ;
158+ }
159+
160+ .demo-title .logo {
161+ display : inline-block ;
162+ width : 20px ;
163+ height : 20px ;
164+ margin-right : 5px ;
165+ background-repeat : no-repeat ;
166+ background-position : center center ;
167+ background-size : cover ;
168+ }
169+
170+ .demo-title .title {
171+ flex : 1 2 ;
172+ width : auto ;
173+ overflow : hidden ;
174+ text-overflow : ellipsis ;
175+ white-space : nowrap ;
176+ }
177+
178+ .demo-title .title a {
179+ color : var (--vp-c-text-1 );
180+ text-decoration : none ;
181+ font-weight : 600 ;
182+ }
183+
184+ .demo-title .title a :hover {
185+ color : var (--vp-c-brand );
186+ }
187+
188+ .demo-title .github {
189+ display : flex ;
190+ margin-left : 10px ;
191+ color : var (--vp-c-text-2 );
192+ transition : color 0.3s ;
193+ }
194+
195+ .demo-title .github :hover {
196+ color : var (--vp-c-text-1 );
197+ }
198+
199+ .demo-title .vpi-social-github {
200+ display : inline-block ;
201+ width : 20px ;
202+ height : 20px ;
203+ }
204+
205+ .demo-content p {
206+ display : -webkit-box ;
207+ margin : 0 ;
208+ overflow : hidden ;
209+ font-size : 14px ;
210+ line-height : 1.5 ;
211+ color : var (--vp-c-text-2 );
212+ -webkit-box-orient : vertical ;
213+ -webkit-line-clamp : 3 ;
214+ flex-grow : 1 ;
215+ }
216+
217+ /* 错误状态和无预览状态样式 */
218+ .error-container ,
219+ .no-preview-container {
220+ width : 100% ;
221+ height : 100% ;
222+ display : flex ;
223+ align-items : center ;
224+ justify-content : center ;
225+ background-color : var (--vp-c-bg-soft );
226+ }
227+
228+
229+ .no-preview-content {
230+ display : flex ;
231+ flex-direction : column ;
232+ align-items : center ;
233+ gap : 12px ;
234+ }
235+
236+
237+ .error-container {
238+ color : var (--vp-c-text-3 );
239+ }
240+
241+
242+ .no-preview-container {
243+ color : #adb5bd ;
244+ }
245+
246+ .no-preview-text {
247+ font-size : 14px ;
248+ color : #6c757d ;
249+ }
250+ </style >
0 commit comments