420 lines
13 KiB
Markdown
420 lines
13 KiB
Markdown
# 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 根据项目源码自动生成。*
|