688 lines
19 KiB
Markdown
688 lines
19 KiB
Markdown
# 海洋项目管理系统 - 前端技术架构文档
|
||
|
||
## 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
|