valaxy博客全局美化教程(五)

本系列教程共十篇

效果演示


原本的友链页面比较单调,也没有留言板,就改了一下

新增文件

以下是新增文件部分哦,共4个文件

第一个比较特殊,说明一下,公告部分再次修改

1
2
3
4
5
6
7
8
const rules = [
{ icon: '🎉', text: '本站支持交换友链,在您提出申请之前,请将本站添加到友链' },
{ icon: '🍵', text: '为了保障本站用户,本站仅支持个人网站的友链申请' },
{ icon: '💡', text: '申请本站友链需要拥有独立域名(非免费域名),建议开启全站 HTTPS' },
{ icon: '⌛', text: '如果友情链接已经添加,请保持网站的正常访问,会定期清理僵尸网站' },
{ icon: '🐚', text: '网站有一定的实质性内容和主题,不能是空壳网站和练手网站' },
{ icon: '💕', text: '感谢您对本站的支持,如果您已经满足上述要求,请在下方表单提交友链申请~~~' },
]

这里面的中文可以自由修改
下面这部分是自己站点的友链信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  本站友链信息如下:
</p>
<ul class="friend-link-site-info__list">
<li>
站点名称:<span class="friend-link-highlight">{{ siteInfo.name }}</span>
</li>
<li>
站点链接:<span class="friend-link-highlight">{{ siteInfo.url }}</span>
</li>
<li>
站长头像:<span class="friend-link-highlight">{{ siteInfo.avatar }}</span>
</li>
<li>
站点描述:<span class="friend-link-highlight">{{ siteInfo.desc }}</span>
</li>
<li>
站点截图:<span class="friend-link-highlight">{{ siteInfo.siteshot }}</span>

正常情况会自动获取配置文件的链接、名称、头像等东西自动获取,但是如果你发现获取的信息不对,或者你就是想不一样,可以修改,举个例子,我要修改站点截图,就将{{ siteInfo.siteshot }}整个替换为你想加的链接,完整示例如下:

1
2
3
4
</span>
</li>
<li>
站点截图:<span class="friend-link-highlight">https://你的图床.png</span>

还有,开头的:

1
2
3
4
5
const props = withDefaults(defineProps<{
siteshot?: string
}>(), {
siteshot: 'https://你的图床.png',
})

图片链接记得替换

好啦,开始新增第一个文件吧

新增 components\FriendLinkNotice.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
<script setup lang="ts">
import { useSiteConfig } from 'valaxy'
import { computed } from 'vue'

const props = withDefaults(defineProps<{
siteshot?: string
}>(), {
siteshot: 'https://你的图床.png',
})

const siteConfig = useSiteConfig()

const ymlTemplate = `- name: #站点名称
link: #站点链接
avatar: #站长头像
descr: #站点描述
siteshot: #站点截图`

const ymlLines = computed(() => ymlTemplate.split('\n'))

const siteInfo = computed(() => {
const url = siteConfig.value.url || ''
return {
name: `${siteConfig.value.title || 'AIOVTUE'}'s blog`,
url: url.endsWith('/') ? url : `${url}/`,
avatar: siteConfig.value.author?.avatar || '',
desc: siteConfig.value.description || '',
siteshot: props.siteshot,
}
})

const rules = [
{ icon: '🎉', text: '本站支持交换友链,在您提出申请之前,请将本站添加到友链' },
{ icon: '🍵', text: '为了保障本站用户,本站仅支持个人网站的友链申请' },
{ icon: '💡', text: '申请本站友链需要拥有独立域名(非免费域名),建议开启全站 HTTPS' },
{ icon: '⌛', text: '如果友情链接已经添加,请保持网站的正常访问,会定期清理僵尸网站' },
{ icon: '🐚', text: '网站有一定的实质性内容和主题,不能是空壳网站和练手网站' },
{ icon: '💕', text: '感谢您对本站的支持,如果您已经满足上述要求,请在下方表单提交友链申请~~~' },
]

async function copyYml() {
try {
await navigator.clipboard.writeText(ymlTemplate)
}
catch (err) {
console.error(err)
}
}
</script>

