13 KiB
13 KiB
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') 判断登录状态:
// 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 变量体系
: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 实例配置
const api = axios.create({
baseURL: 'http://localhost:8889/api', // 开发环境
timeout: 30000,
})
4.2 请求拦截器
自动注入 Authorization 头部:
api.interceptors.request.use(config => {
const token = localStorage.getItem('admin_token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
})
4.3 响应拦截器
Token 过期时自动跳转登录页:
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)
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 退出登录
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 典型数据获取模式
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)
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 生产部署
npm run build # 输出到 dist/ 目录
生产环境使用 serve 或 Nginx 托管静态文件:
npx serve -s dist -l 8888 --cors
10. Tailwind 配置
10.1 自定义颜色
// 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 根据项目源码自动生成。