效果预览
友链数据存储
友链数据通常是经常需要添加和修改的,所以我们不能直接写死到页面上。这里我们单独提一个文件去存储友链数据,并且友链数据的格式基本也是类似的。
新建 docs/pages/links.md
文件,内容如下:
markdown
---
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
irregular: true
descr: 由 Vite 和 Vue 驱动的静态站点生成器
- name: VitePress 快速上手教程
link: https://vitepress.yiov.top/
avatar: https://vitepress.dev/vitepress-logo-mini.svg
irregular: true
descr: 如果你也想搭建它,那跟我一起做吧
- title: 传送门
desc: 聚集众多优秀独立博客,随机传送 🚀
list:
- name: 十年之约
link: https://foreverblog.cn/go.html
avatar: https://img.foreverblog.cn/logo_en_default.png
descr: 十年之约,奔赴某个独立博客的十年
irregular: true
- name: Web Teleporter
link: https://webteleporter.top/
avatar: https://webteleporter.top/img/logo.png
descr: 独立博客传送门
---
注:irregular
字段是对特殊图片进行处理
友链页面创建
Vitepress
中创建单独页面的方法,可以参考 VitePress 新建页面和注册组件
新建组件
新建一个文件夹 docs/.vitepress/theme/components/WLink
用来存放友链组件
WLink/index.vue
html
<template>
<div id="main">
<div class="title">
<h1>我的友链</h1>
</div>
<div class="content">
<div class="link" v-for="(item, index) of linksData" :key="index">
<div class="title-wrapper">
<h3>{{ item.title }}</h3>
</div>
<p>{{ item.desc }}</p>
<div class="link-list">
<el-row class="container-row" :gutter="24">
<el-col v-for="temp of item.list" :key="temp.link" class="link-wrapper" :xs="24" :sm="12" :md="6">
<LinkSite :data="temp" />
</el-col>
</el-row>
</div>
</div>
</div>
<div class="message">
<div class="title-wrapper">
<h3>留链吗</h3>
</div>
<p>留恋的小伙伴,想要和我做友链 💞</p>
<div class="card">
<p>留言请参照置顶评论里的格式,siteshot 字段非必需</p>
<Twikoo />
</div>
</div>
</div>
</template>
<script setup lang='ts'>
import { useData } from 'vitepress'
import LinkSite from './LinkSite.vue'
import Twikoo from '../WTwikoo/index.vue'
import { LinkList } from '../../type/WLink'
const { frontmatter: fm } = useData()
const linksData = fm.value.links as LinkList[]
</script>
<style>
.message .twikoo {
margin-top: 0;
}
</style>
<style lang='scss' scoped>
.content {
margin-top: var(--weiz-spacing-8xl);
margin-bottom: var(--weiz-spacing-12xl);
}
.title-wrapper {
margin: var(--weiz-spacing-8xl) 0;
height: 1px;
background: var(--vp-c-text-5);
position: relative;
> h3 {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%) translateY(-50%);
background: var(--vp-c-bg-soft);
padding: 0 var(--weiz-spacing-8xl);
font-size: var(--weiz-font-size-xl);
font-weight: var(--weiz-font-weight-bold);
line-height: var(--weiz-text-xl-line-height);
text-align: center;
}
&+p {
margin-bottom: var(--weiz-spacing-6xl);
font-weight: var(--weiz-font-weight-medium);
text-align: center;
}
}
.link-list .el-col {
margin-bottom: var(--weiz-spacing-6xl);
}
.card {
padding: var(--weiz-spacing-6xl);
border-radius: var(--weiz-card-border-radius);
background-color: var(--vp-c-bg);
box-shadow: var(--weiz-shadow);
> p {
margin-bottom: var(--weiz-spacing-6xl);
}
}
</style>
WLink/LinkSite.vue
html
<template>
<div class="link-item">
<a :href="data.link" :title="data.name" target="_blank">
<div class="link-icon">
<img v-if="!imageFailed" :class="{ irregular: data.irregular }" :src="data.avatar" @error="handleImageError()" :alt="data.name" />
<!-- 替代内容:显示首字母 -->
<span v-else class="iconPlaceholder">{{ data.name.charAt(0) }}</span>
</div>
<div class="link-info">
<div class="link-name">{{ data.name }}</div>
<div class="link-desc" :title="data.descr">{{ data.descr }}</div>
</div>
</a>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { Link } from '../../type/WLink'
const props = defineProps<{ data: Link }>()
const data = props.data
// 记录图片加载状态
const imageFailed = ref(false)
// 处理图片加载失败
const handleImageError = () => {
imageFailed.value = true // 更新加载状态
}
</script>
<style lang="scss" scoped>
.link-item {
position: relative;
overflow: hidden;
width: 100%;
height: 100px;
display: inline-block;
border-radius: var(--weiz-card-border-radius);
background-color: var(--vp-c-bg);
box-shadow: var(--weiz-shadow);
&:hover {
.link-icon > img,
.iconPlaceholder {
transform: scale(2.4);
&.irregular {
transform: scale(1.6);
}
}
.link-info {
margin-left: 20px;
}
}
> a {
position: relative;
z-index: 1;
height: 100%;
display: flex;
align-items: center;
}
.link-icon {
flex: 0 0 auto;
width: 100px;
height: 100%;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
transition: var(--weiz-transition-6);
> img {
width: 60px;
height: 60px;
border-radius: 50%;
vertical-align: top;
object-fit: cover;
transition: var(--weiz-transition-6);
&[src^="https://vitepress.dev/"]
{
border-radius: 0;
}
&.irregular {
object-fit: contain;
}
}
.iconPlaceholder {
font-size: var(--weiz-font-size-3xl);
font-weight: var(--weiz-font-weight-bold);
display: inline-block;
transition: var(--weiz-transition-6);
}
}
.link-info {
flex: 1;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
transition: var(--weiz-transition-6);
}
.link-name {
width: 100%;
font-size: var(--weiz-font-size-st);
line-height: var(--weiz-text-st-line-height);
font-weight: var(--weiz-font-weight-semibold);
margin-bottom: var(--weiz-spacing-2xl);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
transition: var(--weiz-transition-6);
}
.link-desc {
width: 100%;
padding-right: var(--weiz-spacing-6xl);
font-weight: var(--weiz-font-weight-semibold);
color: var(--vp-c-text-2);
line-height: var(--weiz-text-xs-line-height);
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
line-clamp: 3;
transition: var(--weiz-transition-6);
}
}
</style>
.vitepress/theme/type/WLink.ts
ts
export interface LinkList {
// 标题
title: string
// 描述
desc: string
// 列表
list: Link[]
}
export interface Link {
// 站点名称
name: string
// 站点链接
link: string
// 头像/站点logo
avatar: string
// 头像不规则
irregular?: boolean
// 站点描述
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 评论