<template>
<section class="friend-link-notice">
<header class="friend-link-notice__header">
<h2 class="friend-link-notice__title">
<span class="friend-link-notice__title-icon" aria-hidden="true"></span>
加入本站友链方式
</h2>
<p class="friend-link-notice__subtitle">
参照以下格式留言即可
</p>
</header>

<div class="friend-link-code">
<div class="friend-link-code__bar">
<div class="friend-link-code__dots" aria-hidden="true">
<span class="dot dot-red" />
<span class="dot dot-yellow" />
<span class="dot dot-green" />
</div>
<span class="friend-link-code__lang">YML</span>
<button
type="button"
class="friend-link-code__copy"
title="复制代码"
@click="copyYml"
>
📋
</button>
</div>

<pre class="friend-link-code__body"><code><span
v-for="(line, index) in ymlLines"
:key="index"
class="friend-link-code__line"
><span class="friend-link-code__ln">{{ index + 1 }}</span><span class="friend-link-code__text">{{ line }}</span>
</span></code></pre>

<span class="friend-link-code__cat" aria-hidden="true">🐱</span>
</div>

<div class="friend-link-rules">
<span class="friend-link-rules__plus" aria-hidden="true">+</span>

<ul class="friend-link-rules__list">
<li
v-for="(rule, index) in rules"
:key="index"
class="friend-link-rules__item"
>
<span class="friend-link-rules__icon">{{ rule.icon }}</span>
<span>{{ rule.text }}</span>
</li>
</ul>

<div class="friend-link-site-info">
<p class="friend-link-site-info__title">
<span aria-hidden="true">📍</span>
本站友链信息如下:
</p>
<ul class="friend-link-site-info__list">
<li>
站点名称:<span class="friend-link-highlight">{{ siteInfo.name }}</span>
</li>
<li>
站点链接:<span class="friend-link-highlight">{{ siteInfo.url }}</span>
</li>
<li>
站长头像:<span class="friend-link-highlight">{{ siteInfo.avatar }}</span>
</li>
<li>
站点描述:<span class="friend-link-highlight">{{ siteInfo.desc }}</span>
</li>
<li>
站点截图:<span class="friend-link-highlight">{{ siteInfo.siteshot }}</span>
</li>
</ul>
</div>
</div>
</section>
</template>

<style lang="scss" scoped>
.friend-link-notice {
--notice-accent: #c9a0dc;
--notice-accent-deep: #9b7bb8;
--notice-pink: #e8a4b8;
--notice-title: #e07a96;

width: 100%;
box-sizing: border-box;

@at-root html.dark & {
--notice-accent: #9b7bb8;
--notice-accent-deep: #b89fd0;
--notice-title: #f0a8bc;
}
}

.friend-link-notice__header {
margin-bottom: 14px;
}

.friend-link-notice__title {
display: flex;
align-items: center;
gap: 6px;
margin: 0 0 6px;
font-size: 1.05rem;
font-weight: 700;
color: var(--notice-title);
}

.friend-link-notice__title-icon {
font-size: 0.95rem;
line-height: 1;
}

.friend-link-notice__subtitle {
margin: 0;
font-size: 0.82rem;
color: #888;

@at-root html.dark & {
color: #b0b0b8;
}
}

.friend-link-code {
position: relative;
overflow: hidden;
margin-bottom: 16px;
border-radius: 12px;
border: 1px solid oklch(0% 0 0 / 12%);
box-shadow: 0 8px 22px oklch(0% 0 0 / 10%);
background: #2b303b;
}

.friend-link-code__bar {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 12px;
background: #232733;
border-bottom: 1px solid oklch(100% 0 0 / 6%);
}

.friend-link-code__dots {
display: flex;
gap: 6px;
}

.dot {
width: 10px;
height: 10px;
border-radius: 50%;
}

.dot-red {
background: #ff5f57;
}

.dot-yellow {
background: #febc2e;
}

.dot-green {
background: #28c840;
}

.friend-link-code__lang {
flex: 1;
font-size: 0.82rem;
font-weight: 700;
color: #fff;
letter-spacing: 0.06em;
}

