update all

This commit is contained in:
zibright
2025-07-22 17:46:39 +08:00
parent 0e18cc8790
commit d189b1b8c8
18 changed files with 964 additions and 43 deletions

230
README.md
View File

@@ -1,16 +1,37 @@
# Nuxt Content 博客
# Nuxt Content 博客系统
这是一个使用 Nuxt 3 和 Nuxt Content 构建的博客系统
这是一个使用 Nuxt 3 和 Nuxt Content 构建的现代化个人博客系统,支持 Markdown 内容管理,具有清新的设计风格和丰富的功能特性
## 功能特点
## 功能特点
- 📝 基于 Markdown 的内容管理
- 🎨 使用 TailwindCSS 构建的响应式设计
- 🚀 基于 Nuxt 3 的快速渲染
- 📋 自动生成文章列表
- 📅 支持文章元数据(标题、描述、日期等)
### 📝 内容管理
- 基于 Markdown 的内容创作
- 支持文章封面图
- 文章标签系统
- 自动生成文章目录
- 代码块语法高亮
- 代码块一键复制功能
- 按日期自动排序文章
## 快速开始
### 🎨 界面设计
- 响应式布局,适配多种设备
- 清新现代的设计风格
- 平滑过渡动画效果
- 暗色模式支持(开发中)
### 🚀 核心页面
- 个性化首页展示
- 文章列表页
- 友情链接页
- 关于我页面
### 🛠 技术特性
- 基于 Nuxt 3 的快速渲染
- TailwindCSS 的原子化 CSS
- 支持 Vue 组件在 Markdown 中使用
- TypeScript 支持
## 🚀 快速开始
### 安装依赖
@@ -32,7 +53,7 @@ pnpm dev
pnpm build
```
## 添加新文章
## 📝 创建文章
`content` 目录下创建新的 `.md` 文件。文件需要包含以下格式的头部信息:
@@ -41,27 +62,206 @@ pnpm build
title: 文章标题
description: 文章描述
date: YYYY-MM-DD
tags: ['标签1', '标签2']
cover: '封面图片URL' # 可选
---
# 文章内容
```
## 项目结构
## 📁 项目结构
```
├── content/ # Markdown 文章
│ └── posts/ # 博客文章
├── components/ # Vue 组件
├── layouts/ # 页面布局
├── layouts/ # 页面布局
├── pages/ # 页面路由
└── public/ # 静态资源
│ ├── index.vue # 首页
│ ├── articles.vue # 文章列表
│ ├── friends.vue # 友链页面
│ └── about.vue # 关于页面
├── public/ # 静态资源
└── assets/ # 样式和资源文件
```
## 技术栈
## 🔧 自定义配置
### 修改个人信息
1. 替换 `public/avatar.jpg` 更换头像
2. 编辑 `pages/about.vue` 更新个人介绍
3.`pages/friends.vue` 中修改友链信息
### 自定义主题
-`tailwind.config.js` 中修改主题配置
- 编辑 `layouts/default.vue` 调整全局样式
## 🔮 即将推出的功能
- [ ] 文章评论系统
- [ ] 全文搜索功能
- [ ] 文章目录导航
- [ ] 暗色模式
- [ ] SEO 优化
- [ ] RSS 订阅
## <20> 部署指南
### 静态部署 (推荐)
1. 构建静态文件:
```bash
# 生成静态文件
pnpm generate
```
生成的静态文件将位于 `.output/public` 目录中。
2. 将生成的文件部署到任意静态托管服务:
- GitHub Pages
- Netlify
- Vercel
- 自有服务器
#### Nginx配置示例
```nginx
server {
listen 80;
server_name yourdomain.com;
root /path/to/your/blog/.output/public;
index index.html;
# 启用 gzip 压缩
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
location / {
try_files $uri $uri/ /index.html;
}
# 缓存静态资源
location /assets {
expires 7d;
add_header Cache-Control "public, no-transform";
}
}
```
### Node.js 服务器部署
1. 构建项目:
```bash
pnpm build
```
2. 在服务器上启动:
```bash
# 使用 PM2 启动(推荐)
pm2 start .output/server/index.mjs --name "blog"
# 或直接启动
node .output/server/index.mjs
```
#### PM2 配置示例 (ecosystem.config.js)
```javascript
module.exports = {
apps: [{
name: 'blog',
script: '.output/server/index.mjs',
instances: 'max',
exec_mode: 'cluster',
env: {
PORT: 3000,
NODE_ENV: 'production'
}
}]
}
```
### Docker 部署
1. 创建 Dockerfile
```dockerfile
FROM node:18-alpine
WORKDIR /app
# 安装 pnpm
RUN npm install -g pnpm
# 复制项目文件
COPY . .
# 安装依赖并构建
RUN pnpm install
RUN pnpm build
# 暴露端口
EXPOSE 3000
# 启动服务
CMD ["node", ".output/server/index.mjs"]
```
2. 构建并运行容器:
```bash
# 构建镜像
docker build -t nuxt-blog .
# 运行容器
docker run -d -p 3000:3000 nuxt-blog
```
### Docker Compose 部署
创建 `docker-compose.yml`
```yaml
version: '3'
services:
blog:
build: .
ports:
- "3000:3000"
restart: always
environment:
- NODE_ENV=production
```
启动服务:
```bash
docker-compose up -d
```
## <20>🛠 技术栈
- [Nuxt 3](https://nuxt.com/)
- [Nuxt Content](https://content.nuxt.com/)
- [TailwindCSS](https://tailwindcss.com/)
- [Vue 3](https://vuejs.org/)
- [TypeScript](https://www.typescriptlang.org/)
## 许可证
## 📝 许可证
MIT
## 🤝 贡献
欢迎提交 Issue 和 Pull Request
1. Fork 本项目
2. 创建您的特性分支 (`git checkout -b feature/AmazingFeature`)
3. 提交您的修改 (`git commit -m 'Add some AmazingFeature'`)
4. 推送到分支 (`git push origin feature/AmazingFeature`)
5. 打开一个 Pull Request

27
assets/css/code.css Normal file
View File

@@ -0,0 +1,27 @@
/* 代码块样式 */
.prose pre {
position: relative !important;
padding: 0.5rem 0.75rem !important;
margin: 0.5rem 0 !important;
border-radius: 0.375rem !important;
background-color: #1f2937 !important;
overflow-x: auto !important;
}
.prose pre code {
display: block !important;
padding: 0 !important;
padding-right: 2.5rem !important;
margin: 0 !important;
border-radius: 0 !important;
background: none !important;
color: #e5e7eb !important;
font-size: 0.875rem !important;
line-height: 1.25 !important;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace !important;
}
/* 代码块语言标签 */
.prose pre::before {
display: none; /* 隐藏语言标签 */
}

104
components/CopyCode.vue Normal file
View File

@@ -0,0 +1,104 @@
<script setup>
const props = defineProps({
code: {
type: String,
required: true
}
})
const copied = ref(false)
const buttonRef = ref(null)
onMounted(() => {
// 确保按钮在代码块内部正确定位
if (buttonRef.value) {
const preElement = buttonRef.value.closest('pre')
if (preElement) {
preElement.style.position = 'relative'
}
}
})
const copyCode = () => {
navigator.clipboard.writeText(props.code).then(() => {
copied.value = true
setTimeout(() => {
copied.value = false
}, 2000)
})
}
</script>
<template>
<div class="code-block-wrapper">
<slot />
<button
ref="buttonRef"
@click="copyCode"
class="copy-button"
:class="{ 'copied': copied }"
type="button"
>
<span v-if="!copied" class="flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7v8a2 2 0 002 2h6M8 7V5a2 2 0 012-2h4.586a1 1 0 01.707.293l4.414 4.414a1 1 0 01.293.707V15a2 2 0 01-2 2h-2M8 7H6a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2v-2" />
</svg>
复制
</span>
<span v-else class="flex items-center text-green-400">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
</svg>
已复制
</span>
</button>
</div>
</template>
<style>
.code-block-wrapper {
display: block;
width: 100%;
}
.code-block-wrapper pre {
margin: 0;
position: relative;
}
.copy-button {
position: absolute !important;
top: 0.5rem !important;
right: 0.5rem !important;
display: flex !important;
align-items: center !important;
padding: 0.375rem 0.5rem !important;
font-size: 0.75rem !important;
color: #ffffff !important;
background-color: rgba(55, 65, 81, 0.9) !important;
border-radius: 0.375rem !important;
opacity: 0;
transition: all 0.2s ease !important;
z-index: 20 !important;
cursor: pointer !important;
border: none !important;
outline: none !important;
}
pre:hover .copy-button {
opacity: 1;
}
.copy-button:hover {
background-color: rgba(55, 65, 81, 1) !important;
transform: scale(1.05);
}
.copy-button.copied {
background-color: rgba(5, 150, 105, 0.9) !important;
}
.copy-button.copied:hover {
background-color: rgba(5, 150, 105, 1) !important;
}
</style>

View File

@@ -0,0 +1,110 @@
<template>
<div class="relative group">
<pre ref="codeBlock"
:class="[`language-${language}`, 'overflow-x-auto']"
><code :class="`language-${language}`"><slot /></code></pre>
<button
@click="copyCode"
class="copy-button"
:class="{ 'copied': copied }"
type="button"
>
<span v-if="!copied" class="flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7v8a2 2 0 002 2h6M8 7V5a2 2 0 012-2h4.586a1 1 0 01.707.293l4.414 4.414a1 1 0 01.293.707V15a2 2 0 01-2 2h-2M8 7H6a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2v-2" />
</svg>
复制
</span>
<span v-else class="flex items-center text-green-400">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
</svg>
已复制
</span>
</button>
</div>
</template>
<script setup>
const props = defineProps({
code: {
type: String,
default: ''
},
language: {
type: String,
default: 'plaintext'
},
filename: {
type: String,
default: ''
}
})
const copied = ref(false)
const codeBlock = ref(null)
const copyCode = async () => {
const code = props.code || codeBlock.value?.querySelector('code')?.textContent || ''
await navigator.clipboard.writeText(code)
copied.value = true
setTimeout(() => {
copied.value = false
}, 2000)
}
</script>
<style scoped>
.copy-button {
position: absolute;
top: 0.5rem;
right: 0.5rem;
display: flex;
align-items: center;
padding: 0.375rem 0.5rem;
font-size: 0.75rem;
color: #ffffff;
background-color: rgba(55, 65, 81, 0.9);
border-radius: 0.375rem;
opacity: 0;
transition: all 0.2s ease;
z-index: 20;
cursor: pointer;
border: none;
outline: none;
}
.group:hover .copy-button {
opacity: 1;
}
.copy-button:hover {
background-color: rgba(55, 65, 81, 1);
transform: scale(1.05);
}
.copy-button.copied {
background-color: rgba(5, 150, 105, 0.9);
}
.copy-button.copied:hover {
background-color: rgba(5, 150, 105, 1);
}
pre {
margin: 0;
padding: 1rem;
border-radius: 0.5rem;
background-color: #1f2937;
overflow-x: auto;
}
pre code {
display: block;
padding-right: 2.5rem;
color: #e5e7eb;
font-size: 0.875rem;
line-height: 1.5;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
}
</style>

View File

@@ -0,0 +1,37 @@
---
title: 使用 Nuxt Content 搭建个人博客
description: 本文介绍如何使用 Nuxt Content 模块快速搭建一个现代化的个人博客系统
date: 2025-07-21
tags: ['Nuxt', 'Vue', 'Web开发']
cover: 'https://picsum.photos/800/400'
---
Nuxt Content 是一个强大的文档驱动模块,让我们可以轻松地使用 Markdown 文件来管理博客内容。本文将介绍如何使用它来搭建一个现代化的个人博客系统。
## 特性
- 基于文件的 CMS
- Markdown 支持
- 强大的 MDC 语法
- 自动生成目录
- 代码高亮
- 全文搜索
- 支持 Vue 组件
## 示例代码
```js
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nuxt/content'],
content: {
highlight: {
theme: 'github-dark'
}
}
})
```
## 总结
Nuxt Content 提供了一个简单而强大的方式来构建内容驱动的网站。通过合理的目录结构和配置,我们可以快速搭建出一个功能完善的博客系统。

118
content/ssh-key-linux.md Normal file
View File

@@ -0,0 +1,118 @@
---
title: 通过SSH密钥连接LINUX服务器
description: 本文介绍如何通过SSH密钥连接LINUX服务器
date: 2025-07-22
tags: ['SSH', '密钥', 'Linux']
cover: 'https://picsum.photos/800/221'
---
## 前提条件
使用**ROOT**用户个人喜好、客户端使用Windows PowerShell终端、服务端使用Debian12+或是Ubuntu24+
## 客户端操作
### 创建密钥对
随便在哪按 WIN + X 再按 I 打开<mark>PowerShell</mark>终端
进入用户的.ssh文件夹
```shell
cd ~\.ssh
```
创建密钥对使用椭圆加密算法相比RSA更加短小精悍
```shell
ssh-keygen -t ed25519
```
输出Enter file in which to save the key是对密钥对进行命名我这里输入test
之后输出Enter passphrase (empty for no passphrase)是对私钥进行加密输入私钥的密码不会显示不设置就按Enter跳过
然后确认密码依旧按Enter跳过
出现一个方框图形,表示创建成功
之后 ~\.ssh 文件夹中会出现一个 test 私钥文件和一个 test.pub 公钥文件
至此,客户端的操作暂时结束
## 服务端操作
### 上传公钥至服务器
#### 创建实例阶段
在创建实例阶段,如阿里云服务器,可以在**管理设置-登录凭证**中选择密钥对;**登录名**选择root**密钥**对选择右边创建密钥对
创建密钥对界面,密钥对名称随意;创建类型选择导入已有密钥对,
在客户端Windows电脑中进入 ~\.ssh文件夹中找到第一步创建的test.pub,用任意编辑器打开后,复制其内容到公钥内容框中,标签键随意,
然后回到实例创建页面,在密钥对选择刚刚创建的密钥对即可
#### 已有服务器
使用SSH密码登录到服务器切换到ROOT用户
```shell
sudo -i
```
##### 复制公钥到服务器
进入/root/.ssh文件夹
```shell
cd /root/.ssh
```
创建或编辑 authorized_keys 文件
```shell
vim authorized_keys
```
按 I 进入编辑模式,将公钥内容复制进去
按 Esc ,输入 :wq 保存并退出
##### 编辑SSH-SERVER配置文件
进入/etc/ssh文件夹
```shell
cd /etc/ssh
```
编辑sshd_config文件
```shell
vim sshd_config
```
同样按 I 进入编辑模式,并确保下列三个参数如下
```shell
PermitRootLogin yes #允许使用ROOT用户登录
PubkeyAuthentication yes #使用密钥对
PasswordAuthentication no #禁用密码
```
按 Esc 退出编辑模式,输入 :wq 保存并退出
##### 重启SSH-SERVER服务
一般使用
```shell
systemctl restart sshd
```
不行就试试
```shell
service ssh restart
```
##### **不要断开SSH连接防止配置有问题连接不上**
## 使用密钥对进行连接
### 直接连接
新开一个PowerShell终端
```shell
ssh -i ~/.ssh/test root@192.168.21.5
```
输出 Enter passphrase for key 提示输入私钥密码(输入的内容不会显示),之前没设置就直接按 Enter ;不出意外就可以连接上了
### 编辑config文件进行简便连接
**注意:** 只用使用Windows的终端才能简便连接使用mobaxterm等ssh软件这个配置是没用的
在Windows客户端中进入 ~\.ssh 文件夹
```shell
cd ~\.ssh
```
使用记事本编辑config文件
```shell
noteapd config
```
格式如
```shell
Host test
HostName 192.168.21.5
IdentityFile ~/.ssh/test
User root
```
如果端口不是22则需要加上端口如ssh使用222端口
```shell
Host test
HostName 192.168.21.5
IdentityFile ~/.ssh/test
Port 222
User root
```
保存
然后在PowerShell终端中就可以进行简便连接如
```shell
ssh test
```
这里的test是 config 文件 Host 后的字符可以用Emoji表情
**注意:** 在使用SSH密钥对克隆GitHub或是其他git相关网站时Host最好设置为网站的域名你问为什么时间的教训罢了~
## 至此教程结束

View File

@@ -1,7 +1,7 @@
---
title: 欢迎来到我的博客
description: 这是我的第一篇博客文章
date: 2025-07-22
date: 2025-07-21
---
# 欢迎来到我的博客

View File

@@ -1,17 +1,46 @@
<template>
<div class="min-h-screen bg-gray-100">
<header class="bg-white shadow">
<nav class="container mx-auto px-4 py-6">
<NuxtLink to="/" class="text-2xl font-bold text-gray-800">我的博客</NuxtLink>
<div class="min-h-screen bg-[#faf6f1]">
<header class="bg-white/80 backdrop-blur-sm sticky top-0 z-50 border-b border-gray-200">
<nav class="container mx-auto px-4 py-4">
<div class="flex justify-center space-x-8">
<NuxtLink to="/" class="nav-link">首页</NuxtLink>
<NuxtLink to="/articles" class="nav-link">文稿</NuxtLink>
<NuxtLink to="/friends" class="nav-link">朋友们</NuxtLink>
<NuxtLink to="/about" class="nav-link">留言</NuxtLink>
</div>
</nav>
</header>
<main class="container mx-auto px-4 py-8">
<slot />
</main>
<footer class="bg-white shadow mt-8">
<footer class="bg-white/80 mt-8 border-t border-gray-200">
<div class="container mx-auto px-4 py-6 text-center text-gray-600">
© 2025 我的博客. 保留所有权利.
<p class="mb-2">© 2025 我的博客. 保留所有权利.</p>
<div class="flex justify-center space-x-4 mt-4">
<a href="https://github.com/yourusername" target="_blank" class="text-gray-400 hover:text-gray-600">
<span class="sr-only">GitHub</span>
<svg class="h-6 w-6" fill="currentColor" viewBox="0 0 24 24"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>
</a>
<a href="https://space.bilibili.com/yourid" target="_blank" class="text-gray-400 hover:text-gray-600">
<span class="sr-only">Bilibili</span>
<svg class="h-6 w-6" fill="currentColor" viewBox="0 0 24 24"><path d="M17.813 4.653h.854c1.51.054 2.769.578 3.773 1.574 1.004.995 1.524 2.249 1.56 3.76v7.36c-.036 1.51-.556 2.769-1.56 3.773s-2.262 1.524-3.773 1.56H5.333c-1.51-.036-2.769-.556-3.773-1.56S.036 18.858 0 17.347v-7.36c.036-1.511.556-2.765 1.56-3.76 1.004-.996 2.262-1.52 3.773-1.574h.774l-1.174-1.12a1.234 1.234 0 0 1-.373-.906c0-.356.124-.658.373-.907l.027-.027c.267-.249.573-.373.92-.373.347 0 .653.124.92.373L9.653 4.44c.071.071.134.142.187.213h4.267a.836.836 0 0 1 .16-.213l2.853-2.747c.267-.249.573-.373.92-.373.347 0 .662.151.929.4.267.249.391.551.391.907 0 .355-.124.657-.373.906zM5.333 7.24c-.746.018-1.373.276-1.88.773-.506.498-.769 1.13-.786 1.894v7.52c.017.764.28 1.395.786 1.893.507.498 1.134.756 1.88.773h13.334c.746-.017 1.373-.275 1.88-.773.506-.498.769-1.129.786-1.893v-7.52c-.017-.765-.28-1.396-.786-1.894-.507-.497-1.134-.755-1.88-.773zM8 11.107c.373 0 .684.124.933.373.25.249.383.569.4.96v1.173c-.017.391-.15.711-.4.96-.249.25-.56.374-.933.374s-.684-.125-.933-.374c-.25-.249-.383-.569-.4-.96V12.44c0-.373.129-.689.386-.947.258-.257.574-.386.947-.386zm8 0c.373 0 .684.124.933.373.25.249.383.569.4.96v1.173c-.017.391-.15.711-.4.96-.249.25-.56.374-.933.374s-.684-.125-.933-.374c-.25-.249-.383-.569-.4-.96V12.44c.017-.391.15-.711.4-.96.249-.249.56-.373.933-.373z"/></svg>
</a>
<a href="mailto:your@email.com" class="text-gray-400 hover:text-gray-600">
<span class="sr-only">Email</span>
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/></svg>
</a>
</div>
</div>
</footer>
</div>
</template>
<style>
.nav-link {
@apply px-4 py-2 text-gray-600 hover:text-gray-900 rounded-full hover:bg-gray-100 transition-colors;
}
.nav-link.router-link-active {
@apply text-gray-900 bg-gray-100;
}
</style>

View File

@@ -2,10 +2,15 @@
export default defineNuxtConfig({
devtools: { enabled: true },
modules: ['@nuxt/content', '@nuxtjs/tailwindcss'],
css: ['~/assets/css/code.css'],
content: {
// https://content.nuxtjs.org/api/configuration
highlight: {
theme: 'github-dark'
theme: {
default: 'github-dark',
dark: 'github-dark'
},
preload: ['bash', 'shell', 'sh', 'javascript', 'js', 'typescript', 'ts', 'json', 'md', 'yaml', 'vue'],
},
markdown: {
toc: {
@@ -13,5 +18,17 @@ export default defineNuxtConfig({
searchDepth: 3
}
}
},
app: {
head: {
charset: 'utf-8',
viewport: 'width=device-width, initial-scale=1'
}
},
nitro: {
compressPublicAssets: true
},
experimental: {
componentIslands: true
}
})

View File

@@ -12,6 +12,7 @@
"@nuxt/content": "^2.12.0",
"@nuxt/devtools": "latest",
"@nuxtjs/tailwindcss": "^6.11.4",
"@tailwindcss/typography": "^0.5.16",
"nuxt": "^3.10.0"
}
}

View File

@@ -1,11 +1,28 @@
<script setup>
const route = useRoute()
</script>
<template>
<article class="prose lg:prose-xl mx-auto">
<ContentDoc />
<article class="prose prose-slate lg:prose-lg dark:prose-invert mx-auto px-4 py-8">
<ContentDoc :path="route.path" v-slot="{ doc }">
<div class="mb-8">
<h1 class="text-4xl font-bold mb-4">{{ doc.title }}</h1>
<div class="flex items-center gap-4 text-gray-500">
<time :datetime="doc.date">{{ new Date(doc.date).toLocaleDateString('zh-CN', {
year: 'numeric',
month: 'long',
day: 'numeric'
}) }}</time>
<div v-if="doc.tags" class="flex gap-2">
<span v-for="tag in doc.tags" :key="tag"
class="px-2 py-1 bg-gray-100 rounded-full text-sm">
{{ tag }}
</span>
</div>
</div>
</div>
<ContentRenderer :value="doc" :components="{ prose }" />
</ContentDoc>
</article>
</template>
<style>
.prose {
max-width: 65ch;
margin: 0 auto;
}

44
pages/about.vue Normal file
View File

@@ -0,0 +1,44 @@
<template>
<div class="max-w-4xl mx-auto">
<div class="bg-white rounded-2xl shadow-sm p-8 mb-8">
<div class="flex flex-col md:flex-row items-center gap-8">
<div class="w-48 h-48 rounded-full overflow-hidden">
<img src="/avatar.jpg" alt="头像" class="w-full h-full object-cover"
onerror="this.src='https://api.dicebear.com/7.x/adventurer/svg?seed=custom'"/>
</div>
<div class="flex-1 text-center md:text-left">
<h1 class="text-3xl font-bold mb-4">ZiBright</h1>
<p class="text-gray-600 mb-4">在不停学习中~</p>
<div class="flex justify-center md:justify-start space-x-4">
<a href="https://github.com/yourusername" target="_blank"
class="p-2 rounded-lg hover:bg-gray-100 transition-colors">
<svg class="h-6 w-6 text-gray-600" fill="currentColor" viewBox="0 0 24 24">
<path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/>
</svg>
</a>
<a href="https://space.bilibili.com/yourid" target="_blank"
class="p-2 rounded-lg hover:bg-gray-100 transition-colors">
<svg class="h-6 w-6 text-gray-600" fill="currentColor" viewBox="0 0 24 24">
<path d="M17.813 4.653h.854c1.51.054 2.769.578 3.773 1.574 1.004.995 1.524 2.249 1.56 3.76v7.36c-.036 1.51-.556 2.769-1.56 3.773s-2.262 1.524-3.773 1.56H5.333c-1.51-.036-2.769-.556-3.773-1.56S.036 18.858 0 17.347v-7.36c.036-1.511.556-2.765 1.56-3.76 1.004-.996 2.262-1.52 3.773-1.574h.774l-1.174-1.12a1.234 1.234 0 0 1-.373-.906c0-.356.124-.658.373-.907l.027-.027c.267-.249.573-.373.92-.373.347 0 .653.124.92.373L9.653 4.44c.071.071.134.142.187.213h4.267a.836.836 0 0 1 .16-.213l2.853-2.747c.267-.249.573-.373.92-.373.347 0 .662.151.929.4.267.249.391.551.391.907 0 .355-.124.657-.373.906zM5.333 7.24c-.746.018-1.373.276-1.88.773-.506.498-.769 1.13-.786 1.894v7.52c.017.764.28 1.395.786 1.893.507.498 1.134.756 1.88.773h13.334c.746-.017 1.373-.275 1.88-.773.506-.498.769-1.129.786-1.893v-7.52c-.017-.765-.28-1.396-.786-1.894-.507-.497-1.134-.755-1.88-.773zM8 11.107c.373 0 .684.124.933.373.25.249.383.569.4.96v1.173c-.017.391-.15.711-.4.96-.249.25-.56.374-.933.374s-.684-.125-.933-.374c-.25-.249-.383-.569-.4-.96V12.44c0-.373.129-.689.386-.947.258-.257.574-.386.947-.386zm8 0c.373 0 .684.124.933.373.25.249.383.569.4.96v1.173c-.017.391-.15.711-.4.96-.249.25-.56.374-.933.374s-.684-.125-.933-.374c-.25-.249-.383-.569-.4-.96V12.44c.017-.391.15-.711.4-.96.249-.249.56-.373.933-.373z"/>
</svg>
</a>
<a href="mailto:your@email.com"
class="p-2 rounded-lg hover:bg-gray-100 transition-colors">
<svg class="h-6 w-6 text-gray-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/>
</svg>
</a>
</div>
</div>
</div>
</div>
<div class="bg-white rounded-2xl shadow-sm p-8">
<h2 class="text-2xl font-bold mb-6">关于我</h2>
<div class="prose max-w-none">
<p>这里是一段关于我的介绍你可以写一些个人经历兴趣爱好等</p>
<p>支持 Markdown 格式可以自由发挥</p>
</div>
</div>
</div>
</template>

45
pages/articles.vue Normal file
View File

@@ -0,0 +1,45 @@
<template>
<div class="max-w-4xl mx-auto">
<div class="mb-8">
<h1 class="text-3xl font-bold mb-4">所有文章</h1>
<div class="text-gray-600">记录学习与生活的点点滴滴</div>
</div>
<div class="grid gap-6">
<ContentList path="/" :query="{ sort: [{ date: -1 }] }" v-slot="{ list }">
<div v-for="article in list" :key="article._path"
class="bg-white rounded-lg shadow-sm p-6 hover:shadow-md transition-shadow">
<NuxtLink :to="article._path" class="block group">
<div class="flex justify-between items-start gap-4">
<div>
<h2 class="text-xl font-bold mb-2 group-hover:text-blue-600 transition-colors">
{{ article.title }}
</h2>
<p class="text-gray-600 mb-4 line-clamp-2">{{ article.description }}</p>
<div class="flex items-center text-sm text-gray-500 gap-4">
<span>
{{ new Date(article.date).toLocaleDateString('zh-CN', {
year: 'numeric',
month: 'long',
day: 'numeric'
}) }}
</span>
<span v-if="article.tags" class="flex gap-2">
<span v-for="tag in article.tags" :key="tag"
class="px-2 py-1 bg-gray-100 rounded-full text-xs">
{{ tag }}
</span>
</span>
</div>
</div>
<div v-if="article.cover" class="flex-shrink-0">
<img :src="article.cover" :alt="article.title"
class="w-32 h-24 object-cover rounded-lg" />
</div>
</div>
</NuxtLink>
</div>
</ContentList>
</div>
</div>
</template>

57
pages/friends.vue Normal file
View File

@@ -0,0 +1,57 @@
<script setup>
const friends = [
{
name: '示例博客',
avatar: 'https://api.dicebear.com/7.x/adventurer/svg?seed=demo1',
url: 'https://example.com',
description: '这是一个示例博客'
},
{
name: '另一个博客',
avatar: 'https://api.dicebear.com/7.x/adventurer/svg?seed=demo2',
url: 'https://example2.com',
description: '这是另一个示例博客'
}
]
</script>
<template>
<div class="max-w-4xl mx-auto">
<div class="mb-8">
<h1 class="text-3xl font-bold mb-4">友链</h1>
<div class="text-gray-600">友情链接</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<a v-for="friend in friends" :key="friend.name" :href="friend.url" target="_blank"
class="block bg-white rounded-lg shadow-sm p-6 hover:shadow-md transition-all">
<div class="flex items-center gap-4">
<img :src="friend.avatar" :alt="friend.name" class="w-16 h-16 rounded-full" />
<div>
<h3 class="text-lg font-bold mb-1">{{ friend.name }}</h3>
<p class="text-gray-600 text-sm">{{ friend.description }}</p>
</div>
</div>
</a>
</div>
<div class="mt-12 bg-white rounded-lg shadow-sm p-6">
<h2 class="text-xl font-bold mb-4">申请友链</h2>
<p class="text-gray-600 mb-4">如果您想要申请友链请确保您的网站满足以下条件</p>
<ul class="list-disc list-inside text-gray-600 mb-6">
<li>网站内容合法合规</li>
<li>网站能够正常访问</li>
<li>网站已添加本站友链</li>
</ul>
<p class="text-gray-600">
符合条件的朋友可以通过以下格式发送邮件至
<a href="mailto:your@email.com" class="text-blue-600 hover:underline">your@email.com</a>
</p>
<pre class="bg-gray-50 p-4 rounded-lg mt-4 text-sm">
名称您的网站名称
简介网站简介
链接网站链接
头像头像链接</pre>
</div>
</div>
</template>

View File

@@ -1,18 +1,71 @@
<template>
<div>
<h1 class="text-4xl font-bold mb-8">最新文章</h1>
<div class="grid gap-6">
<ContentList path="/" v-slot="{ list }">
<div v-for="article in list" :key="article._path" class="bg-white shadow rounded-lg p-6">
<NuxtLink :to="article._path">
<h2 class="text-2xl font-bold mb-2">{{ article.title }}</h2>
<p class="text-gray-600">{{ article.description }}</p>
<div class="mt-4 text-sm text-gray-500">
{{ new Date(article.date).toLocaleDateString() }}
</div>
</NuxtLink>
<div class="max-w-4xl mx-auto">
<!-- 个人信息卡片 -->
<div class="bg-white rounded-2xl shadow-sm p-8 mb-8">
<div class="flex flex-col md:flex-row items-center gap-8">
<div class="w-48 h-48 rounded-full overflow-hidden">
<img src="/avatar.jpg" alt="头像" class="w-full h-full object-cover"
onerror="this.src='https://api.dicebear.com/7.x/adventurer/svg?seed=custom'"/>
</div>
</ContentList>
<div class="flex-1 text-center md:text-left">
<h1 class="text-3xl font-bold mb-4">ZiBright</h1>
<div class="text-lg text-gray-600 mb-6">这里是我的博客</div>
<div class="flex justify-center md:justify-start gap-4">
<NuxtLink to="/about" class="inline-flex items-center gap-2 px-4 py-2 bg-gray-100 rounded-full hover:bg-gray-200 transition-colors">
<span>关于我</span>
</NuxtLink>
<NuxtLink to="/friends" class="inline-flex items-center gap-2 px-4 py-2 bg-gray-100 rounded-full hover:bg-gray-200 transition-colors">
<span>朋友们</span>
</NuxtLink>
</div>
</div>
</div>
</div>
<!-- 最新文章 -->
<div class="mb-8">
<div class="flex justify-between items-center mb-6">
<h2 class="text-2xl font-bold">最新文章</h2>
<NuxtLink to="/articles" class="text-gray-600 hover:text-gray-900">
查看全部
</NuxtLink>
</div>
<div class="grid gap-6">
<ContentList path="/" :query="{ sort: [{ date: -1 }] }" v-slot="{ list }">
<div v-for="article in list.slice(0, 5)" :key="article._path"
class="bg-white rounded-lg shadow-sm p-6 hover:shadow-md transition-shadow">
<NuxtLink :to="article._path" class="block group">
<div class="flex justify-between items-start gap-4">
<div>
<h3 class="text-xl font-bold mb-2 group-hover:text-blue-600 transition-colors">
{{ article.title }}
</h3>
<p class="text-gray-600 mb-4 line-clamp-2">{{ article.description }}</p>
<div class="flex items-center text-sm text-gray-500 gap-4">
<span>
{{ new Date(article.date).toLocaleDateString('zh-CN', {
year: 'numeric',
month: 'long',
day: 'numeric'
}) }}
</span>
<span v-if="article.tags" class="flex gap-2">
<span v-for="tag in article.tags" :key="tag"
class="px-2 py-1 bg-gray-100 rounded-full text-xs">
{{ tag }}
</span>
</span>
</div>
</div>
<div v-if="article.cover" class="flex-shrink-0">
<img :src="article.cover" :alt="article.title"
class="w-32 h-24 object-cover rounded-lg" />
</div>
</div>
</NuxtLink>
</div>
</ContentList>
</div>
</div>
</div>
</template>

40
pnpm-lock.yaml generated
View File

@@ -17,6 +17,9 @@ importers:
'@nuxtjs/tailwindcss':
specifier: ^6.11.4
version: 6.14.0(magicast@0.3.5)
'@tailwindcss/typography':
specifier: ^0.5.16
version: 0.5.16(tailwindcss@3.4.17)
nuxt:
specifier: ^3.10.0
version: 3.17.7(@netlify/blobs@9.1.2)(@parcel/watcher@2.5.1)(@types/node@24.0.15)(@vue/compiler-sfc@3.5.17)(db0@0.3.2)(ioredis@5.6.1)(magicast@0.3.5)(rollup@4.45.1)(terser@5.43.1)(typescript@5.8.3)(vite@6.3.5(@types/node@24.0.15)(jiti@2.4.2)(terser@5.43.1)(yaml@2.8.0))(yaml@2.8.0)
@@ -1054,6 +1057,11 @@ packages:
'@speed-highlight/core@1.2.7':
resolution: {integrity: sha512-0dxmVj4gxg3Jg879kvFS/msl4s9F3T9UXC1InxgOf7t5NvcPD97u/WTA5vL/IxWHMn7qSxBozqrnnE2wvl1m8g==}
'@tailwindcss/typography@0.5.16':
resolution: {integrity: sha512-0wDLwCVF5V3x3b1SGXPCDcdsbDHMBe+lkFzBRaHeLvNi+nrrnZ1lA18u+OTWO8iSWU2GxUOCvlXtDuqftc1oiA==}
peerDependencies:
tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1'
'@tybys/wasm-util@0.10.0':
resolution: {integrity: sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==}
@@ -2653,6 +2661,9 @@ packages:
lodash-es@4.17.21:
resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==}
lodash.castarray@4.4.0:
resolution: {integrity: sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==}
lodash.debounce@4.0.8:
resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==}
@@ -2662,9 +2673,15 @@ packages:
lodash.isarguments@3.1.0:
resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==}
lodash.isplainobject@4.0.6:
resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==}
lodash.memoize@4.1.2:
resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==}
lodash.merge@4.6.2:
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
lodash.uniq@4.5.0:
resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==}
@@ -3447,6 +3464,10 @@ packages:
peerDependencies:
postcss: ^8.4.32
postcss-selector-parser@6.0.10:
resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==}
engines: {node: '>=4'}
postcss-selector-parser@6.1.2:
resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==}
engines: {node: '>=4'}
@@ -5709,6 +5730,14 @@ snapshots:
'@speed-highlight/core@1.2.7': {}
'@tailwindcss/typography@0.5.16(tailwindcss@3.4.17)':
dependencies:
lodash.castarray: 4.4.0
lodash.isplainobject: 4.0.6
lodash.merge: 4.6.2
postcss-selector-parser: 6.0.10
tailwindcss: 3.4.17
'@tybys/wasm-util@0.10.0':
dependencies:
tslib: 2.8.1
@@ -7488,14 +7517,20 @@ snapshots:
lodash-es@4.17.21: {}
lodash.castarray@4.4.0: {}
lodash.debounce@4.0.8: {}
lodash.defaults@4.2.0: {}
lodash.isarguments@3.1.0: {}
lodash.isplainobject@4.0.6: {}
lodash.memoize@4.1.2: {}
lodash.merge@4.6.2: {}
lodash.uniq@4.5.0: {}
lodash@4.17.21: {}
@@ -8610,6 +8645,11 @@ snapshots:
postcss: 8.5.6
postcss-value-parser: 4.2.0
postcss-selector-parser@6.0.10:
dependencies:
cssesc: 3.0.0
util-deprecate: 1.0.2
postcss-selector-parser@6.1.2:
dependencies:
cssesc: 3.0.0

BIN
public/avatar.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

22
tailwind.config.js Normal file
View File

@@ -0,0 +1,22 @@
/** @type {import('tailwindcss').Config} */
export default {
theme: {
extend: {
typography: {
DEFAULT: {
css: {
'code::before': {
content: '""'
},
'code::after': {
content: '""'
}
}
}
}
}
},
plugins: [
require('@tailwindcss/typography')
]
}