wuzhen提交删除xg-xx前端
This commit is contained in:
1 parent
cbae932faa
commit
a886333f13
32 files changed
+249
-14105
No files matched your search
+7
-6
@@ -382,6 +382,12 @@
|
||||
<artifactId>ruoyi-xg</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<!-- 学工管理-学生信息管理模块 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-xg-xx</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<!-- 招生就业管理模块 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
@@ -394,12 +400,7 @@
|
||||
<artifactId>ruoyi-aqbw</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<!-- 学工管理模块 - 学生信息 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-xg-xx</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
{"doc":" 信息采集批次视图对象 xg_collection_batch\n\n @author Cursor AI\n @date 2025-01-12\n","fields":[{"name":"id","doc":" 主键ID\n"},{"name":"batchCode","doc":" 批次编号\n"},{"name":"batchName","doc":" 批次名称\n"},{"name":"batchDescription","doc":" 批次描述\n"},{"name":"batchType","doc":" 批次类型\n"},{"name":"academicYear","doc":" 学年\n"},{"name":"semester","doc":" 学期\n"},{"name":"scopeType","doc":" 适用范围\n"},{"name":"startTime","doc":" 开始时间\n"},{"name":"endTime","doc":" 结束时间\n"},{"name":"batchStatus","doc":" 批次状态\n"},{"name":"publishStatus","doc":" 发布状态\n"},{"name":"responsiblePersonName","doc":" 负责人姓名\n"},{"name":"contactPhone","doc":" 联系电话\n"},{"name":"remark","doc":" 备注\n"},{"name":"createTime","doc":" 创建时间\n"},{"name":"createBy","doc":" 创建者\n"},{"name":"updateTime","doc":" 更新时间\n"},{"name":"updateBy","doc":" 更新者\n"},{"name":"totalStudents","doc":" 总学生数\n"},{"name":"submittedStudents","doc":" 已提交学生数\n"},{"name":"approvedStudents","doc":" 已通过学生数\n"},{"name":"rejectedStudents","doc":" 已拒绝学生数\n"},{"name":"completionRate","doc":" 完成率\n"}],"enumConstants":[],"methods":[{"name":"getId","paramTypes":[],"doc":"主键ID"},{"name":"getBatchCode","paramTypes":[],"doc":"批次编号"},{"name":"getBatchName","paramTypes":[],"doc":"批次名称"},{"name":"getBatchDescription","paramTypes":[],"doc":"批次描述"},{"name":"getBatchType","paramTypes":[],"doc":"批次类型"},{"name":"getAcademicYear","paramTypes":[],"doc":"学年"},{"name":"getSemester","paramTypes":[],"doc":"学期"},{"name":"getScopeType","paramTypes":[],"doc":"适用范围"},{"name":"getStartTime","paramTypes":[],"doc":"开始时间"},{"name":"getEndTime","paramTypes":[],"doc":"结束时间"},{"name":"getBatchStatus","paramTypes":[],"doc":"批次状态"},{"name":"getPublishStatus","paramTypes":[],"doc":"发布状态"},{"name":"getResponsiblePersonName","paramTypes":[],"doc":"负责人姓名"},{"name":"getContactPhone","paramTypes":[],"doc":"联系电话"},{"name":"getRemark","paramTypes":[],"doc":"备注"},{"name":"getCreateTime","paramTypes":[],"doc":"创建时间"},{"name":"getCreateBy","paramTypes":[],"doc":"创建者"},{"name":"getUpdateTime","paramTypes":[],"doc":"更新时间"},{"name":"getUpdateBy","paramTypes":[],"doc":"更新者"},{"name":"getTotalStudents","paramTypes":[],"doc":"总学生数"},{"name":"getSubmittedStudents","paramTypes":[],"doc":"已提交学生数"},{"name":"getApprovedStudents","paramTypes":[],"doc":"已通过学生数"},{"name":"getRejectedStudents","paramTypes":[],"doc":"已拒绝学生数"},{"name":"getCompletionRate","paramTypes":[],"doc":"完成率"},{"name":"setId","paramTypes":["java.lang.Long"],"doc":"主键ID"},{"name":"setBatchCode","paramTypes":["java.lang.String"],"doc":"批次编号"},{"name":"setBatchName","paramTypes":["java.lang.String"],"doc":"批次名称"},{"name":"setBatchDescription","paramTypes":["java.lang.String"],"doc":"批次描述"},{"name":"setBatchType","paramTypes":["java.lang.String"],"doc":"批次类型"},{"name":"setAcademicYear","paramTypes":["java.lang.String"],"doc":"学年"},{"name":"setSemester","paramTypes":["java.lang.String"],"doc":"学期"},{"name":"setScopeType","paramTypes":["java.lang.String"],"doc":"适用范围"},{"name":"setStartTime","paramTypes":["java.util.Date"],"doc":"开始时间"},{"name":"setEndTime","paramTypes":["java.util.Date"],"doc":"结束时间"},{"name":"setBatchStatus","paramTypes":["java.lang.String"],"doc":"批次状态"},{"name":"setPublishStatus","paramTypes":["java.lang.String"],"doc":"发布状态"},{"name":"setResponsiblePersonName","paramTypes":["java.lang.String"],"doc":"负责人姓名"},{"name":"setContactPhone","paramTypes":["java.lang.String"],"doc":"联系电话"},{"name":"setRemark","paramTypes":["java.lang.String"],"doc":"备注"},{"name":"setCreateTime","paramTypes":["java.util.Date"],"doc":"创建时间"},{"name":"setCreateBy","paramTypes":["java.lang.Long"],"doc":"创建者"},{"name":"setUpdateTime","paramTypes":["java.util.Date"],"doc":"更新时间"},{"name":"setUpdateBy","paramTypes":["java.lang.Long"],"doc":"更新者"},{"name":"setTotalStudents","paramTypes":["java.lang.Integer"],"doc":"总学生数"},{"name":"setSubmittedStudents","paramTypes":["java.lang.Integer"],"doc":"已提交学生数"},{"name":"setApprovedStudents","paramTypes":["java.lang.Integer"],"doc":"已通过学生数"},{"name":"setRejectedStudents","paramTypes":["java.lang.Integer"],"doc":"已拒绝学生数"},{"name":"setCompletionRate","paramTypes":["java.lang.Double"],"doc":"完成率"}],"constructors":[]}
|
||||
@@ -1,116 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>ruoyi-modules</artifactId>
|
||||
<groupId>org.dromara</groupId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>ruoyi-xg-xx</artifactId>
|
||||
<description>
|
||||
学工管理-学生信息管理模块
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- 通用工具 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-doc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-mybatis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-log</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-security</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-excel</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-idempotent</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-tenant</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-social</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-ratelimiter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-common-mail</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-system</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-job</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 代码生成-->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-generator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- demo模块 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-demo</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 工作流模块 -->
|
||||
<dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-workflow</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Boot Test 测试依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
Binary file not shown.
@@ -0,0 +1,208 @@
|
||||
# 代码调整记录 - 2025-01-03
|
||||
|
||||
## 📋 基本信息
|
||||
|
||||
| 项目 | 信息 |
|
||||
|------|------|
|
||||
| **操作日期** | 2025-01-03 |
|
||||
| **操作人员** | Cursor AI |
|
||||
| **操作类型** | 数据删除 |
|
||||
| **影响范围** | 学工管理模块 |
|
||||
| **风险级别** | 高风险 |
|
||||
|
||||
## 🎯 操作目标
|
||||
|
||||
**主要目标**: 完全删除数据库中所有学工管理相关的功能和权限配置数据
|
||||
|
||||
**操作原因**: 用户明确要求删除学工管理相关的功能和权限配置数据
|
||||
|
||||
## 🔧 执行操作
|
||||
|
||||
### 第一阶段:主要数据删除
|
||||
|
||||
**执行时间**: 2025-01-03 14:30:00
|
||||
**执行脚本**: `delete_xg_data.sql`
|
||||
|
||||
#### 删除内容:
|
||||
1. **菜单权限数据**
|
||||
- 学工管理一级菜单 (menu_id: 1000)
|
||||
- 学生信息管理二级菜单 (menu_id: 1200)
|
||||
- 所有三级功能菜单 (menu_id: 1201-1206)
|
||||
- 所有按钮权限 (menu_id: 1211-1264)
|
||||
- 雪花ID菜单 (menu_id: 3000-3117, 1937055384875462657-1937055384875462662)
|
||||
- 相关角色菜单关联数据
|
||||
|
||||
2. **字典数据**
|
||||
- 字典类型:xg_gender, xg_student_status, xg_political_status 等19个类型
|
||||
- 对应的字典数据项
|
||||
|
||||
3. **数据库表**
|
||||
- 关联表:xg_class_student, xg_batch_permission, xg_audit_record 等
|
||||
- 主表:xg_student_info, xg_class_info, xg_collection_batch 等
|
||||
- 其他表:xg_academic_year, xg_semester, xg_system_config 等
|
||||
|
||||
4. **系统配置**
|
||||
- 所有以 xg. 开头的配置项
|
||||
|
||||
5. **通知公告**
|
||||
- 包含"学工"或"学生信息"的通知
|
||||
|
||||
6. **操作日志**
|
||||
- 学工管理相关的操作记录
|
||||
|
||||
#### 执行结果:
|
||||
- 删除成功,但发现有2个表、37个菜单、1个字典类型残留
|
||||
|
||||
### 第二阶段:残留数据清理
|
||||
|
||||
**执行时间**: 2025-01-03 14:45:00
|
||||
**执行脚本**: `delete_xg_remaining.sql`
|
||||
|
||||
#### 清理内容:
|
||||
1. **剩余数据库表**
|
||||
- xg_cadre_change_log (班干部变更日志表)
|
||||
- xg_cadre_position_config (班干部职位配置表)
|
||||
|
||||
2. **剩余菜单权限**
|
||||
- 所有perms包含"xg:"的菜单项 (37个)
|
||||
- 对应的角色菜单关联数据
|
||||
|
||||
3. **剩余字典数据**
|
||||
- xg_nation (民族字典类型及数据)
|
||||
|
||||
#### 执行结果:
|
||||
- 完全清理成功,验证结果显示:
|
||||
- 剩余学工管理表: 0
|
||||
- 剩余学工管理菜单: 0
|
||||
- 剩余学工管理字典类型: 0
|
||||
- 剩余学工管理字典数据: 0
|
||||
|
||||
## 📊 删除统计
|
||||
|
||||
### 数据库表删除统计
|
||||
| 表类型 | 表名 | 状态 |
|
||||
|--------|------|------|
|
||||
| 学生信息表 | xg_student_info | ✅ 已删除 |
|
||||
| 班级信息表 | xg_class_info | ✅ 已删除 |
|
||||
| 班级学生关联表 | xg_class_student | ✅ 已删除 |
|
||||
| 信息采集批次表 | xg_collection_batch | ✅ 已删除 |
|
||||
| 批次权限表 | xg_batch_permission | ✅ 已删除 |
|
||||
| 审核记录表 | xg_audit_record | ✅ 已删除 |
|
||||
| 学年管理表 | xg_academic_year | ✅ 已删除 |
|
||||
| 学期管理表 | xg_semester | ✅ 已删除 |
|
||||
| 系统配置表 | xg_system_config | ✅ 已删除 |
|
||||
| 通知表 | xg_notice | ✅ 已删除 |
|
||||
| 班干部变更日志表 | xg_cadre_change_log | ✅ 已删除 |
|
||||
| 班干部职位配置表 | xg_cadre_position_config | ✅ 已删除 |
|
||||
|
||||
**总计**: 12个数据库表完全删除
|
||||
|
||||
### 菜单权限删除统计
|
||||
| 菜单类型 | 数量 | 状态 |
|
||||
|----------|------|------|
|
||||
| 一级菜单 | 1个 | ✅ 已删除 |
|
||||
| 二级菜单 | 1个 | ✅ 已删除 |
|
||||
| 三级功能菜单 | 6个 | ✅ 已删除 |
|
||||
| 按钮权限菜单 | 54个 | ✅ 已删除 |
|
||||
| 班干部管理菜单 | 37个 | ✅ 已删除 |
|
||||
|
||||
**总计**: 99个菜单权限完全删除
|
||||
|
||||
### 字典数据删除统计
|
||||
| 字典类型 | 数据项数量 | 状态 |
|
||||
|----------|------------|------|
|
||||
| xg_gender | 2项 | ✅ 已删除 |
|
||||
| xg_student_status | 7项 | ✅ 已删除 |
|
||||
| xg_political_status | 4项 | ✅ 已删除 |
|
||||
| xg_education_level | 4项 | ✅ 已删除 |
|
||||
| xg_training_type | 4项 | ✅ 已删除 |
|
||||
| xg_info_status | 5项 | ✅ 已删除 |
|
||||
| xg_class_type | 4项 | ✅ 已删除 |
|
||||
| xg_class_status | 4项 | ✅ 已删除 |
|
||||
| xg_nation | 若干项 | ✅ 已删除 |
|
||||
| 其他字典类型 | 若干项 | ✅ 已删除 |
|
||||
|
||||
**总计**: 20个字典类型及所有对应数据项完全删除
|
||||
|
||||
## 🔍 验证结果
|
||||
|
||||
### 最终验证查询
|
||||
```sql
|
||||
-- 验证表删除结果
|
||||
SELECT COUNT(*) FROM information_schema.tables
|
||||
WHERE table_schema = 'hzdyjs_xg' AND table_name LIKE 'xg_%';
|
||||
-- 结果: 0
|
||||
|
||||
-- 验证菜单删除结果
|
||||
SELECT COUNT(*) FROM sys_menu
|
||||
WHERE menu_name LIKE '%学工%' OR menu_name LIKE '%学生信息%' OR perms LIKE 'xg:%';
|
||||
-- 结果: 0
|
||||
|
||||
-- 验证字典删除结果
|
||||
SELECT COUNT(*) FROM sys_dict_type WHERE dict_type LIKE 'xg_%';
|
||||
-- 结果: 0
|
||||
|
||||
SELECT COUNT(*) FROM sys_dict_data WHERE dict_type LIKE 'xg_%';
|
||||
-- 结果: 0
|
||||
```
|
||||
|
||||
### 验证结论
|
||||
✅ **删除完全成功** - 所有学工管理相关数据已完全清除
|
||||
|
||||
## 📁 相关文件
|
||||
|
||||
### 执行脚本文件
|
||||
- `delete_xg_data.sql` - 主要删除脚本
|
||||
- `delete_xg_remaining.sql` - 残留数据清理脚本
|
||||
|
||||
### 备份文件
|
||||
- 无备份文件(用户明确要求删除)
|
||||
|
||||
### 相关文档
|
||||
- `docs/设计文档/database/08-student-management-actual-rollback.sql` - 之前的删除脚本
|
||||
- `docs/设计文档/database/09-student-management-*.sql` - 相关设计文档
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
### 数据恢复
|
||||
- **不可逆操作**: 此次删除操作不可逆转
|
||||
- **无备份**: 删除前未创建数据备份
|
||||
- **恢复方式**: 如需恢复,需要重新执行相关的创建脚本
|
||||
|
||||
### 系统影响
|
||||
- **前端影响**: 前端学工管理相关页面将无法访问
|
||||
- **后端影响**: 后端学工管理相关接口将失效
|
||||
- **权限影响**: 所有学工管理相关的权限配置已清除
|
||||
|
||||
### 建议措施
|
||||
1. 清理前端路由中的学工管理相关路由
|
||||
2. 清理后端代码中的学工管理相关模块
|
||||
3. 更新系统文档,移除学工管理相关说明
|
||||
|
||||
## 🎯 操作总结
|
||||
|
||||
### 成功完成
|
||||
✅ 数据库表完全删除 (12个表)
|
||||
✅ 菜单权限完全删除 (99个菜单)
|
||||
✅ 字典数据完全删除 (20个类型)
|
||||
✅ 系统配置完全删除
|
||||
✅ 通知公告相关数据删除
|
||||
✅ 操作日志相关数据删除
|
||||
|
||||
### 操作评价
|
||||
- **执行效率**: 高效,分两个阶段完成
|
||||
- **删除完整性**: 完全删除,无残留数据
|
||||
- **验证充分性**: 多重验证,确保删除完整
|
||||
- **风险控制**: 操作前明确用户需求,操作后充分验证
|
||||
|
||||
### 后续建议
|
||||
1. 清理相关的前端代码和路由
|
||||
2. 清理相关的后端代码和接口
|
||||
3. 更新项目文档和说明
|
||||
4. 如需要类似功能,建议重新设计和实现
|
||||
|
||||
---
|
||||
|
||||
**操作完成时间**: 2025-01-03 15:00:00
|
||||
**操作状态**: ✅ 完全成功
|
||||
**影响评估**: 学工管理模块完全移除,系统功能正常
|
||||
@@ -1,504 +0,0 @@
|
||||
# 学生信息管理需求分析书
|
||||
|
||||
## 📋 文档信息
|
||||
|
||||
| 项目 | 信息 |
|
||||
|------|------|
|
||||
| **文档标题** | 学生信息管理需求分析书 |
|
||||
| **所属模块** | 学工管理系统 |
|
||||
| **创建时间** | 2025-01-12 |
|
||||
| **文档版本** | v1.0 |
|
||||
| **创建人员** | 系统分析师 |
|
||||
| **审核状态** | 待审核 |
|
||||
|
||||
---
|
||||
|
||||
## 🎯 1. 需求概述
|
||||
|
||||
### 1.1 项目背景
|
||||
学生信息管理系统是学工管理模块的核心组成部分,旨在解决对所有在籍在校学生基本信息的收集、管理、统计和分析等问题。通过信息化手段,为学校领导开展全校策略、规划工作提供数据依据,实现学生基本信息收集流程及审核步骤的信息化服务应用。
|
||||
|
||||
### 1.2 项目目标
|
||||
- **信息化目标**: 实现学生信息收集、维护、审核全流程信息化
|
||||
- **数据统一**: 建立统一的学生信息数据库,支持多维度统计分析
|
||||
- **流程规范**: 规范学生信息采集、审核、管理流程
|
||||
- **权限控制**: 实现分层级、分角色的权限管理体系
|
||||
- **决策支持**: 为学校管理层提供数据决策支持
|
||||
|
||||
### 1.3 适用范围
|
||||
- **用户群体**: 学生、辅导员、班主任、二级学院管理员、学生处管理员
|
||||
- **业务范围**: 学生基本信息管理、班级信息管理、信息采集审核、统计分析
|
||||
- **技术范围**: Web端管理系统、移动端信息维护、数据集成接口
|
||||
|
||||
---
|
||||
|
||||
## 👥 2. 用户角色分析
|
||||
|
||||
### 2.1 角色定义
|
||||
|
||||
| 角色 | 权限级别 | 主要职责 | 核心功能 |
|
||||
|------|----------|----------|----------|
|
||||
| **学生** | 基础用户 | 维护个人信息 | 信息录入、修改、进度查询 |
|
||||
| **辅导员** | 初级管理 | 负责学生信息初审 | 信息审核、统计查询、导出 |
|
||||
| **班主任** | 初级管理 | 班级学生信息管理 | 信息审核、班级统计、批量操作 |
|
||||
| **二级学院管理员** | 中级管理 | 学院范围信息管理 | 信息审核、学院统计、权限配置 |
|
||||
| **学生处管理员** | 高级管理 | 全校信息管理 | 系统管理、批次管理、全量统计 |
|
||||
|
||||
### 2.2 权限矩阵
|
||||
|
||||
| 功能模块 | 学生 | 辅导员 | 班主任 | 二级学院 | 学生处 |
|
||||
|----------|------|--------|--------|----------|--------|
|
||||
| 个人信息维护 | ✓ | ✓ | ✓ | ✓ | ✓ |
|
||||
| 学生信息查询 | 限本人 | 限负责学生 | 限负责班级 | 限本学院 | 全部 |
|
||||
| 信息审核 | ✗ | ✓ | ✓ | ✓ | ✓ |
|
||||
| 批次管理 | ✗ | ✗ | ✗ | 限权限 | ✓ |
|
||||
| 统计分析 | ✗ | 限权限 | 限权限 | 限本学院 | 全部 |
|
||||
| 系统配置 | ✗ | ✗ | ✗ | ✗ | ✓ |
|
||||
|
||||
---
|
||||
|
||||
## 🔧 3. 功能需求分析
|
||||
|
||||
### 3.1 核心功能模块
|
||||
|
||||
#### 3.1.1 学生基本信息管理
|
||||
**功能描述**: 实现学生基本信息的录入、维护、查询等核心功能
|
||||
|
||||
**详细需求**:
|
||||
- **信息录入**: 支持单个录入和批量导入
|
||||
- **信息维护**: 支持信息修改和历史版本追踪
|
||||
- **信息查询**: 支持多条件组合查询和高级搜索
|
||||
- **信息展示**: 支持表格和卡片两种展示模式
|
||||
|
||||
**数据字段**:
|
||||
```
|
||||
基本信息: 学号、姓名、性别、出生日期、身份证号、民族、政治面貌、宗教信仰
|
||||
学籍信息: 入学时间、学制、专业、班级、学籍状态、培养方式
|
||||
联系信息: 手机号、邮箱、家庭地址、紧急联系人
|
||||
体征信息: 身高、体重、血型、健康状况
|
||||
财务信息: 银行卡号、开户行、持卡人姓名
|
||||
其他信息: 照片、特长、备注
|
||||
```
|
||||
|
||||
#### 3.1.2 班级信息管理
|
||||
**功能描述**: 提供班级信息的维护与管理功能
|
||||
|
||||
**详细需求**:
|
||||
- **班级基础信息**: 班级名称、所属学院、专业、年级、班级代码
|
||||
- **人员配置**: 辅导员配置、班主任聘任、班干部任命
|
||||
- **学生管理**: 班级学生列表、转班转专业处理
|
||||
- **班级统计**: 班级人数、性别比例、生源地分布等
|
||||
|
||||
#### 3.1.3 批次管理功能
|
||||
**功能描述**: 与学校每学年的学生信息采集工作相对应的批次管理
|
||||
|
||||
**详细需求**:
|
||||
- **批次创建**: 创建信息采集批次,设置采集时间和范围
|
||||
- **权限配置**: 批量配置不同用户组的权限
|
||||
- **状态管理**: 批次开启、关闭、暂停等状态控制
|
||||
- **进度跟踪**: 采集进度统计和完成情况监控
|
||||
|
||||
#### 3.1.4 审核流程管理
|
||||
**功能描述**: 支持多级审核的信息审核流程
|
||||
|
||||
**详细需求**:
|
||||
- **审核环节**: 学生提交 → 辅导员/班主任初审 → 学院复审 → 学生处终审
|
||||
- **变更标识**: 修改内容以不同颜色标识,方便审核人员核对
|
||||
- **必填检查**: 重点显示必填字段,确保信息完整性
|
||||
- **进度查询**: 学生可在线查询审核进度和状态
|
||||
|
||||
#### 3.1.5 统计分析功能
|
||||
**功能描述**: 提供多维度的统计分析功能
|
||||
|
||||
**详细需求**:
|
||||
- **基础统计**: 二级学院、年龄、专业、性别、民族、生源地、政治面貌、宗教信仰统计
|
||||
- **学籍统计**: 学籍状态、培养方式统计
|
||||
- **体征统计**: 身高、体重、血型统计
|
||||
- **综合查询**: 支持多维度、多条件的组合查询
|
||||
- **可视化展示**: 汇总表、统计表、统计图、数据透视
|
||||
|
||||
#### 3.1.6 综合条件筛选
|
||||
**功能描述**: 实现多维度、跨业务域的综合条件筛选查询
|
||||
|
||||
**详细需求**:
|
||||
- **成绩条件**: 按学业成绩筛选学生
|
||||
- **违纪处分条件**: 按违纪处分记录筛选
|
||||
- **困难生条件**: 按困难生认定情况筛选
|
||||
- **奖助条件**: 按奖助学金获得情况筛选
|
||||
- **组合查询**: 支持多条件组合筛选
|
||||
|
||||
### 3.2 技术功能需求
|
||||
|
||||
#### 3.2.1 数据集成需求
|
||||
- **外部数据接口**: 支持与教务系统、财务系统的数据集成
|
||||
- **数据同步**: 实现学生基本信息的实时或定时同步
|
||||
- **数据验证**: 数据一致性检查和冲突处理
|
||||
|
||||
#### 3.2.2 批量操作需求
|
||||
- **批量导入**: 支持Excel批量导入学生信息
|
||||
- **批量导出**: 支持多种格式的数据导出
|
||||
- **批量修改**: 支持批量修改学生信息
|
||||
- **批量审核**: 支持批量审核操作
|
||||
|
||||
#### 3.2.3 搜索功能需求
|
||||
- **高级搜索**: 支持多字段组合搜索
|
||||
- **模糊搜索**: 支持姓名、学号等字段模糊搜索
|
||||
- **条件筛选**: 支持下拉选择和范围筛选
|
||||
- **搜索历史**: 保存常用搜索条件
|
||||
|
||||
---
|
||||
|
||||
## 📊 4. 非功能性需求
|
||||
|
||||
### 4.1 性能需求
|
||||
- **响应时间**: 页面加载时间不超过3秒
|
||||
- **并发处理**: 支持1000人同时在线操作
|
||||
- **数据处理**: 支持10万级学生数据的快速查询
|
||||
- **批量处理**: 支持5000条记录的批量导入
|
||||
|
||||
### 4.2 可用性需求
|
||||
- **系统可用性**: 7×24小时可用,宕机时间不超过0.1%
|
||||
- **界面友好**: 操作简单直观,符合用户习惯
|
||||
- **响应式设计**: 支持PC和移动设备访问
|
||||
- **错误处理**: 完善的错误提示和异常处理
|
||||
|
||||
### 4.3 安全性需求
|
||||
- **身份认证**: 支持统一身份认证和多因子认证
|
||||
- **权限控制**: 基于角色的访问控制
|
||||
- **数据加密**: 敏感数据加密存储和传输
|
||||
- **操作审计**: 完整的操作日志记录
|
||||
|
||||
### 4.4 可维护性需求
|
||||
- **模块化设计**: 系统采用模块化架构
|
||||
- **代码规范**: 遵循代码开发规范
|
||||
- **文档完整**: 提供完整的技术文档
|
||||
- **版本管理**: 支持系统版本升级和回滚
|
||||
|
||||
---
|
||||
|
||||
## 🔄 5. 业务流程分析
|
||||
|
||||
### 5.1 学生信息采集流程
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[学生处创建采集批次] --> B[配置采集权限]
|
||||
B --> C[发布采集通知]
|
||||
C --> D[学生登录系统]
|
||||
D --> E[填写/修改个人信息]
|
||||
E --> F[提交审核]
|
||||
F --> G[辅导员/班主任初审]
|
||||
G --> H{初审通过?}
|
||||
H -->|是| I[学院复审]
|
||||
H -->|否| J[退回修改]
|
||||
J --> E
|
||||
I --> K{复审通过?}
|
||||
K -->|是| L[学生处终审]
|
||||
K -->|否| J
|
||||
L --> M{终审通过?}
|
||||
M -->|是| N[信息确认完成]
|
||||
M -->|否| J
|
||||
N --> O[批次统计汇总]
|
||||
```
|
||||
|
||||
### 5.2 权限配置流程
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[学生处管理员] --> B[创建采集批次]
|
||||
B --> C[选择用户组]
|
||||
C --> D[配置功能权限]
|
||||
D --> E[设置数据范围]
|
||||
E --> F[设置时间范围]
|
||||
F --> G[保存权限配置]
|
||||
G --> H[通知相关用户]
|
||||
```
|
||||
|
||||
### 5.3 数据统计流程
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[用户选择统计维度] --> B[设置筛选条件]
|
||||
B --> C[执行统计查询]
|
||||
C --> D[生成统计结果]
|
||||
D --> E[数据可视化展示]
|
||||
E --> F[导出统计报告]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🗄️ 6. 数据模型设计
|
||||
|
||||
### 6.1 核心数据实体
|
||||
|
||||
#### 6.1.1 学生信息表 (student_info)
|
||||
```sql
|
||||
CREATE TABLE student_info (
|
||||
id BIGINT PRIMARY KEY COMMENT '主键ID',
|
||||
student_id VARCHAR(32) UNIQUE COMMENT '学号',
|
||||
name VARCHAR(50) COMMENT '姓名',
|
||||
gender CHAR(1) COMMENT '性别',
|
||||
birth_date DATE COMMENT '出生日期',
|
||||
id_card VARCHAR(18) COMMENT '身份证号',
|
||||
nation VARCHAR(20) COMMENT '民族',
|
||||
political_status VARCHAR(20) COMMENT '政治面貌',
|
||||
religion VARCHAR(20) COMMENT '宗教信仰',
|
||||
phone VARCHAR(20) COMMENT '手机号',
|
||||
email VARCHAR(100) COMMENT '邮箱',
|
||||
-- 更多字段...
|
||||
create_time DATETIME COMMENT '创建时间',
|
||||
update_time DATETIME COMMENT '更新时间',
|
||||
create_by BIGINT COMMENT '创建人',
|
||||
update_by BIGINT COMMENT '更新人',
|
||||
deleted TINYINT DEFAULT 0 COMMENT '逻辑删除',
|
||||
tenant_id BIGINT COMMENT '租户ID'
|
||||
);
|
||||
```
|
||||
|
||||
#### 6.1.2 班级信息表 (class_info)
|
||||
```sql
|
||||
CREATE TABLE class_info (
|
||||
id BIGINT PRIMARY KEY COMMENT '主键ID',
|
||||
class_code VARCHAR(32) UNIQUE COMMENT '班级代码',
|
||||
class_name VARCHAR(100) COMMENT '班级名称',
|
||||
college_id BIGINT COMMENT '学院ID',
|
||||
major VARCHAR(100) COMMENT '专业',
|
||||
grade VARCHAR(10) COMMENT '年级',
|
||||
counselor_id BIGINT COMMENT '辅导员ID',
|
||||
head_teacher_id BIGINT COMMENT '班主任ID',
|
||||
-- 基础字段...
|
||||
);
|
||||
```
|
||||
|
||||
#### 6.1.3 信息采集批次表 (collection_batch)
|
||||
```sql
|
||||
CREATE TABLE collection_batch (
|
||||
id BIGINT PRIMARY KEY COMMENT '主键ID',
|
||||
batch_name VARCHAR(100) COMMENT '批次名称',
|
||||
batch_code VARCHAR(32) UNIQUE COMMENT '批次代码',
|
||||
start_time DATETIME COMMENT '开始时间',
|
||||
end_time DATETIME COMMENT '结束时间',
|
||||
status VARCHAR(20) COMMENT '状态',
|
||||
description TEXT COMMENT '描述',
|
||||
-- 基础字段...
|
||||
);
|
||||
```
|
||||
|
||||
### 6.2 关系设计
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
STUDENT_INFO ||--o{ CLASS_STUDENT : belongs_to
|
||||
CLASS_INFO ||--o{ CLASS_STUDENT : contains
|
||||
COLLEGE_INFO ||--o{ CLASS_INFO : has
|
||||
STUDENT_INFO ||--o{ AUDIT_RECORD : has
|
||||
COLLECTION_BATCH ||--o{ AUDIT_RECORD : contains
|
||||
USER_INFO ||--o{ AUDIT_RECORD : creates
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔌 7. 接口设计
|
||||
|
||||
### 7.1 核心API接口
|
||||
|
||||
#### 7.1.1 学生信息管理接口
|
||||
```
|
||||
GET /api/student/list - 学生信息列表
|
||||
GET /api/student/{id} - 学生信息详情
|
||||
POST /api/student - 新增学生信息
|
||||
PUT /api/student/{id} - 修改学生信息
|
||||
DELETE /api/student/{id} - 删除学生信息
|
||||
POST /api/student/import - 批量导入学生信息
|
||||
POST /api/student/export - 导出学生信息
|
||||
```
|
||||
|
||||
#### 7.1.2 班级管理接口
|
||||
```
|
||||
GET /api/class/list - 班级列表
|
||||
GET /api/class/{id} - 班级详情
|
||||
POST /api/class - 新增班级
|
||||
PUT /api/class/{id} - 修改班级
|
||||
GET /api/class/{id}/students - 班级学生列表
|
||||
```
|
||||
|
||||
#### 7.1.3 批次管理接口
|
||||
```
|
||||
GET /api/batch/list - 批次列表
|
||||
POST /api/batch - 创建批次
|
||||
PUT /api/batch/{id} - 修改批次
|
||||
PUT /api/batch/{id}/status - 修改批次状态
|
||||
GET /api/batch/{id}/progress - 批次进度统计
|
||||
```
|
||||
|
||||
#### 7.1.4 审核流程接口
|
||||
```
|
||||
POST /api/audit/submit - 提交审核
|
||||
PUT /api/audit/{id}/approve - 审核通过
|
||||
PUT /api/audit/{id}/reject - 审核拒绝
|
||||
GET /api/audit/list - 审核列表
|
||||
GET /api/audit/{id}/progress - 审核进度
|
||||
```
|
||||
|
||||
#### 7.1.5 统计分析接口
|
||||
```
|
||||
GET /api/statistics/basic - 基础统计
|
||||
GET /api/statistics/college - 学院统计
|
||||
GET /api/statistics/major - 专业统计
|
||||
GET /api/statistics/custom - 自定义统计
|
||||
POST /api/statistics/export - 导出统计报告
|
||||
```
|
||||
|
||||
### 7.2 接口规范
|
||||
- **认证方式**: Bearer Token
|
||||
- **请求格式**: JSON
|
||||
- **响应格式**: 统一响应格式(R类型或TableDataInfo)
|
||||
- **错误处理**: 统一错误码和错误信息
|
||||
- **参数验证**: Bean Validation参数校验
|
||||
|
||||
---
|
||||
|
||||
## 💻 8. 技术实现方案
|
||||
|
||||
### 8.1 技术架构
|
||||
|
||||
#### 8.1.1 前端技术栈
|
||||
- **框架**: Vue 3.5.13 + TypeScript 5.8.3
|
||||
- **UI组件**: Element Plus (Art Design Pro风格)
|
||||
- **状态管理**: Pinia
|
||||
- **路由管理**: Vue Router
|
||||
- **HTTP客户端**: Axios
|
||||
- **构建工具**: Vite
|
||||
|
||||
#### 8.1.2 后端技术栈
|
||||
- **框架**: Spring Boot 3.4.6
|
||||
- **ORM**: MyBatis-Plus
|
||||
- **数据库**: MySQL 8.0+
|
||||
- **缓存**: Redis
|
||||
- **认证授权**: Sa-Token
|
||||
- **文档工具**: Knife4j
|
||||
|
||||
#### 8.1.3 移动端技术栈
|
||||
- **框架**: Vue 3.4.21 + uni-app
|
||||
- **UI组件**: uview-plus 3.4.45
|
||||
- **发布平台**: H5、微信小程序、App
|
||||
|
||||
### 8.2 数据库设计规范
|
||||
- **主键**: 雪花ID (bigint)
|
||||
- **逻辑删除**: deleted字段
|
||||
- **多租户**: tenant_id字段
|
||||
- **基础字段**: create_time, update_time, create_by, update_by
|
||||
|
||||
### 8.3 安全设计
|
||||
- **数据加密**: 敏感数据AES加密存储
|
||||
- **传输加密**: HTTPS + AES+RSA双重加密
|
||||
- **权限控制**: 基于角色的访问控制(RBAC)
|
||||
- **操作审计**: 完整的操作日志记录
|
||||
|
||||
---
|
||||
|
||||
## 📝 9. 开发计划
|
||||
|
||||
### 9.1 开发阶段
|
||||
|
||||
#### 第一阶段:基础功能开发 (2周)
|
||||
- 学生信息基本管理功能
|
||||
- 班级信息管理功能
|
||||
- 用户权限管理
|
||||
- 基础数据字典
|
||||
|
||||
#### 第二阶段:核心功能开发 (3周)
|
||||
- 批次管理功能
|
||||
- 审核流程功能
|
||||
- 信息采集功能
|
||||
- 数据导入导出
|
||||
|
||||
#### 第三阶段:高级功能开发 (2周)
|
||||
- 统计分析功能
|
||||
- 综合条件筛选
|
||||
- 可视化展示
|
||||
- 移动端适配
|
||||
|
||||
#### 第四阶段:测试优化 (1周)
|
||||
- 功能测试
|
||||
- 性能测试
|
||||
- 用户体验优化
|
||||
- 文档完善
|
||||
|
||||
### 9.2 交付物清单
|
||||
- **需求文档**: 需求分析书、用户手册
|
||||
- **设计文档**: 技术架构设计、数据库设计、接口设计
|
||||
- **代码文件**: 前端代码、后端代码、移动端代码
|
||||
- **测试文件**: 测试用例、测试报告、测试脚本
|
||||
- **部署文件**: 部署脚本、配置文件、部署手册
|
||||
|
||||
---
|
||||
|
||||
## 🎯 10. 验收标准
|
||||
|
||||
### 10.1 功能验收标准
|
||||
- ✅ 学生信息管理功能完整可用
|
||||
- ✅ 班级信息管理功能正常
|
||||
- ✅ 批次管理功能正常
|
||||
- ✅ 审核流程功能正常
|
||||
- ✅ 统计分析功能正常
|
||||
- ✅ 权限控制功能正常
|
||||
- ✅ 数据导入导出功能正常
|
||||
|
||||
### 10.2 性能验收标准
|
||||
- ✅ 页面响应时间 < 3秒
|
||||
- ✅ 支持1000人同时在线
|
||||
- ✅ 支持10万级数据查询
|
||||
- ✅ 批量导入5000条记录正常
|
||||
|
||||
### 10.3 质量验收标准
|
||||
- ✅ 功能测试通过率 > 95%
|
||||
- ✅ 性能测试达标
|
||||
- ✅ 安全测试通过
|
||||
- ✅ 用户体验良好
|
||||
- ✅ 文档完整准确
|
||||
|
||||
---
|
||||
|
||||
## 📚 11. 相关文档
|
||||
|
||||
### 11.1 技术文档
|
||||
- [数据库设计文档](../../设计文档/database/)
|
||||
- [API接口文档](../../开发文档/API/)
|
||||
- [技术架构文档](../../开发文档/)
|
||||
|
||||
### 11.2 业务文档
|
||||
- [业务流程文档](../../业务模块/student-management/)
|
||||
- [用户操作手册](../../测试文档/)
|
||||
- [系统配置手册](../../中间件配置/)
|
||||
|
||||
### 11.3 项目文档
|
||||
- [项目总览](../../项目总览/)
|
||||
- [变更记录](../../变更管理/)
|
||||
- [测试记录](../../测试文档/)
|
||||
|
||||
---
|
||||
|
||||
## 📞 附录
|
||||
|
||||
### 附录A:术语表
|
||||
- **学工**: 学生工作的简称
|
||||
- **批次**: 信息采集的时间段和范围
|
||||
- **审核流程**: 信息确认的多级审核过程
|
||||
- **综合条件筛选**: 跨业务域的复合查询条件
|
||||
|
||||
### 附录B:参考资料
|
||||
- [教育部学生信息管理规范]
|
||||
- [高校学生信息系统建设指南]
|
||||
- [数据安全管理办法]
|
||||
|
||||
### 附录C:联系方式
|
||||
- **需求联系人**: 学生处信息管理员
|
||||
- **技术联系人**: 系统开发团队
|
||||
- **项目经理**: 项目管理办公室
|
||||
|
||||
---
|
||||
|
||||
**文档状态**: 📝 待审核
|
||||
**最后更新**: 2025-01-12
|
||||
**版本号**: v1.0
|
||||
@@ -1,302 +0,0 @@
|
||||
# 学生信息采集批次功能重新设计方案
|
||||
|
||||
## 需求背景
|
||||
原有的独立信息采集批次管理功能设计不符合业务需求。正确的业务逻辑应该是:
|
||||
- 信息采集批次是学生信息管理的组成部分
|
||||
- 批次用于控制学生信息的修改权限和时间
|
||||
- 批次可以针对特定条件(届别、学院、专业、班级)创建
|
||||
|
||||
## 功能重新定位
|
||||
|
||||
### 1. 模块归属调整
|
||||
- **原设计**: 独立的信息采集批次管理模块
|
||||
- **新设计**: 集成到学生信息管理模块中
|
||||
|
||||
### 2. 功能定位
|
||||
- **主要功能**: 学生信息修改的权限控制机制
|
||||
- **次要功能**: 批次管理和统计
|
||||
- **核心价值**: 在指定时间内,允许符合条件的学生修改个人信息
|
||||
|
||||
## 详细功能设计
|
||||
|
||||
### 1. 学生信息管理页面改造
|
||||
|
||||
#### A. 页面结构
|
||||
```
|
||||
学生信息管理
|
||||
├── 学生信息列表 (主标签页)
|
||||
│ ├── 学生信息查询
|
||||
│ ├── 学生信息详情
|
||||
│ ├── 学生信息修改
|
||||
│ └── 批量操作
|
||||
├── 信息采集批次 (新增标签页)
|
||||
│ ├── 批次列表
|
||||
│ ├── 新增批次
|
||||
│ ├── 批次详情
|
||||
│ ├── 批次统计
|
||||
│ └── 批次权限管理
|
||||
└── 系统设置 (可选)
|
||||
├── 字段配置
|
||||
└── 权限配置
|
||||
```
|
||||
|
||||
#### B. 批次创建功能
|
||||
**目标条件选择**:
|
||||
- 届别选择: 2021届、2022届、2023届等
|
||||
- 学院选择: 可选择一个或多个学院
|
||||
- 专业选择: 可选择一个或多个专业(可选)
|
||||
- 班级选择: 可选择一个或多个班级(可选)
|
||||
|
||||
**批次设置**:
|
||||
- 批次名称: 如"2024年春季学期信息核对"
|
||||
- 批次描述: 详细说明
|
||||
- 开始时间: 学生可以开始修改信息的时间
|
||||
- 结束时间: 学生不能再修改信息的时间
|
||||
- 提醒设置: 开始前、结束前的提醒时间
|
||||
|
||||
### 2. 权限控制逻辑
|
||||
|
||||
#### A. 学生端权限
|
||||
- **批次开放期间**: 符合条件的学生可以修改个人信息
|
||||
- **批次关闭期间**: 学生无法修改个人信息
|
||||
- **无批次期间**: 学生无法修改个人信息(默认状态)
|
||||
|
||||
#### B. 管理员权限
|
||||
- **随时修改**: 管理员可以随时修改学生信息
|
||||
- **批次管理**: 创建、修改、删除、发布批次
|
||||
- **权限分配**: 可以为特定用户分配特殊权限
|
||||
|
||||
#### C. 权限检查流程
|
||||
```
|
||||
学生尝试修改信息
|
||||
↓
|
||||
检查当前是否有活跃批次
|
||||
↓
|
||||
检查学生是否符合批次条件
|
||||
↓
|
||||
检查当前时间是否在批次时间范围内
|
||||
↓
|
||||
允许/拒绝修改
|
||||
```
|
||||
|
||||
### 3. 数据库设计调整
|
||||
|
||||
#### A. 批次表结构增强
|
||||
```sql
|
||||
-- 修改现有批次表
|
||||
ALTER TABLE xg_collection_batch
|
||||
ADD COLUMN target_grade VARCHAR(50) COMMENT '目标届别(如:2021,2022,2023)',
|
||||
ADD COLUMN target_college_ids TEXT COMMENT '目标学院ID列表(JSON格式)',
|
||||
ADD COLUMN target_major_ids TEXT COMMENT '目标专业ID列表(JSON格式)',
|
||||
ADD COLUMN target_class_ids TEXT COMMENT '目标班级ID列表(JSON格式)',
|
||||
ADD COLUMN remind_before_start INT DEFAULT 24 COMMENT '开始前提醒时间(小时)',
|
||||
ADD COLUMN remind_before_end INT DEFAULT 24 COMMENT '结束前提醒时间(小时)',
|
||||
ADD COLUMN auto_close TINYINT DEFAULT 1 COMMENT '是否自动关闭',
|
||||
ADD COLUMN notification_sent TINYINT DEFAULT 0 COMMENT '是否已发送通知';
|
||||
```
|
||||
|
||||
#### B. 新增批次学生关联表
|
||||
```sql
|
||||
-- 批次学生关联表(用于快速查询和统计)
|
||||
CREATE TABLE xg_collection_batch_student (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
batch_id BIGINT NOT NULL COMMENT '批次ID',
|
||||
student_id BIGINT NOT NULL COMMENT '学生ID',
|
||||
student_code VARCHAR(50) NOT NULL COMMENT '学号',
|
||||
student_name VARCHAR(100) NOT NULL COMMENT '学生姓名',
|
||||
college_id BIGINT COMMENT '学院ID',
|
||||
major_id BIGINT COMMENT '专业ID',
|
||||
class_id BIGINT COMMENT '班级ID',
|
||||
grade VARCHAR(50) COMMENT '届别',
|
||||
can_edit TINYINT DEFAULT 1 COMMENT '是否可编辑',
|
||||
has_submitted TINYINT DEFAULT 0 COMMENT '是否已提交',
|
||||
submit_time DATETIME COMMENT '提交时间',
|
||||
last_edit_time DATETIME COMMENT '最后编辑时间',
|
||||
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
INDEX idx_batch_id (batch_id),
|
||||
INDEX idx_student_id (student_id),
|
||||
INDEX idx_batch_student (batch_id, student_id),
|
||||
FOREIGN KEY (batch_id) REFERENCES xg_collection_batch(id) ON DELETE CASCADE
|
||||
);
|
||||
```
|
||||
|
||||
#### C. 新增批次操作日志表
|
||||
```sql
|
||||
-- 批次操作日志表
|
||||
CREATE TABLE xg_collection_batch_log (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
batch_id BIGINT NOT NULL COMMENT '批次ID',
|
||||
student_id BIGINT COMMENT '学生ID',
|
||||
operation_type VARCHAR(50) NOT NULL COMMENT '操作类型(CREATE,UPDATE,SUBMIT,ADMIN_MODIFY)',
|
||||
operation_desc TEXT COMMENT '操作描述',
|
||||
operator_id BIGINT COMMENT '操作人ID',
|
||||
operator_name VARCHAR(100) COMMENT '操作人姓名',
|
||||
ip_address VARCHAR(50) COMMENT 'IP地址',
|
||||
user_agent TEXT COMMENT '用户代理',
|
||||
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
INDEX idx_batch_id (batch_id),
|
||||
INDEX idx_student_id (student_id),
|
||||
INDEX idx_create_time (create_time)
|
||||
);
|
||||
```
|
||||
|
||||
### 4. 前端页面设计
|
||||
|
||||
#### A. 学生信息管理主页面
|
||||
- **标签页结构**: 学生信息列表 + 信息采集批次
|
||||
- **批次状态显示**: 在学生信息列表顶部显示当前活跃批次
|
||||
- **权限控制**: 根据批次状态控制修改按钮的可用性
|
||||
|
||||
#### B. 信息采集批次标签页
|
||||
- **批次列表**: 显示所有批次,包含状态、目标条件、时间范围等
|
||||
- **批次创建**: 友好的批次创建向导
|
||||
- **批次统计**: 实时显示批次进度和完成情况
|
||||
- **批次管理**: 发布、暂停、结束批次等操作
|
||||
|
||||
#### C. 批次创建向导
|
||||
```
|
||||
步骤1: 基本信息
|
||||
- 批次名称
|
||||
- 批次描述
|
||||
- 批次类型
|
||||
|
||||
步骤2: 目标条件
|
||||
- 届别选择
|
||||
- 学院选择
|
||||
- 专业选择(可选)
|
||||
- 班级选择(可选)
|
||||
|
||||
步骤3: 时间设置
|
||||
- 开始时间
|
||||
- 结束时间
|
||||
- 提醒设置
|
||||
|
||||
步骤4: 预览确认
|
||||
- 显示批次详情
|
||||
- 显示符合条件的学生数量
|
||||
- 确认创建
|
||||
```
|
||||
|
||||
### 5. 后端接口设计
|
||||
|
||||
#### A. 批次管理接口
|
||||
```java
|
||||
// 集成到学生信息管理Controller中
|
||||
@RestController
|
||||
@RequestMapping("/xg-xx/studentInfo")
|
||||
public class XgStudentInfoController {
|
||||
|
||||
// 原有学生信息管理接口...
|
||||
|
||||
// 新增批次管理接口
|
||||
@GetMapping("/batches")
|
||||
public R<List<XgCollectionBatchVo>> getBatches();
|
||||
|
||||
@PostMapping("/batch")
|
||||
public R<Void> createBatch(@RequestBody XgCollectionBatchBo bo);
|
||||
|
||||
@GetMapping("/batch/{id}/students")
|
||||
public R<List<XgStudentInfoVo>> getBatchStudents(@PathVariable Long id);
|
||||
|
||||
@PostMapping("/batch/{id}/publish")
|
||||
public R<Void> publishBatch(@PathVariable Long id);
|
||||
|
||||
@GetMapping("/batch/current")
|
||||
public R<XgCollectionBatchVo> getCurrentBatch();
|
||||
|
||||
@GetMapping("/student/{studentId}/canEdit")
|
||||
public R<Boolean> canEditStudentInfo(@PathVariable Long studentId);
|
||||
}
|
||||
```
|
||||
|
||||
#### B. 权限控制接口
|
||||
```java
|
||||
// 权限控制服务
|
||||
@Service
|
||||
public class StudentInfoPermissionService {
|
||||
|
||||
/**
|
||||
* 检查学生是否可以修改信息
|
||||
*/
|
||||
public boolean canEditStudentInfo(Long studentId);
|
||||
|
||||
/**
|
||||
* 检查当前是否有活跃批次
|
||||
*/
|
||||
public XgCollectionBatchVo getCurrentActiveBatch();
|
||||
|
||||
/**
|
||||
* 检查学生是否符合批次条件
|
||||
*/
|
||||
public boolean isStudentInBatch(Long studentId, Long batchId);
|
||||
|
||||
/**
|
||||
* 记录学生信息修改日志
|
||||
*/
|
||||
public void logStudentInfoChange(Long studentId, String operation);
|
||||
}
|
||||
```
|
||||
|
||||
### 6. 实现计划
|
||||
|
||||
#### 阶段1: 数据库调整
|
||||
- 修改批次表结构
|
||||
- 创建关联表和日志表
|
||||
- 数据迁移脚本
|
||||
|
||||
#### 阶段2: 后端功能重构
|
||||
- 将批次管理集成到学生信息管理Controller
|
||||
- 实现权限控制逻辑
|
||||
- 完善批次创建和管理功能
|
||||
|
||||
#### 阶段3: 前端页面改造
|
||||
- 修改学生信息管理页面为标签页结构
|
||||
- 实现批次管理标签页
|
||||
- 完善权限控制界面逻辑
|
||||
|
||||
#### 阶段4: 测试和优化
|
||||
- 功能测试
|
||||
- 权限测试
|
||||
- 性能优化
|
||||
|
||||
## 预期效果
|
||||
|
||||
### 1. 用户体验提升
|
||||
- **统一入口**: 所有学生信息相关功能在一个页面
|
||||
- **直观权限**: 清晰显示当前是否可以修改信息
|
||||
- **便捷操作**: 批次管理与学生信息管理紧密集成
|
||||
|
||||
### 2. 管理效率提升
|
||||
- **精确控制**: 可以针对特定条件创建批次
|
||||
- **实时监控**: 批次进度和完成情况实时显示
|
||||
- **操作记录**: 完整的操作日志追踪
|
||||
|
||||
### 3. 系统稳定性
|
||||
- **权限控制**: 严格的权限检查机制
|
||||
- **数据完整性**: 完善的数据验证和日志记录
|
||||
- **可扩展性**: 易于扩展新的批次类型和条件
|
||||
|
||||
## 技术实现要点
|
||||
|
||||
### 1. 权限控制
|
||||
- 使用AOP实现权限检查
|
||||
- 缓存活跃批次信息提高性能
|
||||
- 支持多级权限控制
|
||||
|
||||
### 2. 数据处理
|
||||
- 使用JSON存储复杂条件
|
||||
- 批量处理学生数据
|
||||
- 异步处理大量数据操作
|
||||
|
||||
### 3. 用户界面
|
||||
- 响应式设计适配不同设备
|
||||
- 友好的交互提示
|
||||
- 实时状态更新
|
||||
|
||||
---
|
||||
|
||||
**文档版本**: 1.0
|
||||
**创建时间**: 2025-01-13
|
||||
**最后更新**: 2025-01-13
|
||||
**状态**: 设计中
|
||||
@@ -1,4 +1,36 @@
|
||||
org.dromara.workflow.domain.vo.FlowCategoryVo
|
||||
org.dromara.hq.domain.vo.HqPmRepairProgressVo
|
||||
org.dromara.hq.domain.vo.HqPmRepairOrderVo
|
||||
org.dromara.hq.domain.vo.HqSatSurveyQuestionVo
|
||||
org.dromara.hq.domain.vo.HqPmRepairCategoryVo
|
||||
org.dromara.hq.domain.bo.HqSatOptionBo
|
||||
org.dromara.hq.domain.vo.HqSatCategoryVo
|
||||
org.dromara.hq.domain.bo.HqPmRepairAttachmentBo
|
||||
org.dromara.hq.domain.bo.HqHallInfoBo
|
||||
org.dromara.hq.domain.vo.HqVenueInfoVo
|
||||
org.dromara.hq.domain.bo.HqSatSurveyBo
|
||||
org.dromara.hq.domain.vo.HqVenueReservationVo
|
||||
org.dromara.hq.domain.vo.HqSatSurveyVo
|
||||
org.dromara.hq.domain.vo.HqSatRecordVo
|
||||
org.dromara.hq.domain.bo.HqHallReservationBo
|
||||
org.dromara.hq.domain.bo.HqPmServiceEvaluationBo
|
||||
org.dromara.hq.domain.bo.HqSatRecordBo
|
||||
org.dromara.workflow.domain.vo.TestLeaveVo
|
||||
org.dromara.hq.domain.bo.HqSatQuestionBo
|
||||
org.dromara.workflow.domain.bo.TestLeaveBo
|
||||
org.dromara.workflow.domain.bo.FlowCategoryBo
|
||||
org.dromara.hq.domain.bo.HqSatDetailBo
|
||||
org.dromara.hq.domain.bo.HqVenueInfoBo
|
||||
org.dromara.hq.domain.bo.HqPmRepairProgressBo
|
||||
org.dromara.hq.domain.vo.HqPmRepairAttachmentVo
|
||||
org.dromara.hq.domain.bo.HqPmRepairOrderBo
|
||||
org.dromara.hq.domain.bo.HqPmRepairCategoryBo
|
||||
org.dromara.hq.domain.vo.HqPmServiceEvaluationVo
|
||||
org.dromara.hq.domain.vo.HqSatOptionVo
|
||||
org.dromara.hq.domain.vo.HqHallReservationVo
|
||||
org.dromara.workflow.domain.vo.FlowCategoryVo
|
||||
org.dromara.hq.domain.bo.HqSatCategoryBo
|
||||
org.dromara.hq.domain.vo.HqSatDetailVo
|
||||
org.dromara.hq.domain.vo.HqSatQuestionVo
|
||||
org.dromara.hq.domain.bo.HqSatSurveyQuestionBo
|
||||
org.dromara.hq.domain.bo.HqVenueReservationBo
|
||||
org.dromara.workflow.domain.bo.FlowCategoryBo
|
||||
org.dromara.hq.domain.vo.HqHallInfoVo
|
||||
@@ -1,396 +0,0 @@
|
||||
import { BaseEntity, PageQuery } from './types';
|
||||
|
||||
/**
|
||||
* 学年管理 VO
|
||||
*/
|
||||
export interface AcademicYearVO {
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
id: string | number;
|
||||
|
||||
/**
|
||||
* 租户编号
|
||||
*/
|
||||
tenantId: string;
|
||||
|
||||
/**
|
||||
* 学年编码
|
||||
*/
|
||||
yearCode: string;
|
||||
|
||||
/**
|
||||
* 学年名称
|
||||
*/
|
||||
yearName: string;
|
||||
|
||||
/**
|
||||
* 开始日期
|
||||
*/
|
||||
startDate: string;
|
||||
|
||||
/**
|
||||
* 结束日期
|
||||
*/
|
||||
endDate: string;
|
||||
|
||||
/**
|
||||
* 是否当前学年:0-否,1-是
|
||||
*/
|
||||
isCurrent: boolean;
|
||||
|
||||
/**
|
||||
* 学年状态:1-启用,0-停用
|
||||
*/
|
||||
yearStatus: string;
|
||||
|
||||
/**
|
||||
* 学年描述
|
||||
*/
|
||||
description?: string;
|
||||
|
||||
/**
|
||||
* 排序序号
|
||||
*/
|
||||
sortOrder: number;
|
||||
|
||||
/**
|
||||
* 备注信息
|
||||
*/
|
||||
remark?: string;
|
||||
|
||||
/**
|
||||
* 创建者
|
||||
*/
|
||||
createBy: string | number;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
createTime: string;
|
||||
|
||||
/**
|
||||
* 更新者
|
||||
*/
|
||||
updateBy?: string | number;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
updateTime?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 学年管理 Form
|
||||
*/
|
||||
export interface AcademicYearForm extends BaseEntity {
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
id?: string | number;
|
||||
|
||||
/**
|
||||
* 学年编码
|
||||
*/
|
||||
yearCode?: string;
|
||||
|
||||
/**
|
||||
* 学年名称
|
||||
*/
|
||||
yearName?: string;
|
||||
|
||||
/**
|
||||
* 开始日期
|
||||
*/
|
||||
startDate?: string;
|
||||
|
||||
/**
|
||||
* 结束日期
|
||||
*/
|
||||
endDate?: string;
|
||||
|
||||
/**
|
||||
* 是否当前学年
|
||||
*/
|
||||
isCurrent?: boolean;
|
||||
|
||||
/**
|
||||
* 学年状态
|
||||
*/
|
||||
yearStatus?: string;
|
||||
|
||||
/**
|
||||
* 学年描述
|
||||
*/
|
||||
description?: string;
|
||||
|
||||
/**
|
||||
* 排序序号
|
||||
*/
|
||||
sortOrder?: number;
|
||||
|
||||
/**
|
||||
* 备注信息
|
||||
*/
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 学年管理 Query
|
||||
*/
|
||||
export interface AcademicYearQuery extends PageQuery {
|
||||
/**
|
||||
* 学年编码
|
||||
*/
|
||||
yearCode?: string;
|
||||
|
||||
/**
|
||||
* 学年名称
|
||||
*/
|
||||
yearName?: string;
|
||||
|
||||
/**
|
||||
* 是否当前学年
|
||||
*/
|
||||
isCurrent?: boolean;
|
||||
|
||||
/**
|
||||
* 学年状态
|
||||
*/
|
||||
yearStatus?: string;
|
||||
|
||||
/**
|
||||
* 开始日期范围
|
||||
*/
|
||||
startDateRange?: string[];
|
||||
|
||||
/**
|
||||
* 结束日期范围
|
||||
*/
|
||||
endDateRange?: string[];
|
||||
|
||||
/**
|
||||
* 创建时间范围
|
||||
*/
|
||||
createTimeRange?: string[];
|
||||
|
||||
/**
|
||||
* 日期范围参数
|
||||
*/
|
||||
params?: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* 学期管理 VO
|
||||
*/
|
||||
export interface SemesterVO {
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
id: string | number;
|
||||
|
||||
/**
|
||||
* 租户编号
|
||||
*/
|
||||
tenantId: string;
|
||||
|
||||
/**
|
||||
* 学年ID
|
||||
*/
|
||||
academicYearId: string | number;
|
||||
|
||||
/**
|
||||
* 学年名称
|
||||
*/
|
||||
academicYearName?: string;
|
||||
|
||||
/**
|
||||
* 学期编码
|
||||
*/
|
||||
semesterCode: string;
|
||||
|
||||
/**
|
||||
* 学期名称
|
||||
*/
|
||||
semesterName: string;
|
||||
|
||||
/**
|
||||
* 学期类型:1-春季学期,2-秋季学期,3-夏季学期,4-短学期
|
||||
*/
|
||||
semesterType: string;
|
||||
|
||||
/**
|
||||
* 开始日期
|
||||
*/
|
||||
startDate: string;
|
||||
|
||||
/**
|
||||
* 结束日期
|
||||
*/
|
||||
endDate: string;
|
||||
|
||||
/**
|
||||
* 是否当前学期:0-否,1-是
|
||||
*/
|
||||
isCurrent: boolean;
|
||||
|
||||
/**
|
||||
* 学期状态:1-启用,0-停用
|
||||
*/
|
||||
semesterStatus: string;
|
||||
|
||||
/**
|
||||
* 学期描述
|
||||
*/
|
||||
description?: string;
|
||||
|
||||
/**
|
||||
* 排序序号
|
||||
*/
|
||||
sortOrder: number;
|
||||
|
||||
/**
|
||||
* 备注信息
|
||||
*/
|
||||
remark?: string;
|
||||
|
||||
/**
|
||||
* 创建者
|
||||
*/
|
||||
createBy: string | number;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
createTime: string;
|
||||
|
||||
/**
|
||||
* 更新者
|
||||
*/
|
||||
updateBy?: string | number;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
updateTime?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 学期管理 Form
|
||||
*/
|
||||
export interface SemesterForm extends BaseEntity {
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
id?: string | number;
|
||||
|
||||
/**
|
||||
* 学年ID
|
||||
*/
|
||||
academicYearId?: string | number;
|
||||
|
||||
/**
|
||||
* 学期编码
|
||||
*/
|
||||
semesterCode?: string;
|
||||
|
||||
/**
|
||||
* 学期名称
|
||||
*/
|
||||
semesterName?: string;
|
||||
|
||||
/**
|
||||
* 学期类型
|
||||
*/
|
||||
semesterType?: string;
|
||||
|
||||
/**
|
||||
* 开始日期
|
||||
*/
|
||||
startDate?: string;
|
||||
|
||||
/**
|
||||
* 结束日期
|
||||
*/
|
||||
endDate?: string;
|
||||
|
||||
/**
|
||||
* 是否当前学期
|
||||
*/
|
||||
isCurrent?: boolean;
|
||||
|
||||
/**
|
||||
* 学期状态
|
||||
*/
|
||||
semesterStatus?: string;
|
||||
|
||||
/**
|
||||
* 学期描述
|
||||
*/
|
||||
description?: string;
|
||||
|
||||
/**
|
||||
* 排序序号
|
||||
*/
|
||||
sortOrder?: number;
|
||||
|
||||
/**
|
||||
* 备注信息
|
||||
*/
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 学期管理 Query
|
||||
*/
|
||||
export interface SemesterQuery extends PageQuery {
|
||||
/**
|
||||
* 学年ID
|
||||
*/
|
||||
academicYearId?: string | number;
|
||||
|
||||
/**
|
||||
* 学期编码
|
||||
*/
|
||||
semesterCode?: string;
|
||||
|
||||
/**
|
||||
* 学期名称
|
||||
*/
|
||||
semesterName?: string;
|
||||
|
||||
/**
|
||||
* 学期类型
|
||||
*/
|
||||
semesterType?: string;
|
||||
|
||||
/**
|
||||
* 是否当前学期
|
||||
*/
|
||||
isCurrent?: boolean;
|
||||
|
||||
/**
|
||||
* 学期状态
|
||||
*/
|
||||
semesterStatus?: string;
|
||||
|
||||
/**
|
||||
* 开始日期范围
|
||||
*/
|
||||
startDateRange?: string[];
|
||||
|
||||
/**
|
||||
* 结束日期范围
|
||||
*/
|
||||
endDateRange?: string[];
|
||||
|
||||
/**
|
||||
* 创建时间范围
|
||||
*/
|
||||
createTimeRange?: string[];
|
||||
|
||||
/**
|
||||
* 日期范围参数
|
||||
*/
|
||||
params?: any;
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { AcademicYearVO, AcademicYearForm, AcademicYearQuery } from './academicTypes';
|
||||
|
||||
/**
|
||||
* 查询学年管理列表
|
||||
* @param query 查询参数
|
||||
*/
|
||||
export const listAcademicYear = (query?: AcademicYearQuery): AxiosPromise<AcademicYearVO[]> => {
|
||||
return request({
|
||||
url: '/xg-xx/academicYear/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询学年管理详细信息
|
||||
* @param id 学年ID
|
||||
*/
|
||||
export const getAcademicYear = (id: string | number): AxiosPromise<AcademicYearVO> => {
|
||||
return request({
|
||||
url: `/xg-xx/academicYear/${id}`,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 新增学年管理
|
||||
* @param data 学年管理数据
|
||||
*/
|
||||
export const addAcademicYear = (data: AcademicYearForm) => {
|
||||
return request({
|
||||
url: '/xg-xx/academicYear',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 修改学年管理
|
||||
* @param data 学年管理数据
|
||||
*/
|
||||
export const updateAcademicYear = (data: AcademicYearForm) => {
|
||||
return request({
|
||||
url: '/xg-xx/academicYear',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除学年管理
|
||||
* @param id 学年ID
|
||||
*/
|
||||
export const delAcademicYear = (id: string | number | Array<string | number>) => {
|
||||
return request({
|
||||
url: `/xg-xx/academicYear/${id}`,
|
||||
method: 'delete'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 导出学年管理列表
|
||||
* @param query 查询参数
|
||||
*/
|
||||
export const exportAcademicYear = (query?: AcademicYearQuery) => {
|
||||
return request({
|
||||
url: '/xg-xx/academicYear/export',
|
||||
method: 'post',
|
||||
data: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询当前学年
|
||||
*/
|
||||
export const getCurrentAcademicYear = (): AxiosPromise<AcademicYearVO> => {
|
||||
return request({
|
||||
url: '/xg-xx/academicYear/current',
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 设置当前学年
|
||||
* @param id 学年ID
|
||||
*/
|
||||
export const setCurrentAcademicYear = (id: string | number) => {
|
||||
return request({
|
||||
url: `/xg-xx/academicYear/setCurrent/${id}`,
|
||||
method: 'put'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询可用的学年选项
|
||||
*/
|
||||
export const getAvailableAcademicYearOptions = (): AxiosPromise<AcademicYearVO[]> => {
|
||||
return request({
|
||||
url: '/xg-xx/academicYear/availableOptions',
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 校验学年名称是否唯一
|
||||
* @param yearName 学年名称
|
||||
* @param id 学年ID
|
||||
*/
|
||||
export const checkAcademicYearName = (yearName: string, id?: string | number): AxiosPromise<boolean> => {
|
||||
return request({
|
||||
url: '/xg-xx/academicYear/checkYearName',
|
||||
method: 'get',
|
||||
params: { yearName, id }
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 校验学年编码是否唯一
|
||||
* @param yearCode 学年编码
|
||||
* @param id 学年ID
|
||||
*/
|
||||
export const checkAcademicYearCode = (yearCode: string, id?: string | number): AxiosPromise<boolean> => {
|
||||
return request({
|
||||
url: '/xg-xx/academicYear/checkYearCode',
|
||||
method: 'get',
|
||||
params: { yearCode, id }
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 激活学年
|
||||
* @param id 学年ID
|
||||
*/
|
||||
export const activateAcademicYear = (id: string | number) => {
|
||||
return request({
|
||||
url: `/xg-xx/academicYear/activate/${id}`,
|
||||
method: 'put'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 停用学年
|
||||
* @param id 学年ID
|
||||
*/
|
||||
export const deactivateAcademicYear = (id: string | number) => {
|
||||
return request({
|
||||
url: `/xg-xx/academicYear/deactivate/${id}`,
|
||||
method: 'put'
|
||||
});
|
||||
};
|
||||
@@ -1,194 +0,0 @@
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
|
||||
/**
|
||||
* 业务期间查询参数
|
||||
*/
|
||||
export interface XgBusinessPeriodQuery {
|
||||
id?: number;
|
||||
batchId?: number;
|
||||
periodName?: string;
|
||||
periodType?: string;
|
||||
targetRole?: string;
|
||||
startTime?: string;
|
||||
endTime?: string;
|
||||
status?: string;
|
||||
enabled?: boolean;
|
||||
keyword?: string;
|
||||
batchName?: string;
|
||||
pageNum?: number;
|
||||
pageSize?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 业务期间表单
|
||||
*/
|
||||
export interface XgBusinessPeriodForm {
|
||||
id?: number;
|
||||
batchId?: number;
|
||||
periodName?: string;
|
||||
periodType?: string;
|
||||
targetRole?: string;
|
||||
startTime?: string;
|
||||
endTime?: string;
|
||||
status?: string;
|
||||
description?: string;
|
||||
sortOrder?: number;
|
||||
enabled?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 业务期间视图对象
|
||||
*/
|
||||
export interface XgBusinessPeriodVO {
|
||||
id?: number;
|
||||
batchId?: number;
|
||||
batchName?: string;
|
||||
periodName?: string;
|
||||
periodType?: string;
|
||||
targetRole?: string;
|
||||
startTime?: string;
|
||||
endTime?: string;
|
||||
status?: string;
|
||||
description?: string;
|
||||
sortOrder?: number;
|
||||
enabled?: boolean;
|
||||
createTime?: string;
|
||||
updateTime?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询业务期间列表
|
||||
*/
|
||||
export const listBusinessPeriod = (query: XgBusinessPeriodQuery): AxiosPromise<XgBusinessPeriodVO[]> => {
|
||||
return request({
|
||||
url: '/xg/businessPeriod/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询所有业务期间列表(不分页)
|
||||
*/
|
||||
export const listAllBusinessPeriod = (query: XgBusinessPeriodQuery): AxiosPromise<XgBusinessPeriodVO[]> => {
|
||||
return request({
|
||||
url: '/xg/businessPeriod/listAll',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取业务期间详细信息
|
||||
*/
|
||||
export const getBusinessPeriod = (id: string | number): AxiosPromise<XgBusinessPeriodVO> => {
|
||||
return request({
|
||||
url: '/xg/businessPeriod/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 新增业务期间
|
||||
*/
|
||||
export const addBusinessPeriod = (data: XgBusinessPeriodForm) => {
|
||||
return request({
|
||||
url: '/xg/businessPeriod',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 修改业务期间
|
||||
*/
|
||||
export const updateBusinessPeriod = (data: XgBusinessPeriodForm) => {
|
||||
return request({
|
||||
url: '/xg/businessPeriod',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除业务期间
|
||||
*/
|
||||
export const delBusinessPeriod = (id: string | number | Array<string | number>) => {
|
||||
return request({
|
||||
url: '/xg/businessPeriod/' + id,
|
||||
method: 'delete'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 导出业务期间
|
||||
*/
|
||||
export const exportBusinessPeriod = (query: XgBusinessPeriodQuery) => {
|
||||
return request({
|
||||
url: '/xg/businessPeriod/export',
|
||||
method: 'post',
|
||||
data: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据批次ID查询业务期间列表
|
||||
*/
|
||||
export const queryByBatchId = (batchId: number): AxiosPromise<XgBusinessPeriodVO[]> => {
|
||||
return request({
|
||||
url: '/xg/businessPeriod/byBatch/' + batchId,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询当前活跃的业务期间
|
||||
*/
|
||||
export const queryActivePeriods = (): AxiosPromise<XgBusinessPeriodVO[]> => {
|
||||
return request({
|
||||
url: '/xg/businessPeriod/active',
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 检查业务期间是否开放
|
||||
*/
|
||||
export const checkPeriodOpen = (batchId: number): AxiosPromise<boolean> => {
|
||||
return request({
|
||||
url: '/xg/businessPeriod/checkOpen/' + batchId,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 开启业务期间
|
||||
*/
|
||||
export const openPeriod = (id: string | number) => {
|
||||
return request({
|
||||
url: '/xg/businessPeriod/open/' + id,
|
||||
method: 'post'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 关闭业务期间
|
||||
*/
|
||||
export const closePeriod = (id: string | number) => {
|
||||
return request({
|
||||
url: '/xg/businessPeriod/close/' + id,
|
||||
method: 'post'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 校验业务期间时间是否冲突
|
||||
*/
|
||||
export const checkPeriodTimeConflict = (data: XgBusinessPeriodForm): AxiosPromise<boolean> => {
|
||||
return request({
|
||||
url: '/xg/businessPeriod/checkTimeConflict',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
@@ -1,265 +0,0 @@
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { XgClassCadreVO, XgClassCadreForm, XgClassCadreQuery } from '@/api/xg-xx/types';
|
||||
|
||||
// 查询班级干部信息列表
|
||||
export const listClassCadre = (query: XgClassCadreQuery): AxiosPromise<XgClassCadreVO[]> => {
|
||||
return request({
|
||||
url: '/xg/classCadre/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
// 查询班级干部信息详细
|
||||
export const getClassCadre = (id: string | number): AxiosPromise<XgClassCadreVO> => {
|
||||
return request({
|
||||
url: '/xg/classCadre/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
// 新增班级干部信息
|
||||
export const addClassCadre = (data: XgClassCadreForm) => {
|
||||
return request({
|
||||
url: '/xg/classCadre',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
// 修改班级干部信息
|
||||
export const updateClassCadre = (data: XgClassCadreForm) => {
|
||||
return request({
|
||||
url: '/xg/classCadre',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
// 删除班级干部信息
|
||||
export const delClassCadre = (ids: string | number | Array<string | number>) => {
|
||||
return request({
|
||||
url: '/xg/classCadre/' + ids,
|
||||
method: 'delete'
|
||||
});
|
||||
};
|
||||
|
||||
// 导出班级干部信息
|
||||
export const exportClassCadre = (query: XgClassCadreQuery) => {
|
||||
return request({
|
||||
url: '/xg/classCadre/export',
|
||||
method: 'post',
|
||||
data: query
|
||||
});
|
||||
};
|
||||
|
||||
// ==================== 业务功能接口 ====================
|
||||
|
||||
// 根据班级ID查询班干部列表
|
||||
export const getCadresByClass = (classId: string | number): AxiosPromise<XgClassCadreVO[]> => {
|
||||
return request({
|
||||
url: '/xg/classCadre/class/' + classId,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
// 根据学生ID查询班干部信息
|
||||
export const getCadresByStudent = (studentId: string | number): AxiosPromise<XgClassCadreVO[]> => {
|
||||
return request({
|
||||
url: '/xg/classCadre/student/' + studentId,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
// 查询当前在任班干部列表
|
||||
export const getCurrentCadres = (classId?: string | number): AxiosPromise<XgClassCadreVO[]> => {
|
||||
return request({
|
||||
url: '/xg/classCadre/current',
|
||||
method: 'get',
|
||||
params: { classId }
|
||||
});
|
||||
};
|
||||
|
||||
// 根据职务查询班干部
|
||||
export const getCadreByPosition = (classId: string | number, positionCode: string): AxiosPromise<XgClassCadreVO> => {
|
||||
return request({
|
||||
url: '/xg/classCadre/position',
|
||||
method: 'get',
|
||||
params: { classId, positionCode }
|
||||
});
|
||||
};
|
||||
|
||||
// 任命班干部
|
||||
export const appointCadre = (data: XgClassCadreForm) => {
|
||||
return request({
|
||||
url: '/xg/classCadre/appoint',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
// 免职班干部
|
||||
export const dismissCadre = (cadreId: string | number, dismissalReason: string, dismissalDate: string) => {
|
||||
return request({
|
||||
url: '/xg/classCadre/dismiss/' + cadreId,
|
||||
method: 'put',
|
||||
params: { dismissalReason, dismissalDate }
|
||||
});
|
||||
};
|
||||
|
||||
// 调任班干部
|
||||
export const transferCadre = (
|
||||
cadreId: string | number,
|
||||
newPositionCode: string,
|
||||
newPositionName: string,
|
||||
transferReason: string,
|
||||
effectiveDate: string
|
||||
) => {
|
||||
return request({
|
||||
url: '/xg/classCadre/transfer/' + cadreId,
|
||||
method: 'put',
|
||||
params: { newPositionCode, newPositionName, transferReason, effectiveDate }
|
||||
});
|
||||
};
|
||||
|
||||
// 续任班干部
|
||||
export const renewCadre = (cadreId: string | number, newTermDuration: number, renewalReason: string) => {
|
||||
return request({
|
||||
url: '/xg/classCadre/renew/' + cadreId,
|
||||
method: 'put',
|
||||
params: { newTermDuration, renewalReason }
|
||||
});
|
||||
};
|
||||
|
||||
// 班干部辞职
|
||||
export const resignCadre = (cadreId: string | number, resignationReason: string, resignationDate: string) => {
|
||||
return request({
|
||||
url: '/xg/classCadre/resign/' + cadreId,
|
||||
method: 'put',
|
||||
params: { resignationReason, resignationDate }
|
||||
});
|
||||
};
|
||||
|
||||
// ==================== 统计查询接口 ====================
|
||||
|
||||
// 查询即将到期的班干部列表
|
||||
export const getExpiringCadres = (days: number = 30): AxiosPromise<XgClassCadreVO[]> => {
|
||||
return request({
|
||||
url: '/xg/classCadre/expiring',
|
||||
method: 'get',
|
||||
params: { days }
|
||||
});
|
||||
};
|
||||
|
||||
// 查询已到期的班干部列表
|
||||
export const getExpiredCadres = (): AxiosPromise<XgClassCadreVO[]> => {
|
||||
return request({
|
||||
url: '/xg/classCadre/expired',
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
// 根据学院查询班干部统计
|
||||
export const getCadresByCollege = (collegeName: string): AxiosPromise<XgClassCadreVO[]> => {
|
||||
return request({
|
||||
url: '/xg/classCadre/college/' + collegeName,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
// 根据年级查询班干部统计
|
||||
export const getCadresByGrade = (grade: string): AxiosPromise<XgClassCadreVO[]> => {
|
||||
return request({
|
||||
url: '/xg/classCadre/grade/' + grade,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
// 查询班干部任期统计
|
||||
export const getCadreTermStatistics = (startDate: string, endDate: string): AxiosPromise<XgClassCadreVO[]> => {
|
||||
return request({
|
||||
url: '/xg/classCadre/term-statistics',
|
||||
method: 'get',
|
||||
params: { startDate, endDate }
|
||||
});
|
||||
};
|
||||
|
||||
// 根据职务级别查询班干部
|
||||
export const getCadresByLevel = (positionLevel: number): AxiosPromise<XgClassCadreVO[]> => {
|
||||
return request({
|
||||
url: '/xg/classCadre/level/' + positionLevel,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
// 查询班干部工作表现统计
|
||||
export const getCadresByPerformance = (performance: string): AxiosPromise<XgClassCadreVO[]> => {
|
||||
return request({
|
||||
url: '/xg/classCadre/performance/' + performance,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
// ==================== 辅助功能接口 ====================
|
||||
|
||||
// 检查职务是否已被占用
|
||||
export const checkPositionOccupied = (classId: string | number, positionCode: string, excludeId?: string | number): AxiosPromise<boolean> => {
|
||||
return request({
|
||||
url: '/xg/classCadre/check-position',
|
||||
method: 'get',
|
||||
params: { classId, positionCode, excludeId }
|
||||
});
|
||||
};
|
||||
|
||||
// 检查学生是否已有其他职务
|
||||
export const checkStudentHasOtherPosition = (studentId: string | number, excludeId?: string | number): AxiosPromise<boolean> => {
|
||||
return request({
|
||||
url: '/xg/classCadre/check-student',
|
||||
method: 'get',
|
||||
params: { studentId, excludeId }
|
||||
});
|
||||
};
|
||||
|
||||
// 批量更新班干部状态
|
||||
export const batchUpdateCadreStatus = (cadreIds: Array<string | number>, status: string) => {
|
||||
return request({
|
||||
url: '/xg/classCadre/batch-status',
|
||||
method: 'put',
|
||||
params: { cadreIds: cadreIds.join(','), status }
|
||||
});
|
||||
};
|
||||
|
||||
// 自动处理到期班干部
|
||||
export const autoProcessExpiredCadres = () => {
|
||||
return request({
|
||||
url: '/xg/classCadre/auto-process-expired',
|
||||
method: 'post'
|
||||
});
|
||||
};
|
||||
|
||||
// 发送到期提醒通知
|
||||
export const sendExpirationReminder = (days: number = 30) => {
|
||||
return request({
|
||||
url: '/xg/classCadre/send-reminder',
|
||||
method: 'post',
|
||||
params: { days }
|
||||
});
|
||||
};
|
||||
|
||||
// 导入班干部信息
|
||||
export const importClassCadre = (data: FormData) => {
|
||||
return request({
|
||||
url: '/xg/classCadre/import',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
// 获取导入模板
|
||||
export const importTemplate = () => {
|
||||
return request({
|
||||
url: '/xg/classCadre/import-template',
|
||||
method: 'post'
|
||||
});
|
||||
};
|
||||
@@ -1,205 +0,0 @@
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { ClassInfoVO, ClassInfoForm, ClassInfoQuery, ClassOption, TeacherOption } from './types';
|
||||
|
||||
/**
|
||||
* 查询班级信息列表
|
||||
* @param query 查询参数
|
||||
*/
|
||||
export const listClassInfo = (query?: ClassInfoQuery): AxiosPromise<ClassInfoVO[]> => {
|
||||
return request({
|
||||
url: '/xg/classInfo/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询班级信息详细信息
|
||||
* @param id 班级ID
|
||||
*/
|
||||
export const getClassInfo = (id: string | number): AxiosPromise<ClassInfoVO> => {
|
||||
return request({
|
||||
url: `/xg/classInfo/${id}`,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 新增班级信息
|
||||
* @param data 班级信息数据
|
||||
*/
|
||||
export const addClassInfo = (data: ClassInfoForm) => {
|
||||
return request({
|
||||
url: '/xg/classInfo',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 修改班级信息
|
||||
* @param data 班级信息数据
|
||||
*/
|
||||
export const updateClassInfo = (data: ClassInfoForm) => {
|
||||
return request({
|
||||
url: '/xg/classInfo',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除班级信息
|
||||
* @param id 班级ID
|
||||
*/
|
||||
export const delClassInfo = (id: string | number | Array<string | number>) => {
|
||||
return request({
|
||||
url: `/xg/classInfo/${id}`,
|
||||
method: 'delete'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据班级代码查询班级信息
|
||||
* @param classCode 班级代码
|
||||
*/
|
||||
export const getClassInfoByCode = (classCode: string): AxiosPromise<ClassInfoVO> => {
|
||||
return request({
|
||||
url: `/xg/classInfo/byCode/${classCode}`,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询学院班级列表
|
||||
* @param collegeCode 学院代码
|
||||
*/
|
||||
export const getClassInfoByCollege = (collegeCode: string): AxiosPromise<ClassInfoVO[]> => {
|
||||
return request({
|
||||
url: `/xg/classInfo/byCollege/${collegeCode}`,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取班级选项列表
|
||||
*/
|
||||
export const getClassInfoOptions = (): AxiosPromise<ClassOption[]> => {
|
||||
return request({
|
||||
url: '/xg/classInfo/options',
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 检查班级代码是否存在
|
||||
* @param classCode 班级代码
|
||||
*/
|
||||
export const checkClassCode = (classCode: string): AxiosPromise<boolean> => {
|
||||
return request({
|
||||
url: '/xg/classInfo/checkClassCode',
|
||||
method: 'get',
|
||||
params: { classCode }
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 更新班级人数统计
|
||||
* @param classId 班级ID
|
||||
*/
|
||||
export const updateClassStudentCount = (classId: string | number) => {
|
||||
return request({
|
||||
url: `/xg/classInfo/${classId}/updateCount`,
|
||||
method: 'put'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取辅导员选项列表
|
||||
* @param collegeCode 学院代码(可选)
|
||||
*/
|
||||
export const getCounselorOptions = (collegeCode?: string): AxiosPromise<TeacherOption[]> => {
|
||||
return request({
|
||||
url: '/xg/classInfo/counselorOptions',
|
||||
method: 'get',
|
||||
params: { collegeCode }
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取班主任选项列表
|
||||
* @param collegeCode 学院代码(可选)
|
||||
*/
|
||||
export const getClassTeacherOptions = (collegeCode?: string): AxiosPromise<TeacherOption[]> => {
|
||||
return request({
|
||||
url: '/xg/classInfo/classTeacherOptions',
|
||||
method: 'get',
|
||||
params: { collegeCode }
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 导出班级信息
|
||||
* @param query 查询参数
|
||||
*/
|
||||
export const exportClassInfo = (query?: ClassInfoQuery) => {
|
||||
return request({
|
||||
url: '/xg/classInfo/export',
|
||||
method: 'post',
|
||||
data: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 批量生成班级代码
|
||||
* @param params 生成参数
|
||||
*/
|
||||
export const generateClassCode = (params: {
|
||||
collegeCode: string;
|
||||
majorCode: string;
|
||||
enrollmentYear: number;
|
||||
classCount: number;
|
||||
}): AxiosPromise<string[]> => {
|
||||
return request({
|
||||
url: '/xg/classInfo/generateClassCode',
|
||||
method: 'post',
|
||||
data: params
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询班级学生统计
|
||||
* @param classId 班级ID
|
||||
*/
|
||||
export const getClassStudentStatistics = (
|
||||
classId: string | number
|
||||
): AxiosPromise<{
|
||||
totalCount: number;
|
||||
maleCount: number;
|
||||
femaleCount: number;
|
||||
activeCount: number;
|
||||
suspendedCount: number;
|
||||
graduatedCount: number;
|
||||
}> => {
|
||||
return request({
|
||||
url: `/xg/classInfo/${classId}/statistics`,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取班级信息总体统计
|
||||
*/
|
||||
export const getClassInfoStatistics = (): AxiosPromise<{
|
||||
totalCount: number;
|
||||
activeCount: number;
|
||||
suspendedCount: number;
|
||||
graduatedCount: number;
|
||||
averageStudentCount: number;
|
||||
}> => {
|
||||
return request({
|
||||
url: '/xg/classInfo/statistics',
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
@@ -1,162 +0,0 @@
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { XgCollectionBatchVO, XgCollectionBatchForm, XgCollectionBatchQuery } from './types';
|
||||
|
||||
/**
|
||||
* 查询信息采集批次列表
|
||||
*/
|
||||
export const listCollectionBatch = (query: XgCollectionBatchQuery): AxiosPromise<XgCollectionBatchVO[]> => {
|
||||
return request({
|
||||
url: '/xg/collectionBatch/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询所有信息采集批次列表(不分页)
|
||||
*/
|
||||
export const listAllCollectionBatch = (query: XgCollectionBatchQuery): AxiosPromise<XgCollectionBatchVO[]> => {
|
||||
return request({
|
||||
url: '/xg/collectionBatch/listAll',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询信息采集批次详细
|
||||
*/
|
||||
export const getCollectionBatch = (id: string | number): AxiosPromise<XgCollectionBatchVO> => {
|
||||
return request({
|
||||
url: '/xg/collectionBatch/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 新增信息采集批次
|
||||
*/
|
||||
export const addCollectionBatch = (data: XgCollectionBatchForm) => {
|
||||
return request({
|
||||
url: '/xg/collectionBatch',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 修改信息采集批次
|
||||
*/
|
||||
export const updateCollectionBatch = (data: XgCollectionBatchForm) => {
|
||||
return request({
|
||||
url: '/xg/collectionBatch',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除信息采集批次
|
||||
*/
|
||||
export const delCollectionBatch = (id: string | number | Array<string | number>) => {
|
||||
return request({
|
||||
url: '/xg/collectionBatch/' + id,
|
||||
method: 'delete'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 导出信息采集批次
|
||||
*/
|
||||
export const exportCollectionBatch = (query: XgCollectionBatchQuery) => {
|
||||
return request({
|
||||
url: '/xg/collectionBatch/export',
|
||||
method: 'post',
|
||||
data: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取当前活跃的批次列表
|
||||
*/
|
||||
export const getActiveBatches = (): AxiosPromise<XgCollectionBatchVO[]> => {
|
||||
return request({
|
||||
url: '/xg/collectionBatch/active',
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 发布批次
|
||||
*/
|
||||
export const publishBatch = (id: string | number) => {
|
||||
return request({
|
||||
url: '/xg/collectionBatch/publish/' + id,
|
||||
method: 'post'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 取消发布批次
|
||||
*/
|
||||
export const unpublishBatch = (id: string | number) => {
|
||||
return request({
|
||||
url: '/xg/collectionBatch/unpublish/' + id,
|
||||
method: 'post'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 结束批次
|
||||
*/
|
||||
export const completeBatch = (id: string | number) => {
|
||||
return request({
|
||||
url: '/xg/collectionBatch/complete/' + id,
|
||||
method: 'post'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 复制批次
|
||||
*/
|
||||
export const copyBatch = (id: string | number, newBatchName: string, newBatchCode: string) => {
|
||||
return request({
|
||||
url: '/xg/collectionBatch/copy/' + id,
|
||||
method: 'post',
|
||||
params: {
|
||||
newBatchName,
|
||||
newBatchCode
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取批次统计信息
|
||||
*/
|
||||
export const getBatchStatistics = (id: string | number) => {
|
||||
return request({
|
||||
url: '/xg/collectionBatch/statistics/' + id,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 更新批次统计信息
|
||||
*/
|
||||
export const updateBatchStatistics = (id: string | number) => {
|
||||
return request({
|
||||
url: '/xg/collectionBatch/updateStatistics/' + id,
|
||||
method: 'post'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取批次选项列表(用于下拉框)
|
||||
*/
|
||||
export const getBatchOptions = () => {
|
||||
return request({
|
||||
url: '/xg/collectionBatch/options',
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
@@ -1,18 +0,0 @@
|
||||
/**
|
||||
* 学工管理模块 API 入口
|
||||
*/
|
||||
|
||||
// 学生信息管理
|
||||
export * from './studentInfo';
|
||||
// 班级信息管理
|
||||
export * from './classInfo';
|
||||
// 班级干部管理
|
||||
export * from './classCadre';
|
||||
// 学年管理
|
||||
export * from './academicYear';
|
||||
// 学期管理
|
||||
export * from './semester';
|
||||
// 类型定义
|
||||
export * from './types';
|
||||
export * from './academicTypes';
|
||||
export * from './collectionBatch';
|
||||
@@ -1,128 +0,0 @@
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import { SemesterVO, SemesterForm, SemesterQuery } from './academicTypes';
|
||||
|
||||
/**
|
||||
* 查询学期管理列表
|
||||
* @param query 查询参数
|
||||
*/
|
||||
export const listSemester = (query?: SemesterQuery): AxiosPromise<SemesterVO[]> => {
|
||||
return request({
|
||||
url: '/xg-xx/semester/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询学期管理详细信息
|
||||
* @param id 学期ID
|
||||
*/
|
||||
export const getSemester = (id: string | number): AxiosPromise<SemesterVO> => {
|
||||
return request({
|
||||
url: `/xg-xx/semester/${id}`,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 新增学期管理
|
||||
* @param data 学期管理数据
|
||||
*/
|
||||
export const addSemester = (data: SemesterForm) => {
|
||||
return request({
|
||||
url: '/xg-xx/semester',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 修改学期管理
|
||||
* @param data 学期管理数据
|
||||
*/
|
||||
export const updateSemester = (data: SemesterForm) => {
|
||||
return request({
|
||||
url: '/xg-xx/semester',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除学期管理
|
||||
* @param id 学期ID
|
||||
*/
|
||||
export const delSemester = (id: string | number | Array<string | number>) => {
|
||||
return request({
|
||||
url: `/xg-xx/semester/${id}`,
|
||||
method: 'delete'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 导出学期管理列表
|
||||
* @param query 查询参数
|
||||
*/
|
||||
export const exportSemester = (query?: SemesterQuery) => {
|
||||
return request({
|
||||
url: '/xg-xx/semester/export',
|
||||
method: 'post',
|
||||
data: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据学年ID查询学期列表
|
||||
* @param academicYearId 学年ID
|
||||
*/
|
||||
export const getSemestersByAcademicYear = (academicYearId: string | number): AxiosPromise<SemesterVO[]> => {
|
||||
return request({
|
||||
url: `/xg-xx/semester/getByAcademicYear/${academicYearId}`,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询当前学期
|
||||
*/
|
||||
export const getCurrentSemester = (): AxiosPromise<SemesterVO> => {
|
||||
return request({
|
||||
url: '/xg-xx/semester/current',
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 设置当前学期
|
||||
* @param id 学期ID
|
||||
*/
|
||||
export const setCurrentSemester = (id: string | number) => {
|
||||
return request({
|
||||
url: `/xg-xx/semester/setCurrent/${id}`,
|
||||
method: 'put'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询可用的学期选项
|
||||
*/
|
||||
export const getAvailableSemesterOptions = (): AxiosPromise<SemesterVO[]> => {
|
||||
return request({
|
||||
url: '/xg-xx/semester/availableOptions',
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 校验学期名称是否唯一
|
||||
* @param semesterName 学期名称
|
||||
* @param id 学期ID
|
||||
*/
|
||||
export const checkSemesterName = (semesterName: string, id?: string | number): AxiosPromise<boolean> => {
|
||||
return request({
|
||||
url: '/xg-xx/semester/checkSemesterName',
|
||||
method: 'get',
|
||||
params: { semesterName, id }
|
||||
});
|
||||
};
|
||||
@@ -1,478 +0,0 @@
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
import {
|
||||
StudentInfoVO,
|
||||
StudentInfoForm,
|
||||
StudentInfoQuery,
|
||||
StudentStatisticsVO,
|
||||
BatchUpdateStatusForm,
|
||||
StudentAuditForm,
|
||||
SubmitAuditForm,
|
||||
ClassOption,
|
||||
CollegeOption,
|
||||
MajorOption,
|
||||
ImportResult
|
||||
} from './types';
|
||||
|
||||
/**
|
||||
* 查询学生信息列表
|
||||
* @param query 查询参数
|
||||
*/
|
||||
export const listStudentInfo = (query?: StudentInfoQuery): AxiosPromise<StudentInfoVO[]> => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询学生信息详细信息
|
||||
* @param id 学生ID
|
||||
*/
|
||||
export const getStudentInfo = (id: string | number): AxiosPromise<StudentInfoVO> => {
|
||||
return request({
|
||||
url: `/xg/studentInfo/${id}`,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据学号查询学生信息
|
||||
* @param studentNo 学号
|
||||
*/
|
||||
export const getStudentInfoByStudentNo = (studentNo: string): AxiosPromise<StudentInfoVO> => {
|
||||
return request({
|
||||
url: `/xg/studentInfo/byStudentNo/${studentNo}`,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据身份证号查询学生信息
|
||||
* @param idCard 身份证号
|
||||
*/
|
||||
export const getStudentInfoByIdCard = (idCard: string): AxiosPromise<StudentInfoVO> => {
|
||||
return request({
|
||||
url: `/xg/studentInfo/byIdCard/${idCard}`,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询班级学生列表
|
||||
* @param classCode 班级代码
|
||||
*/
|
||||
export const getStudentInfoByClass = (classCode: string): AxiosPromise<StudentInfoVO[]> => {
|
||||
return request({
|
||||
url: `/xg/studentInfo/byClass/${classCode}`,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询学院学生列表
|
||||
* @param collegeCode 学院代码
|
||||
*/
|
||||
export const getStudentInfoByCollege = (collegeCode: string): AxiosPromise<StudentInfoVO[]> => {
|
||||
return request({
|
||||
url: `/xg/studentInfo/byCollege/${collegeCode}`,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 新增学生信息
|
||||
* @param data 学生信息数据
|
||||
*/
|
||||
export const addStudentInfo = (data: StudentInfoForm) => {
|
||||
return request({
|
||||
url: '/xg/studentInfo',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 修改学生信息
|
||||
* @param data 学生信息数据
|
||||
*/
|
||||
export const updateStudentInfo = (data: StudentInfoForm) => {
|
||||
return request({
|
||||
url: '/xg/studentInfo',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除学生信息
|
||||
* @param id 学生ID
|
||||
*/
|
||||
export const delStudentInfo = (id: string | number | Array<string | number>) => {
|
||||
return request({
|
||||
url: `/xg/studentInfo/${id}`,
|
||||
method: 'delete'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 导出学生信息
|
||||
* @param query 查询参数
|
||||
*/
|
||||
export const exportStudentInfo = (query?: StudentInfoQuery) => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/export',
|
||||
method: 'post',
|
||||
data: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 批量导入学生信息
|
||||
* @param file 文件
|
||||
* @param updateSupport 是否更新支持
|
||||
*/
|
||||
export const importStudentInfo = (file: File, updateSupport: boolean = false): AxiosPromise<ImportResult> => {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
formData.append('updateSupport', updateSupport.toString());
|
||||
|
||||
return request({
|
||||
url: '/xg/studentInfo/importData',
|
||||
method: 'post',
|
||||
data: formData,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取导入模板
|
||||
*/
|
||||
export const importTemplate = () => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/importTemplate',
|
||||
method: 'post'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 批量更新学生信息状态
|
||||
* @param data 批量更新数据
|
||||
*/
|
||||
export const batchUpdateStudentInfoStatus = (data: BatchUpdateStatusForm) => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/batchUpdateStatus',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 提交学生信息审核
|
||||
* @param data 提交审核数据
|
||||
*/
|
||||
export const submitStudentInfoAudit = (data: SubmitAuditForm) => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/submitAudit',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 审核学生信息
|
||||
* @param data 审核数据
|
||||
*/
|
||||
export const auditStudentInfo = (data: StudentAuditForm) => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/audit',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询学生统计信息
|
||||
* @param query 查询参数
|
||||
*/
|
||||
export const getStudentStatistics = (query?: {
|
||||
collegeCode?: string;
|
||||
classCode?: string;
|
||||
enrollmentYear?: number;
|
||||
studentStatus?: string;
|
||||
}): AxiosPromise<StudentStatisticsVO[]> => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/statistics',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 检查学号是否存在
|
||||
* @param studentNo 学号
|
||||
* @param id 学生ID(编辑时传入)
|
||||
*/
|
||||
export const checkStudentNo = (studentNo: string, id?: string | number): AxiosPromise<boolean> => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/checkStudentNo',
|
||||
method: 'get',
|
||||
params: { studentNo, id }
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 检查身份证号是否存在
|
||||
* @param idCard 身份证号
|
||||
* @param id 学生ID(编辑时传入)
|
||||
*/
|
||||
export const checkIdCard = (idCard: string, id?: string | number): AxiosPromise<boolean> => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/checkIdCard',
|
||||
method: 'get',
|
||||
params: { idCard, id }
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取班级选项列表
|
||||
* @param collegeCode 学院代码(可选)
|
||||
*/
|
||||
export const getClassOptions = (collegeCode?: string): AxiosPromise<ClassOption[]> => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/classOptions',
|
||||
method: 'get',
|
||||
params: { collegeCode }
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取学院选项列表
|
||||
*/
|
||||
export const getCollegeOptions = (): AxiosPromise<CollegeOption[]> => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/collegeOptions',
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取专业选项列表
|
||||
* @param collegeCode 学院代码(可选)
|
||||
*/
|
||||
export const getMajorOptions = (collegeCode?: string): AxiosPromise<MajorOption[]> => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/majorOptions',
|
||||
method: 'get',
|
||||
params: { collegeCode }
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取学生选项列表(用于下拉选择)
|
||||
* @param query 查询参数
|
||||
*/
|
||||
export const getStudentOptions = (query?: {
|
||||
name?: string;
|
||||
classCode?: string;
|
||||
collegeCode?: string;
|
||||
}): AxiosPromise<Array<{ value: string | number; label: string; studentNo?: string; className?: string }>> => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/studentOptions',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 同步学生信息
|
||||
* @param source 同步来源:1-教务系统,2-招生系统
|
||||
*/
|
||||
export const syncStudentInfo = (source: string) => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/sync',
|
||||
method: 'post',
|
||||
data: { source }
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 生成学生学号
|
||||
* @param collegeCode 学院代码
|
||||
* @param enrollmentYear 入学年份
|
||||
*/
|
||||
export const generateStudentNo = (collegeCode: string, enrollmentYear: number): AxiosPromise<string> => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/generateStudentNo',
|
||||
method: 'get',
|
||||
params: { collegeCode, enrollmentYear }
|
||||
});
|
||||
};
|
||||
|
||||
// ==================== 批次相关接口 ====================
|
||||
|
||||
/**
|
||||
* 根据批次ID查询学生信息
|
||||
* @param batchId 批次ID
|
||||
* @param query 查询参数
|
||||
*/
|
||||
export const getStudentsByBatch = (batchId: string | number, query?: StudentInfoQuery): AxiosPromise<StudentInfoVO[]> => {
|
||||
return request({
|
||||
url: `/xg/studentInfo/batch/${batchId}`,
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 关联学生到批次
|
||||
* @param studentId 学生ID
|
||||
* @param batchId 批次ID
|
||||
*/
|
||||
export const associateStudentToBatch = (studentId: string | number, batchId: string | number) => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/associateToBatch',
|
||||
method: 'post',
|
||||
data: { studentId, batchId }
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 批量关联学生到批次
|
||||
* @param studentIds 学生ID数组
|
||||
* @param batchId 批次ID
|
||||
*/
|
||||
export const batchAssociateStudentsToBatch = (studentIds: (string | number)[], batchId: string | number) => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/batchAssociateToBatch',
|
||||
method: 'post',
|
||||
data: { studentIds, batchId }
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 提交学生信息到批次
|
||||
* @param studentId 学生ID
|
||||
* @param batchId 批次ID
|
||||
*/
|
||||
export const submitStudentInfo = (studentId: string | number, batchId: string | number) => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/submit',
|
||||
method: 'post',
|
||||
data: { studentId, batchId }
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 撤回学生信息提交
|
||||
* @param studentId 学生ID
|
||||
* @param batchId 批次ID
|
||||
*/
|
||||
export const withdrawStudentInfo = (studentId: string | number, batchId: string | number) => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/withdraw',
|
||||
method: 'post',
|
||||
data: { studentId, batchId }
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取学生批次进度
|
||||
* @param studentId 学生ID
|
||||
* @param batchId 批次ID
|
||||
*/
|
||||
export const getStudentBatchProgress = (studentId: string | number, batchId: string | number) => {
|
||||
return request({
|
||||
url: `/xg/studentInfo/batchProgress/${studentId}/${batchId}`,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 批量审核学生信息
|
||||
* @param studentIds 学生ID数组
|
||||
* @param batchId 批次ID
|
||||
* @param approvalStatus 审核状态
|
||||
* @param approvalComments 审核意见
|
||||
*/
|
||||
export const batchAuditStudentInfo = (data: {
|
||||
studentIds: (string | number)[];
|
||||
batchId: string | number;
|
||||
approvalStatus: string;
|
||||
approvalComments?: string;
|
||||
}) => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/batchAudit',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 检查学生编辑权限
|
||||
* @param studentId 学生ID
|
||||
* @param batchId 批次ID
|
||||
*/
|
||||
export const checkEditPermission = (studentId: string | number, batchId: string | number) => {
|
||||
return request({
|
||||
url: `/xg/studentInfo/checkEditPermission/${studentId}/${batchId}`,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取学生批次统计信息
|
||||
* @param batchId 批次ID
|
||||
*/
|
||||
export const getStudentBatchStatistics = (batchId: string | number) => {
|
||||
return request({
|
||||
url: `/xg/studentInfo/batchStatistics/${batchId}`,
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 批量提交学生信息
|
||||
* @param studentIds 学生ID数组
|
||||
* @param batchId 批次ID
|
||||
*/
|
||||
export const batchSubmitStudentInfo = (studentIds: (string | number)[], batchId: string | number) => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/batchSubmit',
|
||||
method: 'post',
|
||||
data: { studentIds, batchId }
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 批量撤回学生信息
|
||||
* @param studentIds 学生ID数组
|
||||
* @param batchId 批次ID
|
||||
*/
|
||||
export const batchWithdrawStudentInfo = (studentIds: (string | number)[], batchId: string | number) => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/batchWithdraw',
|
||||
method: 'post',
|
||||
data: { studentIds, batchId }
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取所有学生选项(用于批次关联)
|
||||
* @param query 查询参数
|
||||
*/
|
||||
export const getAllStudentOptions = (query?: {
|
||||
name?: string;
|
||||
studentNo?: string;
|
||||
classCode?: string;
|
||||
collegeCode?: string;
|
||||
}): AxiosPromise<Array<{ id: string | number; name: string; studentNo: string; className?: string; collegeName?: string }>> => {
|
||||
return request({
|
||||
url: '/xg/studentInfo/allOptions',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
@@ -1,182 +0,0 @@
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
|
||||
/**
|
||||
* 学生信息审核查询参数
|
||||
*/
|
||||
export interface XgStudentInfoApprovalQuery {
|
||||
batchId?: number;
|
||||
studentId?: number;
|
||||
studentName?: string;
|
||||
studentNo?: string;
|
||||
classCode?: string;
|
||||
className?: string;
|
||||
collegeCode?: string;
|
||||
collegeName?: string;
|
||||
submissionStatus?: string;
|
||||
approvalStatus?: string;
|
||||
submissionTimeStart?: string;
|
||||
submissionTimeEnd?: string;
|
||||
approvalTimeStart?: string;
|
||||
approvalTimeEnd?: string;
|
||||
approvalBy?: number;
|
||||
approvalByName?: string;
|
||||
keyword?: string;
|
||||
pageNum?: number;
|
||||
pageSize?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 学生信息审核视图对象
|
||||
*/
|
||||
export interface XgStudentInfoApprovalVO {
|
||||
id?: number;
|
||||
batchId?: number;
|
||||
batchName?: string;
|
||||
studentId?: number;
|
||||
studentName?: string;
|
||||
studentNo?: string;
|
||||
classCode?: string;
|
||||
className?: string;
|
||||
collegeCode?: string;
|
||||
collegeName?: string;
|
||||
majorCode?: string;
|
||||
majorName?: string;
|
||||
submissionStatus?: string;
|
||||
approvalStatus?: string;
|
||||
submissionTime?: string;
|
||||
approvalTime?: string;
|
||||
approvalBy?: number;
|
||||
approvalByName?: string;
|
||||
approvalComments?: string;
|
||||
phone?: string;
|
||||
idCard?: string;
|
||||
createTime?: string;
|
||||
updateTime?: string;
|
||||
totalCount?: number;
|
||||
pendingCount?: number;
|
||||
approvedCount?: number;
|
||||
rejectedCount?: number;
|
||||
submittedCount?: number;
|
||||
draftCount?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询待审核的学生信息列表
|
||||
*/
|
||||
export const queryPendingApprovalList = (query: XgStudentInfoApprovalQuery): AxiosPromise<XgStudentInfoApprovalVO[]> => {
|
||||
return request({
|
||||
url: '/xg/studentInfoApproval/pending',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询已审核的学生信息列表
|
||||
*/
|
||||
export const queryApprovedList = (query: XgStudentInfoApprovalQuery): AxiosPromise<XgStudentInfoApprovalVO[]> => {
|
||||
return request({
|
||||
url: '/xg/studentInfoApproval/approved',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 审核学生信息
|
||||
*/
|
||||
export const approveStudentInfo = (studentId: number, batchId: number, approved: boolean, comments?: string) => {
|
||||
return request({
|
||||
url: '/xg/studentInfoApproval/approve',
|
||||
method: 'post',
|
||||
params: {
|
||||
studentId,
|
||||
batchId,
|
||||
approved,
|
||||
comments
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 批量审核学生信息
|
||||
*/
|
||||
export const batchApproveStudentInfo = (studentIds: number[], batchId: number, approved: boolean, comments?: string) => {
|
||||
return request({
|
||||
url: '/xg/studentInfoApproval/batchApprove',
|
||||
method: 'post',
|
||||
data: studentIds,
|
||||
params: {
|
||||
batchId,
|
||||
approved,
|
||||
comments
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 撤销审核
|
||||
*/
|
||||
export const revokeApproval = (studentId: number, batchId: number, reason?: string) => {
|
||||
return request({
|
||||
url: '/xg/studentInfoApproval/revoke',
|
||||
method: 'post',
|
||||
params: {
|
||||
studentId,
|
||||
batchId,
|
||||
reason
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询学生信息审核历史
|
||||
*/
|
||||
export const queryApprovalHistory = (studentId: number, batchId: number): AxiosPromise<XgStudentInfoApprovalVO[]> => {
|
||||
return request({
|
||||
url: '/xg/studentInfoApproval/history',
|
||||
method: 'get',
|
||||
params: {
|
||||
studentId,
|
||||
batchId
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取审核统计信息
|
||||
*/
|
||||
export const getApprovalStatistics = (batchId: number): AxiosPromise<XgStudentInfoApprovalVO> => {
|
||||
return request({
|
||||
url: '/xg/studentInfoApproval/statistics',
|
||||
method: 'get',
|
||||
params: {
|
||||
batchId
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 检查审核权限
|
||||
*/
|
||||
export const checkApprovalPermission = (batchId: number): AxiosPromise<boolean> => {
|
||||
return request({
|
||||
url: '/xg/studentInfoApproval/checkPermission',
|
||||
method: 'get',
|
||||
params: {
|
||||
batchId
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 导出审核结果
|
||||
*/
|
||||
export const exportApprovalResults = (query: XgStudentInfoApprovalQuery) => {
|
||||
return request({
|
||||
url: '/xg/studentInfoApproval/export',
|
||||
method: 'post',
|
||||
data: query
|
||||
});
|
||||
};
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,306 +0,0 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryFormRef" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form-item label="学年编码" prop="yearCode">
|
||||
<el-input v-model="queryParams.yearCode" placeholder="请输入学年编码" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="学年名称" prop="yearName">
|
||||
<el-input v-model="queryParams.yearName" placeholder="请输入学年名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="学年状态" prop="yearStatus">
|
||||
<el-select v-model="queryParams.yearStatus" placeholder="请选择学年状态" clearable>
|
||||
<el-option label="启用" value="1" />
|
||||
<el-option label="停用" value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否当前" prop="isCurrent">
|
||||
<el-select v-model="queryParams.isCurrent" placeholder="请选择是否当前学年" clearable>
|
||||
<el-option label="是" :value="true" />
|
||||
<el-option label="否" :value="false" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['xg:academicYear:add']">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['xg:academicYear:edit']">修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['xg:academicYear:remove']"
|
||||
>删除</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['xg:academicYear:export']">导出</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="academicYearList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="学年编码" align="center" prop="yearCode" />
|
||||
<el-table-column label="学年名称" align="center" prop="yearName" />
|
||||
<el-table-column label="开始日期" align="center" prop="startDate" width="180">
|
||||
<template #default="scope">
|
||||
<span>{{ parseTime(scope.row.startDate, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="结束日期" align="center" prop="endDate" width="180">
|
||||
<template #default="scope">
|
||||
<span>{{ parseTime(scope.row.endDate, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="是否当前" align="center" prop="isCurrent">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="sys_yes_no" :value="scope.row.isCurrent" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="学年状态" align="center" prop="yearStatus">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="sys_normal_disable" :value="scope.row.yearStatus" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="排序" align="center" prop="sortOrder" />
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template #default="scope">
|
||||
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="修改" placement="top">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['xg:academicYear:edit']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['xg:academicYear:remove']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="设为当前" placement="top" v-if="!scope.row.isCurrent">
|
||||
<el-button link type="success" icon="Check" @click="handleSetCurrent(scope.row)" v-hasPermi="['xg:academicYear:edit']"></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||||
|
||||
<!-- 添加或修改学年管理对话框 -->
|
||||
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
|
||||
<el-form ref="academicYearFormRef" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="学年编码" prop="yearCode">
|
||||
<el-input v-model="form.yearCode" placeholder="请输入学年编码" />
|
||||
</el-form-item>
|
||||
<el-form-item label="学年名称" prop="yearName">
|
||||
<el-input v-model="form.yearName" placeholder="请输入学年名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="开始日期" prop="startDate">
|
||||
<el-date-picker clearable v-model="form.startDate" type="date" value-format="YYYY-MM-DD" placeholder="请选择开始日期" />
|
||||
</el-form-item>
|
||||
<el-form-item label="结束日期" prop="endDate">
|
||||
<el-date-picker clearable v-model="form.endDate" type="date" value-format="YYYY-MM-DD" placeholder="请选择结束日期" />
|
||||
</el-form-item>
|
||||
<el-form-item label="是否当前" prop="isCurrent">
|
||||
<el-radio-group v-model="form.isCurrent">
|
||||
<el-radio :value="true">是</el-radio>
|
||||
<el-radio :value="false">否</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="学年状态" prop="yearStatus">
|
||||
<el-radio-group v-model="form.yearStatus">
|
||||
<el-radio value="1">启用</el-radio>
|
||||
<el-radio value="0">停用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="排序序号" prop="sortOrder">
|
||||
<el-input-number v-model="form.sortOrder" :min="0" />
|
||||
</el-form-item>
|
||||
<el-form-item label="学年描述" prop="description">
|
||||
<el-input v-model="form.description" type="textarea" placeholder="请输入学年描述" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="AcademicYear" lang="ts">
|
||||
import {
|
||||
listAcademicYear,
|
||||
getAcademicYear,
|
||||
delAcademicYear,
|
||||
addAcademicYear,
|
||||
updateAcademicYear,
|
||||
setCurrentAcademicYear,
|
||||
exportAcademicYear
|
||||
} from '@/api/xg-xx/academicYear';
|
||||
import { AcademicYearVO, AcademicYearQuery, AcademicYearForm } from '@/api/xg-xx/academicTypes';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { sys_yes_no, sys_normal_disable } = proxy?.useDict('sys_yes_no', 'sys_normal_disable');
|
||||
|
||||
const academicYearList = ref<AcademicYearVO[]>([]);
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref<Array<string | number>>([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
const academicYearFormRef = ref<ElFormInstance>();
|
||||
|
||||
const dialog = reactive<DialogOption>({
|
||||
visible: false,
|
||||
title: ''
|
||||
});
|
||||
|
||||
const initFormData: AcademicYearForm = {
|
||||
id: undefined,
|
||||
yearCode: '',
|
||||
yearName: '',
|
||||
startDate: '',
|
||||
endDate: '',
|
||||
isCurrent: false,
|
||||
yearStatus: '1',
|
||||
sortOrder: 0,
|
||||
description: '',
|
||||
remark: ''
|
||||
};
|
||||
|
||||
const data = reactive<PageData<AcademicYearForm, AcademicYearQuery>>({
|
||||
form: { ...initFormData },
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
yearCode: '',
|
||||
yearName: '',
|
||||
yearStatus: '',
|
||||
isCurrent: undefined,
|
||||
params: {}
|
||||
},
|
||||
rules: {
|
||||
yearCode: [{ required: true, message: '学年编码不能为空', trigger: 'blur' }],
|
||||
yearName: [{ required: true, message: '学年名称不能为空', trigger: 'blur' }],
|
||||
startDate: [{ required: true, message: '开始日期不能为空', trigger: 'blur' }],
|
||||
endDate: [{ required: true, message: '结束日期不能为空', trigger: 'blur' }],
|
||||
yearStatus: [{ required: true, message: '学年状态不能为空', trigger: 'change' }],
|
||||
sortOrder: [{ required: true, message: '排序序号不能为空', trigger: 'blur' }]
|
||||
}
|
||||
});
|
||||
|
||||
const { queryParams, form, rules } = toRefs(data);
|
||||
|
||||
/** 查询学年管理列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
const res = await listAcademicYear(queryParams.value);
|
||||
academicYearList.value = res.rows;
|
||||
total.value = res.total;
|
||||
loading.value = false;
|
||||
};
|
||||
|
||||
/** 取消按钮 */
|
||||
const cancel = () => {
|
||||
reset();
|
||||
dialog.visible = false;
|
||||
};
|
||||
|
||||
/** 表单重置 */
|
||||
const reset = () => {
|
||||
form.value = { ...initFormData };
|
||||
academicYearFormRef.value?.resetFields();
|
||||
};
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value?.resetFields();
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
/** 多选框选中数据 */
|
||||
const handleSelectionChange = (selection: AcademicYearVO[]) => {
|
||||
ids.value = selection.map((item) => item.id);
|
||||
single.value = selection.length !== 1;
|
||||
multiple.value = !selection.length;
|
||||
};
|
||||
|
||||
/** 新增按钮操作 */
|
||||
const handleAdd = () => {
|
||||
reset();
|
||||
dialog.visible = true;
|
||||
dialog.title = '添加学年管理';
|
||||
};
|
||||
|
||||
/** 修改按钮操作 */
|
||||
const handleUpdate = async (row?: AcademicYearVO) => {
|
||||
reset();
|
||||
const _id = row?.id || ids.value[0];
|
||||
const res = await getAcademicYear(_id);
|
||||
Object.assign(form.value, res.data);
|
||||
dialog.visible = true;
|
||||
dialog.title = '修改学年管理';
|
||||
};
|
||||
|
||||
/** 提交按钮 */
|
||||
const submitForm = () => {
|
||||
academicYearFormRef.value?.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
if (form.value.id) {
|
||||
await updateAcademicYear(form.value);
|
||||
proxy?.$modal.msgSuccess('修改成功');
|
||||
} else {
|
||||
await addAcademicYear(form.value);
|
||||
proxy?.$modal.msgSuccess('新增成功');
|
||||
}
|
||||
dialog.visible = false;
|
||||
await getList();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (row?: AcademicYearVO) => {
|
||||
const _ids = row?.id || ids.value;
|
||||
await proxy?.$modal.confirm('是否确认删除学年管理编号为"' + _ids + '"的数据项?');
|
||||
await delAcademicYear(_ids);
|
||||
proxy?.$modal.msgSuccess('删除成功');
|
||||
await getList();
|
||||
};
|
||||
|
||||
/** 设为当前学年 */
|
||||
const handleSetCurrent = async (row: AcademicYearVO) => {
|
||||
await proxy?.$modal.confirm('是否确认设置"' + row.yearName + '"为当前学年?');
|
||||
await setCurrentAcademicYear(row.id);
|
||||
proxy?.$modal.msgSuccess('设置成功');
|
||||
await getList();
|
||||
};
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = () => {
|
||||
proxy?.$download.excel(exportAcademicYear, queryParams.value, `学年管理_${new Date().getTime()}.xlsx`);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
@@ -1,697 +0,0 @@
|
||||
<template>
|
||||
<div class="p-2 page-container">
|
||||
<!-- 搜索区域 -->
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" label-width="100px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="批次选择" prop="batchId">
|
||||
<el-select v-model="queryParams.batchId" placeholder="请选择批次" clearable style="width: 100%">
|
||||
<el-option v-for="batch in batchOptions" :key="batch.id" :label="batch.batchName" :value="batch.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="期间名称" prop="periodName">
|
||||
<el-input v-model="queryParams.periodName" placeholder="请输入期间名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="期间类型" prop="periodType">
|
||||
<el-select v-model="queryParams.periodType" placeholder="请选择期间类型" clearable style="width: 100%">
|
||||
<el-option label="学生填报" value="STUDENT_SUBMIT" />
|
||||
<el-option label="辅导员审核" value="COUNSELOR_REVIEW" />
|
||||
<el-option label="学院审核" value="COLLEGE_REVIEW" />
|
||||
<el-option label="学校审核" value="SCHOOL_REVIEW" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="目标角色" prop="targetRole">
|
||||
<el-select v-model="queryParams.targetRole" placeholder="请选择目标角色" clearable style="width: 100%">
|
||||
<el-option label="学生" value="STUDENT" />
|
||||
<el-option label="辅导员" value="COUNSELOR" />
|
||||
<el-option label="班主任" value="CLASS_TEACHER" />
|
||||
<el-option label="学院管理员" value="COLLEGE_ADMIN" />
|
||||
<el-option label="学校管理员" value="SCHOOL_ADMIN" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable style="width: 100%">
|
||||
<el-option label="未开始" value="PENDING" />
|
||||
<el-option label="进行中" value="ACTIVE" />
|
||||
<el-option label="已结束" value="CLOSED" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="启用状态" prop="enabled">
|
||||
<el-select v-model="queryParams.enabled" placeholder="请选择启用状态" clearable style="width: 100%">
|
||||
<el-option label="启用" :value="true" />
|
||||
<el-option label="禁用" :value="false" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<!-- 主要内容区域 -->
|
||||
<el-card shadow="hover">
|
||||
<!-- 操作按钮区域 -->
|
||||
<div class="toolbar-wrapper">
|
||||
<div class="toolbar-left">
|
||||
<el-button type="primary" icon="Plus" @click="handleAdd" v-hasPermi="['xg:businessPeriod:add']">
|
||||
新增期间
|
||||
</el-button>
|
||||
<el-button type="success" icon="Edit" :disabled="single" @click="handleUpdate" v-hasPermi="['xg:businessPeriod:edit']">
|
||||
修改
|
||||
</el-button>
|
||||
<el-button type="danger" icon="Delete" :disabled="multiple" @click="handleDelete" v-hasPermi="['xg:businessPeriod:remove']">
|
||||
删除
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<div class="toolbar-right">
|
||||
<el-button type="warning" icon="Download" @click="handleExport" v-hasPermi="['xg:businessPeriod:export']">
|
||||
导出
|
||||
</el-button>
|
||||
<el-button type="info" icon="Refresh" @click="handleRefresh">
|
||||
刷新
|
||||
</el-button>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 表格区域 -->
|
||||
<div class="table-wrapper">
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="periodList"
|
||||
@selection-change="handleSelectionChange"
|
||||
@sort-change="handleSortChange"
|
||||
row-key="id"
|
||||
border
|
||||
stripe
|
||||
size="small"
|
||||
>
|
||||
<el-table-column type="selection" width="50" align="center" />
|
||||
<el-table-column label="序号" type="index" width="60" align="center" />
|
||||
<el-table-column label="期间名称" prop="periodName" width="150" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="批次名称" prop="batchName" width="150" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="期间类型" prop="periodType" width="120" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getPeriodTypeTagType(scope.row.periodType)" size="small">
|
||||
{{ getPeriodTypeText(scope.row.periodType) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="目标角色" prop="targetRole" width="120" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getTargetRoleTagType(scope.row.targetRole)" size="small">
|
||||
{{ getTargetRoleText(scope.row.targetRole) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="开始时间" prop="startTime" width="160" align="center" sortable="custom">
|
||||
<template #default="scope">
|
||||
<span>{{ proxy.parseTime(scope.row.startTime, '{y}-{m}-{d} {h}:{i}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="结束时间" prop="endTime" width="160" align="center" sortable="custom">
|
||||
<template #default="scope">
|
||||
<span>{{ proxy.parseTime(scope.row.endTime, '{y}-{m}-{d} {h}:{i}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" prop="status" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getStatusTagType(scope.row.status)" size="small">
|
||||
{{ getStatusText(scope.row.status) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="启用状态" prop="enabled" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.enabled"
|
||||
@change="handleToggleEnabled(scope.row)"
|
||||
:disabled="!hasPermission(['xg:businessPeriod:edit'])"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="排序" prop="sortOrder" width="80" align="center" />
|
||||
<el-table-column label="描述" prop="description" width="200" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="操作" align="center" width="200" fixed="right" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<div class="action-buttons">
|
||||
<el-tooltip content="查看" placement="top">
|
||||
<el-button link type="info" icon="View" size="small" @click="handleView(scope.row)" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="修改" placement="top">
|
||||
<el-button link type="primary" icon="Edit" size="small" @click="handleUpdate(scope.row)" v-hasPermi="['xg:businessPeriod:edit']" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button link type="danger" icon="Delete" size="small" @click="handleDelete(scope.row)" v-hasPermi="['xg:businessPeriod:remove']" />
|
||||
</el-tooltip>
|
||||
<template v-if="scope.row.status === 'PENDING'">
|
||||
<el-tooltip content="开启" placement="top">
|
||||
<el-button link type="success" icon="VideoPlay" size="small" @click="handleOpen(scope.row)" v-hasPermi="['xg:businessPeriod:manage']" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<template v-else-if="scope.row.status === 'ACTIVE'">
|
||||
<el-tooltip content="关闭" placement="top">
|
||||
<el-button link type="warning" icon="VideoPause" size="small" @click="handleClose(scope.row)" v-hasPermi="['xg:businessPeriod:manage']" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="pagination-container">
|
||||
<pagination v-show="total > 0" :total="total" :page="queryParams.pageNum" :limit="queryParams.pageSize" @pagination="getList" />
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 新增/修改对话框 -->
|
||||
<el-dialog :title="dialog.title" v-model="dialog.visible" width="800px" append-to-body>
|
||||
<el-form ref="periodFormRef" :model="form" :rules="rules" label-width="120px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="批次选择" prop="batchId">
|
||||
<el-select v-model="form.batchId" placeholder="请选择批次" style="width: 100%">
|
||||
<el-option v-for="batch in batchOptions" :key="batch.id" :label="batch.batchName" :value="batch.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="期间名称" prop="periodName">
|
||||
<el-input v-model="form.periodName" placeholder="请输入期间名称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="期间类型" prop="periodType">
|
||||
<el-select v-model="form.periodType" placeholder="请选择期间类型" style="width: 100%">
|
||||
<el-option label="学生填报" value="STUDENT_SUBMIT" />
|
||||
<el-option label="辅导员审核" value="COUNSELOR_REVIEW" />
|
||||
<el-option label="学院审核" value="COLLEGE_REVIEW" />
|
||||
<el-option label="学校审核" value="SCHOOL_REVIEW" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="目标角色" prop="targetRole">
|
||||
<el-select v-model="form.targetRole" placeholder="请选择目标角色" style="width: 100%">
|
||||
<el-option label="学生" value="STUDENT" />
|
||||
<el-option label="辅导员" value="COUNSELOR" />
|
||||
<el-option label="班主任" value="CLASS_TEACHER" />
|
||||
<el-option label="学院管理员" value="COLLEGE_ADMIN" />
|
||||
<el-option label="学校管理员" value="SCHOOL_ADMIN" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="开始时间" prop="startTime">
|
||||
<el-date-picker
|
||||
v-model="form.startTime"
|
||||
type="datetime"
|
||||
placeholder="请选择开始时间"
|
||||
style="width: 100%"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="结束时间" prop="endTime">
|
||||
<el-date-picker
|
||||
v-model="form.endTime"
|
||||
type="datetime"
|
||||
placeholder="请选择结束时间"
|
||||
style="width: 100%"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="排序" prop="sortOrder">
|
||||
<el-input-number v-model="form.sortOrder" :min="0" :max="9999" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="启用状态" prop="enabled">
|
||||
<el-switch v-model="form.enabled" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="描述" prop="description">
|
||||
<el-input
|
||||
v-model="form.description"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder="请输入期间描述"
|
||||
maxlength="500"
|
||||
show-word-limit
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="cancel">取消</el-button>
|
||||
<el-button type="primary" @click="submitForm">确定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted, getCurrentInstance } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import {
|
||||
listBusinessPeriod,
|
||||
getBusinessPeriod,
|
||||
addBusinessPeriod,
|
||||
updateBusinessPeriod,
|
||||
delBusinessPeriod,
|
||||
openPeriod,
|
||||
closePeriod,
|
||||
checkPeriodTimeConflict,
|
||||
exportBusinessPeriod,
|
||||
type XgBusinessPeriodQuery,
|
||||
type XgBusinessPeriodForm,
|
||||
type XgBusinessPeriodVO
|
||||
} from '@/api/xg-xx/businessPeriod';
|
||||
import { listAllCollectionBatch } from '@/api/xg-xx/collectionBatch';
|
||||
|
||||
const { proxy } = getCurrentInstance() as any;
|
||||
|
||||
// 响应式数据
|
||||
const loading = ref(false);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref<number[]>([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const periodList = ref<XgBusinessPeriodVO[]>([]);
|
||||
const batchOptions = ref<any[]>([]);
|
||||
|
||||
// 查询参数
|
||||
const queryParams = reactive<XgBusinessPeriodQuery>({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
batchId: undefined,
|
||||
periodName: undefined,
|
||||
periodType: undefined,
|
||||
targetRole: undefined,
|
||||
status: undefined,
|
||||
enabled: undefined
|
||||
});
|
||||
|
||||
// 对话框
|
||||
const dialog = reactive({
|
||||
visible: false,
|
||||
title: '',
|
||||
type: 'add' as 'add' | 'edit'
|
||||
});
|
||||
|
||||
// 表单数据
|
||||
const form = reactive<XgBusinessPeriodForm>({
|
||||
id: undefined,
|
||||
batchId: undefined,
|
||||
periodName: '',
|
||||
periodType: '',
|
||||
targetRole: '',
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
status: 'PENDING',
|
||||
description: '',
|
||||
sortOrder: 0,
|
||||
enabled: true
|
||||
});
|
||||
|
||||
// 表单校验规则
|
||||
const rules = {
|
||||
batchId: [{ required: true, message: '请选择批次', trigger: 'change' }],
|
||||
periodName: [{ required: true, message: '请输入期间名称', trigger: 'blur' }],
|
||||
periodType: [{ required: true, message: '请选择期间类型', trigger: 'change' }],
|
||||
targetRole: [{ required: true, message: '请选择目标角色', trigger: 'change' }],
|
||||
startTime: [{ required: true, message: '请选择开始时间', trigger: 'change' }],
|
||||
endTime: [{ required: true, message: '请选择结束时间', trigger: 'change' }]
|
||||
};
|
||||
|
||||
// 获取列表数据
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const response = await listBusinessPeriod(queryParams);
|
||||
periodList.value = response.rows;
|
||||
total.value = response.total;
|
||||
} catch (error) {
|
||||
console.error('获取列表失败:', error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 获取批次选项
|
||||
const getBatchOptions = async () => {
|
||||
try {
|
||||
const response = await listAllCollectionBatch({});
|
||||
batchOptions.value = response.data;
|
||||
} catch (error) {
|
||||
console.error('获取批次选项失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 搜索
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
// 重置搜索
|
||||
const resetQuery = () => {
|
||||
proxy.resetForm('queryFormRef');
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
// 多选框选中数据
|
||||
const handleSelectionChange = (selection: XgBusinessPeriodVO[]) => {
|
||||
ids.value = selection.map(item => item.id!);
|
||||
single.value = selection.length !== 1;
|
||||
multiple.value = !selection.length;
|
||||
};
|
||||
|
||||
// 排序变化
|
||||
const handleSortChange = (column: any) => {
|
||||
// 处理排序逻辑
|
||||
getList();
|
||||
};
|
||||
|
||||
// 新增
|
||||
const handleAdd = () => {
|
||||
reset();
|
||||
dialog.visible = true;
|
||||
dialog.title = '新增业务期间';
|
||||
dialog.type = 'add';
|
||||
};
|
||||
|
||||
// 修改
|
||||
const handleUpdate = async (row?: XgBusinessPeriodVO) => {
|
||||
reset();
|
||||
const id = row?.id || ids.value[0];
|
||||
|
||||
try {
|
||||
const response = await getBusinessPeriod(id);
|
||||
Object.assign(form, response.data);
|
||||
dialog.visible = true;
|
||||
dialog.title = '修改业务期间';
|
||||
dialog.type = 'edit';
|
||||
} catch (error) {
|
||||
console.error('获取详情失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 查看
|
||||
const handleView = (row: XgBusinessPeriodVO) => {
|
||||
// 实现查看逻辑
|
||||
ElMessage.info('查看功能待实现');
|
||||
};
|
||||
|
||||
// 删除
|
||||
const handleDelete = async (row?: XgBusinessPeriodVO) => {
|
||||
const periodIds = row?.id ? [row.id] : ids.value;
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要删除选中的业务期间吗?', '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
await delBusinessPeriod(periodIds);
|
||||
ElMessage.success('删除成功');
|
||||
getList();
|
||||
} catch (error) {
|
||||
console.error('删除失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 开启期间
|
||||
const handleOpen = async (row: XgBusinessPeriodVO) => {
|
||||
try {
|
||||
await ElMessageBox.confirm(`确定要开启期间"${row.periodName}"吗?`, '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
await openPeriod(row.id!);
|
||||
ElMessage.success('开启成功');
|
||||
getList();
|
||||
} catch (error) {
|
||||
console.error('开启失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 关闭期间
|
||||
const handleClose = async (row: XgBusinessPeriodVO) => {
|
||||
try {
|
||||
await ElMessageBox.confirm(`确定要关闭期间"${row.periodName}"吗?`, '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
await closePeriod(row.id!);
|
||||
ElMessage.success('关闭成功');
|
||||
getList();
|
||||
} catch (error) {
|
||||
console.error('关闭失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 切换启用状态
|
||||
const handleToggleEnabled = async (row: XgBusinessPeriodVO) => {
|
||||
try {
|
||||
await updateBusinessPeriod({
|
||||
id: row.id,
|
||||
enabled: row.enabled
|
||||
});
|
||||
ElMessage.success('状态更新成功');
|
||||
} catch (error) {
|
||||
console.error('状态更新失败:', error);
|
||||
// 恢复原状态
|
||||
row.enabled = !row.enabled;
|
||||
}
|
||||
};
|
||||
|
||||
// 提交表单
|
||||
const submitForm = async () => {
|
||||
try {
|
||||
// 时间冲突检查
|
||||
const conflictResponse = await checkPeriodTimeConflict(form);
|
||||
if (conflictResponse.data) {
|
||||
ElMessage.error('时间范围与其他期间冲突,请重新选择');
|
||||
return;
|
||||
}
|
||||
|
||||
if (dialog.type === 'add') {
|
||||
await addBusinessPeriod(form);
|
||||
ElMessage.success('新增成功');
|
||||
} else {
|
||||
await updateBusinessPeriod(form);
|
||||
ElMessage.success('修改成功');
|
||||
}
|
||||
|
||||
dialog.visible = false;
|
||||
getList();
|
||||
} catch (error) {
|
||||
console.error('提交失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 取消
|
||||
const cancel = () => {
|
||||
dialog.visible = false;
|
||||
reset();
|
||||
};
|
||||
|
||||
// 重置表单
|
||||
const reset = () => {
|
||||
Object.assign(form, {
|
||||
id: undefined,
|
||||
batchId: undefined,
|
||||
periodName: '',
|
||||
periodType: '',
|
||||
targetRole: '',
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
status: 'PENDING',
|
||||
description: '',
|
||||
sortOrder: 0,
|
||||
enabled: true
|
||||
});
|
||||
proxy.resetForm('periodFormRef');
|
||||
};
|
||||
|
||||
// 导出
|
||||
const handleExport = () => {
|
||||
proxy?.download(
|
||||
'xg/businessPeriod/export',
|
||||
queryParams,
|
||||
`业务期间_${proxy.parseTime(new Date(), '{y}{m}{d}{h}{i}{s}')}.xlsx`
|
||||
);
|
||||
};
|
||||
|
||||
// 刷新
|
||||
const handleRefresh = () => {
|
||||
getList();
|
||||
};
|
||||
|
||||
// 权限检查
|
||||
const hasPermission = (permissions: string[]) => {
|
||||
return proxy.hasPermi(permissions);
|
||||
};
|
||||
|
||||
// 获取期间类型标签类型
|
||||
const getPeriodTypeTagType = (type: string) => {
|
||||
const typeMap: Record<string, string> = {
|
||||
'STUDENT_SUBMIT': 'primary',
|
||||
'COUNSELOR_REVIEW': 'success',
|
||||
'COLLEGE_REVIEW': 'warning',
|
||||
'SCHOOL_REVIEW': 'danger'
|
||||
};
|
||||
return typeMap[type] || 'info';
|
||||
};
|
||||
|
||||
// 获取期间类型文本
|
||||
const getPeriodTypeText = (type: string) => {
|
||||
const typeMap: Record<string, string> = {
|
||||
'STUDENT_SUBMIT': '学生填报',
|
||||
'COUNSELOR_REVIEW': '辅导员审核',
|
||||
'COLLEGE_REVIEW': '学院审核',
|
||||
'SCHOOL_REVIEW': '学校审核'
|
||||
};
|
||||
return typeMap[type] || '未知';
|
||||
};
|
||||
|
||||
// 获取目标角色标签类型
|
||||
const getTargetRoleTagType = (role: string) => {
|
||||
const roleMap: Record<string, string> = {
|
||||
'STUDENT': 'primary',
|
||||
'COUNSELOR': 'success',
|
||||
'CLASS_TEACHER': 'warning',
|
||||
'COLLEGE_ADMIN': 'danger',
|
||||
'SCHOOL_ADMIN': 'info'
|
||||
};
|
||||
return roleMap[role] || 'info';
|
||||
};
|
||||
|
||||
// 获取目标角色文本
|
||||
const getTargetRoleText = (role: string) => {
|
||||
const roleMap: Record<string, string> = {
|
||||
'STUDENT': '学生',
|
||||
'COUNSELOR': '辅导员',
|
||||
'CLASS_TEACHER': '班主任',
|
||||
'COLLEGE_ADMIN': '学院管理员',
|
||||
'SCHOOL_ADMIN': '学校管理员'
|
||||
};
|
||||
return roleMap[role] || '未知';
|
||||
};
|
||||
|
||||
// 获取状态标签类型
|
||||
const getStatusTagType = (status: string) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
'PENDING': 'info',
|
||||
'ACTIVE': 'success',
|
||||
'CLOSED': 'warning'
|
||||
};
|
||||
return statusMap[status] || 'info';
|
||||
};
|
||||
|
||||
// 获取状态文本
|
||||
const getStatusText = (status: string) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
'PENDING': '未开始',
|
||||
'ACTIVE': '进行中',
|
||||
'CLOSED': '已结束'
|
||||
};
|
||||
return statusMap[status] || '未知';
|
||||
};
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
getBatchOptions();
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-container {
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.toolbar-wrapper {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.toolbar-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.toolbar-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.table-wrapper {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.pagination-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 16px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,276 +0,0 @@
|
||||
<template>
|
||||
<el-dialog :title="dialogTitle" v-model="dialogVisible" width="600px" append-to-body @close="handleClose">
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
|
||||
<el-form-item label="学生信息">
|
||||
<el-input :value="`${cadreData.studentNo} - ${cadreData.studentName}`" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="当前职务">
|
||||
<el-input :value="cadreData.positionName" disabled />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 调任操作专用字段 -->
|
||||
<template v-if="operationType === 'transfer'">
|
||||
<el-form-item label="新职务" prop="newPositionCode">
|
||||
<el-select v-model="form.newPositionCode" placeholder="请选择新职务" style="width: 100%" @change="handlePositionChange">
|
||||
<el-option v-for="item in positionOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="生效日期" prop="effectiveDate">
|
||||
<el-date-picker v-model="form.effectiveDate" type="date" placeholder="选择生效日期" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<!-- 续任操作专用字段 -->
|
||||
<template v-if="operationType === 'renew'">
|
||||
<el-form-item label="新任期时长" prop="newTermDuration">
|
||||
<el-input-number v-model="form.newTermDuration" :min="1" :max="48" placeholder="请输入新任期时长(月)" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<!-- 重新任命操作专用字段 -->
|
||||
<template v-if="operationType === 'reappoint'">
|
||||
<el-form-item label="新职务" prop="newPositionCode">
|
||||
<el-select v-model="form.newPositionCode" placeholder="请选择新职务" style="width: 100%" @change="handlePositionChange">
|
||||
<el-option v-for="item in positionOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="任职日期" prop="appointmentDate">
|
||||
<el-date-picker v-model="form.appointmentDate" type="date" placeholder="选择任职日期" style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="任期开始日期" prop="termStartDate">
|
||||
<el-date-picker v-model="form.termStartDate" type="date" placeholder="选择任期开始日期" style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="任期时长(月)" prop="termDuration">
|
||||
<el-input-number v-model="form.termDuration" :min="1" :max="48" placeholder="请输入任期时长" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<el-form-item label="操作日期" prop="operationDate">
|
||||
<el-date-picker v-model="form.operationDate" type="date" placeholder="选择操作日期" style="width: 100%" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="reasonLabel" prop="reason">
|
||||
<el-input v-model="form.reason" type="textarea" :rows="3" :placeholder="reasonPlaceholder" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="操作备注" prop="operationRemark">
|
||||
<el-input v-model="form.operationRemark" type="textarea" :rows="2" placeholder="请输入操作备注" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="handleClose">取 消</el-button>
|
||||
<el-button type="primary" @click="handleConfirm">确 定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, watch } from 'vue';
|
||||
import type { FormInstance, FormRules } from 'element-plus';
|
||||
import type { XgClassCadreVO } from '@/api/xg-xx/types';
|
||||
|
||||
interface Props {
|
||||
visible: boolean;
|
||||
operationType: string;
|
||||
cadreData: XgClassCadreVO;
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: 'update:visible', value: boolean): void;
|
||||
(e: 'confirm', data: any): void;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const formRef = ref<FormInstance>();
|
||||
|
||||
const form = reactive({
|
||||
operationDate: new Date().toISOString().split('T')[0],
|
||||
reason: '',
|
||||
operationRemark: '',
|
||||
newPositionCode: '',
|
||||
newPositionName: '',
|
||||
newTermDuration: 12,
|
||||
effectiveDate: '',
|
||||
appointmentDate: '',
|
||||
termStartDate: '',
|
||||
termDuration: 12
|
||||
});
|
||||
|
||||
const positionOptions = ref([
|
||||
{ value: 'class_monitor', label: '班长' },
|
||||
{ value: 'deputy_monitor', label: '副班长' },
|
||||
{ value: 'study_committee', label: '学习委员' },
|
||||
{ value: 'organization_committee', label: '组织委员' },
|
||||
{ value: 'publicity_committee', label: '宣传委员' },
|
||||
{ value: 'sports_committee', label: '体育委员' },
|
||||
{ value: 'life_committee', label: '生活委员' },
|
||||
{ value: 'literary_committee', label: '文艺委员' },
|
||||
{ value: 'discipline_committee', label: '纪律委员' },
|
||||
{ value: 'psychology_committee', label: '心理委员' }
|
||||
]);
|
||||
|
||||
const dialogVisible = computed({
|
||||
get: () => props.visible,
|
||||
set: (value) => emit('update:visible', value)
|
||||
});
|
||||
|
||||
const dialogTitle = computed(() => {
|
||||
switch (props.operationType) {
|
||||
case 'dismiss':
|
||||
return '免职操作';
|
||||
case 'transfer':
|
||||
return '调任操作';
|
||||
case 'renew':
|
||||
return '续任操作';
|
||||
case 'resign':
|
||||
return '辞职操作';
|
||||
case 'reappoint':
|
||||
return '重新任命';
|
||||
default:
|
||||
return '操作';
|
||||
}
|
||||
});
|
||||
|
||||
const reasonLabel = computed(() => {
|
||||
switch (props.operationType) {
|
||||
case 'dismiss':
|
||||
return '免职原因';
|
||||
case 'transfer':
|
||||
return '调任原因';
|
||||
case 'renew':
|
||||
return '续任原因';
|
||||
case 'resign':
|
||||
return '辞职原因';
|
||||
case 'reappoint':
|
||||
return '任命原因';
|
||||
default:
|
||||
return '操作原因';
|
||||
}
|
||||
});
|
||||
|
||||
const reasonPlaceholder = computed(() => {
|
||||
switch (props.operationType) {
|
||||
case 'dismiss':
|
||||
return '请输入免职原因';
|
||||
case 'transfer':
|
||||
return '请输入调任原因';
|
||||
case 'renew':
|
||||
return '请输入续任原因';
|
||||
case 'resign':
|
||||
return '请输入辞职原因';
|
||||
case 'reappoint':
|
||||
return '请输入任命原因';
|
||||
default:
|
||||
return '请输入操作原因';
|
||||
}
|
||||
});
|
||||
|
||||
const rules = computed<FormRules>(() => {
|
||||
const baseRules = {
|
||||
operationDate: [{ required: true, message: '操作日期不能为空', trigger: 'change' }],
|
||||
reason: [{ required: true, message: `${reasonLabel.value}不能为空`, trigger: 'blur' }]
|
||||
};
|
||||
|
||||
if (props.operationType === 'transfer') {
|
||||
baseRules.newPositionCode = [{ required: true, message: '新职务不能为空', trigger: 'change' }];
|
||||
baseRules.effectiveDate = [{ required: true, message: '生效日期不能为空', trigger: 'change' }];
|
||||
}
|
||||
|
||||
if (props.operationType === 'renew') {
|
||||
baseRules.newTermDuration = [{ required: true, message: '新任期时长不能为空', trigger: 'blur' }];
|
||||
}
|
||||
|
||||
if (props.operationType === 'reappoint') {
|
||||
baseRules.newPositionCode = [{ required: true, message: '新职务不能为空', trigger: 'change' }];
|
||||
baseRules.appointmentDate = [{ required: true, message: '任职日期不能为空', trigger: 'change' }];
|
||||
baseRules.termStartDate = [{ required: true, message: '任期开始日期不能为空', trigger: 'change' }];
|
||||
baseRules.termDuration = [{ required: true, message: '任期时长不能为空', trigger: 'blur' }];
|
||||
}
|
||||
|
||||
return baseRules;
|
||||
});
|
||||
|
||||
const handlePositionChange = (positionCode: string) => {
|
||||
const position = positionOptions.value.find((item) => item.value === positionCode);
|
||||
if (position) {
|
||||
form.newPositionName = position.label;
|
||||
}
|
||||
};
|
||||
|
||||
const handleConfirm = () => {
|
||||
formRef.value?.validate((valid) => {
|
||||
if (valid) {
|
||||
const operationData = {
|
||||
type: props.operationType,
|
||||
cadreId: props.cadreData.id,
|
||||
operationDate: form.operationDate,
|
||||
reason: form.reason,
|
||||
operationRemark: form.operationRemark
|
||||
};
|
||||
|
||||
if (props.operationType === 'transfer') {
|
||||
operationData.newPositionCode = form.newPositionCode;
|
||||
operationData.newPositionName = form.newPositionName;
|
||||
operationData.effectiveDate = form.effectiveDate;
|
||||
}
|
||||
|
||||
if (props.operationType === 'renew') {
|
||||
operationData.newTermDuration = form.newTermDuration;
|
||||
}
|
||||
|
||||
if (props.operationType === 'reappoint') {
|
||||
operationData.newPositionCode = form.newPositionCode;
|
||||
operationData.newPositionName = form.newPositionName;
|
||||
operationData.appointmentDate = form.appointmentDate;
|
||||
operationData.termStartDate = form.termStartDate;
|
||||
operationData.termDuration = form.termDuration;
|
||||
}
|
||||
|
||||
emit('confirm', operationData);
|
||||
handleClose();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
dialogVisible.value = false;
|
||||
resetForm();
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
Object.assign(form, {
|
||||
operationDate: new Date().toISOString().split('T')[0],
|
||||
reason: '',
|
||||
operationRemark: '',
|
||||
newPositionCode: '',
|
||||
newPositionName: '',
|
||||
newTermDuration: 12,
|
||||
effectiveDate: '',
|
||||
appointmentDate: '',
|
||||
termStartDate: '',
|
||||
termDuration: 12
|
||||
});
|
||||
formRef.value?.resetFields();
|
||||
};
|
||||
|
||||
// 监听对话框打开,重置表单
|
||||
watch(
|
||||
() => props.visible,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
resetForm();
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dialog-footer {
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
@@ -1,771 +0,0 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!-- 搜索区域 -->
|
||||
<el-form v-show="showSearch" ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
|
||||
<el-form-item label="班级" prop="classId">
|
||||
<el-select v-model="queryParams.classId" placeholder="请选择班级" clearable style="width: 200px">
|
||||
<el-option v-for="item in classOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="学号" prop="studentNo">
|
||||
<el-input v-model="queryParams.studentNo" placeholder="请输入学号" clearable style="width: 200px" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="姓名" prop="studentName">
|
||||
<el-input v-model="queryParams.studentName" placeholder="请输入学生姓名" clearable style="width: 200px" @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="职务" prop="positionCode">
|
||||
<el-select v-model="queryParams.positionCode" placeholder="请选择职务" clearable style="width: 200px">
|
||||
<el-option v-for="item in positionOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="任职状态" prop="cadreStatus">
|
||||
<el-select v-model="queryParams.cadreStatus" placeholder="请选择任职状态" clearable style="width: 200px">
|
||||
<el-option label="在任" value="1" />
|
||||
<el-option label="到期" value="2" />
|
||||
<el-option label="辞职" value="3" />
|
||||
<el-option label="免职" value="4" />
|
||||
<el-option label="调任" value="5" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否现任" prop="isCurrent">
|
||||
<el-select v-model="queryParams.isCurrent" placeholder="请选择是否现任" clearable style="width: 200px">
|
||||
<el-option label="是" :value="true" />
|
||||
<el-option label="否" :value="false" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<!-- 工具栏 -->
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['xg:classCadre:add']">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['xg:classCadre:edit']">修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['xg:classCadre:remove']"
|
||||
>删除</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['xg:classCadre:export']">导出</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="info" plain icon="Upload" @click="handleImport" v-hasPermi="['xg:classCadre:import']">导入</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Clock" @click="handleExpiring">到期提醒</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<!-- 表格数据 -->
|
||||
<el-table v-loading="loading" :data="classCadreList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="序号" type="index" width="50" align="center" />
|
||||
<el-table-column label="学号" align="center" prop="studentNo" min-width="120" />
|
||||
<el-table-column label="姓名" align="center" prop="studentName" min-width="100" />
|
||||
<el-table-column label="班级" align="center" prop="className" min-width="150" />
|
||||
<el-table-column label="职务" align="center" prop="positionName" min-width="120">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getPositionTagType(scope.row.positionLevel)">
|
||||
{{ scope.row.positionName }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="任职状态" align="center" prop="cadreStatus" min-width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getCadreStatusTagType(scope.row.cadreStatus)">
|
||||
{{ getCadreStatusText(scope.row.cadreStatus) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="是否现任" align="center" prop="isCurrent" min-width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.isCurrent ? 'success' : 'info'">
|
||||
{{ scope.row.isCurrent ? '是' : '否' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="任职日期" align="center" prop="appointmentDate" min-width="120" />
|
||||
<el-table-column label="任期结束" align="center" prop="termEndDate" min-width="120">
|
||||
<template #default="scope">
|
||||
<span v-if="scope.row.termEndDate">
|
||||
{{ scope.row.termEndDate }}
|
||||
<el-tag v-if="scope.row.daysRemaining <= 30 && scope.row.daysRemaining > 0" type="warning" size="small">
|
||||
{{ scope.row.daysRemaining }}天到期
|
||||
</el-tag>
|
||||
<el-tag v-else-if="scope.row.daysRemaining <= 0" type="danger" size="small"> 已到期 </el-tag>
|
||||
</span>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="工作表现" align="center" prop="workPerformance" min-width="100">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.workPerformance" :type="getPerformanceTagType(scope.row.workPerformance)">
|
||||
{{ getPerformanceText(scope.row.workPerformance) }}
|
||||
</el-tag>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" min-width="280">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="查看详情" placement="top">
|
||||
<el-button link type="primary" icon="View" @click="handleView(scope.row)" v-hasPermi="['xg:classCadre:query']" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="编辑" placement="top">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['xg:classCadre:edit']" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['xg:classCadre:remove']" />
|
||||
</el-tooltip>
|
||||
|
||||
<!-- 任免操作按钮 -->
|
||||
<el-dropdown v-if="scope.row.isCurrent" @command="(command) => handleCadreOperation(command, scope.row)">
|
||||
<el-button link type="warning" icon="Operation">
|
||||
任免 <el-icon class="el-icon--right"><arrow-down /></el-icon>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item command="dismiss" v-hasPermi="['xg:classCadre:dismiss']">免职</el-dropdown-item>
|
||||
<el-dropdown-item command="transfer" v-hasPermi="['xg:classCadre:transfer']">调任</el-dropdown-item>
|
||||
<el-dropdown-item command="renew" v-hasPermi="['xg:classCadre:renew']">续任</el-dropdown-item>
|
||||
<el-dropdown-item command="resign" v-hasPermi="['xg:classCadre:resign']">辞职</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
|
||||
<!-- 重新任命按钮 -->
|
||||
<el-tooltip v-if="!scope.row.isCurrent" content="重新任命" placement="top">
|
||||
<el-button link type="success" icon="UserFilled" @click="handleReappoint(scope.row)" v-hasPermi="['xg:classCadre:appoint']" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页 -->
|
||||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||||
|
||||
<!-- 添加或修改班级干部信息对话框 -->
|
||||
<el-dialog :title="dialog.title" v-model="dialog.visible" width="800px" append-to-body>
|
||||
<el-form ref="classCadreFormRef" :model="form" :rules="rules" label-width="120px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="班级" prop="classId">
|
||||
<el-select v-model="form.classId" placeholder="请选择班级" style="width: 100%">
|
||||
<el-option v-for="item in classOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="学生" prop="studentId">
|
||||
<el-select
|
||||
v-model="form.studentId"
|
||||
placeholder="请选择学生"
|
||||
filterable
|
||||
remote
|
||||
:remote-method="remoteStudentMethod"
|
||||
:loading="studentLoading"
|
||||
style="width: 100%"
|
||||
@change="handleStudentChange"
|
||||
>
|
||||
<el-option v-for="item in studentOptions" :key="item.value" :label="`${item.studentNo} - ${item.label}`" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="职务" prop="positionCode">
|
||||
<el-select v-model="form.positionCode" placeholder="请选择职务" style="width: 100%" @change="handlePositionChange">
|
||||
<el-option v-for="item in positionOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="任职日期" prop="appointmentDate">
|
||||
<el-date-picker v-model="form.appointmentDate" type="date" placeholder="选择任职日期" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="任期开始日期" prop="termStartDate">
|
||||
<el-date-picker v-model="form.termStartDate" type="date" placeholder="选择任期开始日期" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="任期时长(月)" prop="termDuration">
|
||||
<el-input-number v-model="form.termDuration" :min="1" :max="48" placeholder="请输入任期时长" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="是否兼职" prop="isConcurrent">
|
||||
<el-radio-group v-model="form.isConcurrent">
|
||||
<el-radio :value="false">否</el-radio>
|
||||
<el-radio :value="true">是</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="任命原因" prop="appointmentReason">
|
||||
<el-input v-model="form.appointmentReason" placeholder="请输入任命原因" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="联系电话" prop="contactPhone">
|
||||
<el-input v-model="form.contactPhone" placeholder="请输入联系电话" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="联系邮箱" prop="contactEmail">
|
||||
<el-input v-model="form.contactEmail" placeholder="请输入联系邮箱" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item label="工作计划" prop="workPlan">
|
||||
<el-input v-model="form.workPlan" type="textarea" :rows="3" placeholder="请输入工作计划" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" :rows="2" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 查看详情对话框 -->
|
||||
<el-dialog title="班干部详情" v-model="viewDialog.visible" width="900px" append-to-body>
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="学号">{{ viewData.studentNo }}</el-descriptions-item>
|
||||
<el-descriptions-item label="姓名">{{ viewData.studentName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="班级">{{ viewData.className }}</el-descriptions-item>
|
||||
<el-descriptions-item label="职务">{{ viewData.positionName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="任职日期">{{ viewData.appointmentDate }}</el-descriptions-item>
|
||||
<el-descriptions-item label="任期开始">{{ viewData.termStartDate }}</el-descriptions-item>
|
||||
<el-descriptions-item label="任期结束">{{ viewData.termEndDate || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="任期时长">{{ viewData.termDuration }}个月</el-descriptions-item>
|
||||
<el-descriptions-item label="是否现任">
|
||||
<el-tag :type="viewData.isCurrent ? 'success' : 'info'">
|
||||
{{ viewData.isCurrent ? '是' : '否' }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="任职状态">
|
||||
<el-tag :type="getCadreStatusTagType(viewData.cadreStatus)">
|
||||
{{ getCadreStatusText(viewData.cadreStatus) }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="是否兼职">
|
||||
{{ viewData.isConcurrent ? '是' : '否' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="工作表现">
|
||||
<el-tag v-if="viewData.workPerformance" :type="getPerformanceTagType(viewData.workPerformance)">
|
||||
{{ getPerformanceText(viewData.workPerformance) }}
|
||||
</el-tag>
|
||||
<span v-else>-</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="任命原因" :span="2">{{ viewData.appointmentReason || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="联系电话">{{ viewData.contactPhone || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="联系邮箱">{{ viewData.contactEmail || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="工作计划" :span="2">{{ viewData.workPlan || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="主要成就" :span="2">{{ viewData.achievements || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="备注" :span="2">{{ viewData.remark || '-' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 任免操作对话框 -->
|
||||
<CadreOperationDialog
|
||||
v-model:visible="operationDialog.visible"
|
||||
:operation-type="operationDialog.type"
|
||||
:cadre-data="operationDialog.cadreData"
|
||||
@confirm="handleOperationConfirm"
|
||||
/>
|
||||
|
||||
<!-- 导入对话框 -->
|
||||
<ImportDialog
|
||||
v-model:visible="importDialog.visible"
|
||||
:upload-url="'/xg/classCadre/import'"
|
||||
:template-url="'/xg/classCadre/import-template'"
|
||||
@success="handleImportSuccess"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="ClassCadre" lang="ts">
|
||||
import { ref, reactive, onMounted, getCurrentInstance } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import type { FormInstance, FormRules } from 'element-plus';
|
||||
import {
|
||||
listClassCadre,
|
||||
getClassCadre,
|
||||
addClassCadre,
|
||||
updateClassCadre,
|
||||
delClassCadre,
|
||||
exportClassCadre,
|
||||
dismissCadre,
|
||||
transferCadre,
|
||||
renewCadre,
|
||||
resignCadre,
|
||||
getExpiringCadres
|
||||
} from '@/api/xg-xx/classCadre';
|
||||
import { listClassInfo } from '@/api/xg-xx/classInfo';
|
||||
import { listStudentInfo } from '@/api/xg-xx/studentInfo';
|
||||
import type { XgClassCadreVO, XgClassCadreForm, XgClassCadreQuery } from '@/api/xg-xx/types';
|
||||
import CadreOperationDialog from './components/CadreOperationDialog.vue';
|
||||
import ImportDialog from '@/components/ImportDialog/index.vue';
|
||||
|
||||
const { proxy } = getCurrentInstance()!;
|
||||
|
||||
// 数据
|
||||
const classCadreList = ref<XgClassCadreVO[]>([]);
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref<Array<string | number>>([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
|
||||
// 查询参数
|
||||
const queryParams = reactive<XgClassCadreQuery>({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
classId: undefined,
|
||||
studentNo: undefined,
|
||||
studentName: undefined,
|
||||
positionCode: undefined,
|
||||
cadreStatus: undefined,
|
||||
isCurrent: undefined
|
||||
});
|
||||
|
||||
// 表单相关
|
||||
const classCadreFormRef = ref<FormInstance>();
|
||||
const dialog = reactive({
|
||||
visible: false,
|
||||
title: ''
|
||||
});
|
||||
|
||||
const form = reactive<XgClassCadreForm>({
|
||||
id: undefined,
|
||||
classId: undefined,
|
||||
studentId: undefined,
|
||||
positionCode: undefined,
|
||||
appointmentDate: undefined,
|
||||
termStartDate: undefined,
|
||||
termDuration: 12,
|
||||
isConcurrent: false,
|
||||
appointmentReason: undefined,
|
||||
contactPhone: undefined,
|
||||
contactEmail: undefined,
|
||||
workPlan: undefined,
|
||||
remark: undefined
|
||||
});
|
||||
|
||||
const rules = reactive<FormRules<XgClassCadreForm>>({
|
||||
classId: [{ required: true, message: '班级不能为空', trigger: 'change' }],
|
||||
studentId: [{ required: true, message: '学生不能为空', trigger: 'change' }],
|
||||
positionCode: [{ required: true, message: '职务不能为空', trigger: 'change' }],
|
||||
appointmentDate: [{ required: true, message: '任职日期不能为空', trigger: 'blur' }],
|
||||
termStartDate: [{ required: true, message: '任期开始日期不能为空', trigger: 'blur' }],
|
||||
termDuration: [{ required: true, message: '任期时长不能为空', trigger: 'blur' }]
|
||||
});
|
||||
|
||||
// 选项数据
|
||||
const classOptions = ref<Array<{ value: string | number; label: string }>>([]);
|
||||
const studentOptions = ref<Array<{ value: string | number; label: string; studentNo: string }>>([]);
|
||||
const positionOptions = ref([
|
||||
{ value: 'class_monitor', label: '班长' },
|
||||
{ value: 'deputy_monitor', label: '副班长' },
|
||||
{ value: 'study_committee', label: '学习委员' },
|
||||
{ value: 'organization_committee', label: '组织委员' },
|
||||
{ value: 'publicity_committee', label: '宣传委员' },
|
||||
{ value: 'sports_committee', label: '体育委员' },
|
||||
{ value: 'life_committee', label: '生活委员' },
|
||||
{ value: 'literary_committee', label: '文艺委员' },
|
||||
{ value: 'discipline_committee', label: '纪律委员' },
|
||||
{ value: 'psychology_committee', label: '心理委员' }
|
||||
]);
|
||||
|
||||
const studentLoading = ref(false);
|
||||
|
||||
// 查看详情
|
||||
const viewDialog = reactive({
|
||||
visible: false
|
||||
});
|
||||
const viewData = ref<XgClassCadreVO>({} as XgClassCadreVO);
|
||||
|
||||
// 任免操作
|
||||
const operationDialog = reactive({
|
||||
visible: false,
|
||||
type: '',
|
||||
cadreData: {} as XgClassCadreVO
|
||||
});
|
||||
|
||||
// 导入
|
||||
const importDialog = reactive({
|
||||
visible: false
|
||||
});
|
||||
|
||||
// 查询表单引用
|
||||
const queryFormRef = ref<FormInstance>();
|
||||
|
||||
/** 查询班级干部信息列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await listClassCadre(queryParams);
|
||||
classCadreList.value = res.rows;
|
||||
total.value = res.total;
|
||||
} catch (error) {
|
||||
console.error('获取班级干部信息列表失败:', error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value?.resetFields();
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
/** 多选框选中数据 */
|
||||
const handleSelectionChange = (selection: XgClassCadreVO[]) => {
|
||||
ids.value = selection.map((item) => item.id);
|
||||
single.value = selection.length !== 1;
|
||||
multiple.value = !selection.length;
|
||||
};
|
||||
|
||||
/** 新增按钮操作 */
|
||||
const handleAdd = () => {
|
||||
reset();
|
||||
dialog.visible = true;
|
||||
dialog.title = '添加班级干部信息';
|
||||
};
|
||||
|
||||
/** 修改按钮操作 */
|
||||
const handleUpdate = async (row?: XgClassCadreVO) => {
|
||||
reset();
|
||||
const id = row?.id || ids.value[0];
|
||||
try {
|
||||
const res = await getClassCadre(id);
|
||||
Object.assign(form, res.data);
|
||||
dialog.visible = true;
|
||||
dialog.title = '修改班级干部信息';
|
||||
} catch (error) {
|
||||
console.error('获取班级干部信息失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
/** 查看详情 */
|
||||
const handleView = async (row: XgClassCadreVO) => {
|
||||
try {
|
||||
const res = await getClassCadre(row.id);
|
||||
viewData.value = res.data;
|
||||
viewDialog.visible = true;
|
||||
} catch (error) {
|
||||
console.error('获取班级干部信息失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
/** 提交按钮 */
|
||||
const submitForm = () => {
|
||||
classCadreFormRef.value?.validate(async (valid) => {
|
||||
if (valid) {
|
||||
try {
|
||||
if (form.id) {
|
||||
await updateClassCadre(form);
|
||||
ElMessage.success('修改成功');
|
||||
} else {
|
||||
await addClassCadre(form);
|
||||
ElMessage.success('新增成功');
|
||||
}
|
||||
dialog.visible = false;
|
||||
getList();
|
||||
} catch (error) {
|
||||
console.error('提交失败:', error);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (row?: XgClassCadreVO) => {
|
||||
const deleteIds = row?.id ? [row.id] : ids.value;
|
||||
try {
|
||||
await ElMessageBox.confirm('是否确认删除班级干部信息编号为"' + deleteIds + '"的数据项?', '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
await delClassCadre(deleteIds);
|
||||
ElMessage.success('删除成功');
|
||||
getList();
|
||||
} catch (error) {
|
||||
console.error('删除失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = () => {
|
||||
proxy?.download(
|
||||
'/xg/classCadre/export',
|
||||
{
|
||||
...queryParams
|
||||
},
|
||||
`class_cadre_${new Date().getTime()}.xlsx`
|
||||
);
|
||||
};
|
||||
|
||||
/** 导入按钮操作 */
|
||||
const handleImport = () => {
|
||||
importDialog.visible = true;
|
||||
};
|
||||
|
||||
/** 导入成功回调 */
|
||||
const handleImportSuccess = () => {
|
||||
getList();
|
||||
};
|
||||
|
||||
/** 到期提醒 */
|
||||
const handleExpiring = async () => {
|
||||
try {
|
||||
const res = await getExpiringCadres(30);
|
||||
if (res.data.length > 0) {
|
||||
ElMessage.warning(`有 ${res.data.length} 名班干部即将到期,请及时处理`);
|
||||
} else {
|
||||
ElMessage.success('暂无即将到期的班干部');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取到期提醒失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
/** 任免操作 */
|
||||
const handleCadreOperation = (command: string, row: XgClassCadreVO) => {
|
||||
operationDialog.type = command;
|
||||
operationDialog.cadreData = row;
|
||||
operationDialog.visible = true;
|
||||
};
|
||||
|
||||
/** 重新任命 */
|
||||
const handleReappoint = (row: XgClassCadreVO) => {
|
||||
handleCadreOperation('reappoint', row);
|
||||
};
|
||||
|
||||
/** 任免操作确认 */
|
||||
const handleOperationConfirm = async (operationData: any) => {
|
||||
try {
|
||||
switch (operationData.type) {
|
||||
case 'dismiss':
|
||||
await dismissCadre(operationData.cadreId, operationData.reason, operationData.operationDate);
|
||||
ElMessage.success('免职成功');
|
||||
break;
|
||||
case 'transfer':
|
||||
await transferCadre(
|
||||
operationData.cadreId,
|
||||
operationData.newPositionCode,
|
||||
operationData.newPositionName,
|
||||
operationData.reason,
|
||||
operationData.effectiveDate
|
||||
);
|
||||
ElMessage.success('调任成功');
|
||||
break;
|
||||
case 'renew':
|
||||
await renewCadre(operationData.cadreId, operationData.newTermDuration, operationData.reason);
|
||||
ElMessage.success('续任成功');
|
||||
break;
|
||||
case 'resign':
|
||||
await resignCadre(operationData.cadreId, operationData.reason, operationData.operationDate);
|
||||
ElMessage.success('辞职成功');
|
||||
break;
|
||||
}
|
||||
getList();
|
||||
} catch (error) {
|
||||
console.error('操作失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
/** 表单重置 */
|
||||
const reset = () => {
|
||||
Object.assign(form, {
|
||||
id: undefined,
|
||||
classId: undefined,
|
||||
studentId: undefined,
|
||||
positionCode: undefined,
|
||||
appointmentDate: undefined,
|
||||
termStartDate: undefined,
|
||||
termDuration: 12,
|
||||
isConcurrent: false,
|
||||
appointmentReason: undefined,
|
||||
contactPhone: undefined,
|
||||
contactEmail: undefined,
|
||||
workPlan: undefined,
|
||||
remark: undefined
|
||||
});
|
||||
classCadreFormRef.value?.resetFields();
|
||||
};
|
||||
|
||||
/** 取消按钮 */
|
||||
const cancel = () => {
|
||||
dialog.visible = false;
|
||||
reset();
|
||||
};
|
||||
|
||||
/** 远程搜索学生 */
|
||||
const remoteStudentMethod = async (query: string) => {
|
||||
if (query) {
|
||||
studentLoading.value = true;
|
||||
try {
|
||||
const res = await listStudentInfo({ studentNo: query, name: query, pageSize: 20 });
|
||||
studentOptions.value = res.rows.map((item) => ({
|
||||
value: item.id,
|
||||
label: item.name,
|
||||
studentNo: item.studentNo
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error('搜索学生失败:', error);
|
||||
} finally {
|
||||
studentLoading.value = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** 学生选择变化 */
|
||||
const handleStudentChange = (studentId: string | number) => {
|
||||
const student = studentOptions.value.find((item) => item.value === studentId);
|
||||
if (student) {
|
||||
form.studentNo = student.studentNo;
|
||||
form.studentName = student.label;
|
||||
}
|
||||
};
|
||||
|
||||
/** 职务选择变化 */
|
||||
const handlePositionChange = (positionCode: string) => {
|
||||
const position = positionOptions.value.find((item) => item.value === positionCode);
|
||||
if (position) {
|
||||
form.positionName = position.label;
|
||||
}
|
||||
};
|
||||
|
||||
/** 获取班级选项 */
|
||||
const getClassOptions = async () => {
|
||||
try {
|
||||
const res = await listClassInfo({ pageSize: 1000 });
|
||||
classOptions.value = res.rows.map((item) => ({
|
||||
value: item.id,
|
||||
label: `${item.classCode} - ${item.className}`
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error('获取班级选项失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 辅助方法
|
||||
const getPositionTagType = (level: number) => {
|
||||
switch (level) {
|
||||
case 1:
|
||||
return 'danger'; // 班长
|
||||
case 2:
|
||||
return 'warning'; // 副班长
|
||||
case 3:
|
||||
return 'success'; // 委员
|
||||
default:
|
||||
return 'info'; // 普通成员
|
||||
}
|
||||
};
|
||||
|
||||
const getCadreStatusTagType = (status: string) => {
|
||||
switch (status) {
|
||||
case '1':
|
||||
return 'success'; // 在任
|
||||
case '2':
|
||||
return 'warning'; // 到期
|
||||
case '3':
|
||||
return 'info'; // 辞职
|
||||
case '4':
|
||||
return 'danger'; // 免职
|
||||
case '5':
|
||||
return 'primary'; // 调任
|
||||
default:
|
||||
return 'info';
|
||||
}
|
||||
};
|
||||
|
||||
const getCadreStatusText = (status: string) => {
|
||||
switch (status) {
|
||||
case '1':
|
||||
return '在任';
|
||||
case '2':
|
||||
return '到期';
|
||||
case '3':
|
||||
return '辞职';
|
||||
case '4':
|
||||
return '免职';
|
||||
case '5':
|
||||
return '调任';
|
||||
default:
|
||||
return '未知';
|
||||
}
|
||||
};
|
||||
|
||||
const getPerformanceTagType = (performance: string) => {
|
||||
switch (performance) {
|
||||
case 'excellent':
|
||||
return 'success';
|
||||
case 'good':
|
||||
return 'primary';
|
||||
case 'average':
|
||||
return 'warning';
|
||||
case 'poor':
|
||||
return 'danger';
|
||||
default:
|
||||
return 'info';
|
||||
}
|
||||
};
|
||||
|
||||
const getPerformanceText = (performance: string) => {
|
||||
switch (performance) {
|
||||
case 'excellent':
|
||||
return '优秀';
|
||||
case 'good':
|
||||
return '良好';
|
||||
case 'average':
|
||||
return '一般';
|
||||
case 'poor':
|
||||
return '较差';
|
||||
default:
|
||||
return '未知';
|
||||
}
|
||||
};
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
getList();
|
||||
getClassOptions();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.app-container {
|
||||
padding: 20px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,504 +0,0 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-row :gutter="20">
|
||||
<!-- 统计卡片 -->
|
||||
<el-col :span="6" v-for="item in statisticsCards" :key="item.key">
|
||||
<el-card class="statistics-card">
|
||||
<div class="statistics-content">
|
||||
<div class="statistics-icon" :style="{ backgroundColor: item.color }">
|
||||
<el-icon :size="24">
|
||||
<component :is="item.icon" />
|
||||
</el-icon>
|
||||
</div>
|
||||
<div class="statistics-info">
|
||||
<div class="statistics-title">{{ item.title }}</div>
|
||||
<div class="statistics-value">{{ item.value }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20" class="mt-4">
|
||||
<!-- 职务分布图 -->
|
||||
<el-col :span="12">
|
||||
<el-card>
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>职务分布</span>
|
||||
</div>
|
||||
</template>
|
||||
<div ref="positionChartRef" style="height: 300px"></div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
||||
<!-- 任职状态分布图 -->
|
||||
<el-col :span="12">
|
||||
<el-card>
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>任职状态分布</span>
|
||||
</div>
|
||||
</template>
|
||||
<div ref="statusChartRef" style="height: 300px"></div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20" class="mt-4">
|
||||
<!-- 学院统计 -->
|
||||
<el-col :span="12">
|
||||
<el-card>
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>各学院班干部数量</span>
|
||||
</div>
|
||||
</template>
|
||||
<div ref="collegeChartRef" style="height: 300px"></div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
||||
<!-- 工作表现分布 -->
|
||||
<el-col :span="12">
|
||||
<el-card>
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>工作表现分布</span>
|
||||
</div>
|
||||
</template>
|
||||
<div ref="performanceChartRef" style="height: 300px"></div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20" class="mt-4">
|
||||
<!-- 到期提醒列表 -->
|
||||
<el-col :span="24">
|
||||
<el-card>
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>即将到期的班干部</span>
|
||||
<el-button type="primary" size="small" @click="refreshExpiringList">刷新</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<el-table :data="expiringList" style="width: 100%">
|
||||
<el-table-column prop="studentNo" label="学号" width="120" />
|
||||
<el-table-column prop="studentName" label="姓名" width="100" />
|
||||
<el-table-column prop="className" label="班级" width="150" />
|
||||
<el-table-column prop="positionName" label="职务" width="120" />
|
||||
<el-table-column prop="termEndDate" label="任期结束日期" width="120" />
|
||||
<el-table-column prop="daysRemaining" label="剩余天数" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.daysRemaining <= 7 ? 'danger' : 'warning'"> {{ scope.row.daysRemaining }}天 </el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="contactPhone" label="联系电话" width="120" />
|
||||
<el-table-column label="操作" width="120">
|
||||
<template #default="scope">
|
||||
<el-button type="primary" size="small" @click="handleContact(scope.row)"> 联系 </el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="ClassCadreStatistics" lang="ts">
|
||||
import { ref, reactive, onMounted, nextTick } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { User, UserFilled, Trophy, Clock } from '@element-plus/icons-vue';
|
||||
import { listClassCadre, getExpiringCadres, getCadresByCollege, getCadresByPerformance } from '@/api/xg-xx/classCadre';
|
||||
import type { XgClassCadreVO } from '@/api/xg-xx/types';
|
||||
|
||||
// 统计卡片数据
|
||||
const statisticsCards = ref([
|
||||
{
|
||||
key: 'total',
|
||||
title: '总班干部数',
|
||||
value: 0,
|
||||
icon: User,
|
||||
color: '#409EFF'
|
||||
},
|
||||
{
|
||||
key: 'current',
|
||||
title: '在任班干部',
|
||||
value: 0,
|
||||
icon: UserFilled,
|
||||
color: '#67C23A'
|
||||
},
|
||||
{
|
||||
key: 'excellent',
|
||||
title: '优秀班干部',
|
||||
value: 0,
|
||||
icon: Trophy,
|
||||
color: '#E6A23C'
|
||||
},
|
||||
{
|
||||
key: 'expiring',
|
||||
title: '即将到期',
|
||||
value: 0,
|
||||
icon: Clock,
|
||||
color: '#F56C6C'
|
||||
}
|
||||
]);
|
||||
|
||||
// 图表引用
|
||||
const positionChartRef = ref<HTMLDivElement>();
|
||||
const statusChartRef = ref<HTMLDivElement>();
|
||||
const collegeChartRef = ref<HTMLDivElement>();
|
||||
const performanceChartRef = ref<HTMLDivElement>();
|
||||
|
||||
// 到期提醒列表
|
||||
const expiringList = ref<XgClassCadreVO[]>([]);
|
||||
|
||||
// 图表实例
|
||||
let positionChart: echarts.ECharts;
|
||||
let statusChart: echarts.ECharts;
|
||||
let collegeChart: echarts.ECharts;
|
||||
let performanceChart: echarts.ECharts;
|
||||
|
||||
/** 加载统计数据 */
|
||||
const loadStatistics = async () => {
|
||||
try {
|
||||
// 获取总班干部数据
|
||||
const totalRes = await listClassCadre({ pageSize: 10000 });
|
||||
const totalData = totalRes.rows;
|
||||
|
||||
// 统计总数
|
||||
statisticsCards.value[0].value = totalData.length;
|
||||
|
||||
// 统计在任班干部
|
||||
const currentCadres = totalData.filter((item) => item.isCurrent);
|
||||
statisticsCards.value[1].value = currentCadres.length;
|
||||
|
||||
// 统计优秀班干部
|
||||
const excellentCadres = totalData.filter((item) => item.workPerformance === 'excellent');
|
||||
statisticsCards.value[2].value = excellentCadres.length;
|
||||
|
||||
// 统计即将到期
|
||||
const expiringRes = await getExpiringCadres(30);
|
||||
statisticsCards.value[3].value = expiringRes.data.length;
|
||||
expiringList.value = expiringRes.data;
|
||||
|
||||
// 初始化图表
|
||||
await nextTick();
|
||||
initPositionChart(totalData);
|
||||
initStatusChart(totalData);
|
||||
initCollegeChart(totalData);
|
||||
initPerformanceChart(totalData);
|
||||
} catch (error) {
|
||||
console.error('加载统计数据失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
/** 初始化职务分布图 */
|
||||
const initPositionChart = (data: XgClassCadreVO[]) => {
|
||||
if (!positionChartRef.value) return;
|
||||
|
||||
positionChart = echarts.init(positionChartRef.value);
|
||||
|
||||
// 统计职务分布
|
||||
const positionMap = new Map();
|
||||
data.forEach((item) => {
|
||||
const count = positionMap.get(item.positionName) || 0;
|
||||
positionMap.set(item.positionName, count + 1);
|
||||
});
|
||||
|
||||
const positionData = Array.from(positionMap.entries()).map(([name, value]) => ({
|
||||
name,
|
||||
value
|
||||
}));
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'item'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '职务分布',
|
||||
type: 'pie',
|
||||
radius: '50%',
|
||||
data: positionData,
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
positionChart.setOption(option);
|
||||
};
|
||||
|
||||
/** 初始化任职状态分布图 */
|
||||
const initStatusChart = (data: XgClassCadreVO[]) => {
|
||||
if (!statusChartRef.value) return;
|
||||
|
||||
statusChart = echarts.init(statusChartRef.value);
|
||||
|
||||
// 统计任职状态分布
|
||||
const statusMap = new Map();
|
||||
data.forEach((item) => {
|
||||
const statusText = getCadreStatusText(item.cadreStatus);
|
||||
const count = statusMap.get(statusText) || 0;
|
||||
statusMap.set(statusText, count + 1);
|
||||
});
|
||||
|
||||
const statusData = Array.from(statusMap.entries()).map(([name, value]) => ({
|
||||
name,
|
||||
value
|
||||
}));
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'item'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '任职状态',
|
||||
type: 'pie',
|
||||
radius: ['40%', '70%'],
|
||||
data: statusData,
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
statusChart.setOption(option);
|
||||
};
|
||||
|
||||
/** 初始化学院统计图 */
|
||||
const initCollegeChart = (data: XgClassCadreVO[]) => {
|
||||
if (!collegeChartRef.value) return;
|
||||
|
||||
collegeChart = echarts.init(collegeChartRef.value);
|
||||
|
||||
// 统计学院分布
|
||||
const collegeMap = new Map();
|
||||
data.forEach((item) => {
|
||||
const college = item.collegeName || '未知学院';
|
||||
const count = collegeMap.get(college) || 0;
|
||||
collegeMap.set(college, count + 1);
|
||||
});
|
||||
|
||||
const colleges = Array.from(collegeMap.keys());
|
||||
const values = Array.from(collegeMap.values());
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
}
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: colleges,
|
||||
axisLabel: {
|
||||
interval: 0,
|
||||
rotate: 45
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '班干部数量',
|
||||
type: 'bar',
|
||||
data: values,
|
||||
itemStyle: {
|
||||
color: '#409EFF'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
collegeChart.setOption(option);
|
||||
};
|
||||
|
||||
/** 初始化工作表现分布图 */
|
||||
const initPerformanceChart = (data: XgClassCadreVO[]) => {
|
||||
if (!performanceChartRef.value) return;
|
||||
|
||||
performanceChart = echarts.init(performanceChartRef.value);
|
||||
|
||||
// 统计工作表现分布
|
||||
const performanceMap = new Map();
|
||||
data.forEach((item) => {
|
||||
if (item.workPerformance) {
|
||||
const performanceText = getPerformanceText(item.workPerformance);
|
||||
const count = performanceMap.get(performanceText) || 0;
|
||||
performanceMap.set(performanceText, count + 1);
|
||||
}
|
||||
});
|
||||
|
||||
const performanceData = Array.from(performanceMap.entries()).map(([name, value]) => ({
|
||||
name,
|
||||
value
|
||||
}));
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'item'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '工作表现',
|
||||
type: 'pie',
|
||||
radius: '50%',
|
||||
data: performanceData,
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
performanceChart.setOption(option);
|
||||
};
|
||||
|
||||
/** 刷新即将到期列表 */
|
||||
const refreshExpiringList = async () => {
|
||||
try {
|
||||
const res = await getExpiringCadres(30);
|
||||
expiringList.value = res.data;
|
||||
statisticsCards.value[3].value = res.data.length;
|
||||
ElMessage.success('刷新成功');
|
||||
} catch (error) {
|
||||
console.error('刷新失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
/** 联系班干部 */
|
||||
const handleContact = (row: XgClassCadreVO) => {
|
||||
if (row.contactPhone) {
|
||||
window.open(`tel:${row.contactPhone}`);
|
||||
} else {
|
||||
ElMessage.warning('该班干部未设置联系电话');
|
||||
}
|
||||
};
|
||||
|
||||
// 辅助方法
|
||||
const getCadreStatusText = (status: string) => {
|
||||
switch (status) {
|
||||
case '1':
|
||||
return '在任';
|
||||
case '2':
|
||||
return '到期';
|
||||
case '3':
|
||||
return '辞职';
|
||||
case '4':
|
||||
return '免职';
|
||||
case '5':
|
||||
return '调任';
|
||||
default:
|
||||
return '未知';
|
||||
}
|
||||
};
|
||||
|
||||
const getPerformanceText = (performance: string) => {
|
||||
switch (performance) {
|
||||
case 'excellent':
|
||||
return '优秀';
|
||||
case 'good':
|
||||
return '良好';
|
||||
case 'average':
|
||||
return '一般';
|
||||
case 'poor':
|
||||
return '较差';
|
||||
default:
|
||||
return '未知';
|
||||
}
|
||||
};
|
||||
|
||||
// 响应式处理
|
||||
const handleResize = () => {
|
||||
positionChart?.resize();
|
||||
statusChart?.resize();
|
||||
collegeChart?.resize();
|
||||
performanceChart?.resize();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
loadStatistics();
|
||||
window.addEventListener('resize', handleResize);
|
||||
});
|
||||
|
||||
// 清理
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
positionChart?.dispose();
|
||||
statusChart?.dispose();
|
||||
collegeChart?.dispose();
|
||||
performanceChart?.dispose();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.app-container {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.statistics-card {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.statistics-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.statistics-icon {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.statistics-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.statistics-title {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.statistics-value {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.mt-4 {
|
||||
margin-top: 20px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,277 +0,0 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-card class="box-card">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>班级信息导入</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-steps :active="activeStep" finish-status="success" align-center>
|
||||
<el-step title="下载模板" description="下载Excel导入模板"></el-step>
|
||||
<el-step title="填写数据" description="按模板格式填写班级数据"></el-step>
|
||||
<el-step title="上传文件" description="上传填写好的Excel文件"></el-step>
|
||||
<el-step title="导入完成" description="查看导入结果"></el-step>
|
||||
</el-steps>
|
||||
|
||||
<div class="import-content" v-if="activeStep === 0">
|
||||
<div class="step-content">
|
||||
<el-alert title="使用说明" type="info" :closable="false" show-icon>
|
||||
<p>1. 请先下载班级信息导入模板</p>
|
||||
<p>2. 按照模板格式填写班级信息</p>
|
||||
<p>3. 班级代码、班级名称为必填项</p>
|
||||
<p>4. 确保学院代码、专业代码在系统中存在</p>
|
||||
</el-alert>
|
||||
|
||||
<div class="step-actions">
|
||||
<el-button type="primary" @click="downloadTemplate">
|
||||
<el-icon><Download /></el-icon>
|
||||
下载导入模板
|
||||
</el-button>
|
||||
<el-button @click="nextStep">下一步</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="import-content" v-if="activeStep === 1">
|
||||
<div class="step-content">
|
||||
<el-alert title="填写要求" type="warning" :closable="false" show-icon>
|
||||
<p>• 班级代码必须唯一,建议格式:学院代码+年级+专业+序号</p>
|
||||
<p>• 班级名称不能重复</p>
|
||||
<p>• 入学年份和毕业年份必须为4位数字</p>
|
||||
<p>• 最大学生人数建议在20-60人之间</p>
|
||||
</el-alert>
|
||||
|
||||
<div class="step-actions">
|
||||
<el-button @click="prevStep">上一步</el-button>
|
||||
<el-button type="primary" @click="nextStep">下一步</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="import-content" v-if="activeStep === 2">
|
||||
<div class="step-content">
|
||||
<el-upload
|
||||
ref="uploadRef"
|
||||
class="upload-demo"
|
||||
drag
|
||||
:action="uploadAction"
|
||||
:headers="uploadHeaders"
|
||||
:data="uploadData"
|
||||
:before-upload="beforeUpload"
|
||||
:on-success="handleUploadSuccess"
|
||||
:on-error="handleUploadError"
|
||||
:file-list="fileList"
|
||||
:limit="1"
|
||||
accept=".xlsx,.xls"
|
||||
>
|
||||
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
|
||||
<div class="el-upload__text">将Excel文件拖到此处,或<em>点击上传</em></div>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip">只能上传xlsx/xls文件,且不超过10MB</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
|
||||
<div class="upload-options">
|
||||
<el-checkbox v-model="updateSupport"> 是否更新已经存在的班级数据 </el-checkbox>
|
||||
</div>
|
||||
|
||||
<div class="step-actions">
|
||||
<el-button @click="prevStep">上一步</el-button>
|
||||
<el-button type="primary" @click="startImport" :loading="uploading"> 开始导入 </el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="import-content" v-if="activeStep === 3">
|
||||
<div class="step-content">
|
||||
<el-result :icon="importResult.success ? 'success' : 'error'" :title="importResult.title" :sub-title="importResult.message">
|
||||
<template #extra>
|
||||
<el-button type="primary" @click="resetImport">重新导入</el-button>
|
||||
<el-button @click="goToList">查看班级列表</el-button>
|
||||
</template>
|
||||
</el-result>
|
||||
|
||||
<div v-if="importResult.detail" class="import-detail">
|
||||
<el-card>
|
||||
<template #header>
|
||||
<span>导入详情</span>
|
||||
</template>
|
||||
<div v-html="importResult.detail"></div>
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="ClassInfoImport" lang="ts">
|
||||
import { ref, reactive } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { getToken } from '@/utils/auth';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
// 组件状态
|
||||
const activeStep = ref(0);
|
||||
const uploading = ref(false);
|
||||
const fileList = ref([]);
|
||||
const updateSupport = ref(false);
|
||||
|
||||
// 上传配置
|
||||
const uploadAction = ref(import.meta.env.VITE_APP_BASE_API + '/xg/classInfo/importData');
|
||||
const uploadHeaders = ref({
|
||||
Authorization: 'Bearer ' + getToken()
|
||||
});
|
||||
const uploadData = reactive({
|
||||
updateSupport: false
|
||||
});
|
||||
|
||||
// 导入结果
|
||||
const importResult = reactive({
|
||||
success: false,
|
||||
title: '',
|
||||
message: '',
|
||||
detail: ''
|
||||
});
|
||||
|
||||
/** 下载模板 */
|
||||
const downloadTemplate = () => {
|
||||
// 这里应该调用下载模板的API
|
||||
window.open(import.meta.env.VITE_APP_BASE_API + '/xg/classInfo/importTemplate', '_blank');
|
||||
ElMessage.success('模板下载已开始');
|
||||
};
|
||||
|
||||
/** 下一步 */
|
||||
const nextStep = () => {
|
||||
if (activeStep.value < 3) {
|
||||
activeStep.value++;
|
||||
}
|
||||
};
|
||||
|
||||
/** 上一步 */
|
||||
const prevStep = () => {
|
||||
if (activeStep.value > 0) {
|
||||
activeStep.value--;
|
||||
}
|
||||
};
|
||||
|
||||
/** 上传前检查 */
|
||||
const beforeUpload = (file: any) => {
|
||||
const isExcel = file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' || file.type === 'application/vnd.ms-excel';
|
||||
const isLt10M = file.size / 1024 / 1024 < 10;
|
||||
|
||||
if (!isExcel) {
|
||||
ElMessage.error('上传文件只能是 Excel 格式!');
|
||||
return false;
|
||||
}
|
||||
if (!isLt10M) {
|
||||
ElMessage.error('上传文件大小不能超过 10MB!');
|
||||
return false;
|
||||
}
|
||||
|
||||
uploadData.updateSupport = updateSupport.value;
|
||||
return true;
|
||||
};
|
||||
|
||||
/** 开始导入 */
|
||||
const startImport = () => {
|
||||
if (fileList.value.length === 0) {
|
||||
ElMessage.warning('请先选择要上传的文件');
|
||||
return;
|
||||
}
|
||||
|
||||
uploading.value = true;
|
||||
// 文件会自动上传,这里只需要更新状态
|
||||
};
|
||||
|
||||
/** 上传成功处理 */
|
||||
const handleUploadSuccess = (response: any) => {
|
||||
uploading.value = false;
|
||||
|
||||
if (response.code === 200) {
|
||||
importResult.success = true;
|
||||
importResult.title = '导入成功';
|
||||
importResult.message = response.msg;
|
||||
importResult.detail = response.data || '';
|
||||
activeStep.value = 3;
|
||||
} else {
|
||||
importResult.success = false;
|
||||
importResult.title = '导入失败';
|
||||
importResult.message = response.msg || '导入过程中发生错误';
|
||||
importResult.detail = response.data || '';
|
||||
activeStep.value = 3;
|
||||
}
|
||||
};
|
||||
|
||||
/** 上传失败处理 */
|
||||
const handleUploadError = () => {
|
||||
uploading.value = false;
|
||||
importResult.success = false;
|
||||
importResult.title = '导入失败';
|
||||
importResult.message = '文件上传失败,请检查网络连接或文件格式';
|
||||
importResult.detail = '';
|
||||
activeStep.value = 3;
|
||||
};
|
||||
|
||||
/** 重新导入 */
|
||||
const resetImport = () => {
|
||||
activeStep.value = 0;
|
||||
fileList.value = [];
|
||||
updateSupport.value = false;
|
||||
importResult.success = false;
|
||||
importResult.title = '';
|
||||
importResult.message = '';
|
||||
importResult.detail = '';
|
||||
};
|
||||
|
||||
/** 跳转到班级列表 */
|
||||
const goToList = () => {
|
||||
router.push('/xg-xx/classInfo');
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.import-content {
|
||||
margin: 40px 0;
|
||||
}
|
||||
|
||||
.step-content {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.step-actions {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.step-actions .el-button {
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.upload-demo {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.upload-options {
|
||||
margin: 20px 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.import-detail {
|
||||
margin-top: 20px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.el-alert p {
|
||||
margin: 5px 0;
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,916 +0,0 @@
|
||||
<template>
|
||||
<div class="p-2 page-container">
|
||||
<!-- 搜索区域 -->
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" label-width="100px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="批次编码" prop="batchCode">
|
||||
<el-input v-model="queryParams.batchCode" placeholder="请输入批次编码" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="批次名称" prop="batchName">
|
||||
<el-input v-model="queryParams.batchName" placeholder="请输入批次名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="批次类型" prop="batchType">
|
||||
<el-select v-model="queryParams.batchType" placeholder="请选择批次类型" clearable style="width: 100%">
|
||||
<el-option label="常规批次" value="regular" />
|
||||
<el-option label="专项批次" value="special" />
|
||||
<el-option label="紧急批次" value="urgent" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="批次状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择批次状态" clearable style="width: 100%">
|
||||
<el-option label="草稿" value="draft" />
|
||||
<el-option label="进行中" value="active" />
|
||||
<el-option label="已完成" value="completed" />
|
||||
<el-option label="已取消" value="cancelled" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="学年" prop="academicYearId">
|
||||
<el-select v-model="queryParams.academicYearId" placeholder="请选择学年" clearable style="width: 100%">
|
||||
<el-option v-for="year in academicYearOptions" :key="year.value" :label="year.label" :value="year.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="是否发布" prop="isPublished">
|
||||
<el-select v-model="queryParams.isPublished" placeholder="请选择发布状态" clearable style="width: 100%">
|
||||
<el-option label="已发布" :value="true" />
|
||||
<el-option label="未发布" :value="false" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<!-- 主要内容区域 -->
|
||||
<el-card shadow="never" class="button-card table-card">
|
||||
<template #header>
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['xg:collectionBatch:add']">新增批次</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['xg:collectionBatch:edit']">修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['xg:collectionBatch:remove']">删除</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button icon="Download" @click="handleExport" v-hasPermi="['xg:collectionBatch:export']">导出</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button icon="Promotion" :disabled="single" @click="handlePublish" v-hasPermi="['xg:collectionBatch:publish']">发布</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button icon="Finished" :disabled="single" @click="handleComplete" v-hasPermi="['xg:collectionBatch:complete']">结束</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<div class="table-container">
|
||||
<el-table v-loading="loading" border :data="collectionBatchList" @selection-change="handleSelectionChange" style="width: 100%" class="scroll-table">
|
||||
<el-table-column type="selection" width="55" align="center" fixed="left" />
|
||||
<el-table-column label="批次编码" align="center" prop="batchCode" width="120" show-overflow-tooltip fixed="left" />
|
||||
<el-table-column label="批次名称" align="center" prop="batchName" width="180" show-overflow-tooltip fixed="left" />
|
||||
<el-table-column label="批次类型" align="center" prop="batchType" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getBatchTypeTagType(scope.row.batchType)" size="small">
|
||||
{{ getBatchTypeText(scope.row.batchType) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="学年" align="center" prop="academicYearName" width="120" show-overflow-tooltip />
|
||||
<el-table-column label="学期" align="center" prop="semesterName" width="120" show-overflow-tooltip />
|
||||
<el-table-column label="目标范围" align="center" prop="targetScope" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getTargetScopeTagType(scope.row.targetScope)" size="small">
|
||||
{{ getTargetScopeText(scope.row.targetScope) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="开始时间" align="center" prop="startTime" width="160">
|
||||
<template #default="scope">
|
||||
<span>{{ proxy.parseTime(scope.row.startTime, '{y}-{m}-{d} {h}:{i}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="结束时间" align="center" prop="endTime" width="160">
|
||||
<template #default="scope">
|
||||
<span>{{ proxy.parseTime(scope.row.endTime, '{y}-{m}-{d} {h}:{i}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="批次状态" align="center" prop="status" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getStatusTagType(scope.row.status)" size="small">
|
||||
{{ getStatusText(scope.row.status) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="发布状态" align="center" prop="isPublished" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.isPublished ? 'success' : 'info'" size="small">
|
||||
{{ scope.row.isPublished ? '已发布' : '未发布' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="完成进度" align="center" width="120">
|
||||
<template #default="scope">
|
||||
<el-progress :percentage="scope.row.completionRate" :stroke-width="6" :show-text="false" />
|
||||
<div class="text-xs text-gray-500 mt-1">
|
||||
{{ scope.row.completedCount }}/{{ scope.row.totalCount }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="160">
|
||||
<template #default="scope">
|
||||
<span>{{ proxy.parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="200" fixed="right" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<div class="action-buttons">
|
||||
<el-tooltip content="查看" placement="top">
|
||||
<el-button link type="info" icon="View" size="small" @click="handleView(scope.row)" v-hasPermi="['xg:collectionBatch:query']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="修改" placement="top">
|
||||
<el-button link type="primary" icon="Edit" size="small" @click="handleUpdate(scope.row)" v-hasPermi="['xg:collectionBatch:edit']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button link type="danger" icon="Delete" size="small" @click="handleDelete(scope.row)" v-hasPermi="['xg:collectionBatch:remove']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="统计" placement="top">
|
||||
<el-button link type="warning" icon="DataAnalysis" size="small" @click="handleStatistics(scope.row)" v-hasPermi="['xg:collectionBatch:statistics']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="复制" placement="top">
|
||||
<el-button link type="success" icon="DocumentCopy" size="small" @click="handleCopy(scope.row)" v-hasPermi="['xg:collectionBatch:add']"></el-button>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="pagination-container">
|
||||
<pagination v-show="total > 0" :total="total" :page="queryParams.pageNum" :limit="queryParams.pageSize" @pagination="getList" />
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 新增/修改批次对话框 -->
|
||||
<el-dialog :title="dialog.title" v-model="dialog.visible" width="900px" append-to-body>
|
||||
<el-form ref="collectionBatchFormRef" :model="form" :rules="rules" label-width="120px">
|
||||
<el-tabs v-model="activeTab" type="border-card">
|
||||
<el-tab-pane label="基本信息" name="basic">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="批次编码" prop="batchCode">
|
||||
<el-input v-model="form.batchCode" placeholder="请输入批次编码" :disabled="dialog.type === 'edit'" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="批次名称" prop="batchName">
|
||||
<el-input v-model="form.batchName" placeholder="请输入批次名称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="批次类型" prop="batchType">
|
||||
<el-select v-model="form.batchType" placeholder="请选择批次类型" style="width: 100%">
|
||||
<el-option label="常规批次" value="regular" />
|
||||
<el-option label="专项批次" value="special" />
|
||||
<el-option label="紧急批次" value="urgent" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="目标范围" prop="targetScope">
|
||||
<el-select v-model="form.targetScope" placeholder="请选择目标范围" style="width: 100%">
|
||||
<el-option label="全部学生" value="all" />
|
||||
<el-option label="指定学院" value="college" />
|
||||
<el-option label="指定班级" value="class" />
|
||||
<el-option label="指定学生" value="student" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="关联学年" prop="academicYearId">
|
||||
<el-select v-model="form.academicYearId" placeholder="请选择学年" style="width: 100%">
|
||||
<el-option v-for="year in academicYearOptions" :key="year.value" :label="year.label" :value="year.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="关联学期" prop="semesterId">
|
||||
<el-select v-model="form.semesterId" placeholder="请选择学期" style="width: 100%">
|
||||
<el-option v-for="semester in semesterOptions" :key="semester.value" :label="semester.label" :value="semester.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="开始时间" prop="startTime">
|
||||
<el-date-picker v-model="form.startTime" type="datetime" placeholder="选择开始时间" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="结束时间" prop="endTime">
|
||||
<el-date-picker v-model="form.endTime" type="datetime" placeholder="选择结束时间" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="批次状态" prop="status">
|
||||
<el-select v-model="form.status" placeholder="请选择批次状态" style="width: 100%">
|
||||
<el-option label="草稿" value="draft" />
|
||||
<el-option label="进行中" value="active" />
|
||||
<el-option label="已完成" value="completed" />
|
||||
<el-option label="已取消" value="cancelled" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="排序序号" prop="sortOrder">
|
||||
<el-input-number v-model="form.sortOrder" :min="0" :max="9999" placeholder="排序序号" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="高级设置" name="advanced">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="自动提醒" prop="autoRemind">
|
||||
<el-switch v-model="form.autoRemind" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="提醒天数" prop="remindDays">
|
||||
<el-input-number v-model="form.remindDays" :min="1" :max="30" placeholder="提醒天数" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="批次描述" prop="description">
|
||||
<el-input v-model="form.description" type="textarea" :rows="4" placeholder="请输入批次描述" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="通知内容" prop="noticeContent">
|
||||
<el-input v-model="form.noticeContent" type="textarea" :rows="4" placeholder="请输入通知内容" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancelForm">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 查看详情对话框 -->
|
||||
<el-dialog title="批次详情" v-model="detailDialog.visible" width="800px" append-to-body>
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="批次编码">{{ detailForm.batchCode }}</el-descriptions-item>
|
||||
<el-descriptions-item label="批次名称">{{ detailForm.batchName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="批次类型">
|
||||
<el-tag :type="getBatchTypeTagType(detailForm.batchType)" size="small">
|
||||
{{ getBatchTypeText(detailForm.batchType) }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="目标范围">
|
||||
<el-tag :type="getTargetScopeTagType(detailForm.targetScope)" size="small">
|
||||
{{ getTargetScopeText(detailForm.targetScope) }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="关联学年">{{ detailForm.academicYearName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="关联学期">{{ detailForm.semesterName || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="开始时间">{{ proxy.parseTime(detailForm.startTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</el-descriptions-item>
|
||||
<el-descriptions-item label="结束时间">{{ proxy.parseTime(detailForm.endTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</el-descriptions-item>
|
||||
<el-descriptions-item label="批次状态">
|
||||
<el-tag :type="getStatusTagType(detailForm.status)" size="small">
|
||||
{{ getStatusText(detailForm.status) }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="发布状态">
|
||||
<el-tag :type="detailForm.isPublished ? 'success' : 'info'" size="small">
|
||||
{{ detailForm.isPublished ? '已发布' : '未发布' }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="完成进度">
|
||||
<el-progress :percentage="detailForm.completionRate" :stroke-width="6" />
|
||||
<div class="text-xs text-gray-500 mt-1">
|
||||
{{ detailForm.completedCount }}/{{ detailForm.totalCount }}
|
||||
</div>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="自动提醒">
|
||||
<el-tag :type="detailForm.autoRemind ? 'success' : 'info'" size="small">
|
||||
{{ detailForm.autoRemind ? '开启' : '关闭' }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="提醒天数">{{ detailForm.remindDays }}天</el-descriptions-item>
|
||||
<el-descriptions-item label="排序序号">{{ detailForm.sortOrder }}</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">{{ proxy.parseTime(detailForm.createTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</el-descriptions-item>
|
||||
<el-descriptions-item label="更新时间">{{ proxy.parseTime(detailForm.updateTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</el-descriptions-item>
|
||||
<el-descriptions-item label="批次描述" :span="2">{{ detailForm.description || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="通知内容" :span="2">{{ detailForm.noticeContent || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="备注" :span="2">{{ detailForm.remark || '-' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="detailDialog.visible = false">关 闭</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 复制批次对话框 -->
|
||||
<el-dialog title="复制批次" v-model="copyDialog.visible" width="500px" append-to-body>
|
||||
<el-form ref="copyFormRef" :model="copyForm" :rules="copyRules" label-width="120px">
|
||||
<el-form-item label="新批次编码" prop="batchCode">
|
||||
<el-input v-model="copyForm.batchCode" placeholder="请输入新批次编码" />
|
||||
</el-form-item>
|
||||
<el-form-item label="新批次名称" prop="batchName">
|
||||
<el-input v-model="copyForm.batchName" placeholder="请输入新批次名称" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitCopyForm">确 定</el-button>
|
||||
<el-button @click="copyDialog.visible = false">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="CollectionBatch">
|
||||
// 组件导入和基本设置
|
||||
import { nextTick, onMounted, reactive, ref, computed } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { getCurrentInstance } from 'vue';
|
||||
import {
|
||||
listCollectionBatch,
|
||||
getCollectionBatch,
|
||||
addCollectionBatch,
|
||||
updateCollectionBatch,
|
||||
delCollectionBatch,
|
||||
publishBatch,
|
||||
unpublishBatch,
|
||||
completeBatch,
|
||||
copyBatch,
|
||||
getBatchStatistics,
|
||||
updateBatchStatistics
|
||||
} from '@/api/xg-xx/collectionBatch';
|
||||
import { listAcademicYear } from '@/api/xg-xx/academicYear';
|
||||
import { listSemester } from '@/api/xg-xx/semester';
|
||||
import type { XgCollectionBatchVO, XgCollectionBatchForm } from '@/api/xg-xx/types';
|
||||
|
||||
const { proxy } = getCurrentInstance() as any;
|
||||
|
||||
// 响应式数据
|
||||
const collectionBatchList = ref<XgCollectionBatchVO[]>([]);
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref<Array<string | number>>([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const queryFormRef = ref();
|
||||
const collectionBatchFormRef = ref();
|
||||
const copyFormRef = ref();
|
||||
const activeTab = ref('basic');
|
||||
|
||||
// 选项数据
|
||||
const academicYearOptions = ref([]);
|
||||
const semesterOptions = ref([]);
|
||||
|
||||
// 查询参数
|
||||
const queryParams = ref({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
batchCode: undefined,
|
||||
batchName: undefined,
|
||||
batchType: undefined,
|
||||
status: undefined,
|
||||
academicYearId: undefined,
|
||||
isPublished: undefined
|
||||
});
|
||||
|
||||
// 表单参数
|
||||
const form = ref<XgCollectionBatchForm>({});
|
||||
|
||||
// 详情表单
|
||||
const detailForm = ref<XgCollectionBatchVO>({});
|
||||
|
||||
// 复制表单
|
||||
const copyForm = ref({
|
||||
batchCode: '',
|
||||
batchName: ''
|
||||
});
|
||||
|
||||
// 当前复制的批次ID
|
||||
const copyBatchId = ref<string | number>('');
|
||||
|
||||
// 表单校验
|
||||
const rules = ref({
|
||||
batchCode: [{ required: true, message: '批次编码不能为空', trigger: 'blur' }],
|
||||
batchName: [{ required: true, message: '批次名称不能为空', trigger: 'blur' }],
|
||||
batchType: [{ required: true, message: '批次类型不能为空', trigger: 'change' }],
|
||||
targetScope: [{ required: true, message: '目标范围不能为空', trigger: 'change' }],
|
||||
academicYearId: [{ required: true, message: '关联学年不能为空', trigger: 'change' }],
|
||||
startTime: [{ required: true, message: '开始时间不能为空', trigger: 'change' }],
|
||||
endTime: [{ required: true, message: '结束时间不能为空', trigger: 'change' }],
|
||||
status: [{ required: true, message: '批次状态不能为空', trigger: 'change' }]
|
||||
});
|
||||
|
||||
// 复制表单校验
|
||||
const copyRules = ref({
|
||||
batchCode: [{ required: true, message: '批次编码不能为空', trigger: 'blur' }],
|
||||
batchName: [{ required: true, message: '批次名称不能为空', trigger: 'blur' }]
|
||||
});
|
||||
|
||||
// 对话框
|
||||
const dialog = reactive({
|
||||
visible: false,
|
||||
title: '',
|
||||
type: 'add'
|
||||
});
|
||||
|
||||
const detailDialog = reactive({
|
||||
visible: false
|
||||
});
|
||||
|
||||
const copyDialog = reactive({
|
||||
visible: false
|
||||
});
|
||||
|
||||
// 生命周期钩子
|
||||
onMounted(() => {
|
||||
getList();
|
||||
getAcademicYearOptions();
|
||||
getSemesterOptions();
|
||||
});
|
||||
|
||||
// 获取批次列表
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await listCollectionBatch(queryParams.value);
|
||||
collectionBatchList.value = res.rows;
|
||||
total.value = res.total;
|
||||
} catch (error) {
|
||||
console.error('获取批次列表失败:', error);
|
||||
ElMessage.error('获取批次列表失败,请稍后重试');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 获取学年选项
|
||||
const getAcademicYearOptions = async () => {
|
||||
try {
|
||||
const res = await listAcademicYear({ pageNum: 1, pageSize: 1000 });
|
||||
academicYearOptions.value = res.rows.map(item => ({
|
||||
value: item.id,
|
||||
label: item.yearName
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error('获取学年选项失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 获取学期选项
|
||||
const getSemesterOptions = async () => {
|
||||
try {
|
||||
const res = await listSemester({ pageNum: 1, pageSize: 1000 });
|
||||
semesterOptions.value = res.rows.map(item => ({
|
||||
value: item.id,
|
||||
label: item.semesterName
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error('获取学期选项失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 搜索按钮操作
|
||||
const handleQuery = () => {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
// 重置按钮操作
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value?.resetFields();
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
// 多选框选中数据
|
||||
const handleSelectionChange = (selection: XgCollectionBatchVO[]) => {
|
||||
ids.value = selection.map(item => item.id);
|
||||
single.value = selection.length !== 1;
|
||||
multiple.value = !selection.length;
|
||||
};
|
||||
|
||||
// 新增按钮操作
|
||||
const handleAdd = () => {
|
||||
reset();
|
||||
dialog.visible = true;
|
||||
dialog.title = '新增信息采集批次';
|
||||
dialog.type = 'add';
|
||||
};
|
||||
|
||||
// 修改按钮操作
|
||||
const handleUpdate = async (row?: XgCollectionBatchVO) => {
|
||||
reset();
|
||||
const batchId = row?.id || ids.value[0];
|
||||
try {
|
||||
const res = await getCollectionBatch(batchId);
|
||||
Object.assign(form.value, res.data);
|
||||
dialog.visible = true;
|
||||
dialog.title = '修改信息采集批次';
|
||||
dialog.type = 'edit';
|
||||
} catch (error) {
|
||||
console.error('获取批次详情失败:', error);
|
||||
ElMessage.error('获取批次详情失败,请稍后重试');
|
||||
}
|
||||
};
|
||||
|
||||
// 查看详情
|
||||
const handleView = async (row: XgCollectionBatchVO) => {
|
||||
try {
|
||||
const res = await getCollectionBatch(row.id);
|
||||
Object.assign(detailForm.value, res.data);
|
||||
detailDialog.visible = true;
|
||||
} catch (error) {
|
||||
console.error('获取批次详情失败:', error);
|
||||
ElMessage.error('获取批次详情失败,请稍后重试');
|
||||
}
|
||||
};
|
||||
|
||||
// 删除按钮操作
|
||||
const handleDelete = async (row?: XgCollectionBatchVO) => {
|
||||
const batchIds = row?.id || ids.value;
|
||||
let confirmMessage = '';
|
||||
|
||||
if (row) {
|
||||
confirmMessage = `是否确认删除批次"${row.batchName}"?`;
|
||||
} else {
|
||||
const selectedBatches = collectionBatchList.value.filter(item => ids.value.includes(item.id));
|
||||
if (selectedBatches.length === 1) {
|
||||
confirmMessage = `是否确认删除批次"${selectedBatches[0].batchName}"?`;
|
||||
} else {
|
||||
confirmMessage = `是否确认删除选中的${selectedBatches.length}个批次?`;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm(confirmMessage, '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
await delCollectionBatch(batchIds);
|
||||
ElMessage.success('删除成功');
|
||||
await getList();
|
||||
} catch (error) {
|
||||
console.error('删除失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 发布按钮操作
|
||||
const handlePublish = async () => {
|
||||
const batchId = ids.value[0];
|
||||
const selectedBatch = collectionBatchList.value.find(item => item.id === batchId);
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm(`是否确认发布批次"${selectedBatch?.batchName}"?`, '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
await publishBatch(batchId);
|
||||
ElMessage.success('发布成功');
|
||||
await getList();
|
||||
} catch (error) {
|
||||
console.error('发布失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 结束按钮操作
|
||||
const handleComplete = async () => {
|
||||
const batchId = ids.value[0];
|
||||
const selectedBatch = collectionBatchList.value.find(item => item.id === batchId);
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm(`是否确认结束批次"${selectedBatch?.batchName}"?`, '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
await completeBatch(batchId);
|
||||
ElMessage.success('结束成功');
|
||||
await getList();
|
||||
} catch (error) {
|
||||
console.error('结束失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 复制按钮操作
|
||||
const handleCopy = (row: XgCollectionBatchVO) => {
|
||||
copyBatchId.value = row.id;
|
||||
copyForm.value = {
|
||||
batchCode: `${row.batchCode}_copy`,
|
||||
batchName: `${row.batchName}_复制`
|
||||
};
|
||||
copyDialog.visible = true;
|
||||
};
|
||||
|
||||
// 统计按钮操作
|
||||
const handleStatistics = async (row: XgCollectionBatchVO) => {
|
||||
try {
|
||||
// 更新统计信息
|
||||
await updateBatchStatistics(row.id);
|
||||
// 获取统计数据
|
||||
const res = await getBatchStatistics(row.id);
|
||||
|
||||
ElMessageBox.alert(`
|
||||
<div style="text-align: left;">
|
||||
<h4>批次统计信息</h4>
|
||||
<p>批次名称:${row.batchName}</p>
|
||||
<p>总人数:${res.data.totalCount}</p>
|
||||
<p>已完成:${res.data.completedCount}</p>
|
||||
<p>完成率:${res.data.completionRate}%</p>
|
||||
<p>更新时间:${proxy.parseTime(new Date(), '{y}-{m}-{d} {h}:{i}:{s}')}</p>
|
||||
</div>
|
||||
`, '统计信息', {
|
||||
dangerouslyUseHTMLString: true,
|
||||
type: 'info'
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('获取统计信息失败:', error);
|
||||
ElMessage.error('获取统计信息失败,请稍后重试');
|
||||
}
|
||||
};
|
||||
|
||||
// 导出按钮操作
|
||||
const handleExport = () => {
|
||||
proxy?.download(
|
||||
'xg/collectionBatch/export',
|
||||
{
|
||||
...queryParams.value
|
||||
},
|
||||
`信息采集批次_${proxy.parseTime(new Date(), '{y}{m}{d}{h}{i}{s}')}.xlsx`
|
||||
);
|
||||
};
|
||||
|
||||
// 提交表单
|
||||
const submitForm = async () => {
|
||||
if (!collectionBatchFormRef.value) return;
|
||||
|
||||
try {
|
||||
await collectionBatchFormRef.value.validate();
|
||||
|
||||
if (dialog.type === 'add') {
|
||||
await addCollectionBatch(form.value);
|
||||
ElMessage.success('新增成功');
|
||||
} else if (dialog.type === 'edit') {
|
||||
await updateCollectionBatch(form.value);
|
||||
ElMessage.success('修改成功');
|
||||
}
|
||||
|
||||
dialog.visible = false;
|
||||
await getList();
|
||||
} catch (error) {
|
||||
console.error('提交失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 提交复制表单
|
||||
const submitCopyForm = async () => {
|
||||
if (!copyFormRef.value) return;
|
||||
|
||||
try {
|
||||
await copyFormRef.value.validate();
|
||||
await copyBatch(copyBatchId.value, copyForm.value);
|
||||
ElMessage.success('复制成功');
|
||||
copyDialog.visible = false;
|
||||
await getList();
|
||||
} catch (error) {
|
||||
console.error('复制失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 取消表单
|
||||
const cancelForm = () => {
|
||||
dialog.visible = false;
|
||||
reset();
|
||||
};
|
||||
|
||||
// 表单重置
|
||||
const reset = () => {
|
||||
form.value = {
|
||||
batchCode: undefined,
|
||||
batchName: undefined,
|
||||
batchType: 'regular',
|
||||
targetScope: 'all',
|
||||
academicYearId: undefined,
|
||||
semesterId: undefined,
|
||||
startTime: undefined,
|
||||
endTime: undefined,
|
||||
status: 'draft',
|
||||
autoRemind: true,
|
||||
remindDays: 3,
|
||||
sortOrder: 0,
|
||||
description: undefined,
|
||||
noticeContent: undefined,
|
||||
remark: undefined
|
||||
};
|
||||
collectionBatchFormRef.value?.resetFields();
|
||||
};
|
||||
|
||||
// 获取批次类型标签类型
|
||||
const getBatchTypeTagType = (batchType: string) => {
|
||||
switch (batchType) {
|
||||
case 'regular':
|
||||
return '';
|
||||
case 'special':
|
||||
return 'warning';
|
||||
case 'urgent':
|
||||
return 'danger';
|
||||
default:
|
||||
return 'info';
|
||||
}
|
||||
};
|
||||
|
||||
// 获取批次类型文本
|
||||
const getBatchTypeText = (batchType: string) => {
|
||||
switch (batchType) {
|
||||
case 'regular':
|
||||
return '常规';
|
||||
case 'special':
|
||||
return '专项';
|
||||
case 'urgent':
|
||||
return '紧急';
|
||||
default:
|
||||
return '未知';
|
||||
}
|
||||
};
|
||||
|
||||
// 获取目标范围标签类型
|
||||
const getTargetScopeTagType = (targetScope: string) => {
|
||||
switch (targetScope) {
|
||||
case 'all':
|
||||
return 'success';
|
||||
case 'college':
|
||||
return 'warning';
|
||||
case 'class':
|
||||
return '';
|
||||
case 'student':
|
||||
return 'info';
|
||||
default:
|
||||
return 'info';
|
||||
}
|
||||
};
|
||||
|
||||
// 获取目标范围文本
|
||||
const getTargetScopeText = (targetScope: string) => {
|
||||
switch (targetScope) {
|
||||
case 'all':
|
||||
return '全部';
|
||||
case 'college':
|
||||
return '学院';
|
||||
case 'class':
|
||||
return '班级';
|
||||
case 'student':
|
||||
return '学生';
|
||||
default:
|
||||
return '未知';
|
||||
}
|
||||
};
|
||||
|
||||
// 获取状态标签类型
|
||||
const getStatusTagType = (status: string) => {
|
||||
switch (status) {
|
||||
case 'draft':
|
||||
return 'info';
|
||||
case 'active':
|
||||
return 'success';
|
||||
case 'completed':
|
||||
return '';
|
||||
case 'cancelled':
|
||||
return 'danger';
|
||||
default:
|
||||
return 'info';
|
||||
}
|
||||
};
|
||||
|
||||
// 获取状态文本
|
||||
const getStatusText = (status: string) => {
|
||||
switch (status) {
|
||||
case 'draft':
|
||||
return '草稿';
|
||||
case 'active':
|
||||
return '进行中';
|
||||
case 'completed':
|
||||
return '已完成';
|
||||
case 'cancelled':
|
||||
return '已取消';
|
||||
default:
|
||||
return '未知';
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 页面容器样式 */
|
||||
.page-container {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
/* 表格卡片布局 */
|
||||
.table-card {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.table-card :deep(.el-card__body) {
|
||||
padding: 0px 20px 20px 20px !important;
|
||||
}
|
||||
|
||||
/* 移除按钮栏底部边框 */
|
||||
.button-card :deep(.el-card__header) {
|
||||
border-bottom: none !important;
|
||||
}
|
||||
|
||||
/* 分页容器 */
|
||||
.pagination-container {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 16px 20px 0 0 !important;
|
||||
background-color: #fff;
|
||||
margin-top: 0px !important;
|
||||
}
|
||||
|
||||
/* 操作按钮样式 */
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* 对话框底部样式 */
|
||||
.dialog-footer {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* 表格容器 */
|
||||
.table-container {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
/* 滚动表格 */
|
||||
.scroll-table {
|
||||
overflow-x: auto;
|
||||
}
|
||||
</style>
|
||||
@@ -1,348 +0,0 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" ref="queryFormRef" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form-item label="学年" prop="academicYearId">
|
||||
<el-select v-model="queryParams.academicYearId" placeholder="请选择学年" clearable>
|
||||
<el-option v-for="item in academicYearOptions" :key="item.id" :label="item.yearName" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="学期编码" prop="semesterCode">
|
||||
<el-input v-model="queryParams.semesterCode" placeholder="请输入学期编码" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="学期名称" prop="semesterName">
|
||||
<el-input v-model="queryParams.semesterName" placeholder="请输入学期名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="学期类型" prop="semesterType">
|
||||
<el-select v-model="queryParams.semesterType" placeholder="请选择学期类型" clearable>
|
||||
<el-option label="春季学期" value="1" />
|
||||
<el-option label="秋季学期" value="2" />
|
||||
<el-option label="夏季学期" value="3" />
|
||||
<el-option label="短学期" value="4" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="学期状态" prop="semesterStatus">
|
||||
<el-select v-model="queryParams.semesterStatus" placeholder="请选择学期状态" clearable>
|
||||
<el-option label="启用" value="1" />
|
||||
<el-option label="停用" value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否当前" prop="isCurrent">
|
||||
<el-select v-model="queryParams.isCurrent" placeholder="请选择是否当前学期" clearable>
|
||||
<el-option label="是" :value="true" />
|
||||
<el-option label="否" :value="false" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['xg:semester:add']">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['xg:semester:edit']">修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['xg:semester:remove']">删除</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['xg:semester:export']">导出</el-button>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="semesterList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="学年名称" align="center" prop="academicYearName" />
|
||||
<el-table-column label="学期编码" align="center" prop="semesterCode" />
|
||||
<el-table-column label="学期名称" align="center" prop="semesterName" />
|
||||
<el-table-column label="学期类型" align="center" prop="semesterType">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="semester_type" :value="scope.row.semesterType" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="开始日期" align="center" prop="startDate" width="180">
|
||||
<template #default="scope">
|
||||
<span>{{ parseTime(scope.row.startDate, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="结束日期" align="center" prop="endDate" width="180">
|
||||
<template #default="scope">
|
||||
<span>{{ parseTime(scope.row.endDate, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="是否当前" align="center" prop="isCurrent">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="sys_yes_no" :value="scope.row.isCurrent" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="学期状态" align="center" prop="semesterStatus">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="sys_normal_disable" :value="scope.row.semesterStatus" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="排序" align="center" prop="sortOrder" />
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template #default="scope">
|
||||
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="修改" placement="top">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['xg:semester:edit']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['xg:semester:remove']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="设为当前" placement="top" v-if="!scope.row.isCurrent">
|
||||
<el-button link type="success" icon="Check" @click="handleSetCurrent(scope.row)" v-hasPermi="['xg:semester:edit']"></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||||
|
||||
<!-- 添加或修改学期管理对话框 -->
|
||||
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
|
||||
<el-form ref="semesterFormRef" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item label="学年" prop="academicYearId">
|
||||
<el-select v-model="form.academicYearId" placeholder="请选择学年" @change="handleAcademicYearChange">
|
||||
<el-option v-for="item in academicYearOptions" :key="item.id" :label="item.yearName" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="学期编码" prop="semesterCode">
|
||||
<el-input v-model="form.semesterCode" placeholder="请输入学期编码" />
|
||||
</el-form-item>
|
||||
<el-form-item label="学期名称" prop="semesterName">
|
||||
<el-input v-model="form.semesterName" placeholder="请输入学期名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="学期类型" prop="semesterType">
|
||||
<el-select v-model="form.semesterType" placeholder="请选择学期类型">
|
||||
<el-option label="春季学期" value="1" />
|
||||
<el-option label="秋季学期" value="2" />
|
||||
<el-option label="夏季学期" value="3" />
|
||||
<el-option label="短学期" value="4" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="开始日期" prop="startDate">
|
||||
<el-date-picker clearable v-model="form.startDate" type="date" value-format="YYYY-MM-DD" placeholder="请选择开始日期" />
|
||||
</el-form-item>
|
||||
<el-form-item label="结束日期" prop="endDate">
|
||||
<el-date-picker clearable v-model="form.endDate" type="date" value-format="YYYY-MM-DD" placeholder="请选择结束日期" />
|
||||
</el-form-item>
|
||||
<el-form-item label="是否当前" prop="isCurrent">
|
||||
<el-radio-group v-model="form.isCurrent">
|
||||
<el-radio :value="true">是</el-radio>
|
||||
<el-radio :value="false">否</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="学期状态" prop="semesterStatus">
|
||||
<el-radio-group v-model="form.semesterStatus">
|
||||
<el-radio value="1">启用</el-radio>
|
||||
<el-radio value="0">停用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="排序序号" prop="sortOrder">
|
||||
<el-input-number v-model="form.sortOrder" :min="0" />
|
||||
</el-form-item>
|
||||
<el-form-item label="学期描述" prop="description">
|
||||
<el-input v-model="form.description" type="textarea" placeholder="请输入学期描述" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="Semester" lang="ts">
|
||||
import { listSemester, getSemester, delSemester, addSemester, updateSemester, setCurrentSemester, exportSemester } from '@/api/xg-xx/semester';
|
||||
import { getAvailableAcademicYearOptions } from '@/api/xg-xx/academicYear';
|
||||
import { SemesterVO, SemesterQuery, SemesterForm, AcademicYearVO } from '@/api/xg-xx/academicTypes';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { sys_yes_no, sys_normal_disable, semester_type } = proxy?.useDict('sys_yes_no', 'sys_normal_disable', 'semester_type');
|
||||
|
||||
const semesterList = ref<SemesterVO[]>([]);
|
||||
const academicYearOptions = ref<AcademicYearVO[]>([]);
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref<Array<string | number>>([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
const semesterFormRef = ref<ElFormInstance>();
|
||||
|
||||
const dialog = reactive<DialogOption>({
|
||||
visible: false,
|
||||
title: ''
|
||||
});
|
||||
|
||||
const initFormData: SemesterForm = {
|
||||
id: undefined,
|
||||
academicYearId: undefined,
|
||||
semesterCode: '',
|
||||
semesterName: '',
|
||||
semesterType: '',
|
||||
startDate: '',
|
||||
endDate: '',
|
||||
isCurrent: false,
|
||||
semesterStatus: '1',
|
||||
sortOrder: 0,
|
||||
description: '',
|
||||
remark: ''
|
||||
};
|
||||
|
||||
const data = reactive<PageData<SemesterForm, SemesterQuery>>({
|
||||
form: { ...initFormData },
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
academicYearId: undefined,
|
||||
semesterCode: '',
|
||||
semesterName: '',
|
||||
semesterType: '',
|
||||
semesterStatus: '',
|
||||
isCurrent: undefined,
|
||||
params: {}
|
||||
},
|
||||
rules: {
|
||||
academicYearId: [{ required: true, message: '学年不能为空', trigger: 'change' }],
|
||||
semesterCode: [{ required: true, message: '学期编码不能为空', trigger: 'blur' }],
|
||||
semesterName: [{ required: true, message: '学期名称不能为空', trigger: 'blur' }],
|
||||
semesterType: [{ required: true, message: '学期类型不能为空', trigger: 'change' }],
|
||||
startDate: [{ required: true, message: '开始日期不能为空', trigger: 'blur' }],
|
||||
endDate: [{ required: true, message: '结束日期不能为空', trigger: 'blur' }],
|
||||
semesterStatus: [{ required: true, message: '学期状态不能为空', trigger: 'change' }],
|
||||
sortOrder: [{ required: true, message: '排序序号不能为空', trigger: 'blur' }]
|
||||
}
|
||||
});
|
||||
|
||||
const { queryParams, form, rules } = toRefs(data);
|
||||
|
||||
/** 查询学期管理列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
const res = await listSemester(queryParams.value);
|
||||
semesterList.value = res.rows;
|
||||
total.value = res.total;
|
||||
loading.value = false;
|
||||
};
|
||||
|
||||
/** 获取学年选项 */
|
||||
const getAcademicYearOptions = async () => {
|
||||
const res = await getAvailableAcademicYearOptions();
|
||||
academicYearOptions.value = res.data;
|
||||
};
|
||||
|
||||
/** 取消按钮 */
|
||||
const cancel = () => {
|
||||
reset();
|
||||
dialog.visible = false;
|
||||
};
|
||||
|
||||
/** 表单重置 */
|
||||
const reset = () => {
|
||||
form.value = { ...initFormData };
|
||||
semesterFormRef.value?.resetFields();
|
||||
};
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value?.resetFields();
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
/** 多选框选中数据 */
|
||||
const handleSelectionChange = (selection: SemesterVO[]) => {
|
||||
ids.value = selection.map((item) => item.id);
|
||||
single.value = selection.length !== 1;
|
||||
multiple.value = !selection.length;
|
||||
};
|
||||
|
||||
/** 新增按钮操作 */
|
||||
const handleAdd = () => {
|
||||
reset();
|
||||
dialog.visible = true;
|
||||
dialog.title = '添加学期管理';
|
||||
};
|
||||
|
||||
/** 修改按钮操作 */
|
||||
const handleUpdate = async (row?: SemesterVO) => {
|
||||
reset();
|
||||
const _id = row?.id || ids.value[0];
|
||||
const res = await getSemester(_id);
|
||||
Object.assign(form.value, res.data);
|
||||
dialog.visible = true;
|
||||
dialog.title = '修改学期管理';
|
||||
};
|
||||
|
||||
/** 学年变化处理 */
|
||||
const handleAcademicYearChange = () => {
|
||||
// 可以根据学年自动填充一些默认值
|
||||
};
|
||||
|
||||
/** 提交按钮 */
|
||||
const submitForm = () => {
|
||||
semesterFormRef.value?.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
if (form.value.id) {
|
||||
await updateSemester(form.value);
|
||||
proxy?.$modal.msgSuccess('修改成功');
|
||||
} else {
|
||||
await addSemester(form.value);
|
||||
proxy?.$modal.msgSuccess('新增成功');
|
||||
}
|
||||
dialog.visible = false;
|
||||
await getList();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (row?: SemesterVO) => {
|
||||
const _ids = row?.id || ids.value;
|
||||
await proxy?.$modal.confirm('是否确认删除学期管理编号为"' + _ids + '"的数据项?');
|
||||
await delSemester(_ids);
|
||||
proxy?.$modal.msgSuccess('删除成功');
|
||||
await getList();
|
||||
};
|
||||
|
||||
/** 设为当前学期 */
|
||||
const handleSetCurrent = async (row: SemesterVO) => {
|
||||
await proxy?.$modal.confirm('是否确认设置"' + row.semesterName + '"为当前学期?');
|
||||
await setCurrentSemester(row.id);
|
||||
proxy?.$modal.msgSuccess('设置成功');
|
||||
await getList();
|
||||
};
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = () => {
|
||||
proxy?.$download.excel(exportSemester, queryParams.value, `学期管理_${new Date().getTime()}.xlsx`);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getList();
|
||||
getAcademicYearOptions();
|
||||
});
|
||||
</script>
|
||||
@@ -1,774 +0,0 @@
|
||||
<template>
|
||||
<div class="p-2 page-container">
|
||||
<!-- 批次选择区域 -->
|
||||
<el-card shadow="hover" class="mb-4">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>批次管理</span>
|
||||
<div class="header-actions">
|
||||
<el-select v-model="selectedBatchId" placeholder="请选择批次" @change="handleBatchChange" style="width: 200px; margin-right: 10px;">
|
||||
<el-option v-for="batch in batchOptions" :key="batch.id" :label="batch.batchName" :value="batch.id" />
|
||||
</el-select>
|
||||
<el-button type="primary" icon="Refresh" @click="refreshBatchInfo">刷新</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 批次信息展示 -->
|
||||
<div v-if="currentBatch" class="batch-info">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-statistic title="批次名称" :value="currentBatch.batchName" />
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-statistic title="批次状态" :value="getBatchStatusText(currentBatch.status)" />
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-statistic title="总学生数" :value="batchStatistics.totalCount" />
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-statistic title="已提交数" :value="batchStatistics.submittedCount" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20" class="mt-4">
|
||||
<el-col :span="6">
|
||||
<el-statistic title="已通过数" :value="batchStatistics.approvedCount" />
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-statistic title="已拒绝数" :value="batchStatistics.rejectedCount" />
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-statistic title="完成率" :value="batchStatistics.completionRate" suffix="%" />
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="progress-info">
|
||||
<el-progress :percentage="batchStatistics.completionRate" :stroke-width="8" />
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 搜索区域 -->
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" label-width="100px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="学生姓名" prop="name">
|
||||
<el-input v-model="queryParams.name" placeholder="请输入学生姓名" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="学号" prop="studentNo">
|
||||
<el-input v-model="queryParams.studentNo" placeholder="请输入学号" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="班级" prop="className">
|
||||
<el-input v-model="queryParams.className" placeholder="请输入班级名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="提交状态" prop="submissionStatus">
|
||||
<el-select v-model="queryParams.submissionStatus" placeholder="请选择提交状态" clearable style="width: 100%">
|
||||
<el-option label="草稿" value="DRAFT" />
|
||||
<el-option label="已提交" value="SUBMITTED" />
|
||||
<el-option label="已通过" value="APPROVED" />
|
||||
<el-option label="已拒绝" value="REJECTED" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="学院" prop="collegeName">
|
||||
<el-input v-model="queryParams.collegeName" placeholder="请输入学院名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="18">
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<!-- 主要内容区域 -->
|
||||
<el-card shadow="hover">
|
||||
<!-- 操作按钮区域 -->
|
||||
<div class="toolbar-wrapper">
|
||||
<div class="toolbar-left">
|
||||
<el-button type="primary" icon="Plus" @click="handleAssociate" :disabled="!selectedBatchId">
|
||||
关联学生到批次
|
||||
</el-button>
|
||||
<el-button type="success" icon="Select" :disabled="!multiple || ids.length === 0" @click="handleBatchAssociate">
|
||||
批量关联
|
||||
</el-button>
|
||||
<el-button type="warning" icon="Upload" :disabled="!multiple || ids.length === 0" @click="handleBatchSubmit">
|
||||
批量提交
|
||||
</el-button>
|
||||
<el-button type="info" icon="Download" :disabled="!multiple || ids.length === 0" @click="handleBatchWithdraw">
|
||||
批量撤回
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<div class="toolbar-right">
|
||||
<el-button type="danger" icon="Download" @click="handleExport">
|
||||
导出
|
||||
</el-button>
|
||||
<el-button type="info" icon="Refresh" @click="handleRefresh">
|
||||
刷新
|
||||
</el-button>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 表格区域 -->
|
||||
<div class="table-wrapper">
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="studentList"
|
||||
@selection-change="handleSelectionChange"
|
||||
@sort-change="handleSortChange"
|
||||
row-key="id"
|
||||
border
|
||||
stripe
|
||||
size="small"
|
||||
>
|
||||
<el-table-column type="selection" width="50" align="center" />
|
||||
<el-table-column label="序号" type="index" width="60" align="center" />
|
||||
<el-table-column label="学生姓名" prop="name" width="100" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="学号" prop="studentNo" width="120" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="班级" prop="className" width="120" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="学院" prop="collegeName" width="120" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="专业" prop="majorName" width="120" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="联系电话" prop="phone" width="120" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="提交状态" prop="submissionStatus" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getSubmissionStatusTagType(scope.row.submissionStatus)" size="small">
|
||||
{{ getSubmissionStatusText(scope.row.submissionStatus) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="提交时间" prop="submissionTime" width="160" align="center" sortable="custom">
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.submissionTime ? proxy.parseTime(scope.row.submissionTime, '{y}-{m}-{d} {h}:{i}') : '-' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审核状态" prop="approvalStatus" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getApprovalStatusTagType(scope.row.approvalStatus)" size="small">
|
||||
{{ getApprovalStatusText(scope.row.approvalStatus) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="200" fixed="right" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<div class="action-buttons">
|
||||
<el-tooltip content="查看详情" placement="top">
|
||||
<el-button link type="info" icon="View" size="small" @click="handleView(scope.row)" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="编辑" placement="top">
|
||||
<el-button link type="primary" icon="Edit" size="small" @click="handleEdit(scope.row)" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="查看进度" placement="top">
|
||||
<el-button link type="warning" icon="DataAnalysis" size="small" @click="handleProgress(scope.row)" />
|
||||
</el-tooltip>
|
||||
<template v-if="scope.row.submissionStatus === 'DRAFT'">
|
||||
<el-tooltip content="提交" placement="top">
|
||||
<el-button link type="success" icon="Upload" size="small" @click="handleSubmit(scope.row)" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<template v-else-if="scope.row.submissionStatus === 'SUBMITTED'">
|
||||
<el-tooltip content="撤回" placement="top">
|
||||
<el-button link type="danger" icon="Download" size="small" @click="handleWithdraw(scope.row)" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="pagination-container">
|
||||
<pagination v-show="total > 0" :total="total" :page="queryParams.pageNum" :limit="queryParams.pageSize" @pagination="getList" />
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 关联学生到批次对话框 -->
|
||||
<el-dialog title="关联学生到批次" v-model="associateDialog.visible" width="600px" append-to-body>
|
||||
<el-form ref="associateFormRef" :model="associateForm" :rules="associateRules" label-width="100px">
|
||||
<el-form-item label="选择批次" prop="batchId">
|
||||
<el-select v-model="associateForm.batchId" placeholder="请选择批次" style="width: 100%">
|
||||
<el-option v-for="batch in batchOptions" :key="batch.id" :label="batch.batchName" :value="batch.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="选择学生" prop="studentIds">
|
||||
<el-select v-model="associateForm.studentIds" multiple placeholder="请选择学生" style="width: 100%">
|
||||
<el-option v-for="student in allStudents" :key="student.id" :label="`${student.name}(${student.studentNo})`" :value="student.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="associateDialog.visible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitAssociate">确定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 学生进度对话框 -->
|
||||
<el-dialog title="学生批次进度" v-model="progressDialog.visible" width="800px" append-to-body>
|
||||
<div v-if="progressInfo" class="progress-content">
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="学生姓名">{{ progressInfo.studentName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="学号">{{ progressInfo.studentNo }}</el-descriptions-item>
|
||||
<el-descriptions-item label="批次名称">{{ progressInfo.batchName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="提交状态">
|
||||
<el-tag :type="getSubmissionStatusTagType(progressInfo.submissionStatus)">
|
||||
{{ getSubmissionStatusText(progressInfo.submissionStatus) }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="审核状态">
|
||||
<el-tag :type="getApprovalStatusTagType(progressInfo.approvalStatus)">
|
||||
{{ getApprovalStatusText(progressInfo.approvalStatus) }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="提交时间">
|
||||
{{ progressInfo.submissionTime ? proxy.parseTime(progressInfo.submissionTime, '{y}-{m}-{d} {h}:{i}:{s}') : '-' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="审核时间">
|
||||
{{ progressInfo.approvalTime ? proxy.parseTime(progressInfo.approvalTime, '{y}-{m}-{d} {h}:{i}:{s}') : '-' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="审核意见" :span="2">
|
||||
{{ progressInfo.approvalComments || '-' }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted, getCurrentInstance } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { listAllCollectionBatch } from '@/api/xg-xx/collectionBatch';
|
||||
import {
|
||||
getStudentsByBatch,
|
||||
associateStudentToBatch,
|
||||
batchAssociateStudentsToBatch,
|
||||
submitStudentInfo,
|
||||
withdrawStudentInfo,
|
||||
getStudentBatchProgress,
|
||||
batchAuditStudentInfo,
|
||||
checkEditPermission,
|
||||
getStudentBatchStatistics,
|
||||
batchSubmitStudentInfo,
|
||||
batchWithdrawStudentInfo,
|
||||
getAllStudentOptions
|
||||
} from '@/api/xg-xx/studentInfo';
|
||||
|
||||
const { proxy } = getCurrentInstance() as any;
|
||||
|
||||
// 响应式数据
|
||||
const loading = ref(false);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref<number[]>([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const studentList = ref<any[]>([]);
|
||||
const batchOptions = ref<any[]>([]);
|
||||
const allStudents = ref<any[]>([]);
|
||||
const selectedBatchId = ref<number>();
|
||||
const currentBatch = ref<any>();
|
||||
|
||||
// 批次统计信息
|
||||
const batchStatistics = reactive({
|
||||
totalCount: 0,
|
||||
submittedCount: 0,
|
||||
approvedCount: 0,
|
||||
rejectedCount: 0,
|
||||
completionRate: 0
|
||||
});
|
||||
|
||||
// 查询参数
|
||||
const queryParams = reactive({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
batchId: undefined as number | undefined,
|
||||
name: undefined,
|
||||
studentNo: undefined,
|
||||
className: undefined,
|
||||
submissionStatus: undefined,
|
||||
collegeName: undefined
|
||||
});
|
||||
|
||||
// 关联对话框
|
||||
const associateDialog = reactive({
|
||||
visible: false
|
||||
});
|
||||
|
||||
const associateForm = reactive({
|
||||
batchId: undefined as number | undefined,
|
||||
studentIds: [] as number[]
|
||||
});
|
||||
|
||||
const associateRules = {
|
||||
batchId: [{ required: true, message: '请选择批次', trigger: 'change' }],
|
||||
studentIds: [{ required: true, message: '请选择学生', trigger: 'change' }]
|
||||
};
|
||||
|
||||
// 进度对话框
|
||||
const progressDialog = reactive({
|
||||
visible: false
|
||||
});
|
||||
|
||||
const progressInfo = ref<any>();
|
||||
|
||||
// 获取列表数据
|
||||
const getList = async () => {
|
||||
if (!selectedBatchId.value) {
|
||||
studentList.value = [];
|
||||
total.value = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
loading.value = true;
|
||||
try {
|
||||
const response = await getStudentsByBatch(selectedBatchId.value, queryParams);
|
||||
studentList.value = response.rows;
|
||||
total.value = response.total;
|
||||
} catch (error) {
|
||||
console.error('获取列表失败:', error);
|
||||
ElMessage.error('获取学生列表失败');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 获取批次选项
|
||||
const getBatchOptions = async () => {
|
||||
try {
|
||||
const response = await listAllCollectionBatch({});
|
||||
batchOptions.value = response.data;
|
||||
} catch (error) {
|
||||
console.error('获取批次选项失败:', error);
|
||||
ElMessage.error('获取批次选项失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 获取所有学生选项
|
||||
const getAllStudentsList = async () => {
|
||||
try {
|
||||
const response = await getAllStudentOptions();
|
||||
allStudents.value = response.data;
|
||||
} catch (error) {
|
||||
console.error('获取学生选项失败:', error);
|
||||
ElMessage.error('获取学生选项失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 批次改变处理
|
||||
const handleBatchChange = (batchId: number) => {
|
||||
selectedBatchId.value = batchId;
|
||||
queryParams.batchId = batchId;
|
||||
currentBatch.value = batchOptions.value.find(batch => batch.id === batchId);
|
||||
|
||||
// 获取批次统计信息
|
||||
getBatchStatistics();
|
||||
|
||||
// 刷新列表
|
||||
getList();
|
||||
};
|
||||
|
||||
// 获取批次统计信息
|
||||
const getBatchStatistics = async () => {
|
||||
if (!selectedBatchId.value) return;
|
||||
|
||||
try {
|
||||
const response = await getStudentBatchStatistics(selectedBatchId.value);
|
||||
Object.assign(batchStatistics, response.data);
|
||||
} catch (error) {
|
||||
console.error('获取批次统计失败:', error);
|
||||
ElMessage.error('获取批次统计失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 刷新批次信息
|
||||
const refreshBatchInfo = () => {
|
||||
getBatchStatistics();
|
||||
getList();
|
||||
};
|
||||
|
||||
// 搜索
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
// 重置搜索
|
||||
const resetQuery = () => {
|
||||
proxy.resetForm('queryFormRef');
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
// 多选框选中数据
|
||||
const handleSelectionChange = (selection: any[]) => {
|
||||
ids.value = selection.map(item => item.id);
|
||||
single.value = selection.length !== 1;
|
||||
multiple.value = !selection.length;
|
||||
};
|
||||
|
||||
// 排序变化
|
||||
const handleSortChange = (column: any) => {
|
||||
getList();
|
||||
};
|
||||
|
||||
// 关联学生到批次
|
||||
const handleAssociate = () => {
|
||||
associateDialog.visible = true;
|
||||
associateForm.batchId = selectedBatchId.value;
|
||||
associateForm.studentIds = [];
|
||||
|
||||
// 获取所有学生选项
|
||||
getAllStudentsList();
|
||||
};
|
||||
|
||||
// 批量关联
|
||||
const handleBatchAssociate = () => {
|
||||
if (ids.value.length === 0) {
|
||||
ElMessage.warning('请选择要关联的学生');
|
||||
return;
|
||||
}
|
||||
|
||||
associateDialog.visible = true;
|
||||
associateForm.batchId = selectedBatchId.value;
|
||||
associateForm.studentIds = ids.value;
|
||||
|
||||
// 获取所有学生选项
|
||||
getAllStudentsList();
|
||||
};
|
||||
|
||||
// 提交关联
|
||||
const submitAssociate = async () => {
|
||||
if (!associateForm.batchId || associateForm.studentIds.length === 0) {
|
||||
ElMessage.warning('请选择批次和学生');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await batchAssociateStudentsToBatch(associateForm.studentIds, associateForm.batchId);
|
||||
|
||||
ElMessage.success('关联成功');
|
||||
associateDialog.visible = false;
|
||||
|
||||
// 刷新统计信息和列表
|
||||
getBatchStatistics();
|
||||
getList();
|
||||
} catch (error) {
|
||||
console.error('关联失败:', error);
|
||||
ElMessage.error('关联失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 批量提交
|
||||
const handleBatchSubmit = async () => {
|
||||
if (ids.value.length === 0) {
|
||||
ElMessage.warning('请选择要提交的学生');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!selectedBatchId.value) {
|
||||
ElMessage.warning('请先选择批次');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要批量提交选中的学生信息吗?', '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
await batchSubmitStudentInfo(ids.value, selectedBatchId.value);
|
||||
|
||||
ElMessage.success('批量提交成功');
|
||||
|
||||
// 刷新统计信息和列表
|
||||
getBatchStatistics();
|
||||
getList();
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('批量提交失败:', error);
|
||||
ElMessage.error('批量提交失败');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 批量撤回
|
||||
const handleBatchWithdraw = async () => {
|
||||
if (ids.value.length === 0) {
|
||||
ElMessage.warning('请选择要撤回的学生');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!selectedBatchId.value) {
|
||||
ElMessage.warning('请先选择批次');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要批量撤回选中的学生信息吗?', '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
await batchWithdrawStudentInfo(ids.value, selectedBatchId.value);
|
||||
|
||||
ElMessage.success('批量撤回成功');
|
||||
|
||||
// 刷新统计信息和列表
|
||||
getBatchStatistics();
|
||||
getList();
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('批量撤回失败:', error);
|
||||
ElMessage.error('批量撤回失败');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 查看详情
|
||||
const handleView = (row: any) => {
|
||||
proxy.$router.push(`/xg-xx/studentInfo/detail/${row.id}`);
|
||||
};
|
||||
|
||||
// 编辑
|
||||
const handleEdit = (row: any) => {
|
||||
proxy.$router.push(`/xg-xx/studentInfo/edit/${row.id}`);
|
||||
};
|
||||
|
||||
// 提交
|
||||
const handleSubmit = async (row: any) => {
|
||||
if (!selectedBatchId.value) {
|
||||
ElMessage.warning('请先选择批次');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要提交该学生信息吗?', '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
await submitStudentInfo(row.id, selectedBatchId.value);
|
||||
|
||||
ElMessage.success('提交成功');
|
||||
|
||||
// 刷新统计信息和列表
|
||||
getBatchStatistics();
|
||||
getList();
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('提交失败:', error);
|
||||
ElMessage.error('提交失败');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 撤回
|
||||
const handleWithdraw = async (row: any) => {
|
||||
if (!selectedBatchId.value) {
|
||||
ElMessage.warning('请先选择批次');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要撤回该学生信息吗?', '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
await withdrawStudentInfo(row.id, selectedBatchId.value);
|
||||
|
||||
ElMessage.success('撤回成功');
|
||||
|
||||
// 刷新统计信息和列表
|
||||
getBatchStatistics();
|
||||
getList();
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('撤回失败:', error);
|
||||
ElMessage.error('撤回失败');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 查看进度
|
||||
const handleProgress = async (row: any) => {
|
||||
if (!selectedBatchId.value) {
|
||||
ElMessage.warning('请先选择批次');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await getStudentBatchProgress(row.id, selectedBatchId.value);
|
||||
progressInfo.value = response.data;
|
||||
progressDialog.visible = true;
|
||||
} catch (error) {
|
||||
console.error('获取进度失败:', error);
|
||||
ElMessage.error('获取进度失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 导出
|
||||
const handleExport = () => {
|
||||
proxy?.download(
|
||||
'xg/studentInfo/export',
|
||||
queryParams,
|
||||
`学生信息_${proxy.parseTime(new Date(), '{y}{m}{d}{h}{i}{s}')}.xlsx`
|
||||
);
|
||||
};
|
||||
|
||||
// 刷新
|
||||
const handleRefresh = () => {
|
||||
getList();
|
||||
};
|
||||
|
||||
// 获取批次状态文本
|
||||
const getBatchStatusText = (status: string) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
'DRAFT': '草稿',
|
||||
'ACTIVE': '进行中',
|
||||
'COMPLETED': '已完成',
|
||||
'CANCELLED': '已取消'
|
||||
};
|
||||
return statusMap[status] || '未知';
|
||||
};
|
||||
|
||||
// 获取提交状态标签类型
|
||||
const getSubmissionStatusTagType = (status: string): 'success' | 'warning' | 'info' | 'primary' | 'danger' => {
|
||||
const statusMap: Record<string, 'success' | 'warning' | 'info' | 'primary' | 'danger'> = {
|
||||
'DRAFT': 'info',
|
||||
'SUBMITTED': 'warning',
|
||||
'APPROVED': 'success',
|
||||
'REJECTED': 'danger'
|
||||
};
|
||||
return statusMap[status] || 'info';
|
||||
};
|
||||
|
||||
// 获取提交状态文本
|
||||
const getSubmissionStatusText = (status: string) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
'DRAFT': '草稿',
|
||||
'SUBMITTED': '已提交',
|
||||
'APPROVED': '已通过',
|
||||
'REJECTED': '已拒绝'
|
||||
};
|
||||
return statusMap[status] || '未知';
|
||||
};
|
||||
|
||||
// 获取审核状态标签类型
|
||||
const getApprovalStatusTagType = (status: string): 'success' | 'warning' | 'info' | 'primary' | 'danger' => {
|
||||
const statusMap: Record<string, 'success' | 'warning' | 'info' | 'primary' | 'danger'> = {
|
||||
'PENDING': 'warning',
|
||||
'APPROVED': 'success',
|
||||
'REJECTED': 'danger'
|
||||
};
|
||||
return statusMap[status] || 'info';
|
||||
};
|
||||
|
||||
// 获取审核状态文本
|
||||
const getApprovalStatusText = (status: string) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
'PENDING': '待审核',
|
||||
'APPROVED': '已通过',
|
||||
'REJECTED': '已拒绝'
|
||||
};
|
||||
return statusMap[status] || '未知';
|
||||
};
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
getBatchOptions();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-container {
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.batch-info {
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
.toolbar-wrapper {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.toolbar-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.toolbar-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.table-wrapper {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.pagination-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.progress-info {
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.progress-content {
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
.mt-4 {
|
||||
margin-top: 16px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,779 +0,0 @@
|
||||
<template>
|
||||
<div class="batch-management-container">
|
||||
<!-- 搜索区域 -->
|
||||
<el-card shadow="hover" class="mb-4">
|
||||
<el-form ref="queryFormRef" :model="queryParams" label-width="100px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="批次名称" prop="batchName">
|
||||
<el-input v-model="queryParams.batchName" placeholder="请输入批次名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="批次类型" prop="batchType">
|
||||
<el-select v-model="queryParams.batchType" placeholder="请选择批次类型" clearable style="width: 100%">
|
||||
<el-option label="常规批次" value="regular" />
|
||||
<el-option label="专项批次" value="special" />
|
||||
<el-option label="紧急批次" value="urgent" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="批次状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择批次状态" clearable style="width: 100%">
|
||||
<el-option label="草稿" value="DRAFT" />
|
||||
<el-option label="活跃" value="ACTIVE" />
|
||||
<el-option label="已完成" value="COMPLETED" />
|
||||
<el-option label="已取消" value="CANCELLED" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<!-- 主要内容区域 -->
|
||||
<el-card shadow="hover">
|
||||
<!-- 操作按钮区域 -->
|
||||
<div class="toolbar-wrapper">
|
||||
<div class="toolbar-left">
|
||||
<el-button type="primary" icon="Plus" @click="handleAdd">
|
||||
新增批次
|
||||
</el-button>
|
||||
<el-button type="success" icon="Upload" :disabled="!multiple || ids.length === 0" @click="handleBatchPublish">
|
||||
批量发布
|
||||
</el-button>
|
||||
<el-button type="warning" icon="Download" :disabled="!multiple || ids.length === 0" @click="handleBatchUnpublish">
|
||||
批量取消发布
|
||||
</el-button>
|
||||
<el-button type="danger" icon="Delete" :disabled="!multiple || ids.length === 0" @click="handleDelete">
|
||||
批量删除
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<div class="toolbar-right">
|
||||
<el-button type="primary" icon="Download" @click="handleExport">
|
||||
导出
|
||||
</el-button>
|
||||
<el-button type="info" icon="Refresh" @click="handleRefresh">
|
||||
刷新
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 表格区域 -->
|
||||
<div class="table-wrapper">
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="batchList"
|
||||
@selection-change="handleSelectionChange"
|
||||
@sort-change="handleSortChange"
|
||||
row-key="id"
|
||||
border
|
||||
stripe
|
||||
size="small"
|
||||
>
|
||||
<el-table-column type="selection" width="50" align="center" />
|
||||
<el-table-column label="序号" type="index" width="60" align="center" />
|
||||
<el-table-column label="批次名称" prop="batchName" width="150" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="批次编码" prop="batchCode" width="120" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="批次类型" prop="batchType" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getBatchTypeTagType(scope.row.batchType)" size="small">
|
||||
{{ getBatchTypeText(scope.row.batchType) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" prop="status" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getStatusTagType(scope.row.status)" size="small">
|
||||
{{ getStatusText(scope.row.status) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="发布状态" prop="isPublished" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.isPublished ? 'success' : 'info'" size="small">
|
||||
{{ scope.row.isPublished ? '已发布' : '未发布' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="开始时间" prop="startTime" width="160" align="center" sortable="custom">
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.startTime ? parseTime(scope.row.startTime, '{y}-{m}-{d} {h}:{i}') : '-' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="结束时间" prop="endTime" width="160" align="center" sortable="custom">
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.endTime ? parseTime(scope.row.endTime, '{y}-{m}-{d} {h}:{i}') : '-' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="总人数" prop="totalCount" width="80" align="center" />
|
||||
<el-table-column label="完成率" prop="completionRate" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-progress :percentage="scope.row.completionRate" :stroke-width="6" :show-text="false" />
|
||||
<span class="ml-2">{{ scope.row.completionRate }}%</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="200" fixed="right" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<div class="action-buttons">
|
||||
<el-tooltip content="查看详情" placement="top">
|
||||
<el-button link type="info" icon="View" size="small" @click="handleView(scope.row)" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="编辑" placement="top">
|
||||
<el-button link type="primary" icon="Edit" size="small" @click="handleEdit(scope.row)" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="统计" placement="top">
|
||||
<el-button link type="warning" icon="DataAnalysis" size="small" @click="handleStatistics(scope.row)" />
|
||||
</el-tooltip>
|
||||
<template v-if="!scope.row.isPublished">
|
||||
<el-tooltip content="发布" placement="top">
|
||||
<el-button link type="success" icon="Upload" size="small" @click="handlePublish(scope.row)" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-tooltip content="取消发布" placement="top">
|
||||
<el-button link type="danger" icon="Download" size="small" @click="handleUnpublish(scope.row)" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<el-tooltip content="复制" placement="top">
|
||||
<el-button link type="info" icon="CopyDocument" size="small" @click="handleCopy(scope.row)" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button link type="danger" icon="Delete" size="small" @click="handleDelete(scope.row)" />
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="pagination-container">
|
||||
<el-pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
:page-size="queryParams.pageSize"
|
||||
:current-page="queryParams.pageNum"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 批次详情对话框 -->
|
||||
<el-dialog
|
||||
:title="dialogTitle"
|
||||
v-model="batchDialog.visible"
|
||||
width="800px"
|
||||
append-to-body
|
||||
>
|
||||
<el-form ref="batchFormRef" :model="batchForm" :rules="batchRules" label-width="120px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="批次名称" prop="batchName">
|
||||
<el-input v-model="batchForm.batchName" placeholder="请输入批次名称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="批次编码" prop="batchCode">
|
||||
<el-input v-model="batchForm.batchCode" placeholder="请输入批次编码" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="批次类型" prop="batchType">
|
||||
<el-select v-model="batchForm.batchType" placeholder="请选择批次类型" style="width: 100%">
|
||||
<el-option label="常规批次" value="regular" />
|
||||
<el-option label="专项批次" value="special" />
|
||||
<el-option label="紧急批次" value="urgent" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="目标范围" prop="targetScope">
|
||||
<el-select v-model="batchForm.targetScope" placeholder="请选择目标范围" style="width: 100%">
|
||||
<el-option label="全部学生" value="all" />
|
||||
<el-option label="按学院" value="college" />
|
||||
<el-option label="按班级" value="class" />
|
||||
<el-option label="按学生" value="student" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="开始时间" prop="startTime">
|
||||
<el-date-picker
|
||||
v-model="batchForm.startTime"
|
||||
type="datetime"
|
||||
placeholder="请选择开始时间"
|
||||
format="YYYY-MM-DD HH:mm"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="结束时间" prop="endTime">
|
||||
<el-date-picker
|
||||
v-model="batchForm.endTime"
|
||||
type="datetime"
|
||||
placeholder="请选择结束时间"
|
||||
format="YYYY-MM-DD HH:mm"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="自动提醒" prop="autoRemind">
|
||||
<el-switch v-model="batchForm.autoRemind" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="提醒天数" prop="remindDays">
|
||||
<el-input-number v-model="batchForm.remindDays" :min="1" :max="30" placeholder="结束前几天提醒" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item label="批次描述" prop="description">
|
||||
<el-input v-model="batchForm.description" type="textarea" :rows="3" placeholder="请输入批次描述" />
|
||||
</el-form-item>
|
||||
<el-form-item label="通知内容" prop="noticeContent">
|
||||
<el-input v-model="batchForm.noticeContent" type="textarea" :rows="4" placeholder="请输入通知内容" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="batchDialog.visible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitBatch">确定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 批次统计对话框 -->
|
||||
<el-dialog title="批次统计" v-model="statisticsDialog.visible" width="900px" append-to-body>
|
||||
<div v-if="statisticsInfo" class="statistics-content">
|
||||
<el-row :gutter="20" class="mb-4">
|
||||
<el-col :span="6">
|
||||
<el-statistic title="总人数" :value="statisticsInfo.totalCount" />
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-statistic title="已提交" :value="statisticsInfo.submittedCount" />
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-statistic title="已通过" :value="statisticsInfo.approvedCount" />
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-statistic title="完成率" :value="statisticsInfo.completionRate" suffix="%" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-progress :percentage="statisticsInfo.completionRate" :stroke-width="12" />
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="statistics-chart">
|
||||
<!-- 这里可以集成图表组件 -->
|
||||
<p>统计图表区域</p>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted, getCurrentInstance, computed } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { parseTime } from '@/utils/ruoyi';
|
||||
import {
|
||||
listCollectionBatch,
|
||||
getCollectionBatch,
|
||||
addCollectionBatch,
|
||||
updateCollectionBatch,
|
||||
delCollectionBatch,
|
||||
publishCollectionBatch,
|
||||
unpublishCollectionBatch,
|
||||
getCollectionBatchStatistics,
|
||||
copyCollectionBatch
|
||||
} from '@/api/xg-xx/collectionBatch';
|
||||
|
||||
const { proxy } = getCurrentInstance() as any;
|
||||
|
||||
// 定义 emit
|
||||
const emit = defineEmits(['batch-created', 'batch-updated', 'batch-deleted']);
|
||||
|
||||
// 响应式数据
|
||||
const loading = ref(false);
|
||||
const ids = ref<number[]>([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const batchList = ref<any[]>([]);
|
||||
|
||||
// 查询参数
|
||||
const queryParams = reactive({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
batchName: undefined,
|
||||
batchType: undefined,
|
||||
status: undefined
|
||||
});
|
||||
|
||||
// 批次对话框
|
||||
const batchDialog = reactive({
|
||||
visible: false,
|
||||
type: 'add'
|
||||
});
|
||||
|
||||
const batchForm = reactive({
|
||||
id: undefined,
|
||||
batchName: '',
|
||||
batchCode: '',
|
||||
batchType: 'regular',
|
||||
targetScope: 'all',
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
autoRemind: true,
|
||||
remindDays: 3,
|
||||
description: '',
|
||||
noticeContent: ''
|
||||
});
|
||||
|
||||
const batchRules = {
|
||||
batchName: [{ required: true, message: '请输入批次名称', trigger: 'blur' }],
|
||||
batchCode: [{ required: true, message: '请输入批次编码', trigger: 'blur' }],
|
||||
batchType: [{ required: true, message: '请选择批次类型', trigger: 'change' }],
|
||||
targetScope: [{ required: true, message: '请选择目标范围', trigger: 'change' }],
|
||||
startTime: [{ required: true, message: '请选择开始时间', trigger: 'change' }],
|
||||
endTime: [{ required: true, message: '请选择结束时间', trigger: 'change' }]
|
||||
};
|
||||
|
||||
// 统计对话框
|
||||
const statisticsDialog = reactive({
|
||||
visible: false
|
||||
});
|
||||
|
||||
const statisticsInfo = ref<any>();
|
||||
|
||||
// 计算属性
|
||||
const dialogTitle = computed(() => {
|
||||
return batchDialog.type === 'add' ? '新增批次' : '编辑批次';
|
||||
});
|
||||
|
||||
// 获取列表数据
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const response = await listCollectionBatch(queryParams);
|
||||
batchList.value = response.rows;
|
||||
total.value = response.total;
|
||||
} catch (error) {
|
||||
console.error('获取批次列表失败:', error);
|
||||
ElMessage.error('获取批次列表失败');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 搜索
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
// 重置搜索
|
||||
const resetQuery = () => {
|
||||
proxy.resetForm('queryFormRef');
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
// 多选框选中数据
|
||||
const handleSelectionChange = (selection: any[]) => {
|
||||
ids.value = selection.map(item => item.id);
|
||||
single.value = selection.length !== 1;
|
||||
multiple.value = !selection.length;
|
||||
};
|
||||
|
||||
// 排序变化
|
||||
const handleSortChange = (column: any) => {
|
||||
getList();
|
||||
};
|
||||
|
||||
// 分页大小变化
|
||||
const handleSizeChange = (val: number) => {
|
||||
queryParams.pageSize = val;
|
||||
getList();
|
||||
};
|
||||
|
||||
// 当前页变化
|
||||
const handleCurrentChange = (val: number) => {
|
||||
queryParams.pageNum = val;
|
||||
getList();
|
||||
};
|
||||
|
||||
// 新增批次
|
||||
const handleAdd = () => {
|
||||
resetBatchForm();
|
||||
batchDialog.type = 'add';
|
||||
batchDialog.visible = true;
|
||||
};
|
||||
|
||||
// 编辑批次
|
||||
const handleEdit = async (row: any) => {
|
||||
try {
|
||||
const response = await getCollectionBatch(row.id);
|
||||
Object.assign(batchForm, response.data);
|
||||
batchDialog.type = 'edit';
|
||||
batchDialog.visible = true;
|
||||
} catch (error) {
|
||||
console.error('获取批次详情失败:', error);
|
||||
ElMessage.error('获取批次详情失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 查看详情
|
||||
const handleView = async (row: any) => {
|
||||
try {
|
||||
const response = await getCollectionBatch(row.id);
|
||||
Object.assign(batchForm, response.data);
|
||||
batchDialog.type = 'view';
|
||||
batchDialog.visible = true;
|
||||
} catch (error) {
|
||||
console.error('获取批次详情失败:', error);
|
||||
ElMessage.error('获取批次详情失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 批次统计
|
||||
const handleStatistics = async (row: any) => {
|
||||
try {
|
||||
const response = await getCollectionBatchStatistics(row.id);
|
||||
statisticsInfo.value = response.data;
|
||||
statisticsDialog.visible = true;
|
||||
} catch (error) {
|
||||
console.error('获取批次统计失败:', error);
|
||||
ElMessage.error('获取批次统计失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 发布批次
|
||||
const handlePublish = async (row: any) => {
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要发布该批次吗?', '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
await publishCollectionBatch(row.id);
|
||||
ElMessage.success('发布成功');
|
||||
getList();
|
||||
emit('batch-updated');
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('发布失败:', error);
|
||||
ElMessage.error('发布失败');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 取消发布
|
||||
const handleUnpublish = async (row: any) => {
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要取消发布该批次吗?', '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
await unpublishCollectionBatch(row.id);
|
||||
ElMessage.success('取消发布成功');
|
||||
getList();
|
||||
emit('batch-updated');
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('取消发布失败:', error);
|
||||
ElMessage.error('取消发布失败');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 复制批次
|
||||
const handleCopy = async (row: any) => {
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要复制该批次吗?', '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'info'
|
||||
});
|
||||
|
||||
await copyCollectionBatch(row.id, {
|
||||
batchName: row.batchName + '_副本',
|
||||
batchCode: row.batchCode + '_copy'
|
||||
});
|
||||
|
||||
ElMessage.success('复制成功');
|
||||
getList();
|
||||
emit('batch-created');
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('复制失败:', error);
|
||||
ElMessage.error('复制失败');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 批量发布
|
||||
const handleBatchPublish = async () => {
|
||||
if (ids.value.length === 0) {
|
||||
ElMessage.warning('请选择要发布的批次');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要批量发布选中的批次吗?', '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
// 批量发布逻辑
|
||||
for (const id of ids.value) {
|
||||
await publishCollectionBatch(id);
|
||||
}
|
||||
|
||||
ElMessage.success('批量发布成功');
|
||||
getList();
|
||||
emit('batch-updated');
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('批量发布失败:', error);
|
||||
ElMessage.error('批量发布失败');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 批量取消发布
|
||||
const handleBatchUnpublish = async () => {
|
||||
if (ids.value.length === 0) {
|
||||
ElMessage.warning('请选择要取消发布的批次');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要批量取消发布选中的批次吗?', '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
// 批量取消发布逻辑
|
||||
for (const id of ids.value) {
|
||||
await unpublishCollectionBatch(id);
|
||||
}
|
||||
|
||||
ElMessage.success('批量取消发布成功');
|
||||
getList();
|
||||
emit('batch-updated');
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('批量取消发布失败:', error);
|
||||
ElMessage.error('批量取消发布失败');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 删除批次
|
||||
const handleDelete = async (row?: any) => {
|
||||
const batchIds = row ? [row.id] : ids.value;
|
||||
if (batchIds.length === 0) {
|
||||
ElMessage.warning('请选择要删除的批次');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要删除选中的批次吗?', '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
await delCollectionBatch(batchIds);
|
||||
ElMessage.success('删除成功');
|
||||
getList();
|
||||
emit('batch-deleted');
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('删除失败:', error);
|
||||
ElMessage.error('删除失败');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 导出
|
||||
const handleExport = () => {
|
||||
proxy?.download(
|
||||
'xg/collectionBatch/export',
|
||||
queryParams,
|
||||
`批次信息_${parseTime(new Date(), '{y}{m}{d}{h}{i}{s}')}.xlsx`
|
||||
);
|
||||
};
|
||||
|
||||
// 刷新
|
||||
const handleRefresh = () => {
|
||||
getList();
|
||||
};
|
||||
|
||||
// 提交批次
|
||||
const submitBatch = async () => {
|
||||
try {
|
||||
const valid = await proxy.$refs.batchFormRef.validate();
|
||||
if (!valid) return;
|
||||
|
||||
if (batchDialog.type === 'add') {
|
||||
await addCollectionBatch(batchForm);
|
||||
ElMessage.success('新增成功');
|
||||
emit('batch-created');
|
||||
} else {
|
||||
await updateCollectionBatch(batchForm);
|
||||
ElMessage.success('修改成功');
|
||||
emit('batch-updated');
|
||||
}
|
||||
|
||||
batchDialog.visible = false;
|
||||
getList();
|
||||
} catch (error) {
|
||||
console.error('提交失败:', error);
|
||||
ElMessage.error('提交失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 重置表单
|
||||
const resetBatchForm = () => {
|
||||
Object.assign(batchForm, {
|
||||
id: undefined,
|
||||
batchName: '',
|
||||
batchCode: '',
|
||||
batchType: 'regular',
|
||||
targetScope: 'all',
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
autoRemind: true,
|
||||
remindDays: 3,
|
||||
description: '',
|
||||
noticeContent: ''
|
||||
});
|
||||
};
|
||||
|
||||
// 获取批次类型标签类型
|
||||
const getBatchTypeTagType = (type: string) => {
|
||||
const typeMap: Record<string, string> = {
|
||||
'regular': '',
|
||||
'special': 'warning',
|
||||
'urgent': 'danger'
|
||||
};
|
||||
return typeMap[type] || '';
|
||||
};
|
||||
|
||||
// 获取批次类型文本
|
||||
const getBatchTypeText = (type: string) => {
|
||||
const typeMap: Record<string, string> = {
|
||||
'regular': '常规',
|
||||
'special': '专项',
|
||||
'urgent': '紧急'
|
||||
};
|
||||
return typeMap[type] || '未知';
|
||||
};
|
||||
|
||||
// 获取状态标签类型
|
||||
const getStatusTagType = (status: string) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
'DRAFT': 'info',
|
||||
'ACTIVE': 'success',
|
||||
'COMPLETED': 'warning',
|
||||
'CANCELLED': 'danger'
|
||||
};
|
||||
return statusMap[status] || '';
|
||||
};
|
||||
|
||||
// 获取状态文本
|
||||
const getStatusText = (status: string) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
'DRAFT': '草稿',
|
||||
'ACTIVE': '活跃',
|
||||
'COMPLETED': '已完成',
|
||||
'CANCELLED': '已取消'
|
||||
};
|
||||
return statusMap[status] || '未知';
|
||||
};
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.batch-management-container {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.toolbar-wrapper {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.toolbar-left {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.toolbar-right {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.table-wrapper {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.pagination-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.statistics-content {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.statistics-chart {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 200px;
|
||||
background: #f5f5f5;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,925 +0,0 @@
|
||||
<template>
|
||||
<div class="p-2 page-container">
|
||||
<!-- 页面标题 -->
|
||||
<div class="page-header mb-4">
|
||||
<h2 class="page-title">学生信息管理</h2>
|
||||
<p class="page-description">管理学生基本信息、信息采集批次以及审核流程</p>
|
||||
</div>
|
||||
|
||||
<!-- 标签页结构 -->
|
||||
<el-tabs v-model="activeTab" type="card" class="main-tabs">
|
||||
<!-- 学生信息列表标签页 -->
|
||||
<el-tab-pane label="学生信息列表" name="studentList">
|
||||
<div class="tab-content">
|
||||
<!-- 批次选择区域 -->
|
||||
<el-card shadow="hover" class="mb-4">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>批次选择</span>
|
||||
<div class="header-actions">
|
||||
<el-select v-model="selectedBatchId" placeholder="选择批次" @change="handleBatchChange" style="width: 200px; margin-right: 10px;">
|
||||
<el-option label="全部学生" :value="null" />
|
||||
<el-option v-for="batch in batchOptions" :key="batch.id" :label="batch.batchName" :value="batch.id" />
|
||||
</el-select>
|
||||
<el-button type="primary" icon="Refresh" @click="refreshBatchInfo">刷新</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 批次统计信息 -->
|
||||
<div v-if="currentBatch" class="batch-info">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-statistic title="批次名称" :value="currentBatch.batchName" />
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-statistic title="批次状态" :value="getBatchStatusText(currentBatch.status)" />
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-statistic title="总学生数" :value="batchStatistics.totalCount" />
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-statistic title="已提交数" :value="batchStatistics.submittedCount" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20" class="mt-4">
|
||||
<el-col :span="6">
|
||||
<el-statistic title="已通过数" :value="batchStatistics.approvedCount" />
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-statistic title="已拒绝数" :value="batchStatistics.rejectedCount" />
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-statistic title="完成率" :value="batchStatistics.completionRate" suffix="%" />
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div class="progress-info">
|
||||
<el-progress :percentage="batchStatistics.completionRate" :stroke-width="8" />
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 搜索区域 -->
|
||||
<el-card shadow="hover" class="mb-4">
|
||||
<el-form ref="queryFormRef" :model="queryParams" label-width="100px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="学生姓名" prop="name">
|
||||
<el-input v-model="queryParams.name" placeholder="请输入学生姓名" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="学号" prop="studentNo">
|
||||
<el-input v-model="queryParams.studentNo" placeholder="请输入学号" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="班级" prop="className">
|
||||
<el-input v-model="queryParams.className" placeholder="请输入班级名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="提交状态" prop="submissionStatus">
|
||||
<el-select v-model="queryParams.submissionStatus" placeholder="请选择提交状态" clearable style="width: 100%">
|
||||
<el-option label="草稿" value="DRAFT" />
|
||||
<el-option label="已提交" value="SUBMITTED" />
|
||||
<el-option label="已通过" value="APPROVED" />
|
||||
<el-option label="已拒绝" value="REJECTED" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="学院" prop="collegeName">
|
||||
<el-input v-model="queryParams.collegeName" placeholder="请输入学院名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="18">
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<!-- 主要内容区域 -->
|
||||
<el-card shadow="hover">
|
||||
<!-- 操作按钮区域 -->
|
||||
<div class="toolbar-wrapper">
|
||||
<div class="toolbar-left">
|
||||
<el-button type="primary" icon="Plus" @click="handleAdd">
|
||||
新增学生
|
||||
</el-button>
|
||||
<el-button type="success" icon="Select" :disabled="!multiple || ids.length === 0" @click="handleBatchAssociate">
|
||||
批量关联批次
|
||||
</el-button>
|
||||
<el-button type="warning" icon="Upload" :disabled="!multiple || ids.length === 0" @click="handleBatchSubmit">
|
||||
批量提交
|
||||
</el-button>
|
||||
<el-button type="info" icon="Download" :disabled="!multiple || ids.length === 0" @click="handleBatchWithdraw">
|
||||
批量撤回
|
||||
</el-button>
|
||||
<el-button type="danger" icon="Delete" :disabled="!multiple || ids.length === 0" @click="handleDelete">
|
||||
批量删除
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<div class="toolbar-right">
|
||||
<el-button type="primary" icon="Upload" @click="handleImport">
|
||||
导入
|
||||
</el-button>
|
||||
<el-button type="danger" icon="Download" @click="handleExport">
|
||||
导出
|
||||
</el-button>
|
||||
<el-button type="info" icon="Refresh" @click="handleRefresh">
|
||||
刷新
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 表格区域 -->
|
||||
<div class="table-wrapper">
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="studentList"
|
||||
@selection-change="handleSelectionChange"
|
||||
@sort-change="handleSortChange"
|
||||
row-key="id"
|
||||
border
|
||||
stripe
|
||||
size="small"
|
||||
>
|
||||
<el-table-column type="selection" width="50" align="center" />
|
||||
<el-table-column label="序号" type="index" width="60" align="center" />
|
||||
<el-table-column label="学生姓名" prop="name" width="100" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="学号" prop="studentNo" width="120" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="班级" prop="className" width="120" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="学院" prop="collegeName" width="120" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="专业" prop="majorName" width="120" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="联系电话" prop="phone" width="120" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="提交状态" prop="submissionStatus" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getSubmissionStatusTagType(scope.row.submissionStatus)" size="small">
|
||||
{{ getSubmissionStatusText(scope.row.submissionStatus) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="提交时间" prop="submissionTime" width="160" align="center" sortable="custom">
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.submissionTime ? parseTime(scope.row.submissionTime, '{y}-{m}-{d} {h}:{i}') : '-' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审核状态" prop="approvalStatus" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getApprovalStatusTagType(scope.row.approvalStatus)" size="small">
|
||||
{{ getApprovalStatusText(scope.row.approvalStatus) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="200" fixed="right" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<div class="action-buttons">
|
||||
<el-tooltip content="查看详情" placement="top">
|
||||
<el-button link type="info" icon="View" size="small" @click="handleView(scope.row)" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="编辑" placement="top">
|
||||
<el-button link type="primary" icon="Edit" size="small" @click="handleEdit(scope.row)" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="查看进度" placement="top">
|
||||
<el-button link type="warning" icon="DataAnalysis" size="small" @click="handleProgress(scope.row)" />
|
||||
</el-tooltip>
|
||||
<template v-if="scope.row.submissionStatus === 'DRAFT'">
|
||||
<el-tooltip content="提交" placement="top">
|
||||
<el-button link type="success" icon="Upload" size="small" @click="handleSubmit(scope.row)" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<template v-else-if="scope.row.submissionStatus === 'SUBMITTED'">
|
||||
<el-tooltip content="撤回" placement="top">
|
||||
<el-button link type="danger" icon="Download" size="small" @click="handleWithdraw(scope.row)" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button link type="danger" icon="Delete" size="small" @click="handleDelete(scope.row)" />
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="pagination-container">
|
||||
<el-pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
:page-size="queryParams.pageSize"
|
||||
:current-page="queryParams.pageNum"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 信息采集批次标签页 -->
|
||||
<el-tab-pane label="信息采集批次" name="batchManagement">
|
||||
<div class="tab-content">
|
||||
<!-- 批次管理组件 -->
|
||||
<BatchManagementComponent
|
||||
@batch-created="handleBatchCreated"
|
||||
@batch-updated="handleBatchUpdated"
|
||||
@batch-deleted="handleBatchDeleted"
|
||||
/>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<!-- 关联批次对话框 -->
|
||||
<el-dialog title="关联学生到批次" v-model="associateDialog.visible" width="600px" append-to-body>
|
||||
<el-form ref="associateFormRef" :model="associateForm" :rules="associateRules" label-width="100px">
|
||||
<el-form-item label="选择批次" prop="batchId">
|
||||
<el-select v-model="associateForm.batchId" placeholder="请选择批次" style="width: 100%">
|
||||
<el-option v-for="batch in batchOptions" :key="batch.id" :label="batch.batchName" :value="batch.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="已选学生" prop="studentIds">
|
||||
<div class="selected-students">
|
||||
<el-tag v-for="student in selectedStudents" :key="student.id" closable @close="removeStudent(student.id)">
|
||||
{{ student.name }}({{ student.studentNo }})
|
||||
</el-tag>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="associateDialog.visible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitAssociate">确定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 学生进度对话框 -->
|
||||
<el-dialog title="学生批次进度" v-model="progressDialog.visible" width="800px" append-to-body>
|
||||
<div v-if="progressInfo" class="progress-content">
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="学生姓名">{{ progressInfo.studentName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="学号">{{ progressInfo.studentNo }}</el-descriptions-item>
|
||||
<el-descriptions-item label="批次名称">{{ progressInfo.batchName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="提交状态">
|
||||
<el-tag :type="getSubmissionStatusTagType(progressInfo.submissionStatus)">
|
||||
{{ getSubmissionStatusText(progressInfo.submissionStatus) }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="审核状态">
|
||||
<el-tag :type="getApprovalStatusTagType(progressInfo.approvalStatus)">
|
||||
{{ getApprovalStatusText(progressInfo.approvalStatus) }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="提交时间">
|
||||
{{ progressInfo.submissionTime ? parseTime(progressInfo.submissionTime, '{y}-{m}-{d} {h}:{i}:{s}') : '-' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="审核时间">
|
||||
{{ progressInfo.approvalTime ? parseTime(progressInfo.approvalTime, '{y}-{m}-{d} {h}:{i}:{s}') : '-' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="审核意见" :span="2">
|
||||
{{ progressInfo.approvalComments || '-' }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted, getCurrentInstance, computed } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { parseTime } from '@/utils/ruoyi';
|
||||
import { listAllCollectionBatch } from '@/api/xg-xx/collectionBatch';
|
||||
import {
|
||||
listStudentInfo,
|
||||
getStudentsByBatch,
|
||||
addStudentInfo,
|
||||
updateStudentInfo,
|
||||
delStudentInfo,
|
||||
batchAssociateStudentsToBatch,
|
||||
submitStudentInfo,
|
||||
withdrawStudentInfo,
|
||||
getStudentBatchProgress,
|
||||
getStudentBatchStatistics,
|
||||
batchSubmitStudentInfo,
|
||||
batchWithdrawStudentInfo
|
||||
} from '@/api/xg-xx/studentInfo';
|
||||
import BatchManagementComponent from './components/BatchManagementComponent.vue';
|
||||
|
||||
const { proxy } = getCurrentInstance() as any;
|
||||
|
||||
// 响应式数据
|
||||
const activeTab = ref('studentList');
|
||||
const loading = ref(false);
|
||||
const ids = ref<number[]>([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const studentList = ref<any[]>([]);
|
||||
const batchOptions = ref<any[]>([]);
|
||||
const selectedBatchId = ref<number | null>(null);
|
||||
const currentBatch = ref<any>();
|
||||
|
||||
// 批次统计信息
|
||||
const batchStatistics = reactive({
|
||||
totalCount: 0,
|
||||
submittedCount: 0,
|
||||
approvedCount: 0,
|
||||
rejectedCount: 0,
|
||||
completionRate: 0
|
||||
});
|
||||
|
||||
// 查询参数
|
||||
const queryParams = reactive({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
batchId: undefined as number | undefined,
|
||||
name: undefined,
|
||||
studentNo: undefined,
|
||||
className: undefined,
|
||||
submissionStatus: undefined,
|
||||
collegeName: undefined
|
||||
});
|
||||
|
||||
// 关联对话框
|
||||
const associateDialog = reactive({
|
||||
visible: false
|
||||
});
|
||||
|
||||
const associateForm = reactive({
|
||||
batchId: undefined as number | undefined,
|
||||
studentIds: [] as number[]
|
||||
});
|
||||
|
||||
const associateRules = {
|
||||
batchId: [{ required: true, message: '请选择批次', trigger: 'change' }],
|
||||
studentIds: [{ required: true, message: '请选择学生', trigger: 'change' }]
|
||||
};
|
||||
|
||||
// 进度对话框
|
||||
const progressDialog = reactive({
|
||||
visible: false
|
||||
});
|
||||
|
||||
const progressInfo = ref<any>();
|
||||
|
||||
// 计算已选学生
|
||||
const selectedStudents = computed(() => {
|
||||
return studentList.value.filter(student => ids.value.includes(student.id));
|
||||
});
|
||||
|
||||
// 获取列表数据
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
let response;
|
||||
if (selectedBatchId.value) {
|
||||
response = await getStudentsByBatch(selectedBatchId.value, queryParams);
|
||||
} else {
|
||||
response = await listStudentInfo(queryParams);
|
||||
}
|
||||
studentList.value = response.rows;
|
||||
total.value = response.total;
|
||||
} catch (error) {
|
||||
console.error('获取列表失败:', error);
|
||||
ElMessage.error('获取学生列表失败');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 获取批次选项
|
||||
const getBatchOptions = async () => {
|
||||
try {
|
||||
const response = await listAllCollectionBatch({});
|
||||
batchOptions.value = response.data;
|
||||
} catch (error) {
|
||||
console.error('获取批次选项失败:', error);
|
||||
ElMessage.error('获取批次选项失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 批次改变处理
|
||||
const handleBatchChange = (batchId: number) => {
|
||||
selectedBatchId.value = batchId;
|
||||
queryParams.batchId = batchId;
|
||||
currentBatch.value = batchOptions.value.find(batch => batch.id === batchId);
|
||||
|
||||
// 获取批次统计信息
|
||||
if (batchId) {
|
||||
getBatchStatistics();
|
||||
}
|
||||
|
||||
// 刷新列表
|
||||
getList();
|
||||
};
|
||||
|
||||
// 获取批次统计信息
|
||||
const getBatchStatistics = async () => {
|
||||
if (!selectedBatchId.value) return;
|
||||
|
||||
try {
|
||||
const response = await getStudentBatchStatistics(selectedBatchId.value);
|
||||
Object.assign(batchStatistics, response.data);
|
||||
} catch (error) {
|
||||
console.error('获取批次统计失败:', error);
|
||||
ElMessage.error('获取批次统计失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 刷新批次信息
|
||||
const refreshBatchInfo = () => {
|
||||
if (selectedBatchId.value) {
|
||||
getBatchStatistics();
|
||||
}
|
||||
getList();
|
||||
};
|
||||
|
||||
// 搜索
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
// 重置搜索
|
||||
const resetQuery = () => {
|
||||
proxy.resetForm('queryFormRef');
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
// 多选框选中数据
|
||||
const handleSelectionChange = (selection: any[]) => {
|
||||
ids.value = selection.map(item => item.id);
|
||||
single.value = selection.length !== 1;
|
||||
multiple.value = !selection.length;
|
||||
};
|
||||
|
||||
// 排序变化
|
||||
const handleSortChange = (column: any) => {
|
||||
getList();
|
||||
};
|
||||
|
||||
// 分页大小变化
|
||||
const handleSizeChange = (val: number) => {
|
||||
queryParams.pageSize = val;
|
||||
getList();
|
||||
};
|
||||
|
||||
// 当前页变化
|
||||
const handleCurrentChange = (val: number) => {
|
||||
queryParams.pageNum = val;
|
||||
getList();
|
||||
};
|
||||
|
||||
// 新增学生
|
||||
const handleAdd = () => {
|
||||
proxy.$router.push('/xg-xx/studentInfo/add');
|
||||
};
|
||||
|
||||
// 批量关联
|
||||
const handleBatchAssociate = () => {
|
||||
if (ids.value.length === 0) {
|
||||
ElMessage.warning('请选择要关联的学生');
|
||||
return;
|
||||
}
|
||||
|
||||
associateDialog.visible = true;
|
||||
associateForm.batchId = selectedBatchId.value;
|
||||
associateForm.studentIds = ids.value;
|
||||
};
|
||||
|
||||
// 移除学生
|
||||
const removeStudent = (studentId: number) => {
|
||||
const index = associateForm.studentIds.indexOf(studentId);
|
||||
if (index > -1) {
|
||||
associateForm.studentIds.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
// 提交关联
|
||||
const submitAssociate = async () => {
|
||||
if (!associateForm.batchId || associateForm.studentIds.length === 0) {
|
||||
ElMessage.warning('请选择批次和学生');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await batchAssociateStudentsToBatch(associateForm.studentIds, associateForm.batchId);
|
||||
|
||||
ElMessage.success('关联成功');
|
||||
associateDialog.visible = false;
|
||||
|
||||
// 刷新统计信息和列表
|
||||
if (selectedBatchId.value) {
|
||||
getBatchStatistics();
|
||||
}
|
||||
getList();
|
||||
} catch (error) {
|
||||
console.error('关联失败:', error);
|
||||
ElMessage.error('关联失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 批量提交
|
||||
const handleBatchSubmit = async () => {
|
||||
if (ids.value.length === 0) {
|
||||
ElMessage.warning('请选择要提交的学生');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!selectedBatchId.value) {
|
||||
ElMessage.warning('请先选择批次');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要批量提交选中的学生信息吗?', '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
await batchSubmitStudentInfo(ids.value, selectedBatchId.value);
|
||||
|
||||
ElMessage.success('批量提交成功');
|
||||
|
||||
// 刷新统计信息和列表
|
||||
getBatchStatistics();
|
||||
getList();
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('批量提交失败:', error);
|
||||
ElMessage.error('批量提交失败');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 批量撤回
|
||||
const handleBatchWithdraw = async () => {
|
||||
if (ids.value.length === 0) {
|
||||
ElMessage.warning('请选择要撤回的学生');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!selectedBatchId.value) {
|
||||
ElMessage.warning('请先选择批次');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要批量撤回选中的学生信息吗?', '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
await batchWithdrawStudentInfo(ids.value, selectedBatchId.value);
|
||||
|
||||
ElMessage.success('批量撤回成功');
|
||||
|
||||
// 刷新统计信息和列表
|
||||
getBatchStatistics();
|
||||
getList();
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('批量撤回失败:', error);
|
||||
ElMessage.error('批量撤回失败');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 删除学生
|
||||
const handleDelete = async (row?: any) => {
|
||||
const studentIds = row ? [row.id] : ids.value;
|
||||
if (studentIds.length === 0) {
|
||||
ElMessage.warning('请选择要删除的学生');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要删除选中的学生信息吗?', '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
await delStudentInfo(studentIds);
|
||||
|
||||
ElMessage.success('删除成功');
|
||||
|
||||
// 刷新统计信息和列表
|
||||
if (selectedBatchId.value) {
|
||||
getBatchStatistics();
|
||||
}
|
||||
getList();
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('删除失败:', error);
|
||||
ElMessage.error('删除失败');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 查看详情
|
||||
const handleView = (row: any) => {
|
||||
proxy.$router.push(`/xg-xx/studentInfo/detail/${row.id}`);
|
||||
};
|
||||
|
||||
// 编辑
|
||||
const handleEdit = (row: any) => {
|
||||
proxy.$router.push(`/xg-xx/studentInfo/edit/${row.id}`);
|
||||
};
|
||||
|
||||
// 提交
|
||||
const handleSubmit = async (row: any) => {
|
||||
if (!selectedBatchId.value) {
|
||||
ElMessage.warning('请先选择批次');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要提交该学生信息吗?', '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
await submitStudentInfo(row.id, selectedBatchId.value);
|
||||
|
||||
ElMessage.success('提交成功');
|
||||
|
||||
// 刷新统计信息和列表
|
||||
getBatchStatistics();
|
||||
getList();
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('提交失败:', error);
|
||||
ElMessage.error('提交失败');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 撤回
|
||||
const handleWithdraw = async (row: any) => {
|
||||
if (!selectedBatchId.value) {
|
||||
ElMessage.warning('请先选择批次');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要撤回该学生信息吗?', '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
await withdrawStudentInfo(row.id, selectedBatchId.value);
|
||||
|
||||
ElMessage.success('撤回成功');
|
||||
|
||||
// 刷新统计信息和列表
|
||||
getBatchStatistics();
|
||||
getList();
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('撤回失败:', error);
|
||||
ElMessage.error('撤回失败');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 查看进度
|
||||
const handleProgress = async (row: any) => {
|
||||
if (!selectedBatchId.value) {
|
||||
ElMessage.warning('请先选择批次');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await getStudentBatchProgress(row.id, selectedBatchId.value);
|
||||
progressInfo.value = response.data;
|
||||
progressDialog.visible = true;
|
||||
} catch (error) {
|
||||
console.error('获取进度失败:', error);
|
||||
ElMessage.error('获取进度失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 导入
|
||||
const handleImport = () => {
|
||||
proxy.$router.push('/xg-xx/studentInfo/import');
|
||||
};
|
||||
|
||||
// 导出
|
||||
const handleExport = () => {
|
||||
proxy?.download(
|
||||
'xg/studentInfo/export',
|
||||
queryParams,
|
||||
`学生信息_${parseTime(new Date(), '{y}{m}{d}{h}{i}{s}')}.xlsx`
|
||||
);
|
||||
};
|
||||
|
||||
// 刷新
|
||||
const handleRefresh = () => {
|
||||
getList();
|
||||
};
|
||||
|
||||
// 获取批次状态文本
|
||||
const getBatchStatusText = (status: string) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
'DRAFT': '草稿',
|
||||
'ACTIVE': '进行中',
|
||||
'COMPLETED': '已完成',
|
||||
'CANCELLED': '已取消'
|
||||
};
|
||||
return statusMap[status] || '未知';
|
||||
};
|
||||
|
||||
// 获取提交状态标签类型
|
||||
const getSubmissionStatusTagType = (status: string): 'success' | 'warning' | 'info' | 'primary' | 'danger' => {
|
||||
const statusMap: Record<string, 'success' | 'warning' | 'info' | 'primary' | 'danger'> = {
|
||||
'DRAFT': 'info',
|
||||
'SUBMITTED': 'warning',
|
||||
'APPROVED': 'success',
|
||||
'REJECTED': 'danger'
|
||||
};
|
||||
return statusMap[status] || 'info';
|
||||
};
|
||||
|
||||
// 获取提交状态文本
|
||||
const getSubmissionStatusText = (status: string) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
'DRAFT': '草稿',
|
||||
'SUBMITTED': '已提交',
|
||||
'APPROVED': '已通过',
|
||||
'REJECTED': '已拒绝'
|
||||
};
|
||||
return statusMap[status] || '未知';
|
||||
};
|
||||
|
||||
// 获取审核状态标签类型
|
||||
const getApprovalStatusTagType = (status: string): 'success' | 'warning' | 'info' | 'primary' | 'danger' => {
|
||||
const statusMap: Record<string, 'success' | 'warning' | 'info' | 'primary' | 'danger'> = {
|
||||
'PENDING': 'warning',
|
||||
'APPROVED': 'success',
|
||||
'REJECTED': 'danger'
|
||||
};
|
||||
return statusMap[status] || 'info';
|
||||
};
|
||||
|
||||
// 获取审核状态文本
|
||||
const getApprovalStatusText = (status: string) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
'PENDING': '待审核',
|
||||
'APPROVED': '已通过',
|
||||
'REJECTED': '已拒绝'
|
||||
};
|
||||
return statusMap[status] || '未知';
|
||||
};
|
||||
|
||||
// 批次创建成功回调
|
||||
const handleBatchCreated = () => {
|
||||
ElMessage.success('批次创建成功');
|
||||
getBatchOptions(); // 刷新批次选项
|
||||
if (activeTab.value === 'batchManagement') {
|
||||
getList(); // 刷新学生列表
|
||||
}
|
||||
};
|
||||
|
||||
// 批次更新成功回调
|
||||
const handleBatchUpdated = () => {
|
||||
ElMessage.success('批次更新成功');
|
||||
getBatchOptions(); // 刷新批次选项
|
||||
if (activeTab.value === 'batchManagement') {
|
||||
getList(); // 刷新学生列表
|
||||
}
|
||||
};
|
||||
|
||||
// 批次删除成功回调
|
||||
const handleBatchDeleted = () => {
|
||||
ElMessage.success('批次删除成功');
|
||||
getBatchOptions(); // 刷新批次选项
|
||||
if (activeTab.value === 'batchManagement') {
|
||||
getList(); // 刷新学生列表
|
||||
}
|
||||
};
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
getBatchOptions();
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-container {
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 24px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.page-description {
|
||||
font-size: 16px;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.main-tabs {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.batch-info {
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
.toolbar-wrapper {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.toolbar-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.toolbar-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.table-wrapper {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.pagination-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.progress-info {
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.progress-content {
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
.selected-students {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.mt-4 {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.mb-4 {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,630 +0,0 @@
|
||||
<template>
|
||||
<div class="p-2 page-container">
|
||||
<!-- 搜索区域 -->
|
||||
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
|
||||
<div v-show="showSearch" class="mb-[10px]">
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="queryFormRef" :model="queryParams" label-width="100px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="批次选择" prop="batchId">
|
||||
<el-select v-model="queryParams.batchId" placeholder="请选择批次" clearable style="width: 100%">
|
||||
<el-option v-for="batch in batchOptions" :key="batch.id" :label="batch.batchName" :value="batch.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="学生姓名" prop="studentName">
|
||||
<el-input v-model="queryParams.studentName" placeholder="请输入学生姓名" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="学号" prop="studentNo">
|
||||
<el-input v-model="queryParams.studentNo" placeholder="请输入学号" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="提交状态" prop="submissionStatus">
|
||||
<el-select v-model="queryParams.submissionStatus" placeholder="请选择提交状态" clearable style="width: 100%">
|
||||
<el-option label="草稿" value="DRAFT" />
|
||||
<el-option label="已提交" value="SUBMITTED" />
|
||||
<el-option label="已通过" value="APPROVED" />
|
||||
<el-option label="已拒绝" value="REJECTED" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="审核状态" prop="approvalStatus">
|
||||
<el-select v-model="queryParams.approvalStatus" placeholder="请选择审核状态" clearable style="width: 100%">
|
||||
<el-option label="待审核" value="PENDING" />
|
||||
<el-option label="已通过" value="APPROVED" />
|
||||
<el-option label="已拒绝" value="REJECTED" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="班级" prop="className">
|
||||
<el-input v-model="queryParams.className" placeholder="请输入班级名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="学院" prop="collegeName">
|
||||
<el-input v-model="queryParams.collegeName" placeholder="请输入学院名称" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<!-- 主要内容区域 -->
|
||||
<el-card shadow="hover">
|
||||
<!-- 操作按钮区域 -->
|
||||
<div class="toolbar-wrapper">
|
||||
<div class="toolbar-left">
|
||||
<el-button-group>
|
||||
<el-button
|
||||
:type="currentTab === 'pending' ? 'primary' : 'default'"
|
||||
@click="switchTab('pending')"
|
||||
icon="Clock"
|
||||
>
|
||||
待审核 <el-badge :value="pendingCount" class="ml-1" />
|
||||
</el-button>
|
||||
<el-button
|
||||
:type="currentTab === 'approved' ? 'primary' : 'default'"
|
||||
@click="switchTab('approved')"
|
||||
icon="Check"
|
||||
>
|
||||
已审核 <el-badge :value="approvedCount" class="ml-1" />
|
||||
</el-button>
|
||||
</el-button-group>
|
||||
</div>
|
||||
|
||||
<div class="toolbar-right">
|
||||
<el-button
|
||||
type="success"
|
||||
icon="Select"
|
||||
:disabled="!multiple || ids.length === 0"
|
||||
@click="handleBatchApprove(true)"
|
||||
v-hasPermi="['xg:studentInfo:approve']"
|
||||
>
|
||||
批量通过
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
icon="Close"
|
||||
:disabled="!multiple || ids.length === 0"
|
||||
@click="handleBatchApprove(false)"
|
||||
v-hasPermi="['xg:studentInfo:approve']"
|
||||
>
|
||||
批量拒绝
|
||||
</el-button>
|
||||
<el-button
|
||||
type="warning"
|
||||
icon="Download"
|
||||
@click="handleExport"
|
||||
v-hasPermi="['xg:studentInfo:export']"
|
||||
>
|
||||
导出
|
||||
</el-button>
|
||||
<el-button
|
||||
type="info"
|
||||
icon="Refresh"
|
||||
@click="handleRefresh"
|
||||
>
|
||||
刷新
|
||||
</el-button>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 表格区域 -->
|
||||
<div class="table-wrapper">
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="approvalList"
|
||||
@selection-change="handleSelectionChange"
|
||||
@sort-change="handleSortChange"
|
||||
row-key="id"
|
||||
border
|
||||
stripe
|
||||
size="small"
|
||||
>
|
||||
<el-table-column type="selection" width="50" align="center" />
|
||||
<el-table-column label="序号" type="index" width="60" align="center" />
|
||||
<el-table-column label="学生姓名" prop="studentName" width="100" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="学号" prop="studentNo" width="120" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="班级" prop="className" width="120" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="学院" prop="collegeName" width="120" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="批次名称" prop="batchName" width="150" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="提交状态" prop="submissionStatus" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getSubmissionStatusTagType(scope.row.submissionStatus)" size="small">
|
||||
{{ getSubmissionStatusText(scope.row.submissionStatus) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审核状态" prop="approvalStatus" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getApprovalStatusTagType(scope.row.approvalStatus)" size="small">
|
||||
{{ getApprovalStatusText(scope.row.approvalStatus) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="提交时间" prop="submissionTime" width="160" align="center" sortable="custom">
|
||||
<template #default="scope">
|
||||
<span>{{ proxy.parseTime(scope.row.submissionTime, '{y}-{m}-{d} {h}:{i}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审核时间" prop="approvalTime" width="160" align="center" sortable="custom">
|
||||
<template #default="scope">
|
||||
<span>{{ proxy.parseTime(scope.row.approvalTime, '{y}-{m}-{d} {h}:{i}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审核人" prop="approvalByName" width="100" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="审核意见" prop="approvalComments" width="200" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="操作" align="center" width="200" fixed="right" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<div class="action-buttons">
|
||||
<el-tooltip content="查看详情" placement="top">
|
||||
<el-button link type="info" icon="View" size="small" @click="handleView(scope.row)" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="审核历史" placement="top">
|
||||
<el-button link type="warning" icon="Document" size="small" @click="handleHistory(scope.row)" />
|
||||
</el-tooltip>
|
||||
<template v-if="scope.row.approvalStatus === 'PENDING'">
|
||||
<el-tooltip content="通过" placement="top">
|
||||
<el-button link type="success" icon="Check" size="small" @click="handleApprove(scope.row, true)" v-hasPermi="['xg:studentInfo:approve']" />
|
||||
</el-tooltip>
|
||||
<el-tooltip content="拒绝" placement="top">
|
||||
<el-button link type="danger" icon="Close" size="small" @click="handleApprove(scope.row, false)" v-hasPermi="['xg:studentInfo:approve']" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-tooltip content="撤销审核" placement="top">
|
||||
<el-button link type="warning" icon="RefreshLeft" size="small" @click="handleRevoke(scope.row)" v-hasPermi="['xg:studentInfo:approve']" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="pagination-container">
|
||||
<pagination v-show="total > 0" :total="total" :page="queryParams.pageNum" :limit="queryParams.pageSize" @pagination="getList" />
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- 审核对话框 -->
|
||||
<el-dialog :title="approvalDialog.title" v-model="approvalDialog.visible" width="600px" append-to-body>
|
||||
<el-form ref="approvalFormRef" :model="approvalForm" :rules="approvalRules" label-width="100px">
|
||||
<el-form-item label="审核结果" prop="approved">
|
||||
<el-radio-group v-model="approvalForm.approved">
|
||||
<el-radio :label="true">通过</el-radio>
|
||||
<el-radio :label="false">拒绝</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="审核意见" prop="comments">
|
||||
<el-input
|
||||
v-model="approvalForm.comments"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
placeholder="请输入审核意见"
|
||||
maxlength="500"
|
||||
show-word-limit
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="approvalDialog.visible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitApproval">确定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 审核历史对话框 -->
|
||||
<el-dialog title="审核历史" v-model="historyDialog.visible" width="800px" append-to-body>
|
||||
<el-timeline>
|
||||
<el-timeline-item
|
||||
v-for="item in historyList"
|
||||
:key="item.id"
|
||||
:timestamp="proxy.parseTime(item.createTime, '{y}-{m}-{d} {h}:{i}:{s}')"
|
||||
placement="top"
|
||||
>
|
||||
<el-card>
|
||||
<div class="history-item">
|
||||
<div class="history-header">
|
||||
<span class="history-action">{{ getHistoryActionText(item.approvalStatus) }}</span>
|
||||
<span class="history-user">{{ item.approvalByName }}</span>
|
||||
</div>
|
||||
<div class="history-content" v-if="item.approvalComments">
|
||||
{{ item.approvalComments }}
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted, getCurrentInstance } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import {
|
||||
queryPendingApprovalList,
|
||||
queryApprovedList,
|
||||
approveStudentInfo,
|
||||
batchApproveStudentInfo,
|
||||
revokeApproval,
|
||||
queryApprovalHistory,
|
||||
getApprovalStatistics,
|
||||
exportApprovalResults,
|
||||
type XgStudentInfoApprovalQuery,
|
||||
type XgStudentInfoApprovalVO
|
||||
} from '@/api/xg-xx/studentInfoApproval';
|
||||
import { listAllCollectionBatch } from '@/api/xg-xx/collectionBatch';
|
||||
|
||||
const { proxy } = getCurrentInstance() as any;
|
||||
|
||||
// 响应式数据
|
||||
const loading = ref(false);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref<number[]>([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const approvalList = ref<XgStudentInfoApprovalVO[]>([]);
|
||||
const batchOptions = ref<any[]>([]);
|
||||
const currentTab = ref('pending');
|
||||
const pendingCount = ref(0);
|
||||
const approvedCount = ref(0);
|
||||
|
||||
// 查询参数
|
||||
const queryParams = reactive<XgStudentInfoApprovalQuery>({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
batchId: undefined,
|
||||
studentName: undefined,
|
||||
studentNo: undefined,
|
||||
submissionStatus: undefined,
|
||||
approvalStatus: undefined,
|
||||
className: undefined,
|
||||
collegeName: undefined
|
||||
});
|
||||
|
||||
// 审核对话框
|
||||
const approvalDialog = reactive({
|
||||
visible: false,
|
||||
title: '',
|
||||
type: 'single' as 'single' | 'batch'
|
||||
});
|
||||
|
||||
const approvalForm = reactive({
|
||||
approved: true,
|
||||
comments: '',
|
||||
studentIds: [] as number[],
|
||||
batchId: undefined as number | undefined
|
||||
});
|
||||
|
||||
const approvalRules = {
|
||||
approved: [{ required: true, message: '请选择审核结果', trigger: 'change' }]
|
||||
};
|
||||
|
||||
// 审核历史对话框
|
||||
const historyDialog = reactive({
|
||||
visible: false
|
||||
});
|
||||
|
||||
const historyList = ref<XgStudentInfoApprovalVO[]>([]);
|
||||
|
||||
// 获取列表数据
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
let response;
|
||||
if (currentTab.value === 'pending') {
|
||||
response = await queryPendingApprovalList(queryParams);
|
||||
} else {
|
||||
response = await queryApprovedList(queryParams);
|
||||
}
|
||||
|
||||
approvalList.value = response.rows;
|
||||
total.value = response.total;
|
||||
|
||||
// 获取统计信息
|
||||
if (queryParams.batchId) {
|
||||
const statsResponse = await getApprovalStatistics(queryParams.batchId);
|
||||
pendingCount.value = statsResponse.data.pendingCount || 0;
|
||||
approvedCount.value = (statsResponse.data.approvedCount || 0) + (statsResponse.data.rejectedCount || 0);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取列表失败:', error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 获取批次选项
|
||||
const getBatchOptions = async () => {
|
||||
try {
|
||||
const response = await listAllCollectionBatch({});
|
||||
batchOptions.value = response.data;
|
||||
} catch (error) {
|
||||
console.error('获取批次选项失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 切换标签页
|
||||
const switchTab = (tab: string) => {
|
||||
currentTab.value = tab;
|
||||
queryParams.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
// 搜索
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
// 重置搜索
|
||||
const resetQuery = () => {
|
||||
proxy.resetForm('queryFormRef');
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
// 多选框选中数据
|
||||
const handleSelectionChange = (selection: XgStudentInfoApprovalVO[]) => {
|
||||
ids.value = selection.map(item => item.studentId!);
|
||||
single.value = selection.length !== 1;
|
||||
multiple.value = !selection.length;
|
||||
};
|
||||
|
||||
// 排序变化
|
||||
const handleSortChange = (column: any) => {
|
||||
// 处理排序逻辑
|
||||
getList();
|
||||
};
|
||||
|
||||
// 查看详情
|
||||
const handleView = (row: XgStudentInfoApprovalVO) => {
|
||||
// 跳转到学生信息详情页面
|
||||
proxy.$router.push(`/xg-xx/studentInfo/detail/${row.studentId}`);
|
||||
};
|
||||
|
||||
// 审核操作
|
||||
const handleApprove = (row: XgStudentInfoApprovalVO, approved: boolean) => {
|
||||
approvalDialog.visible = true;
|
||||
approvalDialog.title = approved ? '审核通过' : '审核拒绝';
|
||||
approvalDialog.type = 'single';
|
||||
approvalForm.approved = approved;
|
||||
approvalForm.comments = '';
|
||||
approvalForm.studentIds = [row.studentId!];
|
||||
approvalForm.batchId = row.batchId;
|
||||
};
|
||||
|
||||
// 批量审核
|
||||
const handleBatchApprove = (approved: boolean) => {
|
||||
if (ids.value.length === 0) {
|
||||
ElMessage.warning('请选择要审核的学生');
|
||||
return;
|
||||
}
|
||||
|
||||
approvalDialog.visible = true;
|
||||
approvalDialog.title = approved ? '批量审核通过' : '批量审核拒绝';
|
||||
approvalDialog.type = 'batch';
|
||||
approvalForm.approved = approved;
|
||||
approvalForm.comments = '';
|
||||
approvalForm.studentIds = ids.value;
|
||||
approvalForm.batchId = queryParams.batchId;
|
||||
};
|
||||
|
||||
// 提交审核
|
||||
const submitApproval = async () => {
|
||||
try {
|
||||
if (approvalDialog.type === 'single') {
|
||||
await approveStudentInfo(
|
||||
approvalForm.studentIds[0],
|
||||
approvalForm.batchId!,
|
||||
approvalForm.approved,
|
||||
approvalForm.comments
|
||||
);
|
||||
} else {
|
||||
await batchApproveStudentInfo(
|
||||
approvalForm.studentIds,
|
||||
approvalForm.batchId!,
|
||||
approvalForm.approved,
|
||||
approvalForm.comments
|
||||
);
|
||||
}
|
||||
|
||||
ElMessage.success('审核成功');
|
||||
approvalDialog.visible = false;
|
||||
getList();
|
||||
} catch (error) {
|
||||
console.error('审核失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 撤销审核
|
||||
const handleRevoke = async (row: XgStudentInfoApprovalVO) => {
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要撤销该学生的审核吗?', '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
|
||||
await revokeApproval(row.studentId!, row.batchId!);
|
||||
ElMessage.success('撤销成功');
|
||||
getList();
|
||||
} catch (error) {
|
||||
console.error('撤销失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 查看审核历史
|
||||
const handleHistory = async (row: XgStudentInfoApprovalVO) => {
|
||||
try {
|
||||
const response = await queryApprovalHistory(row.studentId!, row.batchId!);
|
||||
historyList.value = response.data;
|
||||
historyDialog.visible = true;
|
||||
} catch (error) {
|
||||
console.error('获取审核历史失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 导出
|
||||
const handleExport = () => {
|
||||
proxy?.download(
|
||||
'xg/studentInfoApproval/export',
|
||||
queryParams,
|
||||
`学生信息审核结果_${proxy.parseTime(new Date(), '{y}{m}{d}{h}{i}{s}')}.xlsx`
|
||||
);
|
||||
};
|
||||
|
||||
// 刷新
|
||||
const handleRefresh = () => {
|
||||
getList();
|
||||
};
|
||||
|
||||
// 获取提交状态标签类型
|
||||
const getSubmissionStatusTagType = (status: string) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
'DRAFT': 'info',
|
||||
'SUBMITTED': 'warning',
|
||||
'APPROVED': 'success',
|
||||
'REJECTED': 'danger'
|
||||
};
|
||||
return statusMap[status] || 'info';
|
||||
};
|
||||
|
||||
// 获取提交状态文本
|
||||
const getSubmissionStatusText = (status: string) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
'DRAFT': '草稿',
|
||||
'SUBMITTED': '已提交',
|
||||
'APPROVED': '已通过',
|
||||
'REJECTED': '已拒绝'
|
||||
};
|
||||
return statusMap[status] || '未知';
|
||||
};
|
||||
|
||||
// 获取审核状态标签类型
|
||||
const getApprovalStatusTagType = (status: string) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
'PENDING': 'warning',
|
||||
'APPROVED': 'success',
|
||||
'REJECTED': 'danger'
|
||||
};
|
||||
return statusMap[status] || 'info';
|
||||
};
|
||||
|
||||
// 获取审核状态文本
|
||||
const getApprovalStatusText = (status: string) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
'PENDING': '待审核',
|
||||
'APPROVED': '已通过',
|
||||
'REJECTED': '已拒绝'
|
||||
};
|
||||
return statusMap[status] || '未知';
|
||||
};
|
||||
|
||||
// 获取历史操作文本
|
||||
const getHistoryActionText = (status: string) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
'PENDING': '提交审核',
|
||||
'APPROVED': '审核通过',
|
||||
'REJECTED': '审核拒绝'
|
||||
};
|
||||
return statusMap[status] || '未知操作';
|
||||
};
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
getBatchOptions();
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-container {
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.toolbar-wrapper {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.toolbar-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.toolbar-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.table-wrapper {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.pagination-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.history-item {
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.history-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.history-action {
|
||||
font-weight: bold;
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.history-user {
|
||||
color: #666;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.history-content {
|
||||
color: #333;
|
||||
line-height: 1.5;
|
||||
background-color: #f9f9f9;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user