.friend-link-code__copy {
display: inline-flex;
align-items: center;
justify-content: center;
width: 28px;
height: 28px;
border: none;
border-radius: 6px;
background: transparent;
color: #c8d0dc;
cursor: pointer;
transition: background 0.2s ease, color 0.2s ease;

&:hover {
background: oklch(100% 0 0 / 8%);
color: #fff;
}
}

.friend-link-code__body {
margin: 0;
padding: 14px 12px 16px;
overflow-x: auto;
background: #2b303b;
}

.friend-link-code__body code {
display: block;
font-family: 'JetBrains Mono', 'Cascadia Code', Consolas, monospace;
font-size: 0.78rem;
line-height: 1.75;
color: #e6edf3;
white-space: pre;
}

.friend-link-code__line {
display: block;
}

.friend-link-code__ln {
display: inline-block;
width: 1.6em;
margin-right: 0.8em;
color: #6e7681;
user-select: none;
text-align: right;
}

.friend-link-code__text {
color: #e6edf3;
}

.friend-link-code__cat {
position: absolute;
right: -4px;
bottom: 8px;
font-size: 1.6rem;
line-height: 1;
pointer-events: none;
transform: rotate(-8deg);
}

.friend-link-rules {
position: relative;
padding: 16px 14px 16px 18px;
border-radius: 0 12px 12px 0;
border-left: 4px solid var(--notice-accent-deep);
background: oklch(96% 0.015 310 / 88%);

@at-root html.dark & {
background: oklch(24% 0.02 300);
border-left-color: #b89fd0;
}
}

.friend-link-rules__plus {
position: absolute;
left: -13px;
bottom: 18px;
display: inline-flex;
align-items: center;
justify-content: center;
width: 22px;
height: 22px;
border-radius: 50%;
background: var(--notice-accent-deep);
color: #fff;
font-size: 1rem;
font-weight: 700;
line-height: 1;
box-shadow: 0 2px 8px oklch(0% 0 0 / 15%);
}

.friend-link-rules__list {
margin: 0;
padding: 0;
list-style: none;
}

.friend-link-rules__item {
display: flex;
gap: 6px;
margin-bottom: 10px;
font-size: 0.82rem;
line-height: 1.65;
color: #4a4a52;

@at-root html.dark & {
color: #e4e4ea;
}
}

.friend-link-rules__icon {
flex-shrink: 0;
}

.friend-link-site-info {
margin-top: 12px;
padding-top: 12px;
border-top: 1px dashed oklch(0% 0 0 / 10%);

@at-root html.dark & {
border-top-color: oklch(100% 0 0 / 14%);
}
}

.friend-link-site-info__title {
margin: 0 0 10px;
font-size: 0.84rem;
font-weight: 600;
color: #3a3a42;

@at-root html.dark & {
color: #f0f0f4;
}
}

.friend-link-site-info__list {
margin: 0;
padding-left: 1.1em;
list-style: circle;
}

.friend-link-site-info__list li {
margin-bottom: 8px;
font-size: 0.8rem;
line-height: 1.6;
color: #4a4a52;
word-break: break-all;

@at-root html.dark & {
color: #d8d8e0;
}
}

.friend-link-highlight {
display: inline;
padding: 1px 6px;
border-radius: 4px;
background: oklch(92% 0.04 350);
color: #3a3a42;

@at-root html.dark & {
background: oklch(38% 0.06 350);
color: #ffe8f0;
}
}

</style>

第二个~
下方文件内部有一行代码图床要替换哦,不换也行,上面自己站点信息站点截图链接自己写就行,不影响

1
const previewFallback = 'https://你的图床.png'

好啦,开始新增~

新增components\SakuraLinks.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
<script lang="ts" setup>
import { computed } from 'vue'

interface FriendLinkItem {
url: string
avatar: string
name: string
blog?: string
desc?: string
descr?: string
color?: string
siteshot?: string
}

interface FriendLinkGroup {
name?: string
desc?: string
links: FriendLinkItem[]
}

