Files
ocean_project_manager/frontend/docs/frontend-architecture.md
T
2026-01-25 15:05:03 +08:00

688 lines
19 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.
# 海洋项目管理系统 - 前端技术架构文档
## 1. 前端技术栈
### 1.1 核心框架
- **React 18**: 用于构建用户界面的JavaScript库
- **React Router 6**: 客户端路由管理
- **Vite**: 现代化的前端构建工具(替代Create React App
### 1.2 UI组件库
- **Ant Design 5**: 企业级UI组件库
- **@ant-design/icons**: Ant Design图标库
### 1.3 HTTP客户端
- **Axios**: HTTP请求库
### 1.4 状态管理
- **React Context API**: 轻量级状态管理
- **useReducer Hook**: 复杂状态逻辑
### 1.5 表单处理
- **Ant Design Form**: 表单组件和验证
- **react-hook-form**: 表单状态管理(可选)
### 1.6 工具库
- **dayjs**: 日期处理(轻量级替代moment.js)
- **lodash**: 工具函数库
- **classnames**: 条件类名处理
### 1.7 开发工具
- **ESLint**: 代码质量检查
- **Prettier**: 代码格式化
- **Vitest**: 单元测试框架
- **React Testing Library**: 组件测试
- **TypeScript**: 类型检查(可选,当前使用JavaScript)
### 1.8 构建和部署
- **Vite**: 开发服务器和构建工具
- **Docker**: 容器化部署(生产环境)
## 2. 前端目录结构
```
frontend/
├── src/ # 源代码目录
│ ├── assets/ # 静态资源
│ │ ├── images/ # 图片
│ │ ├── fonts/ # 字体文件
│ │ └── styles/ # 全局样式
│ │ ├── global.css # 全局CSS
│ │ ├── variables.css # CSS变量
│ │ └── mixins.css # CSS mixins
│ ├── components/ # 可复用组件
│ │ ├── Layout/ # 布局组件
│ │ │ ├── Layout.jsx # 主布局
│ │ │ ├── Header.jsx # 顶部导航
│ │ │ └── Sidebar.jsx # 侧边栏
│ │ ├── Common/ # 通用组件
│ │ │ ├── Button.jsx # 按钮组件
│ │ │ ├── Input.jsx # 输入框组件
│ │ │ ├── Select.jsx # 选择框组件
│ │ │ ├── Table.jsx # 表格组件
│ │ │ ├── Modal.jsx # 弹窗组件
│ │ │ ├── Form.jsx # 表单组件
│ │ │ ├── Card.jsx # 卡片组件
│ │ │ └── Tag.jsx # 标签组件
│ │ ├── Business/ # 业务组件
│ │ │ ├── ProjectCard.jsx # 项目卡片
│ │ │ ├── StatCard.jsx # 统计卡片
│ │ │ └── UserAvatar.jsx # 用户头像
│ │ └── index.js # 组件导出
│ ├── pages/ # 页面组件
│ │ ├── Login/ # 登录页
│ │ │ └── Login.jsx
│ │ ├── Dashboard/ # 仪表盘
│ │ │ └── Dashboard.jsx
│ │ ├── Projects/ # 项目管理
│ │ │ ├── ProjectList.jsx # 项目列表
│ │ │ ├── ProjectForm.jsx # 项目表单
│ │ │ ├── ProjectDetail.jsx # 项目详情
│ │ │ └── ProjectStatistics.jsx # 项目统计
│ │ ├── Users/ # 用户管理(仅管理员)
│ │ │ ├── UserList.jsx # 用户列表
│ │ │ ├── UserForm.jsx # 用户表单
│ │ │ └── UserResetPassword.jsx # 重置密码
│ │ └── Statistics/ # 统计分析
│ │ └── Statistics.jsx
│ ├── contexts/ # Context上下文
│ │ ├── AuthContext.jsx # 认证上下文
│ │ └── ThemeContext.jsx # 主题上下文(可选)
│ ├── hooks/ # 自定义Hooks
│ │ ├── useAuth.js # 认证Hook
│ │ ├── useApi.js # API调用Hook
│ │ ├── usePermission.js # 权限检查Hook
│ │ ├── useTable.js # 表格Hook
│ │ ├── useForm.js # 表单Hook
│ │ └── useDebounce.js # 防抖Hook
│ ├── services/ # API服务
│ │ ├── api.js # Axios配置
│ │ ├── auth.js # 认证API
│ │ ├── user.js # 用户API
│ │ ├── project.js # 项目API
│ │ └── statistics.js # 统计API
│ ├── utils/ # 工具函数
│ │ ├── request.js # 请求封装
│ │ ├── storage.js # 本地存储
│ │ ├── auth.js # 认证工具
│ │ ├── format.js # 格式化函数
│ │ ├── validation.js # 验证函数
│ │ └── constants.js # 常量定义
│ ├── config/ # 配置文件
│ │ ├── routes.js # 路由配置
│ │ ├── menu.js # 菜单配置
│ │ ├── colors.js # 颜色配置
│ │ └── app.config.js # 应用配置
│ ├── router/ # 路由相关
│ │ ├── index.jsx # 路由配置
│ │ ├── PrivateRoute.jsx # 路由守卫
│ │ └── routes.js # 路由定义
│ ├── styles/ # 样式文件
│ │ └── index.css # 主样式文件
│ ├── App.jsx # 根组件
│ └── main.jsx # 应用入口
├── tests/ # 测试目录
│ ├── components/ # 组件测试
│ ├── pages/ # 页面测试
│ ├── hooks/ # Hooks测试
│ └── utils/ # 工具测试
├── public/ # 公共资源
│ ├── favicon.ico # 网站图标
│ ├── logo.png # Logo
│ └── robots.txt # 爬虫配置
├── docs/ # 前端文档
│ ├── frontend-architecture.md # 前端技术架构(本文档)
│ ├── component-design.md # 组件设计文档
│ ├── api-service.md # API服务封装
│ ├── state-management.md # 状态管理设计
│ ├── routing-design.md # 路由设计文档
│ └── development-guide.md # 开发指南
├── .env # 环境变量
├── .env.development # 开发环境变量
├── .env.production # 生产环境变量
├── .eslintrc.js # ESLint配置
├── .prettierrc # Prettier配置
├── package.json # 项目依赖
├── vite.config.js # Vite配置
├── index.html # HTML模板
├── WORKSTANDARDS.md # 前端工作规范
└── README.md # 前端说明文档
```
## 3. 组件架构
### 3.1 组件层级
```
App
├── AuthProvider (认证上下文)
├── Router (路由)
│ ├── PrivateRoute (路由守卫)
│ ├── Login (登录页)
│ └── Layout (主布局)
│ ├── Header (顶部导航)
│ ├── Sidebar (侧边栏)
│ └── Content (内容区)
│ ├── Dashboard (仪表盘)
│ ├── ProjectList (项目列表)
│ ├── ProjectForm (项目表单)
│ ├── ProjectDetail (项目详情)
│ ├── UserList (用户列表)
│ ├── UserForm (用户表单)
│ └── Statistics (统计页)
```
### 3.2 组件设计原则
#### 3.2.1 单一职责原则
- 每个组件只负责一个功能
- 避免组件过大,合理拆分
#### 3.2.2 组件复用性
- 提取可复用的通用组件到 `components/Common/`
- 提取业务相关组件到 `components/Business/`
#### 3.2.3 组件分类
- **布局组件**: Layout, Header, Sidebar
- **通用组件**: Button, Input, Form, Modal, Table
- **业务组件**: ProjectCard, StatCard, UserAvatar
- **页面组件**: Login, Dashboard, ProjectList等
#### 3.2.4 组件Props规范
```jsx
// 推荐的Props定义方式
const MyComponent = ({
title, // 必填props
value, // 可选props
onChange, // 事件回调
className, // 样式类名
style, // 内联样式
children, // 子元素
}) => {
return <div>{title}</div>;
};
MyComponent.propTypes = {
title: PropTypes.string.isRequired,
value: PropTypes.string,
onChange: PropTypes.func,
className: PropTypes.string,
style: PropTypes.object,
children: PropTypes.node,
};
MyComponent.defaultProps = {
value: '',
onChange: () => {},
className: '',
style: {},
};
```
## 4. 状态管理
### 4.1 Context API方案
#### 4.1.1 AuthContext(认证上下文)
```javascript
// contexts/AuthContext.jsx
const AuthContext = createContext();
export const AuthProvider = ({ children }) => {
const [user, setUser] = useState(null);
const [token, setToken] = useState(null);
const [loading, setLoading] = useState(true);
// 登录
const login = async (username, password) => {
// 登录逻辑
};
// 登出
const logout = () => {
// 登出逻辑
};
// 检查登录状态
const checkAuth = async () => {
// 检查逻辑
};
return (
<AuthContext.Provider
value={{
user,
token,
loading,
login,
logout,
checkAuth,
isAuthenticated: !!user,
}}
>
{children}
</AuthContext.Provider>
);
};
```
#### 4.1.2 ThemeContext(主题上下文,可选)
```javascript
// contexts/ThemeContext.jsx
const ThemeContext = createContext();
export const ThemeProvider = ({ children }) => {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme(prev => prev === 'light' ? 'dark' : 'light');
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
};
```
### 4.2 本地状态管理
- 使用 `useState` 管理组件内部状态
- 使用 `useReducer` 管理复杂的状态逻辑
### 4.3 表单状态管理
- 使用 Ant Design Form 组件
- 使用 `react-hook-form`(可选)
## 5. 路由设计
### 5.1 路由配置
```javascript
// router/routes.js
const routes = [
{
path: '/login',
element: <Login />,
meta: { title: '登录', requiresAuth: false },
},
{
path: '/',
element: <Layout />,
meta: { title: '主页', requiresAuth: true },
children: [
{
path: '/dashboard',
element: <Dashboard />,
meta: { title: '仪表盘', icon: 'DashboardOutlined' },
},
{
path: '/projects',
element: <ProjectList />,
meta: { title: '项目管理', icon: 'FileTextOutlined' },
},
{
path: '/projects/create',
element: <ProjectForm />,
meta: { title: '创建项目' },
},
{
path: '/projects/:id',
element: <ProjectDetail />,
meta: { title: '项目详情' },
},
{
path: '/projects/:id/edit',
element: <ProjectForm />,
meta: { title: '编辑项目' },
},
{
path: '/users',
element: <UserList />,
meta: { title: '用户管理', icon: 'UserOutlined', requiresRole: ['admin'] },
},
{
path: '/users/create',
element: <UserForm />,
meta: { title: '创建用户', requiresRole: ['admin'] },
},
{
path: '/statistics',
element: <Statistics />,
meta: { title: '统计分析', icon: 'BarChartOutlined' },
},
],
},
];
```
### 5.2 路由守卫
```javascript
// router/PrivateRoute.jsx
const PrivateRoute = ({ children, requiresAuth, requiresRole }) => {
const { isAuthenticated, user } = useAuth();
const navigate = useNavigate();
if (requiresAuth && !isAuthenticated) {
return <Navigate to="/login" replace />;
}
if (requiresRole && !requiresRole.includes(user?.role)) {
return <Navigate to="/403" replace />;
}
return children;
};
```
## 6. API服务封装
### 6.1 Axios配置
```javascript
// services/api.js
import axios from 'axios';
import { message } from 'antd';
const api = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL || 'http://localhost:5000/api/v1',
timeout: 10000,
headers: {
'Content-Type': 'application/json',
},
});
// 请求拦截器
api.interceptors.request.use(
(config) => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
// 响应拦截器
api.interceptors.response.use(
(response) => {
return response.data;
},
(error) => {
if (error.response) {
const { status, data } = error.response;
switch (status) {
case 401:
message.error('登录已过期,请重新登录');
localStorage.removeItem('token');
window.location.href = '/login';
break;
case 403:
message.error('权限不足');
break;
case 404:
message.error('请求的资源不存在');
break;
case 500:
message.error('服务器错误');
break;
default:
message.error(data.message || '请求失败');
}
} else {
message.error('网络错误,请检查网络连接');
}
return Promise.reject(error);
}
);
export default api;
```
### 6.2 API模块化
```javascript
// services/project.js
import api from './api';
export const projectAPI = {
// 获取项目列表
getList: (params) => api.get('/projects', { params }),
// 获取项目详情
getDetail: (id) => api.get(`/projects/${id}`),
// 创建项目
create: (data) => api.post('/projects', data),
// 更新项目
update: (id, data) => api.put(`/projects/${id}`, data),
// 删除项目
delete: (id) => api.delete(`/projects/${id}`),
// 获取统计信息
getStatistics: (params) => api.get('/projects/statistics', { params }),
// 获取分组统计
getGroupStatistics: (params) => api.get('/projects/statistics/group', { params }),
// 获取时间维度统计
getTimelineStatistics: (params) => api.get('/projects/statistics/timeline', { params }),
};
```
## 7. 权限控制
### 7.1 路由级权限
- 使用 `PrivateRoute` 组件检查认证状态
- 使用 `requiresRole` 属性检查角色权限
### 7.2 组件级权限
```javascript
// hooks/usePermission.js
export const usePermission = () => {
const { user } = useAuth();
const hasRole = (roles) => {
if (!user) return false;
return roles.includes(user.role);
};
const canEdit = (resource, createdBy) => {
if (!user) return false;
if (user.role === 'admin') return true;
if (user.role === 'market' && createdBy === user.id) return true;
return false;
};
return { hasRole, canEdit };
};
```
### 7.3 按钮级权限
```javascript
// 组件中使用
const { hasRole } = usePermission();
{hasRole(['admin', 'market']) && (
<Button type="primary">创建项目</Button>
)}
```
## 8. 性能优化
### 8.1 代码分割
```javascript
import { lazy, Suspense } from 'react';
const ProjectList = lazy(() => import('./pages/Projects/ProjectList'));
const ProjectDetail = lazy(() => import('./pages/Projects/ProjectDetail'));
<Suspense fallback={<Spin />}>
<ProjectList />
</Suspense>
```
### 8.2 图片优化
- 使用懒加载
- 压缩图片文件
- 使用WebP格式
### 8.3 请求优化
- 防抖处理搜索请求
- 取消重复请求
- 使用缓存策略
### 8.4 渲染优化
- 使用 `React.memo` 避免不必要的重渲染
- 使用 `useMemo` 缓存计算结果
- 使用 `useCallback` 缓存函数引用
## 9. 前端安全
### 9.1 XSS防护
- React自动转义HTML
- 避免使用 `dangerouslySetInnerHTML`
- 输入验证和过滤
### 9.2 CSRF防护
- 使用Cookie SameSite属性
- 添加CSRF Token(可选)
### 9.3 敏感信息保护
- Token存储在LocalStorage(可改为HttpOnly Cookie
- 不在URL中传递敏感信息
- 使用HTTPS(生产环境)
### 9.4 内容安全策略(CSP
- 设置CSP头部
- 限制外部资源加载
## 10. 开发规范
### 10.1 代码风格
- 使用ESLint进行代码检查
- 使用Prettier进行代码格式化
- 遵循Airbnb JavaScript风格指南
### 10.2 命名规范
- 组件名使用PascalCase(如 `ProjectList`
- 函数和变量使用camelCase(如 `getUserById`
- 常量使用UPPER_SNAKE_CASE(如 `API_BASE_URL`
- CSS类名使用kebab-case(如 `project-card`
### 10.3 注释规范
```javascript
/**
* 获取项目列表
* @param {Object} params - 查询参数
* @param {number} params.page - 页码
* @param {number} params.pageSize - 每页数量
* @returns {Promise} 项目列表数据
*/
const getProjectList = async (params) => {
// 实现代码
};
```
### 10.4 Git提交规范
```
[frontend] feat: 添加项目列表页面
[frontend] fix: 修复登录bug
[frontend] style: 优化代码格式
[frontend] refactor: 重构API服务
[frontend] test: 添加组件测试
[frontend] docs: 更新文档
[frontend] chore: 更新依赖
```
## 11. 测试策略
### 11.1 单元测试
- 测试工具函数和自定义Hooks
- 使用Vitest测试框架
### 11.2 组件测试
- 测试组件渲染和交互
- 使用React Testing Library
### 11.3 集成测试
- 测试页面和路由
- 测试API调用
### 11.4 E2E测试(可选)
- 测试完整用户流程
- 使用Playwright
## 12. 部署方案
### 12.1 开发环境
```bash
npm run dev
# 访问 http://localhost:3000
```
### 12.2 生产构建
```bash
npm run build
# 生成 dist 目录
```
### 12.3 Docker部署
```dockerfile
# Dockerfile
FROM node:18-alpine as builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
```
### 12.4 Nginx配置
```nginx
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://backend:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
```
## 13. 相关文档
- [组件设计文档](component-design.md) - 详细的组件设计规范
- [API服务封装](api-service.md) - API服务封装详解
- [状态管理设计](state-management.md) - 状态管理方案详解
- [路由设计文档](routing-design.md) - 路由设计详解
- [开发指南](development-guide.md) - 前端开发指南
---
**文档维护**: 前端程序员
**文档类型**: 前端技术架构文档
**最后更新**: 2026-01-25