From 4ba51e17552f8cc2b8720a817a24f288b90d5068 Mon Sep 17 00:00:00 2001 From: Jiao77 Date: Sun, 1 Mar 2026 13:04:52 +0800 Subject: [PATCH] update docs --- README.md | 7 +- docs/developer-guide.md | 804 +++++++++++++++++++++++++++++++++++ docs/user-guide.md | 610 ++++++++++++++++++++++++++ src/layouts/PostLayout.astro | 6 +- 4 files changed, 1423 insertions(+), 4 deletions(-) create mode 100644 docs/developer-guide.md create mode 100644 docs/user-guide.md diff --git a/README.md b/README.md index 0c379d1..574f078 100644 --- a/README.md +++ b/README.md @@ -171,6 +171,11 @@ heroImage: /images/hero.jpg - [Docker](https://www.docker.com/) - 容器化 - [Nginx](https://nginx.org/) - 反向代理 +## 📚 文档 + +- **[使用指南](./docs/user-guide.md)** - 博客使用教程,包括文章编写、MDX 组件、Typst 排版、主题定制等 +- **[开发文档](./docs/developer-guide.md)** - 面向开发者的技术文档,包括 API 接口、数据库结构、深度定制指南等 + ## 📜 License -MIT License © 2024 \ No newline at end of file +MIT License © 2024 diff --git a/docs/developer-guide.md b/docs/developer-guide.md new file mode 100644 index 0000000..69461c1 --- /dev/null +++ b/docs/developer-guide.md @@ -0,0 +1,804 @@ +# NovaBlog 开发者文档 + +本文档面向希望深度定制 NovaBlog 的开发者,详细介绍系统架构、API 接口、数据库结构以及扩展开发指南。 + +--- + +## 目录 + +1. [系统架构](#系统架构) +2. [项目结构](#项目结构) +3. [API 接口文档](#api-接口文档) +4. [数据库结构](#数据库结构) +5. [前端组件开发](#前端组件开发) +6. [后端服务扩展](#后端服务扩展) +7. [部署配置](#部署配置) +8. [性能优化](#性能优化) + +--- + +## 系统架构 + +NovaBlog 采用 **静态渲染 + 轻量级微服务** 的解耦架构: + +``` +┌─────────────────────────────────────────────────────────────┐ +│ 用户浏览器 │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ Nginx 反向代理 │ +│ (静态文件服务 + API 请求转发) │ +└─────────────────────────────────────────────────────────────┘ + │ │ + ▼ ▼ +┌─────────────────────┐ ┌──────────────────────────┐ +│ 静态文件 (CDN) │ │ API Server (Go) │ +│ HTML/CSS/JS/图片 │ │ - 用户认证 │ +│ │ │ - 评论管理 │ +│ 构建时生成 │ │ - 点赞系统 │ +└─────────────────────┘ │ - SQLite 数据库 │ + └──────────────────────────┘ +``` + +### 技术栈 + +| 层级 | 技术 | 说明 | +|------|------|------| +| 前端框架 | Astro 5.x | Islands Architecture,零 JS 默认输出 | +| UI 组件 | Vue 3 | 交互式岛屿组件 | +| 样式方案 | Tailwind CSS 4.x | 原子化 CSS | +| 后端框架 | Go + Gin | 极致轻量,内存占用 < 20MB | +| 数据库 | SQLite | 文件型数据库,无需额外服务 | +| 认证方案 | JWT | 无状态认证 | + +### 架构优势 + +1. **极致性能**:静态页面零运行时,JS 按需加载 +2. **低资源占用**:Go 服务 + SQLite 可在 512MB 内存环境运行 +3. **SEO 友好**:纯静态 HTML 输出,搜索引擎完美抓取 +4. **开发体验**:组件化开发,热重载支持 + +--- + +## 项目结构 + +``` +NovaBlog/ +├── src/ # 前端源码 +│ ├── components/ # Vue/Astro 组件 +│ │ ├── CommentSection.vue # 评论区组件 +│ │ ├── LikeButton.vue # 点赞按钮 +│ │ ├── LoginForm.vue # 登录表单 +│ │ ├── UserStatus.vue # 用户状态栏 +│ │ ├── Counter.vue # 计数器示例 +│ │ ├── TypstBlock.astro # Typst 渲染组件 +│ │ └── TableOfContents.astro # 目录组件 +│ ├── content/ # 内容集合 +│ │ ├── config.ts # 内容配置 +│ │ └── blog/ # 博客文章 +│ ├── layouts/ # 布局组件 +│ │ ├── BaseLayout.astro # 基础布局 +│ │ └── PostLayout.astro # 文章布局 +│ ├── pages/ # 页面路由 +│ │ ├── index.astro # 首页 +│ │ ├── login.astro # 登录页 +│ │ ├── blog/ # 博客相关页面 +│ │ ├── tags/ # 标签页面 +│ │ └── categories/ # 分类页面 +│ ├── styles/ # 全局样式 +│ │ └── global.css # CSS 变量和全局样式 +│ ├── env.d.ts # 类型声明 +│ └── mdx-components.ts # MDX 组件注册 +├── public/ # 静态资源 +│ ├── images/ # 图片资源 +│ └── favicon.svg # 网站图标 +├── server/ # 后端服务 +│ ├── cmd/server/main.go # 服务入口 +│ ├── internal/ # 内部模块 +│ │ ├── config/ # 配置管理 +│ │ ├── database/ # 数据库连接 +│ │ ├── handlers/ # HTTP 处理器 +│ │ ├── middleware/ # 中间件 +│ │ ├── models/ # 数据模型 +│ │ └── utils/ # 工具函数 +│ ├── data/ # 数据文件 (SQLite) +│ ├── migrations/ # 数据库迁移 +│ ├── Dockerfile # Docker 构建文件 +│ └── go.mod # Go 依赖 +├── docs/ # 文档 +├── astro.config.mjs # Astro 配置 +├── tailwind.config.mjs # Tailwind 配置 +├── docker-compose.yml # Docker Compose 配置 +├── nginx.conf # Nginx 配置 +└── package.json # NPM 依赖 +``` + +--- + +## API 接口文档 + +### 基础信息 + +- **Base URL**: `http://localhost:8080/api` +- **认证方式**: JWT Bearer Token +- **内容格式**: JSON + +### 认证接口 + +#### 注册用户 + +```http +POST /api/auth/register +Content-Type: application/json + +{ + "username": "string", // 必填,3-50字符 + "email": "string", // 必填,有效邮箱 + "password": "string", // 必填,6-50字符 + "nickname": "string" // 可选 +} +``` + +**响应**: +```json +{ + "token": "eyJhbGciOiJIUzI1NiIs...", + "user": { + "id": 1, + "username": "testuser", + "email": "test@example.com", + "nickname": "测试用户", + "role": "user", + "created_at": "2024-01-15T10:00:00Z" + } +} +``` + +**错误码**: +- `400`: 请求参数无效 +- `409`: 用户名或邮箱已存在 + +#### 用户登录 + +```http +POST /api/auth/login +Content-Type: application/json + +{ + "username": "string", // 用户名或邮箱 + "password": "string" +} +``` + +**响应**: 同注册接口 + +#### 获取当前用户 + +```http +GET /api/auth/me +Authorization: Bearer +``` + +**响应**: +```json +{ + "user": { + "id": 1, + "username": "testuser", + "email": "test@example.com", + "nickname": "测试用户", + "avatar": "https://...", + "bio": "个人简介", + "role": "user" + } +} +``` + +#### 更新用户资料 + +```http +PUT /api/auth/profile +Authorization: Bearer +Content-Type: application/json + +{ + "nickname": "string", + "avatar": "string", + "bio": "string" +} +``` + +### 评论接口 + +#### 获取文章评论 + +```http +GET /api/comments?post_id={post_id}&page=1&page_size=20 +``` + +**参数**: +- `post_id` (必填): 文章 ID +- `page` (可选): 页码,默认 1 +- `page_size` (可选): 每页数量,默认 20,最大 100 + +**响应**: +```json +{ + "data": [ + { + "id": 1, + "post_id": "hello-novablog", + "user_id": 1, + "content": "很棒的文章!", + "status": "approved", + "created_at": "2024-01-15T10:00:00Z", + "user": { + "id": 1, + "username": "testuser", + "nickname": "测试用户", + "avatar": "https://..." + }, + "replies": [ + { + "id": 2, + "parent_id": 1, + "content": "感谢支持!", + "user": {...} + } + ] + } + ], + "pagination": { + "page": 1, + "page_size": 20, + "total": 100, + "total_page": 5 + } +} +``` + +#### 创建评论 + +```http +POST /api/comments +Authorization: Bearer +Content-Type: application/json + +{ + "post_id": "string", // 必填 + "content": "string", // 必填,1-2000字符 + "parent_id": 1 // 可选,回复的评论ID +} +``` + +#### 删除评论 + +```http +DELETE /api/comments/:id +Authorization: Bearer +``` + +**权限**: 本人或管理员可删除 + +### 点赞接口 + +#### 切换点赞状态 + +```http +POST /api/likes/toggle +Content-Type: application/json +Authorization: Bearer // 可选 + +{ + "post_id": "string" // 必填 +} +``` + +**响应**: +```json +{ + "liked": true, // 当前是否已点赞 + "like_count": 42 // 文章总点赞数 +} +``` + +**说明**: +- 已登录用户:基于 user_id 判断 +- 未登录用户:基于 IP Hash 判断(加盐防反向推导) + +#### 获取点赞状态 + +```http +GET /api/likes/status?post_id={post_id} +Authorization: Bearer // 可选 +``` + +**响应**: 同切换接口 + +### 错误响应格式 + +```json +{ + "error": "错误信息描述" +} +``` + +--- + +## 数据库结构 + +### users 表 + +```sql +CREATE TABLE users ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + created_at DATETIME, + updated_at DATETIME, + deleted_at DATETIME, -- 软删除 + username VARCHAR(50) UNIQUE NOT NULL, + email VARCHAR(100) UNIQUE NOT NULL, + password VARCHAR(255) NOT NULL, -- bcrypt 哈希 + nickname VARCHAR(50), + avatar VARCHAR(255), + role VARCHAR(20) DEFAULT 'user', -- 'admin' | 'user' + bio VARCHAR(500) +); + +CREATE INDEX idx_users_deleted_at ON users(deleted_at); +CREATE INDEX idx_users_username ON users(username); +CREATE INDEX idx_users_email ON users(email); +``` + +### comments 表 + +```sql +CREATE TABLE comments ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + created_at DATETIME, + updated_at DATETIME, + deleted_at DATETIME, + post_id VARCHAR(100) NOT NULL, -- 文章 slug + user_id INTEGER NOT NULL, -- 关联 users.id + parent_id INTEGER, -- 父评论ID,用于嵌套回复 + content TEXT NOT NULL, + status VARCHAR(20) DEFAULT 'approved', -- 'pending' | 'approved' | 'spam' + + FOREIGN KEY (user_id) REFERENCES users(id), + FOREIGN KEY (parent_id) REFERENCES comments(id) +); + +CREATE INDEX idx_comments_post_id ON comments(post_id); +CREATE INDEX idx_comments_user_id ON comments(user_id); +CREATE INDEX idx_comments_parent_id ON comments(parent_id); +CREATE INDEX idx_comments_deleted_at ON comments(deleted_at); +``` + +### likes 表 + +```sql +CREATE TABLE likes ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + created_at DATETIME, + post_id VARCHAR(100) NOT NULL, + user_id INTEGER, -- 登录用户ID,可为空 + ip_hash VARCHAR(64), -- 访客IP哈希 + + FOREIGN KEY (user_id) REFERENCES users(id) +); + +-- 防止同一用户重复点赞 +CREATE UNIQUE INDEX idx_post_user ON likes(post_id, user_id); +-- 防止同一IP重复点赞 +CREATE UNIQUE INDEX idx_post_ip ON likes(post_id, ip_hash); +CREATE INDEX idx_likes_user_id ON likes(user_id); +``` + +### like_counts 表 + +```sql +CREATE TABLE like_counts ( + post_id VARCHAR(100) PRIMARY KEY, + count INTEGER DEFAULT 0 +); +``` + +### post_meta 表(预留) + +```sql +CREATE TABLE post_meta ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + post_id VARCHAR(100) UNIQUE NOT NULL, + view_count INTEGER DEFAULT 0, + like_count INTEGER DEFAULT 0, + created_at DATETIME, + updated_at DATETIME +); + +CREATE INDEX idx_post_meta_post_id ON post_meta(post_id); +``` + +--- + +## 前端组件开发 + +### 创建 Vue 组件 + +在 `src/components/` 下创建 `.vue` 文件: + +```vue + + + + + + +``` + +### 在 MDX 中使用 + +```mdx +import MyComponent from '../components/MyComponent.vue'; + + +``` + +### 创建 Astro 组件 + +Astro 组件用于静态渲染,无客户端交互: + +```astro +--- +// src/components/StaticCard.astro +interface Props { + title: string; + content: string; +} + +const { title, content } = Astro.props; +--- + +
+

{title}

+

{content}

+
+ + +``` + +### 内容集合扩展 + +在 `src/content/config.ts` 中定义新的内容类型: + +```typescript +import { defineCollection, z } from 'astro:content'; + +const docs = defineCollection({ + type: 'content', + schema: z.object({ + title: z.string(), + description: z.string().optional(), + order: z.number().default(0), + }), +}); + +export const collections = { + blog: blogCollection, + docs, // 新增文档集合 +}; +``` + +### 自定义页面路由 + +在 `src/pages/` 下创建 `.astro` 文件: + +```astro +--- +// src/pages/about.astro +import BaseLayout from '../layouts/BaseLayout.astro'; +--- + + +
+

关于我

+

这是一个自定义页面。

+
+
+``` + +--- + +## 后端服务扩展 + +### 添加新的 API 路由 + +1. 创建处理器 (`server/internal/handlers/`): + +```go +// server/internal/handlers/post.go +package handlers + +import ( + "net/http" + "github.com/gin-gonic/gin" +) + +type PostHandler struct{} + +func NewPostHandler() *PostHandler { + return &PostHandler{} +} + +func (h *PostHandler) GetPosts(c *gin.Context) { + // 业务逻辑 + c.JSON(http.StatusOK, gin.H{ + "data": []string{}, + }) +} +``` + +2. 注册路由 (`server/cmd/server/main.go`): + +```go +postHandler := handlers.NewPostHandler() +api.GET("/posts", postHandler.GetPosts) +``` + +### 添加中间件 + +```go +// server/internal/middleware/rateLimit.go +package middleware + +import ( + "github.com/gin-gonic/gin" + "time" +) + +func RateLimit() gin.HandlerFunc { + // 实现限流逻辑 + return func(c *gin.Context) { + // 检查请求频率 + c.Next() + } +} +``` + +使用: +```go +api.Use(middleware.RateLimit()) +``` + +### 添加数据模型 + +```go +// server/internal/models/models.go +type Tag struct { + ID uint `json:"id" gorm:"primaryKey"` + CreatedAt time.Time `json:"created_at"` + Name string `json:"name" gorm:"size:50;uniqueIndex"` + Posts []Post `json:"posts" gorm:"many2many:post_tags;"` +} +``` + +自动迁移会在启动时执行。 + +--- + +## 部署配置 + +### Docker 部署 + +使用 `docker-compose.yml` 一键部署: + +```yaml +version: '3.8' +services: + frontend: + build: . + ports: + - "4321:80" + depends_on: + - api + + api: + build: ./server + ports: + - "8080:8080" + volumes: + - ./server/data:/app/data + environment: + - JWT_SECRET=your-secret-key + - ADMIN_USERNAME=admin + - ADMIN_PASSWORD=admin123 +``` + +启动: +```bash +docker-compose up -d +``` + +### 手动部署 + +#### 前端构建 + +```bash +npm run build +``` + +产物在 `dist/` 目录,可部署到任何静态托管服务。 + +#### 后端构建 + +```bash +cd server +go build -o novablog-server cmd/server/main.go +``` + +运行: +```bash +./novablog-server +``` + +### Nginx 配置 + +```nginx +server { + listen 80; + server_name example.com; + + # 静态文件 + root /var/www/novablog/dist; + index index.html; + + location / { + try_files $uri $uri/ /index.html; + } + + # API 代理 + location /api { + proxy_pass http://localhost:8080; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_cache_bypass $http_upgrade; + } +} +``` + +### 环境变量 + +| 变量名 | 说明 | 默认值 | +|--------|------|--------| +| `PORT` | 服务端口 | `8080` | +| `JWT_SECRET` | JWT 密钥 | 随机生成 | +| `ADMIN_USERNAME` | 管理员用户名 | `admin` | +| `ADMIN_PASSWORD` | 管理员密码 | `admin123` | +| `DB_PATH` | 数据库路径 | `./data/novablog.db` | + +--- + +## 性能优化 + +### 前端优化 + +1. **图片优化** + - 使用 WebP 格式 + - 添加 `loading="lazy"` 懒加载 + - 使用 `srcset` 响应式图片 + +2. **代码分割** + - Astro 默认零 JS + - 交互组件使用 `client:visible` 懒加载 + +3. **缓存策略** + - 静态资源设置长期缓存 + - HTML 设置短期缓存或 ETag + +### 后端优化 + +1. **数据库索引** + - 已在关键字段建立索引 + - 复杂查询使用 EXPLAIN 分析 + +2. **连接池** + - SQLite 使用 WAL 模式 + - 设置合理的连接池大小 + +3. **响应压缩** + - 启用 Gzip 压缩 + - API 响应体积减少 70%+ + +### 监控指标 + +```bash +# 查看 Go 服务内存占用 +ps aux | grep novablog-server + +# 查看数据库大小 +ls -lh server/data/novablog.db +``` + +--- + +## 附录 + +### 常用命令 + +```bash +# 开发 +npm run dev # 启动前端开发服务器 +cd server && go run . # 启动后端服务 + +# 构建 +npm run build # 构建前端 +cd server && go build . # 构建后端 + +# 数据库 +sqlite3 server/data/novablog.db # 打开数据库 CLI + +# Docker +docker-compose up -d # 启动所有服务 +docker-compose logs -f # 查看日志 +docker-compose down # 停止服务 +``` + +### 技术参考 + +- [Astro 文档](https://docs.astro.build) +- [Vue 3 文档](https://vuejs.org) +- [Tailwind CSS 文档](https://tailwindcss.com) +- [Gin 框架文档](https://gin-gonic.com) +- [GORM 文档](https://gorm.io) +- [Typst 文档](https://typst.app/docs) + +--- + +如有问题或建议,欢迎提交 Issue 或 Pull Request! \ No newline at end of file diff --git a/docs/user-guide.md b/docs/user-guide.md new file mode 100644 index 0000000..62cb157 --- /dev/null +++ b/docs/user-guide.md @@ -0,0 +1,610 @@ +# NovaBlog 使用指南 + +欢迎使用 NovaBlog!这是一款面向程序员和极客的轻量级混合架构博客系统。本指南将帮助您快速上手使用 NovaBlog 的各项功能。 + +--- + +## 目录 + +1. [快速开始](#快速开始) +2. [文章管理](#文章管理) +3. [MDX 组件使用](#mdx-组件使用) +4. [Typst 学术排版](#typst-学术排版) +5. [动效 HTML 块](#动效-html-块) +6. [评论系统](#评论系统) +7. [用户注册与登录](#用户注册与登录) +8. [主题定制](#主题定制) +9. [附件管理](#附件管理) +10. [常见问题](#常见问题) + +--- + +## 快速开始 + +### 环境要求 + +- Node.js 18+ +- Go 1.21+ (仅后端开发需要) +- Typst 0.11+ (可选,用于数学公式渲染) + +### 启动开发服务器 + +```bash +# 启动前端开发服务器 +npm run dev + +# 启动后端 API 服务器 +cd server && go run cmd/server/main.go +``` + +访问 `http://localhost:4321` 即可预览博客。 + +--- + +## 文章管理 + +### 创建新文章 + +所有文章存放在 `src/content/blog/` 目录下,支持 `.md` 和 `.mdx` 格式。 + +#### 文章命名规范 + +推荐使用以下命名格式: +- `my-first-post.md` - 英文,小写,连字符分隔 +- `使用指南.md` - 中文也可以,但 URL 会进行编码 + +#### Frontmatter 字段说明 + +每篇文章需要在开头添加 Frontmatter 元数据: + +```yaml +--- +title: 文章标题 +description: 文章描述,用于 SEO 和社交分享 +pubDate: 2024-01-15 +updatedDate: 2024-01-20 # 可选,更新日期 +heroImage: /images/cover.jpg # 可选,封面图 +heroAlt: 封面图描述 # 可选 +author: 作者名 # 可选,默认为站点名称 +tags: # 可选,标签列表 + - JavaScript + - 教程 +category: 技术 # 可选,分类 +--- + +文章内容从这里开始... +``` + +#### 示例文章 + +```markdown +--- +title: 我的第一篇博客 +description: 这是我使用 NovaBlog 发布的第一篇文章 +pubDate: 2024-01-15 +heroImage: /images/hello-world.jpg +tags: + - 随笔 + - 博客 +category: 生活 +--- + +## 欢迎来到我的博客 + +这是正文内容。支持标准 Markdown 语法。 + +### 列表 + +- 项目 1 +- 项目 2 +- 项目 3 + +### 代码块 + +```javascript +console.log('Hello, NovaBlog!'); +``` + +### 引用 + +> 这是一段引用文字。 +``` + +### 文章状态 + +目前文章发布即公开,后续版本将支持: +- `draft: true` - 草稿状态 +- `published: false` - 隐藏文章 + +### 删除文章 + +直接删除 `src/content/blog/` 目录下的对应文件即可。 + +--- + +## MDX 组件使用 + +NovaBlog 原生支持 MDX,允许在 Markdown 中嵌入交互式组件。 + +### 什么是 MDX? + +MDX = Markdown + JSX。它让您可以在 Markdown 中直接使用 React/Vue 组件。 + +### 内置组件 + +#### Counter 计数器组件 + +```mdx +import Counter from '../components/Counter.vue'; + + +``` + +#### TypstBlock 数学公式 + +```mdx +import TypstBlock from '../components/TypstBlock.astro'; + + +``` + +### 自定义组件 + +您可以在 `src/components/` 目录下创建自己的组件: + +```vue + + + + + + +``` + +然后在文章中使用: + +```mdx +import MyButton from '../components/MyButton.vue'; + +点击我 +``` + +### 组件交互性 + +使用 `client:*` 指令控制组件的客户端行为: + +| 指令 | 说明 | +|------|------| +| `client:load` | 页面加载时立即激活 | +| `client:visible` | 组件进入视口时激活 | +| `client:idle` | 浏览器空闲时激活 | +| `client:media` | 满足媒体查询时激活 | + +示例: +```mdx +进入视口时激活 +``` + +--- + +## Typst 学术排版 + +NovaBlog 内置 Typst 支持,可以渲染高质量的数学公式和学术排版。 + +### 什么是 Typst? + +Typst 是新一代排版系统,具有: +- 比 LaTeX 更简洁的语法 +- 更快的编译速度 +- 原生支持数学公式 + +### 基本用法 + +```mdx +import TypstBlock from '../components/TypstBlock.astro'; + + +``` + +### 数学公式示例 + +#### 积分 + +```typst +$ integral_0^1 x^2 dif x = 1/3 $ +``` + +#### 求和 + +```typst +$ sum_(i=1)^n i = (n(n+1))/2 $ +``` + +#### 矩阵 + +```typst +$ A = mat( + 1, 2, 3; + 4, 5, 6; + 7, 8, 9 +) $ +``` + +#### 分数 + +```typst +$ (a + b) / (c + d) $ +``` + +#### 上下标 + +```typst +$ x_1^2 + x_2^2 = r^2 $ +``` + +### 高级排版 + +Typst 还支持: +- 多行公式对齐 +- 定理环境 +- 化学方程式 +- 代码高亮 + +更多语法请参考 [Typst 官方文档](https://typst.app/docs)。 + +--- + +## 动效 HTML 块 + +在 MDX 中可以直接使用 HTML 标签和内联样式,创建带动画的内容块。 + +### 示例:渐变背景卡片 + +```mdx +
+

✨ 特色卡片

+

这是一个带渐变背景的卡片

+
+``` + +### 示例:CSS 动画 + +```mdx + + +
+ 🔔 脉冲动画效果 +
+``` + +### 示例:悬停效果 + +```mdx + + +
+ 🖱️ 鼠标悬停试试 +
+``` + +### 示例:交互式组件 + +结合 Vue 组件创建交互式内容: + +```vue + + + + + + +``` + +--- + +## 评论系统 + +NovaBlog 内置评论系统,支持多级嵌套回复和 Markdown 语法。 + +### 发表评论 + +1. 在文章页面滚动到评论区 +2. 点击登录按钮进行用户认证 +3. 输入评论内容(支持 Markdown) +4. 点击发送 + +### 回复评论 + +1. 点击评论下方的"回复"按钮 +2. 输入回复内容 +3. 提交后将显示在原评论下方 + +### Markdown 支持 + +评论区支持基础 Markdown 语法: + +| 语法 | 效果 | +|------|------| +| `**粗体**` | **粗体** | +| `*斜体*` | *斜体* | +| `` `代码` `` | `代码` | +| `[链接](url)` | [链接](url) | +| `> 引用` | 引用块 | + +### 删除评论 + +用户可以删除自己发表的评论。管理员可以删除任何评论。 + +--- + +## 用户注册与登录 + +### 注册账号 + +1. 点击页面右上角的"登录"按钮 +2. 在登录弹窗中点击"注册" +3. 填写用户名、邮箱和密码 +4. 提交注册 + +### 登录账号 + +支持两种登录方式: +- 用户名 + 密码 +- 邮箱 + 密码 + +### 用户角色 + +| 角色 | 权限 | +|------|------| +| `user` | 发表评论、删除自己的评论 | +| `admin` | 所有权限 + 删除任意评论 | + +### 修改个人资料 + +登录后可以修改: +- 昵称 +- 头像 URL +- 个人简介 + +--- + +## 主题定制 + +### CSS 变量 + +NovaBlog 使用 CSS 变量实现主题系统,可在 `src/styles/global.css` 中修改: + +```css +:root { + /* 主色调 */ + --color-primary-50: #eff6ff; + --color-primary-100: #dbeafe; + --color-primary-500: #3b82f6; + --color-primary-600: #2563eb; + + /* 背景色 */ + --color-background: #ffffff; + --color-muted: #f3f4f6; + + /* 文字色 */ + --color-foreground: #1f2937; + --color-muted-foreground: #6b7280; + + /* 边框色 */ + --color-border: #e5e7eb; +} +``` + +### 暗色主题 + +修改 CSS 变量实现暗色模式: + +```css +.dark { + --color-background: #1a1a2e; + --color-foreground: #eaeaea; + --color-muted: #16213e; + --color-border: #0f3460; +} +``` + +### Tailwind 配置 + +在 `tailwind.config.mjs` 中自定义主题: + +```javascript +export default { + theme: { + extend: { + colors: { + primary: { + 50: '#eff6ff', + // ... 更多颜色 + } + }, + fontFamily: { + sans: ['Inter', 'sans-serif'], + } + } + } +} +``` + +### 布局组件 + +主要布局文件: +- `src/layouts/BaseLayout.astro` - 基础布局(Header/Footer) +- `src/layouts/PostLayout.astro` - 文章布局(目录/评论) + +--- + +## 附件管理 + +### 图片存放位置 + +将图片放在 `public/images/` 目录下: + +``` +public/ +├── images/ +│ ├── posts/ +│ │ ├── hello-world.jpg +│ │ └── tutorial-cover.png +│ └── avatars/ +│ └── default.png +``` + +### 在文章中引用 + +```markdown +![图片描述](/images/posts/hello-world.jpg) +``` + +### 在 Frontmatter 中使用封面图 + +```yaml +--- +heroImage: /images/posts/my-cover.jpg +heroAlt: 这是一张封面图 +--- +``` + +### 其他附件 + +PDF、ZIP 等文件也放在 `public/` 目录: + +``` +public/ +├── downloads/ +│ └── source-code.zip +└── documents/ + └── paper.pdf +``` + +引用方式: +```markdown +[下载源码](/downloads/source-code.zip) +[查看论文](/documents/paper.pdf) +``` + +### 图片优化建议 + +- 使用 WebP 格式减少文件大小 +- 封面图推荐尺寸:1920x1080 +- 文章内图片推荐宽度:800px +- 压缩工具:[Squoosh](https://squoosh.app/) + +--- + +## 常见问题 + +### Q: 文章修改后没有更新? + +A: 开发模式下 Astro 会自动热重载。如果生产构建,需要重新运行 `npm run build`。 + +### Q: Typst 公式渲染失败? + +A: 确保 Typst 已正确安装。运行 `typst --version` 检查。 + +### Q: 评论无法发送? + +A: 检查后端服务是否正常运行在 `localhost:8080`。 + +### Q: 如何修改站点信息? + +A: 编辑 `src/layouts/BaseLayout.astro` 和 `astro.config.mjs` 中的站点配置。 + +### Q: 如何添加 Google Analytics? + +A: 在 `src/layouts/BaseLayout.astro` 的 `` 中添加 GA 脚本。 + +--- + +## 获取帮助 + +- 📖 查看 [开发文档](./developer-guide.md) 了解技术细节 +- 🐛 提交 Issue 反馈问题 +- 💬 在 GitHub Discussions 中讨论 \ No newline at end of file diff --git a/src/layouts/PostLayout.astro b/src/layouts/PostLayout.astro index 3d2103b..69abe43 100644 --- a/src/layouts/PostLayout.astro +++ b/src/layouts/PostLayout.astro @@ -153,9 +153,9 @@ const tocItems = headings.filter(h => h.depth >= 2 && h.depth <= 3);
-
+
-
+
@@ -163,7 +163,7 @@ const tocItems = headings.filter(h => h.depth >= 2 && h.depth <= 3); {hasToc && ( -