Files
nanocode/personal-blog/docs/03-frontend-design.md
T
Ubuntu 32b43f85bd a
2026-07-03 16:58:28 +08:00

420 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Personal Blog - 前端设计文档
> React 18 + Vite + Tailwind CSS + 双主题架构
---
## 1. 项目结构
```
frontend/
├── index.html # HTML 入口(Vite 注入 JS)
├── vite.config.js # Vite 配置(proxy、host、port)
├── tailwind.config.js # Tailwind CSS 配置
├── package.json # 依赖管理
└── src/
├── main.jsx # React 根渲染
├── App.jsx # 根路由配置
├── index.css # 全局样式(双主题 CSS 变量 + 组件类)
├── api/ # API 封装层
│ ├── config.js # Axios 实例 + 拦截器
│ ├── posts.js # 文章 API
│ └── categories.js # 分类 API
├── pages/ # 页面组件(路由级)
│ ├── Home.jsx # 访客端首页
│ ├── PostDetail.jsx # 访客端文章详情
│ ├── PostEdit.jsx # 文章编辑/新建
│ ├── Posts.jsx # 文章管理列表
│ ├── CrawlEdit.jsx # 通用网页采集
│ ├── WechatCrawlEdit.jsx # 公众号采集
│ ├── Categories.jsx # 分类管理
│ ├── Tags.jsx # 标签管理
│ ├── Images.jsx # 图片管理
│ ├── Settings.jsx # 系统设置
│ ├── Login.jsx # 登录页
│ └── About.jsx # 关于页面
├── components/ # 访客端通用组件
│ ├── Layout.jsx # 访客端页面布局(含导航、页脚)
│ └── PostCard.jsx # 文章列表卡片
└── components-preline/ # 管理后台组件库
├── Sidebar.jsx # 侧边栏导航
├── Card.jsx # 卡片容器
├── Button.jsx # 按钮
└── Input.jsx # 输入框/文本域
```
---
## 2. 路由架构 (App.jsx)
```
React Router v7 (BrowserRouter)
│
├── 访客端路径 (Layout 包裹)
│ ├── / → Home.jsx 首页文章列表
│ ├── /post/:slug → PostDetail.jsx 文章详情
│ ├── /category/:id → Home.jsx 分类筛选
│ ├── /tag/:name → Home.jsx 标签筛选
│ ├── /about → About.jsx 关于页面
│ └── /page/:slug → PageView.jsx 自定义页面
│
└── 管理后台路径 (AdminLayout 包裹,需登录)
├── /admin → Dashboard 仪表盘
├── /admin/posts → Posts.jsx 文章列表
├── /admin/posts/new → PostEdit.jsx 新建文章
├── /admin/posts/:id/edit → PostEdit.jsx 编辑文章
├── /admin/crawl → CrawlEdit.jsx 通用采集
├── /admin/wechat-crawl → WechatCrawlEdit.jsx 公众号采集
├── /admin/categories → Categories.jsx 分类管理
├── /admin/tags → Tags.jsx 标签管理
├── /admin/images → Images.jsx 图片管理
├── /admin/settings → Settings.jsx 系统设置
└── /admin/login → Login.jsx 登录页
```
### 2.1 路由守卫
管理后台路由通过 `localStorage.getItem('admin_token')` 判断登录状态:
```jsx
// App.jsx 中的路由守卫逻辑
const isAdminRoute = location.pathname.startsWith('/admin')
const isLoginPage = location.pathname === '/admin/login'
useEffect(() => {
if (isAdminRoute && !isLoginPage && !localStorage.getItem('admin_token')) {
navigate('/admin/login')
}
}, [location.pathname])
```
- 未登录访问管理后台 → 强制跳转到登录页
- 登录页不检查 Token(避免循环跳转)
---
## 3. 双主题设计
项目同时存在两种视觉风格,通过 CSS 变量和独立组件类实现共存。
### 3.1 主题对比
| 维度 | 访客端 (Neobrutalism) | 管理后台 (Preline Admin) |
|------|----------------------|-------------------------|
| **定位** | 面向读者,活泼有趣 | 面向管理员,专业高效 |
| **色彩** | 奶油色底 + 高饱和撞色(粉/黄/青/紫) | 白色底 + 深蓝灰 |
| **边框** | 粗黑实线(2px),直角 | 细灰实线(1px),圆角(rounded-lg) |
| **阴影** | 硬阴影偏移(4px 4px 0) | 柔和阴影(shadow-sm/md) |
| **按钮** | 块状,hover 位移凹陷 | 圆角,hover 背景加深 |
| **滚动条** | 自定义粗黑风格 | 浏览器默认 |
| **选中文字** | 黄色背景 | 默认 |
### 3.2 CSS 变量体系
```css
:root {
/* Neobrutalism 默认配色 */
--brutal-cream: #FEF9E7;
--brutal-yellow: #FFDE59;
--brutal-pink: #FF6B9D;
--brutal-cyan: #00D9FF;
--brutal-lavender: #C084FC;
--brutal-green: #4ADE80;
/* 默认使用 Neobrutalism */
--color-bg: var(--brutal-cream);
--color-card: #FFFFFF;
--color-primary: var(--brutal-pink);
--color-text: #000000;
--border-width: 2px;
--border-radius: 0px;
--shadow: 4px 4px 0px 0px rgba(0,0,0,1);
}
```
### 3.3 组件类映射
访客端使用 `@layer components` 中定义的 `*-brutal` 类:
- `.btn-brutal` / `.btn-brutal-sm`
- `.card-brutal` / `.card-brutal-sm`
- `.input-brutal`
- `.nav-link` / `.nav-link-active`
管理后台使用 `*-preline` 类或直接写 Tailwind 工具类:
- `.btn-preline` / `.btn-preline-secondary` / `.btn-preline-danger`
- `.card-preline` / `.card-preline-header` / `.card-preline-body`
- `.input-preline` / `.label-preline`
- `.sidebar-preline` 等
### 3.4 实现方式
两种主题不是通过切换 CSS 变量实现的,而是:
- **访客端组件** 硬编码使用 `*-brutal` 类
- **管理后台组件** 硬编码使用 `*-preline` 类或 Tailwind 工具类
共存于同一个 `index.css` 文件中,按页面路由分别加载对应组件。
---
## 4. API 层 (api/config.js)
### 4.1 Axios 实例配置
```javascript
const api = axios.create({
baseURL: 'http://localhost:8889/api', // 开发环境
timeout: 30000,
})
```
### 4.2 请求拦截器
自动注入 `Authorization` 头部:
```javascript
api.interceptors.request.use(config => {
const token = localStorage.getItem('admin_token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
})
```
### 4.3 响应拦截器
Token 过期时自动跳转登录页:
```javascript
api.interceptors.response.use(
response => response,
error => {
if (error.response?.status === 401) {
localStorage.removeItem('admin_token')
window.location.href = '/admin/login'
}
return Promise.reject(error)
}
)
```
---
## 5. 管理后台布局 (AdminLayout)
```
┌─────────────────────────────────────────────────────────────┐
│ ┌──────┐ Header │
│ │ Logo │ 面包屑 / 页面标题 管理员 / 退出 │
│ └──────┘────────────────────────────────────────────────── │
│ ┌────────┐ ┌────────────────────────────────────────────┐ │
│ │ 首页 │ │ │ │
│ │ 文章 │ │ 页面内容区域 │ │
│ │ 采集 │ │ │ │
│ │ 分类 │ │ │ │
│ │ 标签 │ │ │ │
│ │ 图片 │ │ │ │
│ │ 设置 │ │ │ │
│ └────────┘ └────────────────────────────────────────────┘ │
│ Sidebar Main Content │
└─────────────────────────────────────────────────────────────┘
```
### 5.1 侧边栏导航 (Sidebar.jsx)
```javascript
const menuItems = [
{ name: '首页', path: '/admin', icon: Home },
{ name: '文章管理', path: '/admin/posts', icon: FileText },
{ name: '文章采集', path: '/admin/crawl', icon: Globe },
{ name: '公众号采集', path: '/admin/wechat-crawl', icon: MessageSquare },
{ name: '分类管理', path: '/admin/categories', icon: Folder },
{ name: '标签管理', path: '/admin/tags', icon: Tag },
{ name: '图片管理', path: '/admin/images', icon: Image },
{ name: '系统设置', path: '/admin/settings', icon: Settings },
]
```
- 固定左侧,宽度 64 (折叠) / 256 (展开) px
- 当前路由高亮
- 移动端自适应汉堡菜单
### 5.2 退出登录
```javascript
const handleLogout = () => {
localStorage.removeItem('admin_token')
window.location.href = '/admin/login'
}
```
---
## 6. 访客端布局 (Layout.jsx)
```
┌─────────────────────────────────────────────┐
│ Logo 首页 · 文章 · 分类 · 关于 · 管理后台 │
├─────────────────────────────────────────────┤
│ │
│ 页面内容 (Outlet) │
│ │
├─────────────────────────────────────────────┤
│ Footer (版权 / 备案) │
└─────────────────────────────────────────────┘
```
### 6.1 导航栏
- Neobrutalism 风格:粗黑边框、黄底高亮当前项
- 移动端:汉堡菜单 + 抽屉式导航
- 导航项从后端 `/api/settings` 动态获取(自定义页面)
### 6.2 页脚
- 左侧:站点名称 + 描述 + 版权
- 右侧:备案号链接 + 社交媒体图标(可选)
- 返回顶部按钮
---
## 7. 核心页面详解
### 7.1 访客端首页 (Home.jsx)
**功能**:
- 展示文章卡片网格
- 支持按分类/标签筛选
- 搜索框
- 分页
**文章卡片 (PostCard.jsx)**:
- 封面图(如有)
- 标题
- 摘要(截断至 120 字符)
- 分类标签
- 日期 + 阅读数
### 7.2 访客端文章详情 (PostDetail.jsx)
**功能**:
- 文章标题、元信息(日期/分类/标签)
- 封面图
- 正文渲染(见 `05-markdown-rendering.md`)
- 分享按钮
- 上一篇/下一篇导航
### 7.3 文章编辑 (PostEdit.jsx)
**功能**:
- 新建/编辑双模式
- 左右双栏布局:
- 左栏:标题、摘要、正文(Markdown textarea)、操作按钮
- 右栏:分类、标签、封面图上传、文章状态
- Markdown 实时预览(react-markdown)
- 保存草稿 / 发布 / 取消
### 7.4 文章列表 (Posts.jsx)
**功能**:
- 表格展示所有文章
- 搜索、分类筛选、状态筛选
- 分页
- 批量操作(删除)
- 快捷操作:编辑、删除、预览
---
## 8. 状态管理
项目未使用 Redux/Zustand 等全局状态库,采用 **React 内置状态 + 服务端状态**:
| 数据 | 管理方式 |
|------|----------|
| 登录 Token | `localStorage` |
| 文章列表 | `useState` + `useEffect` 调用 API |
| 文章详情 | `useState` + `useEffect` |
| 分类/标签 | `useState` + `useEffect` |
| 系统设置 | `useState` + `useEffect` |
| 表单数据 | `useState` (受控组件) |
### 8.1 典型数据获取模式
```jsx
const [posts, setPosts] = useState([])
const [loading, setLoading] = useState(true)
useEffect(() => {
api.get('/posts', { params: { skip: 0, limit: 10 } })
.then(res => setPosts(res.data.items))
.catch(err => console.error(err))
.finally(() => setLoading(false))
}, [])
```
---
## 9. 构建配置 (vite.config.js)
```javascript
export default defineConfig({
plugins: [react()],
server: {
host: '0.0.0.0',
port: 8888,
proxy: {
'/api': { target: 'http://localhost:8889', changeOrigin: true },
'/uploads': { target: 'http://localhost:8889', changeOrigin: true },
},
},
})
```
### 9.1 代理配置
开发环境下,前端请求 `/api/*` 和 `/uploads/*` 自动代理到后端(:8889),解决跨域问题。
### 9.2 生产部署
```bash
npm run build # 输出到 dist/ 目录
```
生产环境使用 `serve` 或 Nginx 托管静态文件:
```bash
npx serve -s dist -l 8888 --cors
```
---
## 10. Tailwind 配置
### 10.1 自定义颜色
```javascript
// tailwind.config.js
theme: {
extend: {
colors: {
'brutal-cream': '#FEF9E7',
'brutal-yellow': '#FFDE59',
'brutal-pink': '#FF6B9D',
'brutal-cyan': '#00D9FF',
'brutal-lavender': '#C084FC',
'brutal-green': '#4ADE80',
},
},
}
```
### 10.2 插件
- `@tailwindcss/forms` — 表单元素基础样式
- `preline` — Preline UI JavaScript 组件交互
---
*本文档由 nanobot 根据项目源码自动生成。*