Files
YaoXiang/frontend/DEVELOPMENT.md
T
2026-06-15 14:50:15 +08:00

13 KiB
Raw Blame History

药箱前端开发文档

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 环境准备

# 进入前端目录
cd frontend

# 安装依赖
npm install

4.2 配置环境变量

# 复制环境变量示例文件
cp .env.example .env

# 编辑 .env 文件
VITE_API_BASE_URL=/api

4.3 启动开发服务器

npm run dev

访问 http://localhost:5173

4.4 构建生产版本

npm run build

构建产物位于 dist/ 目录。

4.5 预览生产版本

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 实现:

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 使用示例

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 客户端配置

// 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 调用示例

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.tsxindex.css
  3. src/components/index.ts 中导出
// 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.tsxindex.css
  3. src/pages/index.ts 中导出
  4. src/router.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 中导出
// 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 变量管理主题:

:root {
  --adm-color-primary: #1677ff;
  --adm-color-success: #52c41a;
  --adm-color-warning: #faad14;
  --adm-color-danger: #ff4d4f;
}

9.2 大屏模式适配

/* 基础样式 */
.quantity-btn {
  width: 48px;
  height: 48px;
}

/* 大屏模式 */
@media (min-width: 768px) {
  .quantity-btn {
    width: 80px;
    height: 80px;
  }
}

9.3 使用 Ant Design Mobile 样式

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 中导出
// types/myType.ts
export interface MyType {
  id: number;
  name: string;
  createdAt: string;
}

export interface MyTypeCreate {
  name: string;
}

10.2 使用类型

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.tsxindex.css
  • 通过 index.ts 导出

11.4 提交规范

feat: 新功能
fix: 修复 bug
docs: 文档更新
style: 代码格式调整
refactor: 重构
test: 测试相关
chore: 构建/工具相关

12. 构建与部署

12.1 开发环境

npm run dev

12.2 生产构建

npm run build

12.3 部署到 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 开发服务器启动失败

检查端口是否被占用:

# Windows
netstat -ano | findstr :5173

# Mac/Linux
lsof -i :5173

13.2 API 请求失败

  1. 检查后端服务是否启动
  2. 检查 .env 中的 VITE_API_BASE_URL 配置
  3. 检查浏览器控制台错误

13.3 类型错误

确保所有组件和函数都有正确的类型注解:

// 错误
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. 添加导航入口