544 lines
13 KiB
Markdown
544 lines
13 KiB
Markdown
# 药箱前端开发文档
|
||
|
||
## 1. 项目概述
|
||
|
||
药箱(YaoXiang)是一个家庭药品与应急物资管理系统,前端采用 React + TypeScript + Vite 技术栈,支持 PWA 和大屏模式。
|
||
|
||
## 2. 技术栈
|
||
|
||
| 技术 | 版本 | 说明 |
|
||
|------|------|------|
|
||
| React | 18.2.0 | UI 框架 |
|
||
| TypeScript | 5.2.2 | 类型系统 |
|
||
| Vite | 5.0.0 | 构建工具 |
|
||
| React Router | 6.20.0 | 路由管理 |
|
||
| Zustand | 4.4.7 | 状态管理 |
|
||
| Ant Design Mobile | 5.34.0 | UI 组件库 |
|
||
| Axios | 1.6.2 | HTTP 客户端 |
|
||
| Day.js | 1.11.10 | 日期处理 |
|
||
|
||
## 3. 项目结构
|
||
|
||
```
|
||
frontend/
|
||
├── public/ # 静态资源
|
||
│ ├── favicon.svg # 网站图标
|
||
│ ├── manifest.json # PWA 配置
|
||
│ └── icons/ # 应用图标
|
||
│
|
||
├── src/
|
||
│ ├── api/ # API 调用层
|
||
│ │ ├── client.ts # Axios 实例配置
|
||
│ │ ├── auth.ts # 认证相关 API
|
||
│ │ ├── medicines.ts # 药品管理 API
|
||
│ │ ├── batches.ts # 批次管理 API
|
||
│ │ ├── categories.ts # 分类管理 API
|
||
│ │ ├── search.ts # 搜索 API
|
||
│ │ ├── notifications.ts # 通知 API
|
||
│ │ ├── users.ts # 用户管理 API
|
||
│ │ └── index.ts # 导出汇总
|
||
│ │
|
||
│ ├── components/ # 公共组件
|
||
│ │ ├── Layout/ # 布局组件(含 TabBar)
|
||
│ │ ├── MedicineCard/ # 药品卡片
|
||
│ │ ├── QuantitySelector/ # 数量选择器
|
||
│ │ ├── SearchBar/ # 搜索栏
|
||
│ │ ├── CameraCapture/ # 摄像头捕获
|
||
│ │ ├── CategoryTree/ # 分类树
|
||
│ │ └── index.ts # 导出汇总
|
||
│ │
|
||
│ ├── pages/ # 页面组件
|
||
│ │ ├── Home/ # 首页(库存概览)
|
||
│ │ ├── Login/ # 登录页
|
||
│ │ ├── MedicineList/ # 药品列表
|
||
│ │ ├── MedicineDetail/ # 药品详情
|
||
│ │ ├── AddMedicine/ # 添加/编辑药品
|
||
│ │ ├── QuickDispense/ # 快速取药(大屏模式)
|
||
│ │ ├── Search/ # 搜索页
|
||
│ │ ├── Notifications/ # 通知中心
|
||
│ │ ├── Settings/ # 设置页
|
||
│ │ └── index.ts # 导出汇总
|
||
│ │
|
||
│ ├── stores/ # 状态管理
|
||
│ │ ├── authStore.ts # 认证状态
|
||
│ │ ├── medicineStore.ts # 药品状态
|
||
│ │ ├── categoryStore.ts # 分类状态
|
||
│ │ ├── notificationStore.ts # 通知状态
|
||
│ │ ├── uiStore.ts # UI 状态
|
||
│ │ └── index.ts # 导出汇总
|
||
│ │
|
||
│ ├── hooks/ # 自定义 Hooks
|
||
│ │ ├── useAuth.ts # 认证 Hook
|
||
│ │ ├── useMedicine.ts # 药品 Hook
|
||
│ │ ├── useCamera.ts # 摄像头 Hook
|
||
│ │ ├── useNotification.ts # 通知 Hook
|
||
│ │ └── index.ts # 导出汇总
|
||
│ │
|
||
│ ├── types/ # TypeScript 类型定义
|
||
│ │ ├── user.ts # 用户类型
|
||
│ │ ├── medicine.ts # 药品类型
|
||
│ │ ├── batch.ts # 批次类型
|
||
│ │ ├── category.ts # 分类类型
|
||
│ │ ├── notification.ts # 通知类型
|
||
│ │ ├── api.ts # API 响应类型
|
||
│ │ └── index.ts # 导出汇总
|
||
│ │
|
||
│ ├── utils/ # 工具函数
|
||
│ │ ├── date.ts # 日期处理
|
||
│ │ ├── storage.ts # 本地存储
|
||
│ │ ├── validators.ts # 表单验证
|
||
│ │ ├── constants.ts # 常量定义
|
||
│ │ └── index.ts # 导出汇总
|
||
│ │
|
||
│ ├── styles/ # 样式文件
|
||
│ │ ├── global.css # 全局样式
|
||
│ │ ├── variables.css # CSS 变量
|
||
│ │ ├── mixins.css # CSS 混入
|
||
│ │ └── index.css # 导入汇总
|
||
│ │
|
||
│ ├── App.tsx # 根组件
|
||
│ ├── main.tsx # 入口文件
|
||
│ └── router.tsx # 路由配置
|
||
│
|
||
├── index.html # HTML 模板
|
||
├── package.json # 依赖配置
|
||
├── vite.config.ts # Vite 配置
|
||
├── tsconfig.json # TypeScript 配置
|
||
├── tsconfig.node.json # Node TypeScript 配置
|
||
├── .env.example # 环境变量示例
|
||
└── .gitignore # Git 忽略文件
|
||
```
|
||
|
||
## 4. 快速开始
|
||
|
||
### 4.1 环境准备
|
||
|
||
```bash
|
||
# 进入前端目录
|
||
cd frontend
|
||
|
||
# 安装依赖
|
||
npm install
|
||
```
|
||
|
||
### 4.2 配置环境变量
|
||
|
||
```bash
|
||
# 复制环境变量示例文件
|
||
cp .env.example .env
|
||
|
||
# 编辑 .env 文件
|
||
VITE_API_BASE_URL=/api
|
||
```
|
||
|
||
### 4.3 启动开发服务器
|
||
|
||
```bash
|
||
npm run dev
|
||
```
|
||
|
||
访问 http://localhost:5173
|
||
|
||
### 4.4 构建生产版本
|
||
|
||
```bash
|
||
npm run build
|
||
```
|
||
|
||
构建产物位于 `dist/` 目录。
|
||
|
||
### 4.5 预览生产版本
|
||
|
||
```bash
|
||
npm run preview
|
||
```
|
||
|
||
## 5. 路由配置
|
||
|
||
### 5.1 路由表
|
||
|
||
| 路径 | 页面 | 说明 | 权限 |
|
||
|------|------|------|------|
|
||
| `/login` | Login | 登录页 | 公开 |
|
||
| `/` | Home | 首页 | 登录用户 |
|
||
| `/medicines` | MedicineList | 药品列表 | 登录用户 |
|
||
| `/medicines/add` | AddMedicine | 添加药品 | admin/user |
|
||
| `/medicines/:id` | MedicineDetail | 药品详情 | 登录用户 |
|
||
| `/medicines/edit/:id` | AddMedicine | 编辑药品 | admin/user |
|
||
| `/quick-dispense` | QuickDispense | 快速取药 | 登录用户 |
|
||
| `/search` | Search | 搜索页 | 登录用户 |
|
||
| `/notifications` | Notifications | 通知中心 | 登录用户 |
|
||
| `/settings` | Settings | 设置页 | 登录用户 |
|
||
|
||
### 5.2 路由守卫
|
||
|
||
路由守卫通过 `useAuth` Hook 实现:
|
||
|
||
```tsx
|
||
import { useAuth } from '../hooks';
|
||
|
||
const ProtectedRoute = ({ children }) => {
|
||
const { isAuthenticated } = useAuth();
|
||
|
||
if (!isAuthenticated) {
|
||
return <Navigate to="/login" replace />;
|
||
}
|
||
|
||
return children;
|
||
};
|
||
```
|
||
|
||
## 6. 状态管理
|
||
|
||
### 6.1 Store 结构
|
||
|
||
| Store | 说明 | 主要状态 |
|
||
|-------|------|----------|
|
||
| authStore | 认证状态 | user, token, isAuthenticated |
|
||
| medicineStore | 药品状态 | medicines, currentMedicine, loading |
|
||
| categoryStore | 分类状态 | categories, loading |
|
||
| notificationStore | 通知状态 | notifications, unreadCount |
|
||
| uiStore | UI 状态 | isLargeScreen, isDarkMode |
|
||
|
||
### 6.2 使用示例
|
||
|
||
```tsx
|
||
import { useMedicineStore } from '../stores';
|
||
|
||
const MyComponent = () => {
|
||
const { medicines, loading, fetchMedicines } = useMedicineStore();
|
||
|
||
useEffect(() => {
|
||
fetchMedicines();
|
||
}, []);
|
||
|
||
return (
|
||
<div>
|
||
{loading ? '加载中...' : medicines.map(m => <div key={m.id}>{m.name}</div>)}
|
||
</div>
|
||
);
|
||
};
|
||
```
|
||
|
||
## 7. API 调用
|
||
|
||
### 7.1 API 客户端配置
|
||
|
||
```typescript
|
||
// api/client.ts
|
||
import axios from 'axios';
|
||
import { useAuthStore } from '../stores/authStore';
|
||
|
||
const client = axios.create({
|
||
baseURL: import.meta.env.VITE_API_BASE_URL || '/api',
|
||
timeout: 30000,
|
||
});
|
||
|
||
// 请求拦截器 - 添加 Token
|
||
client.interceptors.request.use((config) => {
|
||
const token = useAuthStore.getState().token;
|
||
if (token) {
|
||
config.headers.Authorization = `Bearer ${token}`;
|
||
}
|
||
return config;
|
||
});
|
||
|
||
// 响应拦截器 - 处理 401
|
||
client.interceptors.response.use(
|
||
(response) => response.data,
|
||
(error) => {
|
||
if (error.response?.status === 401) {
|
||
useAuthStore.getState().logout();
|
||
window.location.href = '/login';
|
||
}
|
||
return Promise.reject(error);
|
||
}
|
||
);
|
||
```
|
||
|
||
### 7.2 API 调用示例
|
||
|
||
```typescript
|
||
import { medicineApi } from '../api';
|
||
|
||
// 获取药品列表
|
||
const { data, total } = await medicineApi.list({ page: 1, pageSize: 20 });
|
||
|
||
// 创建药品
|
||
const medicine = await medicineApi.create({ name: '布洛芬', specification: '0.3g' });
|
||
|
||
// AI 识别药盒
|
||
const result = await medicineApi.recognize(imageFile);
|
||
```
|
||
|
||
## 8. 组件开发
|
||
|
||
### 8.1 添加新组件
|
||
|
||
1. 在 `src/components/` 目录下创建组件文件夹
|
||
2. 创建 `index.tsx` 和 `index.css`
|
||
3. 在 `src/components/index.ts` 中导出
|
||
|
||
```tsx
|
||
// components/MyComponent/index.tsx
|
||
import React from 'react';
|
||
import './index.css';
|
||
|
||
interface MyComponentProps {
|
||
title: string;
|
||
}
|
||
|
||
const MyComponent: React.FC<MyComponentProps> = ({ title }) => {
|
||
return <div className="my-component">{title}</div>;
|
||
};
|
||
|
||
export default MyComponent;
|
||
```
|
||
|
||
### 8.2 添加新页面
|
||
|
||
1. 在 `src/pages/` 目录下创建页面文件夹
|
||
2. 创建 `index.tsx` 和 `index.css`
|
||
3. 在 `src/pages/index.ts` 中导出
|
||
4. 在 `src/router.tsx` 中添加路由
|
||
|
||
```tsx
|
||
// pages/MyPage/index.tsx
|
||
import React from 'react';
|
||
import './index.css';
|
||
|
||
const MyPage: React.FC = () => {
|
||
return <div className="my-page">My Page</div>;
|
||
};
|
||
|
||
export default MyPage;
|
||
```
|
||
|
||
### 8.3 添加新 Hook
|
||
|
||
1. 在 `src/hooks/` 目录下创建 Hook 文件
|
||
2. 在 `src/hooks/index.ts` 中导出
|
||
|
||
```tsx
|
||
// hooks/useMyHook.ts
|
||
import { useState, useCallback } from 'react';
|
||
|
||
export const useMyHook = () => {
|
||
const [data, setData] = useState(null);
|
||
|
||
const fetchData = useCallback(async () => {
|
||
// 实现逻辑
|
||
}, []);
|
||
|
||
return { data, fetchData };
|
||
};
|
||
```
|
||
|
||
## 9. 样式开发
|
||
|
||
### 9.1 CSS 变量
|
||
|
||
项目使用 CSS 变量管理主题:
|
||
|
||
```css
|
||
:root {
|
||
--adm-color-primary: #1677ff;
|
||
--adm-color-success: #52c41a;
|
||
--adm-color-warning: #faad14;
|
||
--adm-color-danger: #ff4d4f;
|
||
}
|
||
```
|
||
|
||
### 9.2 大屏模式适配
|
||
|
||
```css
|
||
/* 基础样式 */
|
||
.quantity-btn {
|
||
width: 48px;
|
||
height: 48px;
|
||
}
|
||
|
||
/* 大屏模式 */
|
||
@media (min-width: 768px) {
|
||
.quantity-btn {
|
||
width: 80px;
|
||
height: 80px;
|
||
}
|
||
}
|
||
```
|
||
|
||
### 9.3 使用 Ant Design Mobile 样式
|
||
|
||
```tsx
|
||
import { Button } from 'antd-mobile';
|
||
|
||
// 使用组件自带样式
|
||
<Button color="primary" size="large">按钮</Button>
|
||
|
||
// 使用自定义样式
|
||
<div className="custom-wrapper">
|
||
<Button>按钮</Button>
|
||
</div>
|
||
```
|
||
|
||
## 10. 类型定义
|
||
|
||
### 10.1 添加新类型
|
||
|
||
1. 在 `src/types/` 目录下创建类型文件
|
||
2. 在 `src/types/index.ts` 中导出
|
||
|
||
```typescript
|
||
// types/myType.ts
|
||
export interface MyType {
|
||
id: number;
|
||
name: string;
|
||
createdAt: string;
|
||
}
|
||
|
||
export interface MyTypeCreate {
|
||
name: string;
|
||
}
|
||
```
|
||
|
||
### 10.2 使用类型
|
||
|
||
```typescript
|
||
import { MyType, MyTypeCreate } from '../types';
|
||
|
||
const myFunction = (data: MyTypeCreate): MyType => {
|
||
return { id: 1, ...data, createdAt: new Date().toISOString() };
|
||
};
|
||
```
|
||
|
||
## 11. 开发规范
|
||
|
||
### 11.1 代码风格
|
||
|
||
- 使用 TypeScript 严格模式
|
||
- 遵循 ESLint 规则
|
||
- 使用 Prettier 格式化
|
||
|
||
### 11.2 命名规范
|
||
|
||
| 类型 | 规范 | 示例 |
|
||
|------|------|------|
|
||
| 组件 | PascalCase | MedicineCard |
|
||
| Hook | use + PascalCase | useAuth |
|
||
| 函数 | camelCase | fetchMedicines |
|
||
| 变量 | camelCase | medicineList |
|
||
| 常量 | UPPER_SNAKE_CASE | API_BASE_URL |
|
||
| 文件 | PascalCase (组件) / camelCase (其他) | MedicineCard/index.tsx |
|
||
| CSS 类 | kebab-case | medicine-card |
|
||
|
||
### 11.3 文件组织
|
||
|
||
- 每个组件/页面单独一个文件夹
|
||
- 包含 `index.tsx` 和 `index.css`
|
||
- 通过 `index.ts` 导出
|
||
|
||
### 11.4 提交规范
|
||
|
||
```
|
||
feat: 新功能
|
||
fix: 修复 bug
|
||
docs: 文档更新
|
||
style: 代码格式调整
|
||
refactor: 重构
|
||
test: 测试相关
|
||
chore: 构建/工具相关
|
||
```
|
||
|
||
## 12. 构建与部署
|
||
|
||
### 12.1 开发环境
|
||
|
||
```bash
|
||
npm run dev
|
||
```
|
||
|
||
### 12.2 生产构建
|
||
|
||
```bash
|
||
npm run build
|
||
```
|
||
|
||
### 12.3 部署到 Nginx
|
||
|
||
```nginx
|
||
server {
|
||
listen 80;
|
||
server_name your-domain.com;
|
||
|
||
root /path/to/dist;
|
||
index index.html;
|
||
|
||
location / {
|
||
try_files $uri $uri/ /index.html;
|
||
}
|
||
|
||
location /api {
|
||
proxy_pass http://localhost:8000;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
}
|
||
}
|
||
```
|
||
|
||
## 13. 常见问题
|
||
|
||
### 13.1 开发服务器启动失败
|
||
|
||
检查端口是否被占用:
|
||
|
||
```bash
|
||
# Windows
|
||
netstat -ano | findstr :5173
|
||
|
||
# Mac/Linux
|
||
lsof -i :5173
|
||
```
|
||
|
||
### 13.2 API 请求失败
|
||
|
||
1. 检查后端服务是否启动
|
||
2. 检查 `.env` 中的 `VITE_API_BASE_URL` 配置
|
||
3. 检查浏览器控制台错误
|
||
|
||
### 13.3 类型错误
|
||
|
||
确保所有组件和函数都有正确的类型注解:
|
||
|
||
```typescript
|
||
// 错误
|
||
const handleClick = (e) => { ... }
|
||
|
||
// 正确
|
||
const handleClick = (e: React.MouseEvent) => { ... }
|
||
```
|
||
|
||
### 13.4 样式不生效
|
||
|
||
1. 检查 CSS 文件是否正确导入
|
||
2. 检查选择器是否正确
|
||
3. 使用浏览器开发者工具检查样式
|
||
|
||
## 14. 扩展开发
|
||
|
||
### 14.1 添加新的 AI 识别功能
|
||
|
||
1. 在 `src/api/medicines.ts` 中添加 API 调用
|
||
2. 在页面中使用摄像头组件捕获图片
|
||
3. 调用 AI 接口识别
|
||
|
||
### 14.2 添加新的通知渠道
|
||
|
||
1. 在后端添加通知 Provider
|
||
2. 在前端通知页面展示
|
||
|
||
### 14.3 添加新的页面
|
||
|
||
1. 创建页面组件
|
||
2. 添加路由配置
|
||
3. 添加导航入口
|