const props = defineProps<{
links?: string | FriendLinkItem[]
linkGroups?: FriendLinkGroup[]
random?: boolean
errorImg?: string
}>()

const previewFallback = 'https://你的图床.png'

function normalizeLink(link: FriendLinkItem) {
return {
url: link.url,
avatar: link.avatar,
name: link.name,
blog: link.blog || link.name,
desc: link.desc || link.descr || '',
color: link.color || '#0078e7',
siteshot: getSiteshot(link),
}
}

function getSiteshot(link: FriendLinkItem) {
if (link.siteshot)
return link.siteshot

if (!link.url)
return previewFallback

return `https://s0.wp.com/mshots/v1/${encodeURIComponent(link.url)}?w=800&h=450`
}

function shuffleLinks<T>(list: T[]) {
return [...list].sort(() => Math.random() - 0.5)
}

const groupList = computed(() => {
if (Array.isArray(props.linkGroups) && props.linkGroups.length > 0) {
return props.linkGroups.map((group) => {
const links = props.random ? shuffleLinks(group.links) : [...group.links]
return {
name: group.name || '',
desc: group.desc || '',
links: links.map(normalizeLink),
}
})
}

if (typeof props.links === 'string' || !Array.isArray(props.links))
return []

const links = props.random ? shuffleLinks(props.links) : [...props.links]
return [{
name: '',
desc: '',
links: links.map(normalizeLink),
}]
})

function onPreviewError(event: Event) {
const img = event.target as HTMLImageElement
if (img.dataset.fallbackApplied)
return

img.dataset.fallbackApplied = '1'
img.src = props.errorImg || previewFallback
}
</script>

<template>
<div class="links-preview">
<section
v-for="(group, groupIndex) in groupList"
:key="groupIndex"
class="links-preview-group"
>
<header
v-if="group.name"
class="links-preview-group__header"
>
<h2 class="links-preview-group__title">
<span class="links-preview-group__pin" aria-hidden="true">📌</span>
{{ group.name }}
</h2>
<p
v-if="group.desc"
class="links-preview-group__desc"
>
分类描述 🌸:{{ group.desc }}
</p>
</header>

<ul class="links-preview-grid">
<li
v-for="(link, i) in group.links"
:key="`${groupIndex}-${i}`"
class="links-preview-item"
:style="{ '--link-color': link.color }"
>
<a
class="links-preview-card"
:href="link.url"
:title="link.name"
rel="friend noopener"
target="_blank"
>
<div class="links-preview-shot">
<img
:src="link.siteshot"
:alt="`${link.name} 站点预览`"
loading="lazy"
decoding="async"
@error="onPreviewError"
>
</div>

<div class="links-preview-meta">
<img
class="links-preview-avatar"
:src="link.avatar"
:alt="link.name"
loading="lazy"
decoding="async"
>

<div class="links-preview-text">
<div class="links-preview-name">
{{ link.blog }}
</div>
<div class="links-preview-desc">
{{ link.desc }}
</div>
</div>
</div>
</a>
</li>
</ul>
</section>
</div>
</template>

<style lang="scss" scoped>
.links-preview {
--link-card-width: 210px;
width: 100%;
box-sizing: border-box;
}

.links-preview-group + .links-preview-group {
margin-top: 40px;
}

.links-preview-group__header {
margin-bottom: 20px;
}

