VitePress 添加友链界面

效果预览

友链数据存储

友链数据通常是经常需要添加和修改的,所以我们不能直接写死到页面上。这里我们单独提一个文件去存储友链数据,并且友链数据的格式基本也是类似的。

新建 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 评论

相关推荐
知了一笑1 天前
从视觉、文案到交互:三步彻底去除产品AI味
ai编程·网站·manus
程序员鱼皮9 天前
AI 应用开发,不就是调个接口么?
计算机·ai·程序员·互联网·编程·网站
程序员鱼皮2 个月前
开心网、快播、千千静听...我用 AI 给这些逝去的网站建了座 “墓园”
计算机·程序员·互联网·网站
JIAKSK2 个月前
宝塔利用 Git + WebHook 实现Vitepress自动部署
运维·vitepress
皮皮虾仁2 个月前
让 VitePress 文档”图文并茂“的魔法插件:vitepress-plugin-legend
前端·javascript·vitepress
程序员鱼皮2 个月前
让 AI 帮我部署网站,太方便了!
计算机·ai·程序员·互联网·软件开发·网站
IT利刃出鞘3 个月前
站点天下--网站在线和SSL过期监控的可靠助手
网站