1468 lines
38 KiB
Markdown
1468 lines
38 KiB
Markdown
|
|
# 管理后台开发文档
|
|||
|
|
|
|||
|
|
## 版本历史
|
|||
|
|
| 版本 | 日期 | 作者 | 变更说明 |
|
|||
|
|
|------|------|------|----------|
|
|||
|
|
| 1.0 | 2024-01-20 | 前端开发团队 | 初始版本 |
|
|||
|
|
|
|||
|
|
## 1. 项目概述
|
|||
|
|
|
|||
|
|
### 1.1 项目介绍
|
|||
|
|
畜牧养殖管理平台管理后台,为管理员提供全面的系统管理、用户管理、数据统计和业务监控功能。基于现代化的前端技术栈构建,提供高效、易用的管理界面。
|
|||
|
|
|
|||
|
|
### 1.2 技术栈
|
|||
|
|
- **开发框架**: Vue 3 + TypeScript
|
|||
|
|
- **构建工具**: Vite
|
|||
|
|
- **UI框架**: Element Plus
|
|||
|
|
- **状态管理**: Pinia
|
|||
|
|
- **路由管理**: Vue Router 4
|
|||
|
|
- **网络请求**: Axios
|
|||
|
|
- **图表库**: ECharts
|
|||
|
|
- **表格组件**: Element Plus Table
|
|||
|
|
- **表单验证**: Element Plus Form + Async Validator
|
|||
|
|
- **代码规范**: ESLint + Prettier
|
|||
|
|
- **CSS预处理**: Sass/SCSS
|
|||
|
|
|
|||
|
|
### 1.3 项目结构
|
|||
|
|
```
|
|||
|
|
admin-system/
|
|||
|
|
├── public/ # 静态资源
|
|||
|
|
├── src/
|
|||
|
|
│ ├── api/ # API接口
|
|||
|
|
│ ├── assets/ # 资源文件
|
|||
|
|
│ ├── components/ # 通用组件
|
|||
|
|
│ ├── composables/ # 组合式函数
|
|||
|
|
│ ├── layouts/ # 布局组件
|
|||
|
|
│ ├── pages/ # 页面组件
|
|||
|
|
│ │ ├── dashboard/ # 仪表盘
|
|||
|
|
│ │ ├── user/ # 用户管理
|
|||
|
|
│ │ ├── farm/ # 养殖场管理
|
|||
|
|
│ │ ├── animal/ # 动物管理
|
|||
|
|
│ │ ├── order/ # 订单管理
|
|||
|
|
│ │ ├── finance/ # 财务管理
|
|||
|
|
│ │ ├── system/ # 系统管理
|
|||
|
|
│ │ └── statistics/ # 数据统计
|
|||
|
|
│ ├── router/ # 路由配置
|
|||
|
|
│ ├── stores/ # 状态管理
|
|||
|
|
│ ├── styles/ # 样式文件
|
|||
|
|
│ ├── utils/ # 工具函数
|
|||
|
|
│ ├── types/ # TypeScript类型定义
|
|||
|
|
│ └── main.ts # 入口文件
|
|||
|
|
├── tests/ # 测试文件
|
|||
|
|
├── docs/ # 文档
|
|||
|
|
├── .env.development # 开发环境配置
|
|||
|
|
├── .env.production # 生产环境配置
|
|||
|
|
├── package.json
|
|||
|
|
├── tsconfig.json
|
|||
|
|
├── vite.config.ts
|
|||
|
|
└── README.md
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 2. 开发环境搭建
|
|||
|
|
|
|||
|
|
### 2.1 环境要求
|
|||
|
|
- Node.js >= 16.0.0
|
|||
|
|
- npm >= 8.0.0 或 yarn >= 1.22.0
|
|||
|
|
- VSCode + Volar插件
|
|||
|
|
- Chrome浏览器(推荐)
|
|||
|
|
|
|||
|
|
### 2.2 安装步骤
|
|||
|
|
```bash
|
|||
|
|
# 1. 克隆项目
|
|||
|
|
git clone <repository-url>
|
|||
|
|
cd admin-system
|
|||
|
|
|
|||
|
|
# 2. 安装依赖
|
|||
|
|
npm install
|
|||
|
|
# 或
|
|||
|
|
yarn install
|
|||
|
|
|
|||
|
|
# 3. 配置环境变量
|
|||
|
|
cp .env.example .env.development
|
|||
|
|
cp .env.example .env.production
|
|||
|
|
# 编辑环境变量文件
|
|||
|
|
|
|||
|
|
# 4. 启动开发服务器
|
|||
|
|
npm run dev
|
|||
|
|
# 或
|
|||
|
|
yarn dev
|
|||
|
|
|
|||
|
|
# 5. 构建生产版本
|
|||
|
|
npm run build
|
|||
|
|
# 或
|
|||
|
|
yarn build
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2.3 开发工具配置
|
|||
|
|
- **编辑器**: VSCode + Volar + TypeScript Vue Plugin
|
|||
|
|
- **代码规范**: ESLint + Prettier
|
|||
|
|
- **Git钩子**: Husky + lint-staged
|
|||
|
|
- **调试工具**: Vue DevTools
|
|||
|
|
|
|||
|
|
## 3. 开发计划
|
|||
|
|
|
|||
|
|
### 3.1 第一阶段:基础框架搭建(1周)
|
|||
|
|
|
|||
|
|
#### 3.1.1 项目初始化
|
|||
|
|
**任务**: 搭建基础项目架构
|
|||
|
|
**负责人**: 前端架构师
|
|||
|
|
**工期**: 2天
|
|||
|
|
**详细任务**:
|
|||
|
|
- [ ] 创建Vite + Vue3 + TypeScript项目
|
|||
|
|
- [ ] 配置ESLint、Prettier代码规范
|
|||
|
|
- [ ] 配置路由和状态管理
|
|||
|
|
- [ ] 配置环境变量和构建配置
|
|||
|
|
- [ ] 创建基础的目录结构
|
|||
|
|
- [ ] 配置Git工作流和提交规范
|
|||
|
|
|
|||
|
|
#### 3.1.2 基础布局和组件
|
|||
|
|
**任务**: 开发基础布局和通用组件
|
|||
|
|
**负责人**: 前端工程师A
|
|||
|
|
**工期**: 3天
|
|||
|
|
**详细任务**:
|
|||
|
|
- [ ] 开发主布局组件(侧边栏、顶栏、内容区)
|
|||
|
|
- [ ] 开发面包屑导航组件
|
|||
|
|
- [ ] 开发页面标题组件
|
|||
|
|
- [ ] 开发加载状态组件
|
|||
|
|
- [ ] 开发空状态组件
|
|||
|
|
- [ ] 开发确认对话框组件
|
|||
|
|
|
|||
|
|
#### 3.1.3 权限系统和路由守卫
|
|||
|
|
**任务**: 实现权限控制和路由守卫
|
|||
|
|
**负责人**: 前端工程师B
|
|||
|
|
**工期**: 2天
|
|||
|
|
**详细任务**:
|
|||
|
|
- [ ] 实现登录认证机制
|
|||
|
|
- [ ] 实现路由权限控制
|
|||
|
|
- [ ] 实现按钮级权限控制
|
|||
|
|
- [ ] 实现Token自动刷新
|
|||
|
|
- [ ] 实现登录状态持久化
|
|||
|
|
- [ ] 实现权限变更实时更新
|
|||
|
|
|
|||
|
|
### 3.2 第二阶段:核心功能模块开发(4-5周)
|
|||
|
|
|
|||
|
|
#### 3.2.1 用户认证和个人中心
|
|||
|
|
**任务**: 实现用户认证和个人信息管理
|
|||
|
|
**负责人**: 前端工程师A
|
|||
|
|
**工期**: 3天
|
|||
|
|
**详细任务**:
|
|||
|
|
- [ ] 登录页面开发
|
|||
|
|
- [ ] 个人信息页面
|
|||
|
|
- [ ] 修改密码页面
|
|||
|
|
- [ ] 头像上传功能
|
|||
|
|
- [ ] 登录日志查看
|
|||
|
|
- [ ] 安全设置页面
|
|||
|
|
|
|||
|
|
**页面列表**:
|
|||
|
|
```
|
|||
|
|
pages/auth/login.vue # 登录页面
|
|||
|
|
pages/profile/index.vue # 个人信息
|
|||
|
|
pages/profile/password.vue # 修改密码
|
|||
|
|
pages/profile/security.vue # 安全设置
|
|||
|
|
pages/profile/logs.vue # 登录日志
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 3.2.2 仪表盘和数据概览
|
|||
|
|
**任务**: 实现数据仪表盘和统计概览
|
|||
|
|
**负责人**: 前端工程师B
|
|||
|
|
**工期**: 4天
|
|||
|
|
**详细任务**:
|
|||
|
|
- [ ] 数据概览卡片组件
|
|||
|
|
- [ ] 实时数据图表展示
|
|||
|
|
- [ ] 业务趋势分析图表
|
|||
|
|
- [ ] 快捷操作入口
|
|||
|
|
- [ ] 系统通知和消息
|
|||
|
|
- [ ] 数据刷新和实时更新
|
|||
|
|
|
|||
|
|
**页面列表**:
|
|||
|
|
```
|
|||
|
|
pages/dashboard/index.vue # 仪表盘首页
|
|||
|
|
pages/dashboard/analytics.vue # 数据分析
|
|||
|
|
pages/dashboard/reports.vue # 报表中心
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 3.2.3 用户管理模块
|
|||
|
|
**任务**: 实现用户管理功能
|
|||
|
|
**负责人**: 前端工程师C
|
|||
|
|
**工期**: 5天
|
|||
|
|
**详细任务**:
|
|||
|
|
- [ ] 用户列表页面(搜索、筛选、分页)
|
|||
|
|
- [ ] 用户详情页面
|
|||
|
|
- [ ] 添加用户页面
|
|||
|
|
- [ ] 编辑用户页面
|
|||
|
|
- [ ] 用户状态管理(启用/禁用)
|
|||
|
|
- [ ] 批量操作功能
|
|||
|
|
- [ ] 用户导入导出功能
|
|||
|
|
|
|||
|
|
**页面列表**:
|
|||
|
|
```
|
|||
|
|
pages/user/list.vue # 用户列表
|
|||
|
|
pages/user/detail.vue # 用户详情
|
|||
|
|
pages/user/add.vue # 添加用户
|
|||
|
|
pages/user/edit.vue # 编辑用户
|
|||
|
|
pages/user/import.vue # 用户导入
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 3.2.4 角色权限管理模块
|
|||
|
|
**任务**: 实现角色和权限管理
|
|||
|
|
**负责人**: 前端工程师D
|
|||
|
|
**工期**: 4天
|
|||
|
|
**详细任务**:
|
|||
|
|
- [ ] 角色列表页面
|
|||
|
|
- [ ] 角色详情和权限配置
|
|||
|
|
- [ ] 添加/编辑角色页面
|
|||
|
|
- [ ] 权限树形选择组件
|
|||
|
|
- [ ] 用户角色分配页面
|
|||
|
|
- [ ] 权限变更日志
|
|||
|
|
|
|||
|
|
**页面列表**:
|
|||
|
|
```
|
|||
|
|
pages/role/list.vue # 角色列表
|
|||
|
|
pages/role/detail.vue # 角色详情
|
|||
|
|
pages/role/add.vue # 添加角色
|
|||
|
|
pages/role/edit.vue # 编辑角色
|
|||
|
|
pages/role/permissions.vue # 权限配置
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 3.2.5 养殖场管理模块
|
|||
|
|
**任务**: 实现养殖场管理功能
|
|||
|
|
**负责人**: 前端工程师E
|
|||
|
|
**工期**: 6天
|
|||
|
|
**详细任务**:
|
|||
|
|
- [ ] 养殖场列表页面
|
|||
|
|
- [ ] 养殖场详情页面
|
|||
|
|
- [ ] 添加/编辑养殖场页面
|
|||
|
|
- [ ] 养殖场审核功能
|
|||
|
|
- [ ] 养殖场统计分析
|
|||
|
|
- [ ] 地图展示功能
|
|||
|
|
- [ ] 养殖场认证管理
|
|||
|
|
|
|||
|
|
**页面列表**:
|
|||
|
|
```
|
|||
|
|
pages/farm/list.vue # 养殖场列表
|
|||
|
|
pages/farm/detail.vue # 养殖场详情
|
|||
|
|
pages/farm/add.vue # 添加养殖场
|
|||
|
|
pages/farm/edit.vue # 编辑养殖场
|
|||
|
|
pages/farm/audit.vue # 养殖场审核
|
|||
|
|
pages/farm/statistics.vue # 养殖场统计
|
|||
|
|
pages/farm/map.vue # 地图展示
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 3.2.6 动物管理模块
|
|||
|
|
**任务**: 实现动物档案管理功能
|
|||
|
|
**负责人**: 前端工程师F
|
|||
|
|
**工期**: 6天
|
|||
|
|
**详细任务**:
|
|||
|
|
- [ ] 动物列表页面
|
|||
|
|
- [ ] 动物详情页面
|
|||
|
|
- [ ] 动物健康记录管理
|
|||
|
|
- [ ] 动物生长记录管理
|
|||
|
|
- [ ] 动物统计分析
|
|||
|
|
- [ ] 动物批量操作
|
|||
|
|
- [ ] 动物数据导出
|
|||
|
|
|
|||
|
|
**页面列表**:
|
|||
|
|
```
|
|||
|
|
pages/animal/list.vue # 动物列表
|
|||
|
|
pages/animal/detail.vue # 动物详情
|
|||
|
|
pages/animal/health.vue # 健康记录
|
|||
|
|
pages/animal/growth.vue # 生长记录
|
|||
|
|
pages/animal/statistics.vue # 动物统计
|
|||
|
|
pages/animal/batch.vue # 批量操作
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3.3 第三阶段:交易和财务管理(2-3周)
|
|||
|
|
|
|||
|
|
#### 3.3.1 订单管理模块
|
|||
|
|
**任务**: 实现订单管理功能
|
|||
|
|
**负责人**: 前端工程师A
|
|||
|
|
**工期**: 5天
|
|||
|
|
**详细任务**:
|
|||
|
|
- [ ] 订单列表页面
|
|||
|
|
- [ ] 订单详情页面
|
|||
|
|
- [ ] 订单状态管理
|
|||
|
|
- [ ] 订单审核功能
|
|||
|
|
- [ ] 订单统计分析
|
|||
|
|
- [ ] 订单导出功能
|
|||
|
|
- [ ] 退款处理页面
|
|||
|
|
|
|||
|
|
**页面列表**:
|
|||
|
|
```
|
|||
|
|
pages/order/list.vue # 订单列表
|
|||
|
|
pages/order/detail.vue # 订单详情
|
|||
|
|
pages/order/audit.vue # 订单审核
|
|||
|
|
pages/order/refund.vue # 退款处理
|
|||
|
|
pages/order/statistics.vue # 订单统计
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 3.3.2 财务管理模块
|
|||
|
|
**任务**: 实现财务管理功能
|
|||
|
|
**负责人**: 前端工程师B
|
|||
|
|
**工期**: 6天
|
|||
|
|
**详细任务**:
|
|||
|
|
- [ ] 财务概览页面
|
|||
|
|
- [ ] 收入统计页面
|
|||
|
|
- [ ] 支出统计页面
|
|||
|
|
- [ ] 资金流水页面
|
|||
|
|
- [ ] 财务报表生成
|
|||
|
|
- [ ] 对账功能
|
|||
|
|
- [ ] 财务数据导出
|
|||
|
|
|
|||
|
|
**页面列表**:
|
|||
|
|
```
|
|||
|
|
pages/finance/overview.vue # 财务概览
|
|||
|
|
pages/finance/income.vue # 收入统计
|
|||
|
|
pages/finance/expense.vue # 支出统计
|
|||
|
|
pages/finance/flow.vue # 资金流水
|
|||
|
|
pages/finance/reports.vue # 财务报表
|
|||
|
|
pages/finance/reconcile.vue # 对账管理
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 3.3.3 支付管理模块
|
|||
|
|
**任务**: 实现支付管理功能
|
|||
|
|
**负责人**: 前端工程师C
|
|||
|
|
**工期**: 4天
|
|||
|
|
**详细任务**:
|
|||
|
|
- [ ] 支付记录列表
|
|||
|
|
- [ ] 支付详情页面
|
|||
|
|
- [ ] 支付方式配置
|
|||
|
|
- [ ] 支付异常处理
|
|||
|
|
- [ ] 支付统计分析
|
|||
|
|
- [ ] 支付对账功能
|
|||
|
|
|
|||
|
|
**页面列表**:
|
|||
|
|
```
|
|||
|
|
pages/payment/list.vue # 支付记录
|
|||
|
|
pages/payment/detail.vue # 支付详情
|
|||
|
|
pages/payment/config.vue # 支付配置
|
|||
|
|
pages/payment/exception.vue # 异常处理
|
|||
|
|
pages/payment/statistics.vue # 支付统计
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3.4 第四阶段:系统管理和配置(1-2周)
|
|||
|
|
|
|||
|
|
#### 3.4.1 系统配置模块
|
|||
|
|
**任务**: 实现系统配置管理
|
|||
|
|
**负责人**: 前端工程师D
|
|||
|
|
**工期**: 4天
|
|||
|
|
**详细任务**:
|
|||
|
|
- [ ] 系统参数配置
|
|||
|
|
- [ ] 字典数据管理
|
|||
|
|
- [ ] 系统公告管理
|
|||
|
|
- [ ] 邮件模板配置
|
|||
|
|
- [ ] 短信模板配置
|
|||
|
|
- [ ] 系统备份恢复
|
|||
|
|
|
|||
|
|
**页面列表**:
|
|||
|
|
```
|
|||
|
|
pages/system/config.vue # 系统配置
|
|||
|
|
pages/system/dict.vue # 字典管理
|
|||
|
|
pages/system/notice.vue # 公告管理
|
|||
|
|
pages/system/template.vue # 模板管理
|
|||
|
|
pages/system/backup.vue # 备份恢复
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 3.4.2 日志管理模块
|
|||
|
|
**任务**: 实现系统日志管理
|
|||
|
|
**负责人**: 前端工程师E
|
|||
|
|
**工期**: 3天
|
|||
|
|
**详细任务**:
|
|||
|
|
- [ ] 操作日志查看
|
|||
|
|
- [ ] 登录日志查看
|
|||
|
|
- [ ] 系统错误日志
|
|||
|
|
- [ ] 日志搜索和筛选
|
|||
|
|
- [ ] 日志导出功能
|
|||
|
|
- [ ] 日志清理配置
|
|||
|
|
|
|||
|
|
**页面列表**:
|
|||
|
|
```
|
|||
|
|
pages/log/operation.vue # 操作日志
|
|||
|
|
pages/log/login.vue # 登录日志
|
|||
|
|
pages/log/error.vue # 错误日志
|
|||
|
|
pages/log/search.vue # 日志搜索
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 3.4.3 监控管理模块
|
|||
|
|
**任务**: 实现系统监控功能
|
|||
|
|
**负责人**: 前端工程师F
|
|||
|
|
**工期**: 3天
|
|||
|
|
**详细任务**:
|
|||
|
|
- [ ] 系统性能监控
|
|||
|
|
- [ ] 服务状态监控
|
|||
|
|
- [ ] 数据库监控
|
|||
|
|
- [ ] 接口调用监控
|
|||
|
|
- [ ] 告警规则配置
|
|||
|
|
- [ ] 监控报表生成
|
|||
|
|
|
|||
|
|
**页面列表**:
|
|||
|
|
```
|
|||
|
|
pages/monitor/system.vue # 系统监控
|
|||
|
|
pages/monitor/service.vue # 服务监控
|
|||
|
|
pages/monitor/database.vue # 数据库监控
|
|||
|
|
pages/monitor/api.vue # 接口监控
|
|||
|
|
pages/monitor/alert.vue # 告警配置
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3.5 第五阶段:数据统计和报表(1周)
|
|||
|
|
|
|||
|
|
#### 3.5.1 数据统计模块
|
|||
|
|
**任务**: 实现数据统计分析功能
|
|||
|
|
**负责人**: 前端工程师A
|
|||
|
|
**工期**: 4天
|
|||
|
|
**详细任务**:
|
|||
|
|
- [ ] 用户统计分析
|
|||
|
|
- [ ] 业务数据统计
|
|||
|
|
- [ ] 交易数据分析
|
|||
|
|
- [ ] 趋势分析图表
|
|||
|
|
- [ ] 自定义统计报表
|
|||
|
|
- [ ] 数据对比分析
|
|||
|
|
|
|||
|
|
**页面列表**:
|
|||
|
|
```
|
|||
|
|
pages/statistics/user.vue # 用户统计
|
|||
|
|
pages/statistics/business.vue # 业务统计
|
|||
|
|
pages/statistics/trade.vue # 交易统计
|
|||
|
|
pages/statistics/trend.vue # 趋势分析
|
|||
|
|
pages/statistics/custom.vue # 自定义报表
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 3.5.2 报表管理模块
|
|||
|
|
**任务**: 实现报表生成和管理
|
|||
|
|
**负责人**: 前端工程师B
|
|||
|
|
**工期**: 3天
|
|||
|
|
**详细任务**:
|
|||
|
|
- [ ] 报表模板管理
|
|||
|
|
- [ ] 报表生成功能
|
|||
|
|
- [ ] 报表预览和下载
|
|||
|
|
- [ ] 定时报表配置
|
|||
|
|
- [ ] 报表分享功能
|
|||
|
|
- [ ] 报表权限控制
|
|||
|
|
|
|||
|
|
**页面列表**:
|
|||
|
|
```
|
|||
|
|
pages/report/template.vue # 报表模板
|
|||
|
|
pages/report/generate.vue # 报表生成
|
|||
|
|
pages/report/preview.vue # 报表预览
|
|||
|
|
pages/report/schedule.vue # 定时报表
|
|||
|
|
pages/report/share.vue # 报表分享
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3.6 第六阶段:测试和优化(1周)
|
|||
|
|
|
|||
|
|
#### 3.6.1 功能测试
|
|||
|
|
**任务**: 全面功能测试
|
|||
|
|
**负责人**: 测试工程师
|
|||
|
|
**工期**: 3天
|
|||
|
|
**详细任务**:
|
|||
|
|
- [ ] 页面功能测试
|
|||
|
|
- [ ] 权限控制测试
|
|||
|
|
- [ ] 数据操作测试
|
|||
|
|
- [ ] 兼容性测试
|
|||
|
|
- [ ] 性能测试
|
|||
|
|
|
|||
|
|
#### 3.6.2 性能优化
|
|||
|
|
**任务**: 性能优化和体验提升
|
|||
|
|
**负责人**: 前端架构师
|
|||
|
|
**工期**: 2天
|
|||
|
|
**详细任务**:
|
|||
|
|
- [ ] 页面加载速度优化
|
|||
|
|
- [ ] 大数据量处理优化
|
|||
|
|
- [ ] 内存使用优化
|
|||
|
|
- [ ] 网络请求优化
|
|||
|
|
- [ ] 用户体验优化
|
|||
|
|
|
|||
|
|
## 4. 开发规范
|
|||
|
|
|
|||
|
|
### 4.1 代码规范
|
|||
|
|
- **命名规范**: 使用驼峰命名法,组件名使用PascalCase
|
|||
|
|
- **文件命名**: 页面文件使用kebab-case,组件文件使用PascalCase
|
|||
|
|
- **TypeScript**: 严格的类型定义和检查
|
|||
|
|
- **注释规范**: 使用JSDoc格式注释
|
|||
|
|
- **代码格式**: 使用Prettier自动格式化
|
|||
|
|
|
|||
|
|
### 4.2 组件开发规范
|
|||
|
|
- **组件设计**: 遵循单一职责原则
|
|||
|
|
- **属性定义**: 明确的属性类型和默认值
|
|||
|
|
- **事件处理**: 统一的事件命名和处理方式
|
|||
|
|
- **样式隔离**: 使用scoped样式避免样式冲突
|
|||
|
|
- **文档说明**: 完整的组件使用文档
|
|||
|
|
|
|||
|
|
### 4.3 API接口规范
|
|||
|
|
- **接口封装**: 统一的接口封装和错误处理
|
|||
|
|
- **类型定义**: 完整的请求和响应类型定义
|
|||
|
|
- **缓存策略**: 合理的接口缓存策略
|
|||
|
|
- **错误处理**: 统一的错误处理和用户提示
|
|||
|
|
- **加载状态**: 明确的加载状态管理
|
|||
|
|
|
|||
|
|
### 4.4 状态管理规范
|
|||
|
|
- **Store设计**: 合理的Store结构设计
|
|||
|
|
- **模块划分**: 按功能模块划分Store
|
|||
|
|
- **异步处理**: 统一的异步操作处理
|
|||
|
|
- **数据流**: 清晰的数据流向和更新机制
|
|||
|
|
- **持久化**: 关键状态的持久化存储
|
|||
|
|
|
|||
|
|
## 5. 技术实现
|
|||
|
|
|
|||
|
|
### 5.1 项目配置
|
|||
|
|
```typescript
|
|||
|
|
// vite.config.ts
|
|||
|
|
import { defineConfig } from 'vite'
|
|||
|
|
import vue from '@vitejs/plugin-vue'
|
|||
|
|
import { resolve } from 'path'
|
|||
|
|
|
|||
|
|
export default defineConfig({
|
|||
|
|
plugins: [vue()],
|
|||
|
|
|
|||
|
|
resolve: {
|
|||
|
|
alias: {
|
|||
|
|
'@': resolve(__dirname, 'src'),
|
|||
|
|
'@/components': resolve(__dirname, 'src/components'),
|
|||
|
|
'@/utils': resolve(__dirname, 'src/utils'),
|
|||
|
|
'@/api': resolve(__dirname, 'src/api'),
|
|||
|
|
'@/stores': resolve(__dirname, 'src/stores'),
|
|||
|
|
'@/types': resolve(__dirname, 'src/types')
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
server: {
|
|||
|
|
port: 3000,
|
|||
|
|
proxy: {
|
|||
|
|
'/api': {
|
|||
|
|
target: 'http://localhost:8000',
|
|||
|
|
changeOrigin: true,
|
|||
|
|
rewrite: (path) => path.replace(/^\/api/, '')
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
build: {
|
|||
|
|
rollupOptions: {
|
|||
|
|
output: {
|
|||
|
|
manualChunks: {
|
|||
|
|
vendor: ['vue', 'vue-router', 'pinia'],
|
|||
|
|
element: ['element-plus'],
|
|||
|
|
echarts: ['echarts']
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 5.2 路由配置
|
|||
|
|
```typescript
|
|||
|
|
// router/index.ts
|
|||
|
|
import { createRouter, createWebHistory } from 'vue-router'
|
|||
|
|
import type { RouteRecordRaw } from 'vue-router'
|
|||
|
|
import { useAuthStore } from '@/stores/auth'
|
|||
|
|
|
|||
|
|
const routes: RouteRecordRaw[] = [
|
|||
|
|
{
|
|||
|
|
path: '/login',
|
|||
|
|
name: 'Login',
|
|||
|
|
component: () => import('@/pages/auth/login.vue'),
|
|||
|
|
meta: { requiresAuth: false }
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
path: '/',
|
|||
|
|
component: () => import('@/layouts/MainLayout.vue'),
|
|||
|
|
meta: { requiresAuth: true },
|
|||
|
|
children: [
|
|||
|
|
{
|
|||
|
|
path: '',
|
|||
|
|
name: 'Dashboard',
|
|||
|
|
component: () => import('@/pages/dashboard/index.vue'),
|
|||
|
|
meta: { title: '仪表盘', icon: 'dashboard' }
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
path: 'users',
|
|||
|
|
name: 'UserManagement',
|
|||
|
|
component: () => import('@/pages/user/list.vue'),
|
|||
|
|
meta: {
|
|||
|
|
title: '用户管理',
|
|||
|
|
icon: 'user',
|
|||
|
|
permissions: ['user:read']
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
path: 'farms',
|
|||
|
|
name: 'FarmManagement',
|
|||
|
|
component: () => import('@/pages/farm/list.vue'),
|
|||
|
|
meta: {
|
|||
|
|
title: '养殖场管理',
|
|||
|
|
icon: 'farm',
|
|||
|
|
permissions: ['farm:read']
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
const router = createRouter({
|
|||
|
|
history: createWebHistory(),
|
|||
|
|
routes
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 路由守卫
|
|||
|
|
router.beforeEach(async (to, from, next) => {
|
|||
|
|
const authStore = useAuthStore()
|
|||
|
|
|
|||
|
|
if (to.meta.requiresAuth && !authStore.isAuthenticated) {
|
|||
|
|
next('/login')
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (to.meta.permissions) {
|
|||
|
|
const hasPermission = authStore.hasPermissions(to.meta.permissions)
|
|||
|
|
if (!hasPermission) {
|
|||
|
|
next('/403')
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
next()
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
export default router
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 5.3 状态管理
|
|||
|
|
```typescript
|
|||
|
|
// stores/auth.ts
|
|||
|
|
import { defineStore } from 'pinia'
|
|||
|
|
import { ref, computed } from 'vue'
|
|||
|
|
import type { User, LoginCredentials } from '@/types/auth'
|
|||
|
|
import { authApi } from '@/api/auth'
|
|||
|
|
|
|||
|
|
export const useAuthStore = defineStore('auth', () => {
|
|||
|
|
const user = ref<User | null>(null)
|
|||
|
|
const token = ref<string | null>(localStorage.getItem('token'))
|
|||
|
|
const permissions = ref<string[]>([])
|
|||
|
|
|
|||
|
|
const isAuthenticated = computed(() => !!token.value && !!user.value)
|
|||
|
|
|
|||
|
|
const login = async (credentials: LoginCredentials) => {
|
|||
|
|
try {
|
|||
|
|
const response = await authApi.login(credentials)
|
|||
|
|
const { user: userData, token: userToken, permissions: userPermissions } = response.data
|
|||
|
|
|
|||
|
|
user.value = userData
|
|||
|
|
token.value = userToken
|
|||
|
|
permissions.value = userPermissions
|
|||
|
|
|
|||
|
|
localStorage.setItem('token', userToken)
|
|||
|
|
localStorage.setItem('user', JSON.stringify(userData))
|
|||
|
|
|
|||
|
|
return response
|
|||
|
|
} catch (error) {
|
|||
|
|
throw error
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const logout = async () => {
|
|||
|
|
try {
|
|||
|
|
await authApi.logout()
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('Logout error:', error)
|
|||
|
|
} finally {
|
|||
|
|
user.value = null
|
|||
|
|
token.value = null
|
|||
|
|
permissions.value = []
|
|||
|
|
|
|||
|
|
localStorage.removeItem('token')
|
|||
|
|
localStorage.removeItem('user')
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const hasPermissions = (requiredPermissions: string[]) => {
|
|||
|
|
return requiredPermissions.every(permission =>
|
|||
|
|
permissions.value.includes(permission)
|
|||
|
|
)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const refreshToken = async () => {
|
|||
|
|
try {
|
|||
|
|
const response = await authApi.refreshToken()
|
|||
|
|
token.value = response.data.token
|
|||
|
|
localStorage.setItem('token', response.data.token)
|
|||
|
|
return response
|
|||
|
|
} catch (error) {
|
|||
|
|
await logout()
|
|||
|
|
throw error
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 初始化用户信息
|
|||
|
|
const initAuth = async () => {
|
|||
|
|
const savedUser = localStorage.getItem('user')
|
|||
|
|
if (savedUser && token.value) {
|
|||
|
|
try {
|
|||
|
|
user.value = JSON.parse(savedUser)
|
|||
|
|
const response = await authApi.getUserInfo()
|
|||
|
|
user.value = response.data.user
|
|||
|
|
permissions.value = response.data.permissions
|
|||
|
|
} catch (error) {
|
|||
|
|
await logout()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
user,
|
|||
|
|
token,
|
|||
|
|
permissions,
|
|||
|
|
isAuthenticated,
|
|||
|
|
login,
|
|||
|
|
logout,
|
|||
|
|
hasPermissions,
|
|||
|
|
refreshToken,
|
|||
|
|
initAuth
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 5.4 API封装
|
|||
|
|
```typescript
|
|||
|
|
// utils/request.ts
|
|||
|
|
import axios from 'axios'
|
|||
|
|
import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
|
|||
|
|
import { ElMessage } from 'element-plus'
|
|||
|
|
import { useAuthStore } from '@/stores/auth'
|
|||
|
|
|
|||
|
|
class HttpClient {
|
|||
|
|
private instance: AxiosInstance
|
|||
|
|
|
|||
|
|
constructor() {
|
|||
|
|
this.instance = axios.create({
|
|||
|
|
baseURL: import.meta.env.VITE_API_BASE_URL,
|
|||
|
|
timeout: 10000,
|
|||
|
|
headers: {
|
|||
|
|
'Content-Type': 'application/json'
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
this.setupInterceptors()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private setupInterceptors() {
|
|||
|
|
// 请求拦截器
|
|||
|
|
this.instance.interceptors.request.use(
|
|||
|
|
(config) => {
|
|||
|
|
const authStore = useAuthStore()
|
|||
|
|
if (authStore.token) {
|
|||
|
|
config.headers.Authorization = `Bearer ${authStore.token}`
|
|||
|
|
}
|
|||
|
|
return config
|
|||
|
|
},
|
|||
|
|
(error) => {
|
|||
|
|
return Promise.reject(error)
|
|||
|
|
}
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// 响应拦截器
|
|||
|
|
this.instance.interceptors.response.use(
|
|||
|
|
(response: AxiosResponse) => {
|
|||
|
|
const { code, message, data } = response.data
|
|||
|
|
|
|||
|
|
if (code === 200) {
|
|||
|
|
return { data, message }
|
|||
|
|
} else {
|
|||
|
|
ElMessage.error(message || '请求失败')
|
|||
|
|
return Promise.reject(new Error(message))
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
async (error) => {
|
|||
|
|
const { response } = error
|
|||
|
|
|
|||
|
|
if (response?.status === 401) {
|
|||
|
|
const authStore = useAuthStore()
|
|||
|
|
try {
|
|||
|
|
await authStore.refreshToken()
|
|||
|
|
// 重新发起原请求
|
|||
|
|
return this.instance.request(error.config)
|
|||
|
|
} catch (refreshError) {
|
|||
|
|
authStore.logout()
|
|||
|
|
return Promise.reject(refreshError)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (response?.status === 403) {
|
|||
|
|
ElMessage.error('没有权限访问')
|
|||
|
|
} else if (response?.status >= 500) {
|
|||
|
|
ElMessage.error('服务器错误')
|
|||
|
|
} else {
|
|||
|
|
ElMessage.error(response?.data?.message || '请求失败')
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return Promise.reject(error)
|
|||
|
|
}
|
|||
|
|
)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public get<T = any>(url: string, config?: AxiosRequestConfig): Promise<T> {
|
|||
|
|
return this.instance.get(url, config)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public post<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
|
|||
|
|
return this.instance.post(url, data, config)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public put<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
|
|||
|
|
return this.instance.put(url, data, config)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public delete<T = any>(url: string, config?: AxiosRequestConfig): Promise<T> {
|
|||
|
|
return this.instance.delete(url, config)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export const httpClient = new HttpClient()
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 5.5 通用组件示例
|
|||
|
|
```vue
|
|||
|
|
<!-- components/DataTable.vue -->
|
|||
|
|
<template>
|
|||
|
|
<div class="data-table">
|
|||
|
|
<!-- 搜索和操作栏 -->
|
|||
|
|
<div class="table-header">
|
|||
|
|
<div class="search-section">
|
|||
|
|
<el-input
|
|||
|
|
v-model="searchKeyword"
|
|||
|
|
placeholder="请输入搜索关键词"
|
|||
|
|
clearable
|
|||
|
|
@keyup.enter="handleSearch"
|
|||
|
|
style="width: 300px"
|
|||
|
|
>
|
|||
|
|
<template #append>
|
|||
|
|
<el-button @click="handleSearch" :icon="Search" />
|
|||
|
|
</template>
|
|||
|
|
</el-input>
|
|||
|
|
|
|||
|
|
<slot name="filters" :search="handleSearch" />
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="action-section">
|
|||
|
|
<slot name="actions" />
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 表格 -->
|
|||
|
|
<el-table
|
|||
|
|
:data="tableData"
|
|||
|
|
:loading="loading"
|
|||
|
|
v-bind="$attrs"
|
|||
|
|
@selection-change="handleSelectionChange"
|
|||
|
|
@sort-change="handleSortChange"
|
|||
|
|
>
|
|||
|
|
<el-table-column
|
|||
|
|
v-if="showSelection"
|
|||
|
|
type="selection"
|
|||
|
|
width="55"
|
|||
|
|
align="center"
|
|||
|
|
/>
|
|||
|
|
|
|||
|
|
<slot />
|
|||
|
|
|
|||
|
|
<el-table-column
|
|||
|
|
v-if="showActions"
|
|||
|
|
label="操作"
|
|||
|
|
:width="actionWidth"
|
|||
|
|
align="center"
|
|||
|
|
fixed="right"
|
|||
|
|
>
|
|||
|
|
<template #default="scope">
|
|||
|
|
<slot name="actions-column" :row="scope.row" :index="scope.$index" />
|
|||
|
|
</template>
|
|||
|
|
</el-table-column>
|
|||
|
|
</el-table>
|
|||
|
|
|
|||
|
|
<!-- 分页 -->
|
|||
|
|
<div class="table-footer" v-if="showPagination">
|
|||
|
|
<el-pagination
|
|||
|
|
v-model:current-page="currentPage"
|
|||
|
|
v-model:page-size="pageSize"
|
|||
|
|
:total="total"
|
|||
|
|
:page-sizes="pageSizes"
|
|||
|
|
layout="total, sizes, prev, pager, next, jumper"
|
|||
|
|
@size-change="handleSizeChange"
|
|||
|
|
@current-change="handleCurrentChange"
|
|||
|
|
/>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script setup lang="ts">
|
|||
|
|
import { ref, watch } from 'vue'
|
|||
|
|
import { Search } from '@element-plus/icons-vue'
|
|||
|
|
|
|||
|
|
interface Props {
|
|||
|
|
data?: any[]
|
|||
|
|
loading?: boolean
|
|||
|
|
showSelection?: boolean
|
|||
|
|
showActions?: boolean
|
|||
|
|
actionWidth?: number
|
|||
|
|
showPagination?: boolean
|
|||
|
|
total?: number
|
|||
|
|
pageSizes?: number[]
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const props = withDefaults(defineProps<Props>(), {
|
|||
|
|
data: () => [],
|
|||
|
|
loading: false,
|
|||
|
|
showSelection: false,
|
|||
|
|
showActions: true,
|
|||
|
|
actionWidth: 200,
|
|||
|
|
showPagination: true,
|
|||
|
|
total: 0,
|
|||
|
|
pageSizes: () => [10, 20, 50, 100]
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
const emit = defineEmits<{
|
|||
|
|
search: [keyword: string, filters?: any]
|
|||
|
|
selectionChange: [selection: any[]]
|
|||
|
|
sortChange: [sort: { prop: string; order: string }]
|
|||
|
|
pageChange: [page: number, size: number]
|
|||
|
|
}>()
|
|||
|
|
|
|||
|
|
const searchKeyword = ref('')
|
|||
|
|
const currentPage = ref(1)
|
|||
|
|
const pageSize = ref(20)
|
|||
|
|
const tableData = ref(props.data)
|
|||
|
|
|
|||
|
|
watch(() => props.data, (newData) => {
|
|||
|
|
tableData.value = newData
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
const handleSearch = () => {
|
|||
|
|
currentPage.value = 1
|
|||
|
|
emit('search', searchKeyword.value)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const handleSelectionChange = (selection: any[]) => {
|
|||
|
|
emit('selectionChange', selection)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const handleSortChange = (sort: { prop: string; order: string }) => {
|
|||
|
|
emit('sortChange', sort)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const handleSizeChange = (size: number) => {
|
|||
|
|
pageSize.value = size
|
|||
|
|
emit('pageChange', currentPage.value, size)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const handleCurrentChange = (page: number) => {
|
|||
|
|
currentPage.value = page
|
|||
|
|
emit('pageChange', page, pageSize.value)
|
|||
|
|
}
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<style lang="scss" scoped>
|
|||
|
|
.data-table {
|
|||
|
|
.table-header {
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
align-items: center;
|
|||
|
|
margin-bottom: 16px;
|
|||
|
|
|
|||
|
|
.search-section {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
gap: 16px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.action-section {
|
|||
|
|
display: flex;
|
|||
|
|
gap: 8px;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.table-footer {
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: flex-end;
|
|||
|
|
margin-top: 16px;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
</style>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 5.6 页面开发示例
|
|||
|
|
```vue
|
|||
|
|
<!-- pages/user/list.vue -->
|
|||
|
|
<template>
|
|||
|
|
<div class="user-list-page">
|
|||
|
|
<PageHeader title="用户管理" />
|
|||
|
|
|
|||
|
|
<el-card>
|
|||
|
|
<DataTable
|
|||
|
|
:data="userList"
|
|||
|
|
:loading="loading"
|
|||
|
|
:total="total"
|
|||
|
|
show-selection
|
|||
|
|
@search="handleSearch"
|
|||
|
|
@selection-change="handleSelectionChange"
|
|||
|
|
@page-change="handlePageChange"
|
|||
|
|
>
|
|||
|
|
<template #filters="{ search }">
|
|||
|
|
<el-select
|
|||
|
|
v-model="filters.status"
|
|||
|
|
placeholder="用户状态"
|
|||
|
|
clearable
|
|||
|
|
@change="search"
|
|||
|
|
style="width: 120px"
|
|||
|
|
>
|
|||
|
|
<el-option label="启用" value="active" />
|
|||
|
|
<el-option label="禁用" value="inactive" />
|
|||
|
|
</el-select>
|
|||
|
|
|
|||
|
|
<el-select
|
|||
|
|
v-model="filters.role"
|
|||
|
|
placeholder="用户角色"
|
|||
|
|
clearable
|
|||
|
|
@change="search"
|
|||
|
|
style="width: 120px"
|
|||
|
|
>
|
|||
|
|
<el-option
|
|||
|
|
v-for="role in roleOptions"
|
|||
|
|
:key="role.id"
|
|||
|
|
:label="role.name"
|
|||
|
|
:value="role.id"
|
|||
|
|
/>
|
|||
|
|
</el-select>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<template #actions>
|
|||
|
|
<el-button
|
|||
|
|
type="primary"
|
|||
|
|
@click="handleAdd"
|
|||
|
|
v-permission="['user:create']"
|
|||
|
|
>
|
|||
|
|
添加用户
|
|||
|
|
</el-button>
|
|||
|
|
|
|||
|
|
<el-button
|
|||
|
|
type="danger"
|
|||
|
|
:disabled="!selectedUsers.length"
|
|||
|
|
@click="handleBatchDelete"
|
|||
|
|
v-permission="['user:delete']"
|
|||
|
|
>
|
|||
|
|
批量删除
|
|||
|
|
</el-button>
|
|||
|
|
|
|||
|
|
<el-button @click="handleExport">
|
|||
|
|
导出数据
|
|||
|
|
</el-button>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<el-table-column prop="id" label="ID" width="80" />
|
|||
|
|
<el-table-column prop="username" label="用户名" />
|
|||
|
|
<el-table-column prop="email" label="邮箱" />
|
|||
|
|
<el-table-column prop="phone" label="手机号" />
|
|||
|
|
<el-table-column prop="role_name" label="角色" />
|
|||
|
|
<el-table-column prop="status" label="状态" width="100">
|
|||
|
|
<template #default="{ row }">
|
|||
|
|
<el-tag :type="row.status === 'active' ? 'success' : 'danger'">
|
|||
|
|
{{ row.status === 'active' ? '启用' : '禁用' }}
|
|||
|
|
</el-tag>
|
|||
|
|
</template>
|
|||
|
|
</el-table-column>
|
|||
|
|
<el-table-column prop="created_at" label="创建时间" width="180" />
|
|||
|
|
|
|||
|
|
<template #actions-column="{ row }">
|
|||
|
|
<el-button
|
|||
|
|
type="primary"
|
|||
|
|
size="small"
|
|||
|
|
@click="handleEdit(row)"
|
|||
|
|
v-permission="['user:update']"
|
|||
|
|
>
|
|||
|
|
编辑
|
|||
|
|
</el-button>
|
|||
|
|
|
|||
|
|
<el-button
|
|||
|
|
:type="row.status === 'active' ? 'warning' : 'success'"
|
|||
|
|
size="small"
|
|||
|
|
@click="handleToggleStatus(row)"
|
|||
|
|
v-permission="['user:update']"
|
|||
|
|
>
|
|||
|
|
{{ row.status === 'active' ? '禁用' : '启用' }}
|
|||
|
|
</el-button>
|
|||
|
|
|
|||
|
|
<el-button
|
|||
|
|
type="danger"
|
|||
|
|
size="small"
|
|||
|
|
@click="handleDelete(row)"
|
|||
|
|
v-permission="['user:delete']"
|
|||
|
|
>
|
|||
|
|
删除
|
|||
|
|
</el-button>
|
|||
|
|
</template>
|
|||
|
|
</DataTable>
|
|||
|
|
</el-card>
|
|||
|
|
|
|||
|
|
<!-- 用户表单对话框 -->
|
|||
|
|
<UserFormDialog
|
|||
|
|
v-model="showUserDialog"
|
|||
|
|
:user="currentUser"
|
|||
|
|
@success="handleFormSuccess"
|
|||
|
|
/>
|
|||
|
|
</div>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script setup lang="ts">
|
|||
|
|
import { ref, reactive, onMounted } from 'vue'
|
|||
|
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
|||
|
|
import { userApi } from '@/api/user'
|
|||
|
|
import { roleApi } from '@/api/role'
|
|||
|
|
import type { User, UserListParams } from '@/types/user'
|
|||
|
|
import DataTable from '@/components/DataTable.vue'
|
|||
|
|
import PageHeader from '@/components/PageHeader.vue'
|
|||
|
|
import UserFormDialog from './components/UserFormDialog.vue'
|
|||
|
|
|
|||
|
|
const loading = ref(false)
|
|||
|
|
const userList = ref<User[]>([])
|
|||
|
|
const total = ref(0)
|
|||
|
|
const selectedUsers = ref<User[]>([])
|
|||
|
|
const roleOptions = ref([])
|
|||
|
|
const showUserDialog = ref(false)
|
|||
|
|
const currentUser = ref<User | null>(null)
|
|||
|
|
|
|||
|
|
const filters = reactive({
|
|||
|
|
status: '',
|
|||
|
|
role: ''
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
const pagination = reactive({
|
|||
|
|
page: 1,
|
|||
|
|
size: 20
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
const searchParams = reactive<UserListParams>({
|
|||
|
|
keyword: '',
|
|||
|
|
...filters,
|
|||
|
|
...pagination
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
onMounted(() => {
|
|||
|
|
loadUserList()
|
|||
|
|
loadRoleOptions()
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
const loadUserList = async () => {
|
|||
|
|
loading.value = true
|
|||
|
|
try {
|
|||
|
|
const response = await userApi.getUserList(searchParams)
|
|||
|
|
userList.value = response.data.list
|
|||
|
|
total.value = response.data.total
|
|||
|
|
} catch (error) {
|
|||
|
|
ElMessage.error('加载用户列表失败')
|
|||
|
|
} finally {
|
|||
|
|
loading.value = false
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const loadRoleOptions = async () => {
|
|||
|
|
try {
|
|||
|
|
const response = await roleApi.getRoleList({ page: 1, size: 100 })
|
|||
|
|
roleOptions.value = response.data.list
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('加载角色选项失败:', error)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const handleSearch = (keyword: string) => {
|
|||
|
|
searchParams.keyword = keyword
|
|||
|
|
searchParams.status = filters.status
|
|||
|
|
searchParams.role = filters.role
|
|||
|
|
searchParams.page = 1
|
|||
|
|
loadUserList()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const handleSelectionChange = (selection: User[]) => {
|
|||
|
|
selectedUsers.value = selection
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const handlePageChange = (page: number, size: number) => {
|
|||
|
|
searchParams.page = page
|
|||
|
|
searchParams.size = size
|
|||
|
|
loadUserList()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const handleAdd = () => {
|
|||
|
|
currentUser.value = null
|
|||
|
|
showUserDialog.value = true
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const handleEdit = (user: User) => {
|
|||
|
|
currentUser.value = user
|
|||
|
|
showUserDialog.value = true
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const handleDelete = async (user: User) => {
|
|||
|
|
try {
|
|||
|
|
await ElMessageBox.confirm(
|
|||
|
|
`确定要删除用户 "${user.username}" 吗?`,
|
|||
|
|
'确认删除',
|
|||
|
|
{ type: 'warning' }
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
await userApi.deleteUser(user.id)
|
|||
|
|
ElMessage.success('删除成功')
|
|||
|
|
loadUserList()
|
|||
|
|
} catch (error) {
|
|||
|
|
if (error !== 'cancel') {
|
|||
|
|
ElMessage.error('删除失败')
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const handleBatchDelete = async () => {
|
|||
|
|
try {
|
|||
|
|
await ElMessageBox.confirm(
|
|||
|
|
`确定要删除选中的 ${selectedUsers.value.length} 个用户吗?`,
|
|||
|
|
'确认批量删除',
|
|||
|
|
{ type: 'warning' }
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
const userIds = selectedUsers.value.map(user => user.id)
|
|||
|
|
await userApi.batchDeleteUsers(userIds)
|
|||
|
|
ElMessage.success('批量删除成功')
|
|||
|
|
loadUserList()
|
|||
|
|
} catch (error) {
|
|||
|
|
if (error !== 'cancel') {
|
|||
|
|
ElMessage.error('批量删除失败')
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const handleToggleStatus = async (user: User) => {
|
|||
|
|
try {
|
|||
|
|
const newStatus = user.status === 'active' ? 'inactive' : 'active'
|
|||
|
|
await userApi.updateUserStatus(user.id, newStatus)
|
|||
|
|
ElMessage.success('状态更新成功')
|
|||
|
|
loadUserList()
|
|||
|
|
} catch (error) {
|
|||
|
|
ElMessage.error('状态更新失败')
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const handleExport = async () => {
|
|||
|
|
try {
|
|||
|
|
const response = await userApi.exportUsers(searchParams)
|
|||
|
|
// 处理文件下载
|
|||
|
|
const blob = new Blob([response.data])
|
|||
|
|
const url = window.URL.createObjectURL(blob)
|
|||
|
|
const link = document.createElement('a')
|
|||
|
|
link.href = url
|
|||
|
|
link.download = `用户数据_${new Date().toISOString().slice(0, 10)}.xlsx`
|
|||
|
|
link.click()
|
|||
|
|
window.URL.revokeObjectURL(url)
|
|||
|
|
ElMessage.success('导出成功')
|
|||
|
|
} catch (error) {
|
|||
|
|
ElMessage.error('导出失败')
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const handleFormSuccess = () => {
|
|||
|
|
showUserDialog.value = false
|
|||
|
|
loadUserList()
|
|||
|
|
}
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<style lang="scss" scoped>
|
|||
|
|
.user-list-page {
|
|||
|
|
padding: 24px;
|
|||
|
|
}
|
|||
|
|
</style>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 6. 性能优化
|
|||
|
|
|
|||
|
|
### 6.1 构建优化
|
|||
|
|
- **代码分割**: 按路由和功能模块分割代码
|
|||
|
|
- **Tree Shaking**: 移除未使用的代码
|
|||
|
|
- **压缩优化**: 代码压缩和资源优化
|
|||
|
|
- **缓存策略**: 合理的缓存策略配置
|
|||
|
|
- **CDN加速**: 静态资源CDN加速
|
|||
|
|
|
|||
|
|
### 6.2 运行时优化
|
|||
|
|
- **虚拟滚动**: 大数据量列表使用虚拟滚动
|
|||
|
|
- **懒加载**: 组件和图片懒加载
|
|||
|
|
- **防抖节流**: 搜索和操作防抖节流
|
|||
|
|
- **内存管理**: 避免内存泄漏
|
|||
|
|
- **组件缓存**: 合理使用keep-alive
|
|||
|
|
|
|||
|
|
### 6.3 用户体验优化
|
|||
|
|
- **加载状态**: 明确的加载状态提示
|
|||
|
|
- **骨架屏**: 使用骨架屏提升感知性能
|
|||
|
|
- **错误边界**: 错误边界和降级处理
|
|||
|
|
- **响应式设计**: 适配不同屏幕尺寸
|
|||
|
|
- **无障碍支持**: 支持键盘导航和屏幕阅读器
|
|||
|
|
|
|||
|
|
## 7. 测试策略
|
|||
|
|
|
|||
|
|
### 7.1 单元测试
|
|||
|
|
```typescript
|
|||
|
|
// tests/components/DataTable.test.ts
|
|||
|
|
import { mount } from '@vue/test-utils'
|
|||
|
|
import { describe, it, expect } from 'vitest'
|
|||
|
|
import DataTable from '@/components/DataTable.vue'
|
|||
|
|
|
|||
|
|
describe('DataTable', () => {
|
|||
|
|
it('renders correctly with data', () => {
|
|||
|
|
const wrapper = mount(DataTable, {
|
|||
|
|
props: {
|
|||
|
|
data: [
|
|||
|
|
{ id: 1, name: 'Test User', email: 'test@example.com' }
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
expect(wrapper.find('.data-table').exists()).toBe(true)
|
|||
|
|
expect(wrapper.text()).toContain('Test User')
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
it('emits search event when search button clicked', async () => {
|
|||
|
|
const wrapper = mount(DataTable)
|
|||
|
|
|
|||
|
|
await wrapper.find('.search-button').trigger('click')
|
|||
|
|
|
|||
|
|
expect(wrapper.emitted('search')).toBeTruthy()
|
|||
|
|
})
|
|||
|
|
})
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 7.2 集成测试
|
|||
|
|
- **页面测试**: 完整页面功能测试
|
|||
|
|
- **API测试**: 接口集成测试
|
|||
|
|
- **权限测试**: 权限控制测试
|
|||
|
|
- **流程测试**: 完整业务流程测试
|
|||
|
|
- **兼容性测试**: 浏览器兼容性测试
|
|||
|
|
|
|||
|
|
### 7.3 E2E测试
|
|||
|
|
```typescript
|
|||
|
|
// tests/e2e/user-management.spec.ts
|
|||
|
|
import { test, expect } from '@playwright/test'
|
|||
|
|
|
|||
|
|
test('user management flow', async ({ page }) => {
|
|||
|
|
// 登录
|
|||
|
|
await page.goto('/login')
|
|||
|
|
await page.fill('[data-testid="username"]', 'admin')
|
|||
|
|
await page.fill('[data-testid="password"]', 'password')
|
|||
|
|
await page.click('[data-testid="login-button"]')
|
|||
|
|
|
|||
|
|
// 导航到用户管理页面
|
|||
|
|
await page.click('[data-testid="user-menu"]')
|
|||
|
|
await expect(page).toHaveURL('/users')
|
|||
|
|
|
|||
|
|
// 添加用户
|
|||
|
|
await page.click('[data-testid="add-user-button"]')
|
|||
|
|
await page.fill('[data-testid="username-input"]', 'testuser')
|
|||
|
|
await page.fill('[data-testid="email-input"]', 'test@example.com')
|
|||
|
|
await page.click('[data-testid="save-button"]')
|
|||
|
|
|
|||
|
|
// 验证用户已添加
|
|||
|
|
await expect(page.locator('text=testuser')).toBeVisible()
|
|||
|
|
})
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 8. 部署配置
|
|||
|
|
|
|||
|
|
### 8.1 环境配置
|
|||
|
|
```bash
|
|||
|
|
# .env.production
|
|||
|
|
VITE_API_BASE_URL=https://api.xlxumu.com
|
|||
|
|
VITE_APP_TITLE=畜牧养殖管理平台
|
|||
|
|
VITE_APP_VERSION=1.0.0
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 8.2 构建脚本
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"scripts": {
|
|||
|
|
"dev": "vite",
|
|||
|
|
"build": "vue-tsc --noEmit && vite build",
|
|||
|
|
"build:staging": "vue-tsc --noEmit && vite build --mode staging",
|
|||
|
|
"preview": "vite preview",
|
|||
|
|
"test": "vitest",
|
|||
|
|
"test:e2e": "playwright test",
|
|||
|
|
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix",
|
|||
|
|
"type-check": "vue-tsc --noEmit"
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 8.3 Docker配置
|
|||
|
|
```dockerfile
|
|||
|
|
# Dockerfile
|
|||
|
|
FROM node:18-alpine as build-stage
|
|||
|
|
|
|||
|
|
WORKDIR /app
|
|||
|
|
COPY package*.json ./
|
|||
|
|
RUN npm ci --only=production
|
|||
|
|
|
|||
|
|
COPY . .
|
|||
|
|
RUN npm run build
|
|||
|
|
|
|||
|
|
FROM nginx:stable-alpine as production-stage
|
|||
|
|
COPY --from=build-stage /app/dist /usr/share/nginx/html
|
|||
|
|
COPY nginx.conf /etc/nginx/nginx.conf
|
|||
|
|
|
|||
|
|
EXPOSE 80
|
|||
|
|
CMD ["nginx", "-g", "daemon off;"]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 9. 监控运维
|
|||
|
|
|
|||
|
|
### 9.1 性能监控
|
|||
|
|
- **页面性能**: 监控页面加载时间和渲染性能
|
|||
|
|
- **接口性能**: 监控API调用性能和成功率
|
|||
|
|
- **错误监控**: 监控JavaScript错误和异常
|
|||
|
|
- **用户行为**: 监控用户操作行为和路径
|
|||
|
|
- **资源监控**: 监控静态资源加载情况
|
|||
|
|
|
|||
|
|
### 9.2 业务监控
|
|||
|
|
- **用户活跃**: 监控管理员活跃度和使用情况
|
|||
|
|
- **功能使用**: 监控各功能模块使用频率
|
|||
|
|
- **操作统计**: 监控关键操作的执行情况
|
|||
|
|
- **数据质量**: 监控数据的完整性和准确性
|
|||
|
|
- **系统健康**: 监控系统整体健康状态
|
|||
|
|
|
|||
|
|
### 9.3 告警配置
|
|||
|
|
- **错误告警**: 错误率超过阈值时告警
|
|||
|
|
- **性能告警**: 性能指标异常时告警
|
|||
|
|
- **业务告警**: 关键业务指标异常时告警
|
|||
|
|
- **实时通知**: 通过邮件、短信等方式实时通知
|
|||
|
|
- **报表推送**: 定期推送监控报表和分析
|
|||
|
|
|
|||
|
|
## 10. 总结
|
|||
|
|
|
|||
|
|
本开发文档详细规划了畜牧养殖管理平台管理后台的开发计划,包括:
|
|||
|
|
|
|||
|
|
### 10.1 开发亮点
|
|||
|
|
- **现代化技术栈**: 使用Vue3 + TypeScript + Vite等现代化技术
|
|||
|
|
- **组件化开发**: 完善的组件化开发体系和复用机制
|
|||
|
|
- **权限控制**: 细粒度的权限控制和安全防护
|
|||
|
|
- **数据可视化**: 丰富的图表和数据展示功能
|
|||
|
|
- **用户体验**: 注重用户体验和操作效率
|
|||
|
|
|
|||
|
|
### 10.2 技术特色
|
|||
|
|
- **TypeScript**: 严格的类型检查和代码质量保证
|
|||
|
|
- **响应式设计**: 适配不同屏幕尺寸和设备
|
|||
|
|
- **性能优化**: 全面的性能优化和加载策略
|
|||
|
|
- **错误处理**: 完善的错误处理和用户提示机制
|
|||
|
|
- **可维护性**: 清晰的代码结构和文档说明
|
|||
|
|
|
|||
|
|
### 10.3 开发保障
|
|||
|
|
- **规范化**: 完善的开发规范和代码标准
|
|||
|
|
- **自动化**: 自动化测试、构建和部署流程
|
|||
|
|
- **质量控制**: 严格的代码审查和测试要求
|
|||
|
|
- **团队协作**: 明确的分工和协作机制
|
|||
|
|
- **文档完善**: 完整的开发和使用文档
|
|||
|
|
|
|||
|
|
### 10.4 后续优化
|
|||
|
|
- **功能扩展**: 根据业务需求持续扩展功能
|
|||
|
|
- **性能提升**: 持续的性能监控和优化
|
|||
|
|
- **用户体验**: 基于用户反馈优化交互体验
|
|||
|
|
- **技术升级**: 跟进技术发展,适时升级技术栈
|
|||
|
|
- **安全加固**: 持续加强安全防护和风险控制
|