















提示:项目使用了 Tailwind 进行了重构,本文组件样式都对此有所依赖,参见 我的 Tailwind 配置。如果不想使用 Tailwind,可以直接把代码扔给 AI,让其转化为普通 CSS 样式
效果预览

友链数据通常是经常需要添加和修改的,所以我们不能直接写死到页面上。这里我们单独提一个文件去存储友链数据,并且友链数据的格式基本也是类似的。
新建 links.md 文件,内容如下:
docs/pages/links.md
md
---
layout: weiz-link
title: 我的友链
description: 这是唯知笔记网站的友链界面,xxxxxx
links:
- title: 鸣谢
desc: 建站中学习和使用了以下博客/网站的技术和分享,特别鸣谢!🫡
list:
- name: VitePress
link: https://vitepress.dev/zh/
avatar: https://vitepress.dev/vitepress-logo-mini.svg
descr: 由 Vite 和 Vue 驱动的静态站点生成器
- name: VitePress 快速上手教程
link: https://vitepress.yiov.top/
avatar: https://vitepress.dev/vitepress-logo-mini.svg
descr: 如果你也想搭建它,那跟我一起做吧
- title: 传送门
desc: 聚集众多优秀独立博客,随机传送 🚀
list:
- name: 十年之约
link: https://foreverblog.cn/go.html
avatar: https://img.foreverblog.cn/logo_en_default.png
descr: 十年之约,奔赴某个独立博客的十年
- name: Web Teleporter
link: https://webteleporter.top/
avatar: https://webteleporter.top/img/logo.png
descr: 独立博客传送门
outLinks:
- title: 去远方
desc: 去了远方的伙伴,回来的时候请记得联络我📩
list:
- name: 随风起
link: https://blog.bywind.xyz/
avatar: https://blog.bywind.xyz/img/avatar.png
descr: 爱意随风起,风止意难平
---说明:outLinks 字段是网站断联的名单,非必须
Vitepress 中创建单独页面的方法,可以参考 VitePress 新建页面和注册组件
新建一个文件夹 docs/.vitepress/theme/components/WLink 用来存放友链组件
WLink/index.vueWLink/LinkSite.vue.vitepress/theme/type/WLink.ts
vue
<template>
<div id="main" class="mx-auto w-full max-w-384 p-6 lg:p-8">
<h1 class="text-text1 text-2xl! font-bold!">我的友链</h1>
<div class="mb-12">
<div class="link" v-for="(item, index) of linksData" :key="index">
<div class="relative mt-12 mb-6 h-px bg-slate-200 dark:bg-neutral-700">
<h3 class="bg-bg-soft absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 px-6 text-center text-xl! font-bold!">
{{ item.title }}
</h3>
</div>
<p class="mb-8! text-center font-medium!">{{ item.desc }}</p>
<div class="link-container grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
<div v-for="temp of item.list" :key="temp.link" class="link-wrapper">
<LinkSite :data="temp" />
</div>
</div>
</div>
<div class="link" v-for="(item, index) of outLinksData" :key="'out-' + index">
<div class="relative mt-12 mb-6 h-px bg-slate-200 dark:bg-neutral-700">
<h3 class="bg-bg-soft absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 px-6 text-center text-xl! font-bold!">
{{ item.title }}
</h3>
</div>
<p class="mb-4! text-center font-medium!">{{ item.desc }}</p>
<!-- Toggle Button -->
<div class="mb-8 flex justify-center">
<div
@click="toggleOutLink(index)"
class="bg-bg hover:bg-main text-text1 border-border group flex cursor-pointer items-center gap-2 rounded-md border px-4 py-1.5 text-sm font-bold transition-all duration-300 hover:text-white"
>
<span class="pl-2">{{ collapsedStates[index] ? '查看' : '收起' }}</span>
<WIcon tag="right" class="transition-transform duration-300 group-hover:text-white!" :class="collapsedStates[index] ? 'rotate-90' : '-rotate-90'" />
</div>
</div>
<div v-show="!collapsedStates[index]" class="animate-fade-in grid grid-cols-1 gap-6 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5">
<div v-for="temp of item.list" :key="temp.link" class="link-wrapper">
<LinkSite :data="temp" :noAvatar="true" />
</div>
</div>
</div>
</div>
<div class="message">
<div class="relative mt-12 mb-6 h-px bg-slate-200 dark:bg-neutral-700">
<h3 class="bg-bg-soft absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 px-8 text-center text-xl! font-bold!">留链</h3>
</div>
<p class="mb-8! text-center font-medium!">留恋的小伙伴,快来和我做友链 💞</p>
<div class="bg-bg rounded-2xl p-6 shadow-[0_1px_2px_0_rgba(0,0,0,0.05)] dark:shadow-[0_1px_2px_0_rgba(255,255,255,0.05)]">
<p class="mb-3!">
非常高兴能和很多朋友交流,如果你也想加入
<a href="https://note.weizwz.com" target="_blank"><b>唯知笔记</b></a> 友链,可以添加我站后在下方留言,我会尽快添加。
</p>
<div class="note custom-block github-alert mb-3">
<p class="custom-block-title">本站信息</p>
<p>
标题:唯知笔记 <br />
地址:https://note.weizwz.com <br />
头像:https://p.weizwz.com/logo_a4353391cbf0889b.webp <br />
描述:探索知识的无限可能 <br />
截图:https://p.weizwz.com/siteshot_note.webp
</p>
</div>
<div class="mb-6 rounded-lg border border-l-[5px] border-(--vp-c-tip-1) bg-(--vp-c-tip-soft) p-4">
<details class="group">
<summary class="flex cursor-pointer list-none items-center font-medium">
<span class="mr-2 text-lg text-(--vp-c-tip-1) transition-transform duration-200 group-open:rotate-90">▶</span>
友链须知
</summary>
<div class="mt-2 mb-2 pl-1">
<p>1. 本站友链只添加博客类型的网站,其他类型网站不在友链范围之内</p>
<p>2. 为了友链页面的统一性和美观性,可能会对你的昵称进行缩短处理</p>
<p>3. 友链头像使用原博主公布的链接,请不要加入防盗链避免请求失败</p>
<p>4. 友链信息(头像地址,网站地址等)如有修改,请在本页的评论中添加,我会尽快更新</p>
<p>5. 本站将不定期清理失效网站,如网站重新上线后请在本页的评论中添加,我会尽快更新</p>
</div>
</details>
</div>
<Twikoo />
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { useData } from 'vitepress'
import { ref } from 'vue'
import LinkSite from './LinkSite.vue'
import Twikoo from '../WTwikoo/index.vue'
import { LinkList } from '../../type/WLink'
import WIcon from '../WIcon.vue'
const { frontmatter: fm } = useData()
const linksData = (fm.value.links as LinkList[]) || []
const outLinksData = (fm.value.outLinks as LinkList[]) || []
// 折叠状态管理
const collapsedStates = ref<boolean[]>(outLinksData.map(() => true))
const toggleOutLink = (index: number) => {
collapsedStates.value[index] = !collapsedStates.value[index]
}
</script>
<style scoped>
@media (min-width: 96rem) {
.link-container {
grid-template-columns: repeat(5, minmax(0, 1fr));
}
}
.animate-fade-in {
animation: fadeIn 0.5s ease-out;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>vue
<template>
<a
class="bg-bg shadow-shadow group relative flex h-[100px] w-full items-center justify-between gap-4 rounded-2xl p-4 shadow-xs transition-all duration-600 hover:shadow-xl"
:href="data.link"
target="_blank"
>
<div v-if="!noAvatar" class="flex h-16 w-16 items-center justify-center overflow-hidden transition-all duration-600">
<img
v-if="data.avatar && !imageFailed"
:class="[
'h-full w-full align-top transition-all duration-600 object-cover',
data.avatar?.startsWith('https://vitepress.dev/') ? 'rounded-none' : 'rounded-full'
]"
:src="data.avatar"
@error="handleImageError()"
:alt="data.name"
/>
<!-- 替代内容:显示首字母 -->
<div v-else class="bg-main flex h-full w-full items-center justify-center rounded-full text-3xl font-bold text-white transition-all duration-600">
{{ data.name.charAt(0).toLocaleUpperCase() }}
</div>
</div>
<div class="flex h-full flex-1 flex-col justify-center transition-all duration-600">
<h3
class="mb-2! w-full overflow-hidden bg-[linear-gradient(to_right,var(--color-main)_50%,var(--color-text1)_50%)] bg-size-[200%_100%] bg-clip-text bg-position-[100%_0] text-base! font-semibold! whitespace-nowrap text-transparent transition-[background-position] duration-1000 group-hover:bg-position-[0_0]"
>
{{ data.name }}
</h3>
<div class="text-text2 line-clamp-2 w-full overflow-hidden leading-5 font-semibold break-all" :title="data.descr">
{{ data.descr }}
</div>
</div>
</a>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { Link } from '../../type/WLink'
const props = defineProps<{ data: Link; noAvatar?: boolean }>()
const data = props.data
const noAvatar = props.noAvatar
// 记录图片加载状态
const imageFailed = ref(false)
// 处理图片加载失败
const handleImageError = () => {
imageFailed.value = true // 更新加载状态
}
</script>ts
export interface LinkList {
// 标题
title: string
// 描述
desc: string
// 列表
list: Link[]
}
export interface Link {
// 站点名称
name: string
// 站点链接
link: string
// 头像/站点logo
avatar: string
// 站点描述
descr: string
// 站点缩略图
siteshot?: string
}新建的组件需要在主题配置中心进行注册,注册后就可以在 md页面 中使用了。比如我们 links.md 文件中的开头 layout: weiz-link
docs/.vitepress/theme/index.ts
ts
import WLink from './components/WLink/index.vue' // 友链页
export default {
enhanceApp({ app }) {
// 注册全局组件
app.component('weiz-link', WLink)
}
}在组件内集成评论,直接使用评论组件,比如我的是 <Twikoo /> 评论。
Twikoo 评论的集成方式,可参考 VitePress 集成 Twikoo 评论
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。