.links-preview-group__title {
display: flex;
align-items: center;
gap: 6px;
margin: 0 0 8px;
font-size: 1.15rem;
font-weight: 700;
color: var(--sakura-color-primary, #fe9500);
}

.links-preview-group__pin {
font-size: 1rem;
line-height: 1;
}

.links-preview-group__desc {
margin: 0;
font-size: 0.88rem;
line-height: 1.6;
color: var(--sakura-color-text-muted, #888);
}

.links-preview-grid {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
gap: 18px 14px;
margin: 0;
padding: 0;
list-style: none;
}

.links-preview-item {
width: var(--link-card-width, 210px);
max-width: 100%;
flex: 0 0 auto;
min-width: 0;
}

.links-preview-card {
display: block;
color: inherit;
text-decoration: none;
transition: transform 0.25s ease;

&:hover {
transform: translateY(-3px);

.links-preview-shot {
box-shadow: 0 14px 28px oklch(0% 0 0 / 18%);
}

.links-preview-name {
color: var(--link-color, var(--sakura-color-primary, #fe9500));
}
}
}

.links-preview-shot {
overflow: hidden;
aspect-ratio: 16 / 10;
border-radius: 10px;
background: var(--va-c-bg-mute, oklch(94% 0 0));
box-shadow: 0 4px 12px oklch(0% 0 0 / 9%);
transition: box-shadow 0.25s ease;

img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
object-position: top center;
}
}

.links-preview-meta {
display: flex;
align-items: center;
gap: 6px;
margin-top: 8px;
min-width: 0;
}

.links-preview-avatar {
flex-shrink: 0;
width: 32px;
height: 32px;
border-radius: 50%;
object-fit: cover;
border: 2px solid var(--link-color, var(--sakura-color-primary, #fe9500));
background: var(--va-c-bg, #fff);
}

.links-preview-text {
min-width: 0;
flex: 1;
}

.links-preview-name {
font-size: 0.84rem;
font-weight: 700;
line-height: 1.4;
color: var(--sakura-color-text-deep, #333);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
transition: color 0.2s ease;
}

.links-preview-desc {
margin-top: 1px;
font-size: 0.68rem;
line-height: 1.45;
color: var(--sakura-color-text-muted, #888);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

@media (max-width: 768px) {
.links-preview-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 12px;
}

.links-preview-item {
width: 100%;
}

.links-preview-card {
position: relative;
aspect-ratio: 1 / 1;
border-radius: 10px;
overflow: hidden;

&:hover {
transform: none;

.links-preview-name {
color: #fff;
}
}

&::after {
content: '';
position: absolute;
inset: 0;
z-index: 1;
background: linear-gradient(to top, oklch(0% 0 0 / 62%) 0%, oklch(0% 0 0 / 12%) 45%, transparent 70%);
pointer-events: none;
}
}

.links-preview-shot {
position: absolute;
inset: 0;
aspect-ratio: unset;
height: 100%;
border-radius: 10px;
box-shadow: none;
}

.links-preview-meta {
position: absolute;
left: 0;
right: 0;
bottom: 0;
z-index: 2;
margin-top: 0;
padding: 8px;
gap: 6px;
}

.links-preview-avatar {
width: 32px;
height: 32px;
border-width: 1.5px;
}

.links-preview-name {
font-size: 0.74rem;
color: #fff;
}

.links-preview-desc {
font-size: 0.62rem;
color: oklch(92% 0 0 / 88%);
}
}

:global(html.dark) {
.links-preview-shot {
background: oklch(28% 0.01 270);
box-shadow: 0 8px 22px oklch(0% 0 0 / 35%);
}

.links-preview-card:hover .links-preview-shot {
box-shadow: 0 14px 30px oklch(0% 0 0 / 45%);
}

.links-preview-name {
color: var(--sakura-color-text, #eee);
}
}
</style>

第三~

新增components\layouts\SakuraLinksLayout.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
<script setup lang="ts">
import { useFrontmatter } from 'valaxy'
import { computed } from 'vue'

const frontmatter = useFrontmatter()

const coverSrc = computed(() => {
const cover = frontmatter.value?.cover
return typeof cover === 'string' ? cover : ''
})
</script>

<template>
<article class="sakura-page sakura-links-page">
<header
class="links-page-header sakura-page-header"
:class="{ 'has-cover': coverSrc }"
>
<img
v-if="coverSrc"
class="links-page-header__cover"
:src="coverSrc"
:alt="frontmatter.title || '友链页头图'"
loading="eager"
decoding="async"
>

<div class="links-page-header__inner sakura-safe-padding">
<div class="sakura-header-title" flex="~">
<SakuraTitle :fm="frontmatter" />
</div>
</div>
</header>

<div class="sakura-links-main">
<div class="content sakura-page-content">
<RouterView v-slot="{ Component }">
<component :is="Component">
<template #main-content>
<SakuraLinks
:links="frontmatter.links"
:link-groups="frontmatter.linkGroups"
:random="frontmatter.random"
:error-img="frontmatter.errorImg"
/>

<div class="friend-notice-bottom">
<FriendLinkNotice :siteshot="frontmatter.cover" />
</div>
</template>
</component>
</RouterView>
</div>
</div>
</article>
</template>

<style lang="scss">
.sakura-links-page {
--links-page-outer: max(40px, 3vw, env(safe-area-inset-left, 0px));
--links-page-inner: 24px;
--links-page-header-gap: 28px;

width: 100%;
display: flex;
flex-direction: column;
align-items: stretch;

.links-page-header {
margin-top: var(--sakura-navbar-height);
width: 100%;
position: relative;

.sakura-header-title {
justify-content: center;
}

&:not(.has-cover) {
margin-top: var(--sakura-navbar-spacing);
}

&.has-cover {
height: 320px;
margin-bottom: var(--links-page-header-gap);
overflow: hidden;
display: flex;
align-items: flex-end;
justify-content: center;
}

&__cover {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
object-position: center;
z-index: 0;
pointer-events: none;
}

&__inner {
position: relative;
z-index: 1;
width: 100%;
padding-bottom: 25px;
color: #fff;
text-align: center;
}
}

.sakura-links-main {
width: 100%;
padding-inline: var(--links-page-outer);
box-sizing: border-box;
}

.sakura-page-content {
width: 100%;
max-width: none;
box-sizing: border-box;
padding-inline: var(--links-page-inner) !important;
padding-top: var(--links-page-header-gap);
padding-bottom: 32px;
background: var(--sakura-color-background);
border-radius: var(--sakura-radius);
}

.friend-notice-bottom {
width: 100%;
margin: 36px auto 8px;
box-sizing: border-box;
}

.sakura-comment {
width: 100%;
box-sizing: border-box;
}

@media (max-width: 768px) {
--links-page-outer: 0px;
--links-page-inner: 8px;
--links-page-header-gap: 20px;

.sakura-page-content {
padding-inline: max(8px, env(safe-area-inset-left, 0px)) max(8px, env(safe-area-inset-right, 0px)) !important;
border-radius: 0;
}
}
}
</style>

<style lang="scss">
.sakura-links-page .links-page-header.has-cover .sakura-title {
color: inherit;
text-shadow: 2px 2px 10px black;
}
</style>

这是友链信息的索引文件咯,所有的友链在这里配置,这个应该不用过多介绍吧,跟着模版新增就行,格式都给你写好了的,copy~

新增pages\links\index.md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
---
layout: links
title: 来加入我们叭
icon: i-ri-links-line
cover: https://你的图床.png
comment: true

linkGroups:
- name: 紧密相连
desc: 这里是站长自己的网站
links:
- url: https://yybb.us/
avatar: https://tele-tuchuang.pages.dev/file/50837ffeb21853209d7e0.png
name: aiovtue's notion
blog: aiovtue's notion
desc: 所爱隔山海,山海不可平
color: "#0078e7"
siteshot: https://r2tc.20030327.xyz/file/博客/主题/1780643226230_wallhaven-9d1yjk.png

- url: https://20030327.xyz
avatar: https://r2tc.20030327.xyz/file/博客/主题/1780643776417_1780643686584.png
name: AIOVTUE
blog: AIOVTUE
desc: 雨是神的烟花
color: "#0078e7"
siteshot: https://r2tc.20030327.xyz/file/博客/主题/1780643226230_wallhaven-9d1yjk.png

- name: 推荐网站
desc: 这里是最熟悉的小伙伴
links:
- url: https://example.com
avatar: https://api.dicebear.com/7.x/adventurer/svg?seed=xeu
name: demo
blog: demo
desc: demo
color: "#6b8cff"
---

好啦,到此结束

valaxy博客全局美化教程(五)
© AIOVTUE · https://20030327.xyz/posts/d25adc68/· 更新于 2026-06-01