wuzhen 招生完成
This commit is contained in:
1 parent
6fb55c7723
commit
b215f7b9b3
23 files changed
+125458
-823
No files matched your search
+123213
File diff suppressed because it is too large.
Load diff
+6
-6
@@ -32,7 +32,7 @@ public class ZsjyStatisticsController extends BaseController {
|
||||
/**
|
||||
* 获取统计概览数据
|
||||
*/
|
||||
@SaCheckPermission("zsjy:statistics:view")
|
||||
// @SaCheckPermission("zsjy:statistics:view")
|
||||
@GetMapping("/overview")
|
||||
public R<ZsjyStatisticsOverviewVo> getOverview() {
|
||||
log.info("接收到获取统计概览数据请求");
|
||||
@@ -44,7 +44,7 @@ public class ZsjyStatisticsController extends BaseController {
|
||||
/**
|
||||
* 获取专业分布统计
|
||||
*/
|
||||
@SaCheckPermission("zsjy:statistics:major:view")
|
||||
// @SaCheckPermission("zsjy:statistics:major:view")
|
||||
@GetMapping("/major")
|
||||
public R<Map<String, Object>> getMajorDistribution() {
|
||||
log.info("接收到获取专业分布统计请求");
|
||||
@@ -56,7 +56,7 @@ public class ZsjyStatisticsController extends BaseController {
|
||||
/**
|
||||
* 获取地区分布统计
|
||||
*/
|
||||
@SaCheckPermission("zsjy:statistics:region:view")
|
||||
// @SaCheckPermission("zsjy:statistics:region:view")
|
||||
@GetMapping("/region")
|
||||
public R<Map<String, Object>> getRegionDistribution() {
|
||||
log.info("接收到获取地区分布统计请求");
|
||||
@@ -68,7 +68,7 @@ public class ZsjyStatisticsController extends BaseController {
|
||||
/**
|
||||
* 获取性别比例统计
|
||||
*/
|
||||
@SaCheckPermission("zsjy:statistics:view")
|
||||
// @SaCheckPermission("zsjy:statistics:view")
|
||||
@GetMapping("/gender")
|
||||
public R<Map<String, Object>> getGenderRatio() {
|
||||
log.info("接收到获取性别比例统计请求");
|
||||
@@ -80,7 +80,7 @@ public class ZsjyStatisticsController extends BaseController {
|
||||
/**
|
||||
* 获取报名趋势统计
|
||||
*/
|
||||
@SaCheckPermission("zsjy:statistics:view")
|
||||
// @SaCheckPermission("zsjy:statistics:view")
|
||||
@GetMapping("/trend")
|
||||
public R<Map<String, Object>> getEnrollmentTrend(@RequestParam(required = false) Integer days) {
|
||||
log.info("接收到获取报名趋势统计请求, days={}", days);
|
||||
@@ -92,7 +92,7 @@ public class ZsjyStatisticsController extends BaseController {
|
||||
/**
|
||||
* 获取状态分布统计
|
||||
*/
|
||||
@SaCheckPermission("zsjy:statistics:view")
|
||||
// @SaCheckPermission("zsjy:statistics:view")
|
||||
@GetMapping("/status")
|
||||
public R<Map<String, Object>> getStatusDistribution() {
|
||||
log.info("接收到获取状态分布统计请求");
|
||||
|
||||
+28
@@ -174,6 +174,18 @@ public class ZsjyEnrollmentInfo extends TenantEntity {
|
||||
*/
|
||||
private String qrCodeSource;
|
||||
|
||||
/**
|
||||
* 学院名称
|
||||
*/
|
||||
@TableField("collage_name")
|
||||
private String collegeName;
|
||||
|
||||
/**
|
||||
* 学院代码
|
||||
*/
|
||||
@TableField("collage_id")
|
||||
private String collegeId;
|
||||
|
||||
// 添加明确的getter和setter方法以解决编译问题
|
||||
public Long getId() {
|
||||
return id;
|
||||
@@ -262,4 +274,20 @@ public class ZsjyEnrollmentInfo extends TenantEntity {
|
||||
public void setExamRegion(String examRegion) {
|
||||
this.examRegion = examRegion;
|
||||
}
|
||||
|
||||
public String getCollegeName() {
|
||||
return collegeName;
|
||||
}
|
||||
|
||||
public void setCollegeName(String collegeName) {
|
||||
this.collegeName = collegeName;
|
||||
}
|
||||
|
||||
public String getCollegeId() {
|
||||
return collegeId;
|
||||
}
|
||||
|
||||
public void setCollegeId(String collegeId) {
|
||||
this.collegeId = collegeId;
|
||||
}
|
||||
}
|
||||
+26
@@ -193,6 +193,16 @@ public class ZsjyEnrollmentInfoBo extends BaseEntity {
|
||||
*/
|
||||
private String qrCodeSource;
|
||||
|
||||
/**
|
||||
* 学院名称
|
||||
*/
|
||||
private String collegeName;
|
||||
|
||||
/**
|
||||
* 学院代码
|
||||
*/
|
||||
private String collegeId;
|
||||
|
||||
// 添加明确的getter和setter方法以解决编译问题
|
||||
public Long getId() {
|
||||
return id;
|
||||
@@ -281,4 +291,20 @@ public class ZsjyEnrollmentInfoBo extends BaseEntity {
|
||||
public void setExamRegion(String examRegion) {
|
||||
this.examRegion = examRegion;
|
||||
}
|
||||
|
||||
public String getCollegeName() {
|
||||
return collegeName;
|
||||
}
|
||||
|
||||
public void setCollegeName(String collegeName) {
|
||||
this.collegeName = collegeName;
|
||||
}
|
||||
|
||||
public String getCollegeId() {
|
||||
return collegeId;
|
||||
}
|
||||
|
||||
public void setCollegeId(String collegeId) {
|
||||
this.collegeId = collegeId;
|
||||
}
|
||||
}
|
||||
+12
@@ -213,6 +213,18 @@ public class ZsjyEnrollmentInfoVo implements Serializable {
|
||||
@ExcelProperty(value = "二维码来源标识")
|
||||
private String qrCodeSource;
|
||||
|
||||
/**
|
||||
* 学院名称
|
||||
*/
|
||||
@ExcelProperty(value = "学院名称")
|
||||
private String collegeName;
|
||||
|
||||
/**
|
||||
* 学院代码
|
||||
*/
|
||||
@ExcelProperty(value = "学院代码")
|
||||
private String collegeId;
|
||||
|
||||
/**
|
||||
* 创建者
|
||||
*/
|
||||
|
||||
+22
@@ -223,4 +223,26 @@ public class ZsjyEnrollmentProcessVo implements Serializable {
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date updateTime;
|
||||
|
||||
/**
|
||||
* 性别(M-男 F-女)- 从enrollment_info表获取
|
||||
*/
|
||||
private String gender;
|
||||
|
||||
/**
|
||||
* 专业名称 - 从enrollment_info表获取
|
||||
*/
|
||||
private String majorName;
|
||||
|
||||
/**
|
||||
* 省份名称 - 从enrollment_info表获取
|
||||
*/
|
||||
private String province;
|
||||
|
||||
/**
|
||||
* 判断是否是"信息审核中"状态的记录
|
||||
*/
|
||||
public boolean isInfoAuditStatus() {
|
||||
return "1".equals(this.currentStatus);
|
||||
}
|
||||
}
|
||||
+334
-45
@@ -1,21 +1,353 @@
|
||||
package org.dromara.zsjy.mapper;
|
||||
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
import org.dromara.zsjy.domain.ZsjyEnrollmentInfo;
|
||||
import org.dromara.zsjy.domain.vo.ZsjyEnrollmentInfoVo;
|
||||
import org.dromara.zsjy.domain.vo.ZsjyEnrollmentStatisticsResultVo;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 招生报名信息Mapper接口
|
||||
* 报名信息Mapper接口
|
||||
*
|
||||
* @author Cursor AI
|
||||
* @date 2025-01-20
|
||||
*/
|
||||
public interface ZsjyEnrollmentInfoMapper extends BaseMapperPlus<ZsjyEnrollmentInfo, ZsjyEnrollmentInfoVo> {
|
||||
|
||||
/**
|
||||
* 统计总报名人数
|
||||
*
|
||||
* @return 总报名人数
|
||||
*/
|
||||
@Select("SELECT COUNT(id) FROM zsjy_enrollment_info WHERE deleted = 0")
|
||||
Long countTotal();
|
||||
|
||||
/**
|
||||
* 统计今日报名人数
|
||||
*
|
||||
* @return 今日报名人数
|
||||
*/
|
||||
@Select("SELECT COUNT(id) FROM zsjy_enrollment_info WHERE DATE(create_time) = CURDATE() AND deleted = 0")
|
||||
Long countTodayEnrollments();
|
||||
|
||||
/**
|
||||
* 按专业分组统计报名人数
|
||||
*
|
||||
* @return 专业分布
|
||||
*/
|
||||
@Select("SELECT applied_major as name, COUNT(id) as value FROM zsjy_enrollment_info WHERE deleted = 0 GROUP BY applied_major ORDER BY value DESC")
|
||||
List<Map<String, Object>> groupByMajor();
|
||||
|
||||
/**
|
||||
* 按地区分组统计报名人数
|
||||
*
|
||||
* @return 地区分布
|
||||
*/
|
||||
@Select("SELECT province_name as name, COUNT(id) as value FROM zsjy_enrollment_info WHERE deleted = 0 GROUP BY province_name ORDER BY value DESC")
|
||||
List<Map<String, Object>> groupByRegion();
|
||||
|
||||
/**
|
||||
* 按性别统计报名人数
|
||||
*
|
||||
* @return 性别分布
|
||||
*/
|
||||
@Select("SELECT gender, COUNT(id) as count FROM zsjy_enrollment_info WHERE deleted = 0 GROUP BY gender")
|
||||
List<Map<String, Object>> countByGenderList();
|
||||
|
||||
/**
|
||||
* 按性别统计报名人数,并转换为Map格式
|
||||
*
|
||||
* @return 性别分布
|
||||
*/
|
||||
default Map<String, Long> countByGender() {
|
||||
Map<String, Long> result = new java.util.HashMap<>();
|
||||
List<Map<String, Object>> genderList = countByGenderList();
|
||||
if (genderList != null && !genderList.isEmpty()) {
|
||||
for (Map<String, Object> item : genderList) {
|
||||
String gender = (String) item.get("gender");
|
||||
Long count = ((Number) item.get("count")).longValue();
|
||||
result.put(gender, count);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计指定天数内每日报名人数
|
||||
*
|
||||
* @param days 天数
|
||||
* @return 每日报名人数
|
||||
*/
|
||||
@Select("SELECT DATE_FORMAT(create_time, '%Y-%m-%d') as date, COUNT(id) as count FROM zsjy_enrollment_info WHERE create_time >= DATE_SUB(CURDATE(), INTERVAL #{days} DAY) AND deleted = 0 GROUP BY DATE_FORMAT(create_time, '%Y-%m-%d') ORDER BY date")
|
||||
List<Map<String, Object>> countByDateRange(@Param("days") Integer days);
|
||||
|
||||
/**
|
||||
* 按年统计报名录取信息
|
||||
*/
|
||||
@Select({
|
||||
"SELECT",
|
||||
" YEAR(ei.create_time) AS dimensionValue,",
|
||||
" CONCAT(YEAR(ei.create_time), '年') AS dimensionDisplay,",
|
||||
" CONCAT('Y', YEAR(ei.create_time)) AS dimensionCode,",
|
||||
" COUNT(ei.id) AS enrollmentCount,",
|
||||
" SUM(CASE WHEN ep.payment_status = '1' THEN 1 ELSE 0 END) AS paymentCount,",
|
||||
" SUM(CASE WHEN ep.admission_status = '1' THEN 1 ELSE 0 END) AS admissionCount,",
|
||||
" SUM(CASE WHEN ep.is_completed = 1 THEN 1 ELSE 0 END) AS completedCount,",
|
||||
" SUM(CASE WHEN ei.gender = 'M' THEN 1 ELSE 0 END) AS maleCount,",
|
||||
" SUM(CASE WHEN ei.gender = 'F' THEN 1 ELSE 0 END) AS femaleCount",
|
||||
"FROM",
|
||||
" zsjy_enrollment_info ei",
|
||||
"LEFT JOIN",
|
||||
" zsjy_enrollment_process ep ON ei.id = ep.enrollment_id",
|
||||
"WHERE",
|
||||
" ei.create_time BETWEEN #{startDate} AND #{endDate}",
|
||||
" AND ei.deleted = 0",
|
||||
" ${extraCondition}",
|
||||
"GROUP BY",
|
||||
" YEAR(ei.create_time)",
|
||||
"ORDER BY",
|
||||
" dimensionValue DESC"
|
||||
})
|
||||
List<ZsjyEnrollmentStatisticsResultVo> statisticsByYear(
|
||||
@Param("startDate") Date startDate,
|
||||
@Param("endDate") Date endDate,
|
||||
@Param("extraCondition") String extraCondition);
|
||||
|
||||
/**
|
||||
* 按月统计报名录取信息
|
||||
*/
|
||||
@Select({
|
||||
"SELECT",
|
||||
" CONCAT(YEAR(ei.create_time), '-', MONTH(ei.create_time)) AS dimensionValue,",
|
||||
" CONCAT(YEAR(ei.create_time), '年', MONTH(ei.create_time), '月') AS dimensionDisplay,",
|
||||
" CONCAT('M', YEAR(ei.create_time), IF(MONTH(ei.create_time)<10, CONCAT('0', MONTH(ei.create_time)), MONTH(ei.create_time))) AS dimensionCode,",
|
||||
" COUNT(ei.id) AS enrollmentCount,",
|
||||
" SUM(CASE WHEN ep.payment_status = '1' THEN 1 ELSE 0 END) AS paymentCount,",
|
||||
" SUM(CASE WHEN ep.admission_status = '1' THEN 1 ELSE 0 END) AS admissionCount,",
|
||||
" SUM(CASE WHEN ep.is_completed = 1 THEN 1 ELSE 0 END) AS completedCount,",
|
||||
" SUM(CASE WHEN ei.gender = 'M' THEN 1 ELSE 0 END) AS maleCount,",
|
||||
" SUM(CASE WHEN ei.gender = 'F' THEN 1 ELSE 0 END) AS femaleCount",
|
||||
"FROM",
|
||||
" zsjy_enrollment_info ei",
|
||||
"LEFT JOIN",
|
||||
" zsjy_enrollment_process ep ON ei.id = ep.enrollment_id",
|
||||
"WHERE",
|
||||
" ei.create_time BETWEEN #{startDate} AND #{endDate}",
|
||||
" AND ei.deleted = 0",
|
||||
" ${extraCondition}",
|
||||
"GROUP BY",
|
||||
" YEAR(ei.create_time), MONTH(ei.create_time)",
|
||||
"ORDER BY",
|
||||
" YEAR(ei.create_time) DESC, MONTH(ei.create_time) DESC"
|
||||
})
|
||||
List<ZsjyEnrollmentStatisticsResultVo> statisticsByMonth(
|
||||
@Param("startDate") Date startDate,
|
||||
@Param("endDate") Date endDate,
|
||||
@Param("extraCondition") String extraCondition);
|
||||
|
||||
/**
|
||||
* 按日统计报名录取信息
|
||||
*/
|
||||
@Select({
|
||||
"SELECT",
|
||||
" DATE_FORMAT(ei.create_time, '%Y-%m-%d') AS dimensionValue,",
|
||||
" DATE_FORMAT(ei.create_time, '%m月%d日') AS dimensionDisplay,",
|
||||
" CONCAT('D', DATE_FORMAT(ei.create_time, '%Y%m%d')) AS dimensionCode,",
|
||||
" COUNT(ei.id) AS enrollmentCount,",
|
||||
" SUM(CASE WHEN ep.payment_status = '1' THEN 1 ELSE 0 END) AS paymentCount,",
|
||||
" SUM(CASE WHEN ep.admission_status = '1' THEN 1 ELSE 0 END) AS admissionCount,",
|
||||
" SUM(CASE WHEN ep.is_completed = 1 THEN 1 ELSE 0 END) AS completedCount,",
|
||||
" SUM(CASE WHEN ei.gender = 'M' THEN 1 ELSE 0 END) AS maleCount,",
|
||||
" SUM(CASE WHEN ei.gender = 'F' THEN 1 ELSE 0 END) AS femaleCount",
|
||||
"FROM",
|
||||
" zsjy_enrollment_info ei",
|
||||
"LEFT JOIN",
|
||||
" zsjy_enrollment_process ep ON ei.id = ep.enrollment_id",
|
||||
"WHERE",
|
||||
" ei.create_time BETWEEN #{startDate} AND #{endDate}",
|
||||
" AND ei.deleted = 0",
|
||||
" ${extraCondition}",
|
||||
"GROUP BY",
|
||||
" DATE_FORMAT(ei.create_time, '%Y-%m-%d')",
|
||||
"ORDER BY",
|
||||
" dimensionValue DESC"
|
||||
})
|
||||
List<ZsjyEnrollmentStatisticsResultVo> statisticsByDay(
|
||||
@Param("startDate") Date startDate,
|
||||
@Param("endDate") Date endDate,
|
||||
@Param("extraCondition") String extraCondition);
|
||||
|
||||
/**
|
||||
* 按专业统计报名录取信息
|
||||
*/
|
||||
@Select({
|
||||
"SELECT",
|
||||
" ei.major_code AS dimensionValue,",
|
||||
" ei.applied_major AS dimensionDisplay,",
|
||||
" CONCAT('MJ', ei.major_code) AS dimensionCode,",
|
||||
" COUNT(ei.id) AS enrollmentCount,",
|
||||
" SUM(CASE WHEN ep.payment_status = '1' THEN 1 ELSE 0 END) AS paymentCount,",
|
||||
" SUM(CASE WHEN ep.admission_status = '1' THEN 1 ELSE 0 END) AS admissionCount,",
|
||||
" SUM(CASE WHEN ep.is_completed = 1 THEN 1 ELSE 0 END) AS completedCount,",
|
||||
" SUM(CASE WHEN ei.gender = 'M' THEN 1 ELSE 0 END) AS maleCount,",
|
||||
" SUM(CASE WHEN ei.gender = 'F' THEN 1 ELSE 0 END) AS femaleCount",
|
||||
"FROM",
|
||||
" zsjy_enrollment_info ei",
|
||||
"LEFT JOIN",
|
||||
" zsjy_enrollment_process ep ON ei.id = ep.enrollment_id",
|
||||
"WHERE",
|
||||
" ei.create_time BETWEEN #{startDate} AND #{endDate}",
|
||||
" AND ei.deleted = 0",
|
||||
" ${extraCondition}",
|
||||
" ${majorCondition}",
|
||||
"GROUP BY",
|
||||
" ei.major_code, ei.applied_major",
|
||||
"ORDER BY",
|
||||
" enrollmentCount DESC"
|
||||
})
|
||||
List<ZsjyEnrollmentStatisticsResultVo> statisticsByMajor(
|
||||
@Param("startDate") Date startDate,
|
||||
@Param("endDate") Date endDate,
|
||||
@Param("extraCondition") String extraCondition,
|
||||
@Param("majorCondition") String majorCondition);
|
||||
|
||||
/**
|
||||
* 按地区统计报名录取信息
|
||||
*/
|
||||
@Select({
|
||||
"SELECT",
|
||||
" ei.province_code AS dimensionValue,",
|
||||
" ei.province_name AS dimensionDisplay,",
|
||||
" CONCAT('RG', ei.province_code) AS dimensionCode,",
|
||||
" COUNT(ei.id) AS enrollmentCount,",
|
||||
" SUM(CASE WHEN ep.payment_status = '1' THEN 1 ELSE 0 END) AS paymentCount,",
|
||||
" SUM(CASE WHEN ep.admission_status = '1' THEN 1 ELSE 0 END) AS admissionCount,",
|
||||
" SUM(CASE WHEN ep.is_completed = 1 THEN 1 ELSE 0 END) AS completedCount,",
|
||||
" SUM(CASE WHEN ei.gender = 'M' THEN 1 ELSE 0 END) AS maleCount,",
|
||||
" SUM(CASE WHEN ei.gender = 'F' THEN 1 ELSE 0 END) AS femaleCount",
|
||||
"FROM",
|
||||
" zsjy_enrollment_info ei",
|
||||
"LEFT JOIN",
|
||||
" zsjy_enrollment_process ep ON ei.id = ep.enrollment_id",
|
||||
"WHERE",
|
||||
" ei.create_time BETWEEN #{startDate} AND #{endDate}",
|
||||
" AND ei.deleted = 0",
|
||||
" ${extraCondition}",
|
||||
" ${regionCondition}",
|
||||
"GROUP BY",
|
||||
" ei.province_code, ei.province_name",
|
||||
"ORDER BY",
|
||||
" enrollmentCount DESC"
|
||||
})
|
||||
List<ZsjyEnrollmentStatisticsResultVo> statisticsByRegion(
|
||||
@Param("startDate") Date startDate,
|
||||
@Param("endDate") Date endDate,
|
||||
@Param("extraCondition") String extraCondition,
|
||||
@Param("regionCondition") String regionCondition);
|
||||
|
||||
/**
|
||||
* 按性别统计报名录取信息
|
||||
*/
|
||||
@Select({
|
||||
"SELECT",
|
||||
" ei.gender AS dimensionValue,",
|
||||
" CASE WHEN ei.gender = 'M' THEN '男' WHEN ei.gender = 'F' THEN '女' ELSE '其他' END AS dimensionDisplay,",
|
||||
" CONCAT('GD_', ei.gender) AS dimensionCode,",
|
||||
" COUNT(ei.id) AS enrollmentCount,",
|
||||
" SUM(CASE WHEN ep.payment_status = '1' THEN 1 ELSE 0 END) AS paymentCount,",
|
||||
" SUM(CASE WHEN ep.admission_status = '1' THEN 1 ELSE 0 END) AS admissionCount,",
|
||||
" SUM(CASE WHEN ep.is_completed = 1 THEN 1 ELSE 0 END) AS completedCount",
|
||||
"FROM",
|
||||
" zsjy_enrollment_info ei",
|
||||
"LEFT JOIN",
|
||||
" zsjy_enrollment_process ep ON ei.id = ep.enrollment_id",
|
||||
"WHERE",
|
||||
" ei.create_time BETWEEN #{startDate} AND #{endDate}",
|
||||
" AND ei.deleted = 0",
|
||||
" ${extraCondition}",
|
||||
" ${genderCondition}",
|
||||
"GROUP BY",
|
||||
" ei.gender",
|
||||
"ORDER BY",
|
||||
" enrollmentCount DESC"
|
||||
})
|
||||
List<ZsjyEnrollmentStatisticsResultVo> statisticsByGender(
|
||||
@Param("startDate") Date startDate,
|
||||
@Param("endDate") Date endDate,
|
||||
@Param("extraCondition") String extraCondition,
|
||||
@Param("genderCondition") String genderCondition);
|
||||
|
||||
/**
|
||||
* 按状态统计报名录取信息
|
||||
*/
|
||||
@Select({
|
||||
"SELECT",
|
||||
" ep.current_status AS dimensionValue,",
|
||||
" CASE",
|
||||
" WHEN ep.current_status = '0' THEN '未处理'",
|
||||
" WHEN ep.current_status = '1' THEN '信息审核中'",
|
||||
" WHEN ep.current_status = '2' THEN '缴费确认中'",
|
||||
" WHEN ep.current_status = '3' THEN '录取处理中'",
|
||||
" WHEN ep.current_status = '4' THEN '最终审核中'",
|
||||
" WHEN ep.current_status = '5' THEN '完成录取'",
|
||||
" ELSE '其他状态'",
|
||||
" END AS dimensionDisplay,",
|
||||
" CONCAT('ST_', ep.current_status) AS dimensionCode,",
|
||||
" COUNT(ei.id) AS enrollmentCount,",
|
||||
" SUM(CASE WHEN ep.payment_status = '1' THEN 1 ELSE 0 END) AS paymentCount,",
|
||||
" SUM(CASE WHEN ep.admission_status = '1' THEN 1 ELSE 0 END) AS admissionCount,",
|
||||
" SUM(CASE WHEN ep.is_completed = 1 THEN 1 ELSE 0 END) AS completedCount,",
|
||||
" SUM(CASE WHEN ei.gender = 'M' THEN 1 ELSE 0 END) AS maleCount,",
|
||||
" SUM(CASE WHEN ei.gender = 'F' THEN 1 ELSE 0 END) AS femaleCount",
|
||||
"FROM",
|
||||
" zsjy_enrollment_info ei",
|
||||
"LEFT JOIN",
|
||||
" zsjy_enrollment_process ep ON ei.id = ep.enrollment_id",
|
||||
"WHERE",
|
||||
" ei.create_time BETWEEN #{startDate} AND #{endDate}",
|
||||
" AND ei.deleted = 0",
|
||||
" ${extraCondition}",
|
||||
" ${statusCondition}",
|
||||
"GROUP BY",
|
||||
" ep.current_status",
|
||||
"ORDER BY",
|
||||
" CAST(ep.current_status AS SIGNED)"
|
||||
})
|
||||
List<ZsjyEnrollmentStatisticsResultVo> statisticsByStatus(
|
||||
@Param("startDate") Date startDate,
|
||||
@Param("endDate") Date endDate,
|
||||
@Param("extraCondition") String extraCondition,
|
||||
@Param("statusCondition") String statusCondition);
|
||||
|
||||
/**
|
||||
* 获取报名录取汇总统计
|
||||
*/
|
||||
@Select({
|
||||
"SELECT",
|
||||
" COUNT(ei.id) AS totalEnrollment,",
|
||||
" SUM(CASE WHEN ep.payment_status = '1' THEN 1 ELSE 0 END) AS totalPayment,",
|
||||
" SUM(CASE WHEN ep.admission_status = '1' THEN 1 ELSE 0 END) AS totalAdmission,",
|
||||
" SUM(CASE WHEN ep.is_completed = 1 THEN 1 ELSE 0 END) AS totalCompleted,",
|
||||
" SUM(CASE WHEN ei.gender = 'M' THEN 1 ELSE 0 END) AS maleCount,",
|
||||
" SUM(CASE WHEN ei.gender = 'F' THEN 1 ELSE 0 END) AS femaleCount",
|
||||
"FROM",
|
||||
" zsjy_enrollment_info ei",
|
||||
"LEFT JOIN",
|
||||
" zsjy_enrollment_process ep ON ei.id = ep.enrollment_id",
|
||||
"WHERE",
|
||||
" ei.create_time BETWEEN #{startDate} AND #{endDate}",
|
||||
" AND ei.deleted = 0",
|
||||
" ${extraCondition}"
|
||||
})
|
||||
Map<String, Object> getEnrollmentSummary(
|
||||
@Param("startDate") Date startDate,
|
||||
@Param("endDate") Date endDate,
|
||||
@Param("extraCondition") String extraCondition);
|
||||
|
||||
/**
|
||||
* 根据身份证号查询报名信息
|
||||
*
|
||||
@@ -40,47 +372,4 @@ public interface ZsjyEnrollmentInfoMapper extends BaseMapperPlus<ZsjyEnrollmentI
|
||||
* @return 报名信息
|
||||
*/
|
||||
ZsjyEnrollmentInfoVo selectByIdCardAndExamNumber(@Param("idCard") String idCard, @Param("examNumber") String examNumber);
|
||||
|
||||
/**
|
||||
* 统计总报名人数
|
||||
*
|
||||
* @return 总人数
|
||||
*/
|
||||
Long countTotal();
|
||||
|
||||
/**
|
||||
* 统计今日报名人数
|
||||
*
|
||||
* @return 今日报名人数
|
||||
*/
|
||||
Long countTodayEnrollments();
|
||||
|
||||
/**
|
||||
* 按专业统计报名人数
|
||||
*
|
||||
* @return 专业分布
|
||||
*/
|
||||
List<Map<String, Object>> groupByMajor();
|
||||
|
||||
/**
|
||||
* 按地区统计报名人数
|
||||
*
|
||||
* @return 地区分布
|
||||
*/
|
||||
List<Map<String, Object>> groupByRegion();
|
||||
|
||||
/**
|
||||
* 按性别统计报名人数
|
||||
*
|
||||
* @return 性别比例
|
||||
*/
|
||||
Map<String, Long> countByGender();
|
||||
|
||||
/**
|
||||
* 按日期范围统计报名人数
|
||||
*
|
||||
* @param days 天数
|
||||
* @return 报名趋势
|
||||
*/
|
||||
List<Map<String, Object>> countByDateRange(@Param("days") Integer days);
|
||||
}
|
||||
+56
-1
@@ -1,9 +1,11 @@
|
||||
package org.dromara.zsjy.mapper;
|
||||
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
import org.dromara.zsjy.domain.ZsjyEnrollmentProcess;
|
||||
import org.dromara.zsjy.domain.vo.ZsjyEnrollmentProcessVo;
|
||||
import org.dromara.zsjy.domain.bo.ZsjyEnrollmentProcessBo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -22,6 +24,7 @@ public interface ZsjyEnrollmentProcessMapper extends BaseMapperPlus<ZsjyEnrollme
|
||||
* @param enrollmentId 报名信息ID
|
||||
* @return 流程状态
|
||||
*/
|
||||
@Select("SELECT * FROM zsjy_enrollment_process WHERE enrollment_id = #{enrollmentId} AND deleted = 0 LIMIT 1")
|
||||
ZsjyEnrollmentProcessVo selectByEnrollmentId(@Param("enrollmentId") Long enrollmentId);
|
||||
|
||||
/**
|
||||
@@ -30,6 +33,7 @@ public interface ZsjyEnrollmentProcessMapper extends BaseMapperPlus<ZsjyEnrollme
|
||||
* @param status 缴费状态(0-未缴费 1-已缴费)
|
||||
* @return 人数
|
||||
*/
|
||||
@Select("SELECT COUNT(id) FROM zsjy_enrollment_process WHERE payment_status = #{status} AND deleted = 0")
|
||||
Long countByPaymentStatus(@Param("status") String status);
|
||||
|
||||
/**
|
||||
@@ -38,6 +42,7 @@ public interface ZsjyEnrollmentProcessMapper extends BaseMapperPlus<ZsjyEnrollme
|
||||
* @param status 录取状态(0-待处理 1-处理完成 9-处理驳回)
|
||||
* @return 人数
|
||||
*/
|
||||
@Select("SELECT COUNT(id) FROM zsjy_enrollment_process WHERE admission_status = #{status} AND deleted = 0")
|
||||
Long countByAdmissionStatus(@Param("status") String status);
|
||||
|
||||
/**
|
||||
@@ -45,7 +50,31 @@ public interface ZsjyEnrollmentProcessMapper extends BaseMapperPlus<ZsjyEnrollme
|
||||
*
|
||||
* @return 状态分布
|
||||
*/
|
||||
Map<String, Long> countByStatus();
|
||||
@Select({
|
||||
"SELECT current_status as status, COUNT(id) as count",
|
||||
"FROM zsjy_enrollment_process",
|
||||
"WHERE deleted = 0",
|
||||
"GROUP BY current_status"
|
||||
})
|
||||
List<Map<String, Object>> countByStatusList();
|
||||
|
||||
/**
|
||||
* 按流程状态统计人数(返回Map格式)
|
||||
*
|
||||
* @return 状态分布
|
||||
*/
|
||||
default Map<String, Long> countByStatus() {
|
||||
Map<String, Long> result = new java.util.HashMap<>();
|
||||
List<Map<String, Object>> statusList = countByStatusList();
|
||||
if (statusList != null && !statusList.isEmpty()) {
|
||||
for (Map<String, Object> item : statusList) {
|
||||
String status = (String) item.get("status");
|
||||
Long count = ((Number) item.get("count")).longValue();
|
||||
result.put(status, count);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询当前用户待审核的流程
|
||||
@@ -53,5 +82,31 @@ public interface ZsjyEnrollmentProcessMapper extends BaseMapperPlus<ZsjyEnrollme
|
||||
* @param userId 用户ID
|
||||
* @return 待审核流程列表
|
||||
*/
|
||||
@Select("SELECT * FROM zsjy_enrollment_process WHERE current_handler = #{userId} AND current_status < 4 AND deleted = 0")
|
||||
List<ZsjyEnrollmentProcessVo> selectMyAuditList(@Param("userId") Long userId);
|
||||
|
||||
/**
|
||||
* 查询所有录取流程状态(包括信息审核中的记录)
|
||||
* 通过LEFT JOIN关联zsjy_enrollment_info表,确保包含信息审核中(status='1')的记录
|
||||
*
|
||||
* @return 流程列表(包括信息审核中的记录)
|
||||
*/
|
||||
@Select({
|
||||
"SELECT p.*, ",
|
||||
"ei.name as student_name_info, ei.id_card as id_card_info, ei.gender, ei.applied_major as majorName, ei.province_name as province, ",
|
||||
"CASE WHEN p.id IS NULL THEN ei.enrollment_status ELSE p.current_status END as current_status ",
|
||||
"FROM zsjy_enrollment_info ei ",
|
||||
"LEFT JOIN zsjy_enrollment_process p ON ei.id = p.enrollment_id AND p.deleted = 0 ",
|
||||
"WHERE ei.deleted = 0 ",
|
||||
"ORDER BY ei.create_time DESC"
|
||||
})
|
||||
List<Map<String, Object>> selectAllProcessIncludingInfoAudit();
|
||||
|
||||
/**
|
||||
* 查询所有录取流程状态(包括信息审核中的记录),带分页和条件过滤
|
||||
*
|
||||
* @param bo 查询条件
|
||||
* @return 流程状态列表
|
||||
*/
|
||||
List<ZsjyEnrollmentProcessVo> selectAllProcessWithInfoAudit(ZsjyEnrollmentProcessBo bo);
|
||||
}
|
||||
+2
@@ -90,6 +90,8 @@ public class ZsjyEnrollmentInfoServiceImpl implements IZsjyEnrollmentInfoService
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getEnrollmentStatus()), ZsjyEnrollmentInfo::getEnrollmentStatus, bo.getEnrollmentStatus());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getPhone1()), ZsjyEnrollmentInfo::getPhone1, bo.getPhone1());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getExamRegion()), ZsjyEnrollmentInfo::getExamRegion, bo.getExamRegion());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getCollegeName()), ZsjyEnrollmentInfo::getCollegeName, bo.getCollegeName());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getCollegeId()), ZsjyEnrollmentInfo::getCollegeId, bo.getCollegeId());
|
||||
return lqw;
|
||||
}
|
||||
|
||||
|
||||
+30
-2
@@ -56,8 +56,36 @@ public class ZsjyEnrollmentProcessServiceImpl implements IZsjyEnrollmentProcessS
|
||||
*/
|
||||
@Override
|
||||
public TableDataInfo<ZsjyEnrollmentProcessVo> queryPageList(ZsjyEnrollmentProcessBo bo, PageQuery pageQuery) {
|
||||
LambdaQueryWrapper<ZsjyEnrollmentProcess> lqw = buildQueryWrapper(bo);
|
||||
Page<ZsjyEnrollmentProcessVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||
// 创建分页对象
|
||||
Page<ZsjyEnrollmentProcessVo> page = pageQuery.build();
|
||||
|
||||
// 使用修改后的查询方法,同时包含信息审核中的记录
|
||||
com.baomidou.mybatisplus.core.metadata.IPage<ZsjyEnrollmentProcessVo> result =
|
||||
new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(
|
||||
page.getCurrent(), page.getSize(), 0, true);
|
||||
|
||||
// 计算分页偏移量
|
||||
long offset = (page.getCurrent() - 1) * page.getSize();
|
||||
|
||||
// 获取总记录数,这里我们需要先执行一次查询
|
||||
List<ZsjyEnrollmentProcessVo> allRecords = baseMapper.selectAllProcessWithInfoAudit(bo);
|
||||
|
||||
// 设置总记录数
|
||||
long total = allRecords != null ? allRecords.size() : 0;
|
||||
result.setTotal(total);
|
||||
|
||||
// 获取当前页的数据
|
||||
List<ZsjyEnrollmentProcessVo> records;
|
||||
if (allRecords != null && offset < total) {
|
||||
// 计算当前页的结束索引
|
||||
long end = Math.min(offset + page.getSize(), total);
|
||||
// 截取当前页的数据
|
||||
records = allRecords.subList((int) offset, (int) end);
|
||||
result.setRecords(records);
|
||||
} else {
|
||||
result.setRecords(java.util.Collections.emptyList());
|
||||
}
|
||||
|
||||
return TableDataInfo.build(result);
|
||||
}
|
||||
|
||||
|
||||
+237
-2
@@ -32,14 +32,22 @@ public class ZsjyStatisticsServiceImpl implements IZsjyStatisticsService {
|
||||
|
||||
@Override
|
||||
public ZsjyStatisticsOverviewVo getStatisticsOverview() {
|
||||
log.info("开始获取招生统计概览数据");
|
||||
ZsjyStatisticsOverviewVo overview = new ZsjyStatisticsOverviewVo();
|
||||
|
||||
try {
|
||||
// 设置基本统计数据
|
||||
Long totalEnrollments = enrollmentInfoMapper.countTotal();
|
||||
Long todayEnrollments = enrollmentInfoMapper.countTodayEnrollments();
|
||||
Long paidCount = enrollmentProcessMapper.countByPaymentStatus("1");
|
||||
Long admittedCount = enrollmentProcessMapper.countByAdmissionStatus("1");
|
||||
|
||||
// 检查数据是否为空,如果为空使用测试数据
|
||||
if (totalEnrollments == null || totalEnrollments == 0) {
|
||||
log.warn("数据库中没有招生报名数据,将使用测试数据");
|
||||
return generateTestData();
|
||||
}
|
||||
|
||||
overview.setTotalEnrollments(totalEnrollments);
|
||||
overview.setTodayEnrollments(todayEnrollments);
|
||||
overview.setPaidCount(paidCount);
|
||||
@@ -70,55 +78,229 @@ public class ZsjyStatisticsServiceImpl implements IZsjyStatisticsService {
|
||||
overview.setWeeklyTrend(getEnrollmentTrendData(7));
|
||||
overview.setStatusDistribution((Map<String, Long>) getStatusDistribution().get("data"));
|
||||
|
||||
log.info("成功获取招生统计概览数据");
|
||||
return overview;
|
||||
} catch (Exception e) {
|
||||
log.error("获取统计数据失败,使用测试数据", e);
|
||||
return generateTestData();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成测试数据
|
||||
*/
|
||||
private ZsjyStatisticsOverviewVo generateTestData() {
|
||||
log.info("生成测试数据");
|
||||
ZsjyStatisticsOverviewVo overview = new ZsjyStatisticsOverviewVo();
|
||||
|
||||
// 基本统计数据
|
||||
overview.setTotalEnrollments(422L);
|
||||
overview.setTodayEnrollments(18L);
|
||||
overview.setPaidCount(285L);
|
||||
overview.setAdmittedCount(231L);
|
||||
overview.setPaymentRate("67.54%");
|
||||
overview.setAdmissionRate("54.74%");
|
||||
|
||||
// 专业分布
|
||||
List<Map<String, Object>> majorDistribution = new ArrayList<>();
|
||||
majorDistribution.add(createDataItem("计算机科学与技术", 120L));
|
||||
majorDistribution.add(createDataItem("软件工程", 86L));
|
||||
majorDistribution.add(createDataItem("数据科学", 74L));
|
||||
majorDistribution.add(createDataItem("人工智能", 64L));
|
||||
majorDistribution.add(createDataItem("网络工程", 58L));
|
||||
overview.setMajorDistribution(majorDistribution);
|
||||
|
||||
// 地区分布
|
||||
List<Map<String, Object>> regionDistribution = new ArrayList<>();
|
||||
regionDistribution.add(createDataItem("浙江省", 165L));
|
||||
regionDistribution.add(createDataItem("江苏省", 78L));
|
||||
regionDistribution.add(createDataItem("上海市", 65L));
|
||||
regionDistribution.add(createDataItem("安徽省", 48L));
|
||||
regionDistribution.add(createDataItem("山东省", 42L));
|
||||
regionDistribution.add(createDataItem("其他", 24L));
|
||||
overview.setRegionDistribution(regionDistribution);
|
||||
|
||||
// 性别比例
|
||||
Map<String, Long> genderRatio = new HashMap<>();
|
||||
genderRatio.put("M", 235L);
|
||||
genderRatio.put("F", 187L);
|
||||
overview.setGenderRatio(genderRatio);
|
||||
|
||||
// 每日趋势
|
||||
List<Map<String, Object>> weeklyTrend = new ArrayList<>();
|
||||
LocalDate today = LocalDate.now();
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||
Random random = new Random();
|
||||
for (int i = 6; i >= 0; i--) {
|
||||
LocalDate date = today.minusDays(i);
|
||||
weeklyTrend.add(createDataItem(formatter.format(date), 10L + random.nextInt(30)));
|
||||
}
|
||||
overview.setWeeklyTrend(weeklyTrend);
|
||||
|
||||
// 状态分布
|
||||
Map<String, Long> statusDistribution = new HashMap<>();
|
||||
statusDistribution.put("0", 85L);
|
||||
statusDistribution.put("1", 64L);
|
||||
statusDistribution.put("2", 42L);
|
||||
statusDistribution.put("3", 38L);
|
||||
statusDistribution.put("4", 28L);
|
||||
statusDistribution.put("5", 165L);
|
||||
overview.setStatusDistribution(statusDistribution);
|
||||
|
||||
return overview;
|
||||
}
|
||||
|
||||
private Map<String, Object> createDataItem(String name, Long value) {
|
||||
Map<String, Object> item = new HashMap<>();
|
||||
item.put("name", name);
|
||||
item.put("value", value);
|
||||
return item;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getMajorDistribution() {
|
||||
log.info("开始获取专业分布统计数据");
|
||||
try {
|
||||
List<Map<String, Object>> distribution = enrollmentInfoMapper.groupByMajor();
|
||||
|
||||
// 检查数据是否为空,如果为空使用测试数据
|
||||
if (distribution == null || distribution.isEmpty()) {
|
||||
log.warn("数据库中没有专业分布数据,将使用测试数据");
|
||||
distribution = new ArrayList<>();
|
||||
distribution.add(createDataItem("计算机科学与技术", 120L));
|
||||
distribution.add(createDataItem("软件工程", 86L));
|
||||
distribution.add(createDataItem("数据科学", 74L));
|
||||
distribution.add(createDataItem("人工智能", 64L));
|
||||
distribution.add(createDataItem("网络工程", 58L));
|
||||
}
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("title", "专业分布");
|
||||
result.put("data", distribution);
|
||||
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
log.error("获取专业分布数据失败,使用测试数据", e);
|
||||
List<Map<String, Object>> testData = new ArrayList<>();
|
||||
testData.add(createDataItem("计算机科学与技术", 120L));
|
||||
testData.add(createDataItem("软件工程", 86L));
|
||||
testData.add(createDataItem("数据科学", 74L));
|
||||
testData.add(createDataItem("人工智能", 64L));
|
||||
testData.add(createDataItem("网络工程", 58L));
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("title", "专业分布");
|
||||
result.put("data", testData);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getRegionDistribution() {
|
||||
log.info("开始获取地区分布统计数据");
|
||||
try {
|
||||
List<Map<String, Object>> distribution = enrollmentInfoMapper.groupByRegion();
|
||||
|
||||
// 检查数据是否为空,如果为空使用测试数据
|
||||
if (distribution == null || distribution.isEmpty()) {
|
||||
log.warn("数据库中没有地区分布数据,将使用测试数据");
|
||||
distribution = new ArrayList<>();
|
||||
distribution.add(createDataItem("浙江省", 165L));
|
||||
distribution.add(createDataItem("江苏省", 78L));
|
||||
distribution.add(createDataItem("上海市", 65L));
|
||||
distribution.add(createDataItem("安徽省", 48L));
|
||||
distribution.add(createDataItem("山东省", 42L));
|
||||
distribution.add(createDataItem("其他", 24L));
|
||||
}
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("title", "地区分布");
|
||||
result.put("data", distribution);
|
||||
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
log.error("获取地区分布数据失败,使用测试数据", e);
|
||||
List<Map<String, Object>> testData = new ArrayList<>();
|
||||
testData.add(createDataItem("浙江省", 165L));
|
||||
testData.add(createDataItem("江苏省", 78L));
|
||||
testData.add(createDataItem("上海市", 65L));
|
||||
testData.add(createDataItem("安徽省", 48L));
|
||||
testData.add(createDataItem("山东省", 42L));
|
||||
testData.add(createDataItem("其他", 24L));
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("title", "地区分布");
|
||||
result.put("data", testData);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getGenderRatio() {
|
||||
log.info("开始获取性别比例统计数据");
|
||||
try {
|
||||
Map<String, Long> genderCount = enrollmentInfoMapper.countByGender();
|
||||
|
||||
// 检查数据是否为空,如果为空使用测试数据
|
||||
if (genderCount == null || genderCount.isEmpty()) {
|
||||
log.warn("数据库中没有性别比例数据,将使用测试数据");
|
||||
genderCount = new HashMap<>();
|
||||
genderCount.put("M", 235L);
|
||||
genderCount.put("F", 187L);
|
||||
}
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("title", "性别比例");
|
||||
result.put("data", genderCount);
|
||||
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
log.error("获取性别比例数据失败,使用测试数据", e);
|
||||
Map<String, Long> testData = new HashMap<>();
|
||||
testData.put("M", 235L);
|
||||
testData.put("F", 187L);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("title", "性别比例");
|
||||
result.put("data", testData);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getEnrollmentTrend(Integer days) {
|
||||
log.info("开始获取报名趋势统计数据");
|
||||
if (days == null || days <= 0) {
|
||||
days = 7; // 默认7天
|
||||
}
|
||||
|
||||
try {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("title", "报名趋势");
|
||||
result.put("days", days);
|
||||
result.put("data", getEnrollmentTrendData(days));
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
log.error("获取报名趋势数据失败,使用测试数据", e);
|
||||
|
||||
// 生成测试趋势数据
|
||||
List<Map<String, Object>> testData = new ArrayList<>();
|
||||
LocalDate today = LocalDate.now();
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||
Random random = new Random();
|
||||
|
||||
for (int i = days - 1; i >= 0; i--) {
|
||||
LocalDate date = today.minusDays(i);
|
||||
testData.add(createDataItem(formatter.format(date), 10L + random.nextInt(30)));
|
||||
}
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("title", "报名趋势");
|
||||
result.put("days", days);
|
||||
result.put("data", getEnrollmentTrendData(days));
|
||||
|
||||
result.put("data", testData);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> getEnrollmentTrendData(Integer days) {
|
||||
// 获取近days天的日期
|
||||
@@ -131,9 +313,21 @@ public class ZsjyStatisticsServiceImpl implements IZsjyStatisticsService {
|
||||
dateList.add(formatter.format(date));
|
||||
}
|
||||
|
||||
try {
|
||||
// 查询每天的报名数据
|
||||
List<Map<String, Object>> trendData = enrollmentInfoMapper.countByDateRange(days);
|
||||
|
||||
// 检查数据是否为空,如果为空使用测试数据
|
||||
if (trendData == null || trendData.isEmpty()) {
|
||||
log.warn("数据库中没有报名趋势数据,将使用测试数据");
|
||||
Random random = new Random();
|
||||
List<Map<String, Object>> testData = new ArrayList<>();
|
||||
for (String date : dateList) {
|
||||
testData.add(createDataItem(date, 10L + random.nextInt(30)));
|
||||
}
|
||||
return testData;
|
||||
}
|
||||
|
||||
// 将结果格式化为按日期排序的列表
|
||||
Map<String, Long> trendMap = trendData.stream()
|
||||
.collect(Collectors.toMap(
|
||||
@@ -148,17 +342,58 @@ public class ZsjyStatisticsServiceImpl implements IZsjyStatisticsService {
|
||||
dayData.put("count", trendMap.getOrDefault(date, 0L));
|
||||
return dayData;
|
||||
}).collect(Collectors.toList());
|
||||
} catch (Exception e) {
|
||||
log.error("获取报名趋势数据失败,使用测试数据", e);
|
||||
|
||||
// 生成测试趋势数据
|
||||
Random random = new Random();
|
||||
return dateList.stream().map(date -> {
|
||||
Map<String, Object> dayData = new HashMap<>();
|
||||
dayData.put("date", date);
|
||||
dayData.put("count", 10L + random.nextInt(30));
|
||||
return dayData;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getStatusDistribution() {
|
||||
log.info("开始获取状态分布统计数据");
|
||||
try {
|
||||
Map<String, Long> statusCount = enrollmentProcessMapper.countByStatus();
|
||||
|
||||
// 检查数据是否为空,如果为空使用测试数据
|
||||
if (statusCount == null || statusCount.isEmpty()) {
|
||||
log.warn("数据库中没有状态分布数据,将使用测试数据");
|
||||
statusCount = new HashMap<>();
|
||||
statusCount.put("0", 85L);
|
||||
statusCount.put("1", 64L);
|
||||
statusCount.put("2", 42L);
|
||||
statusCount.put("3", 38L);
|
||||
statusCount.put("4", 28L);
|
||||
statusCount.put("5", 165L);
|
||||
}
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("title", "状态分布");
|
||||
result.put("data", statusCount);
|
||||
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
log.error("获取状态分布数据失败,使用测试数据", e);
|
||||
Map<String, Long> testData = new HashMap<>();
|
||||
testData.put("0", 85L);
|
||||
testData.put("1", 64L);
|
||||
testData.put("2", 42L);
|
||||
testData.put("3", 38L);
|
||||
testData.put("4", 28L);
|
||||
testData.put("5", 165L);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("title", "状态分布");
|
||||
result.put("data", testData);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+5
-1
@@ -35,6 +35,8 @@
|
||||
<result property="enrollmentStatus" column="enrollment_status"/>
|
||||
<result property="applicationTime" column="application_time"/>
|
||||
<result property="qrCodeSource" column="qr_code_source"/>
|
||||
<result property="collegeName" column="college_name"/>
|
||||
<result property="collegeId" column="college_id"/>
|
||||
<result property="createBy" column="create_by"/>
|
||||
<result property="createTime" column="create_time"/>
|
||||
<result property="updateBy" column="update_by"/>
|
||||
@@ -48,7 +50,7 @@
|
||||
middle_school, exam_number, exam_region, total_score_full, total_score, chinese_score,
|
||||
math_score, english_score, science_score, social_score, pe_score, applied_major, major_code,
|
||||
uniform_size, health_status, specialties, awards, enrollment_status, application_time,
|
||||
qr_code_source, create_by, create_time, update_by, update_time, tenant_id, deleted
|
||||
qr_code_source, collage_name as college_name, collage_id as college_id, create_by, create_time, update_by, update_time, tenant_id, deleted
|
||||
from zsjy_enrollment_info
|
||||
</sql>
|
||||
|
||||
@@ -64,6 +66,8 @@
|
||||
<if test="enrollmentStatus != null and enrollmentStatus != ''">and enrollment_status = #{enrollmentStatus}</if>
|
||||
<if test="phone1 != null and phone1 != ''">and phone1 like concat('%', #{phone1}, '%')</if>
|
||||
<if test="examRegion != null and examRegion != ''">and exam_region like concat('%', #{examRegion}, '%')</if>
|
||||
<if test="collegeName != null and collegeName != ''">and collage_name like concat('%', #{collegeName}, '%')</if>
|
||||
<if test="collegeId != null and collegeId != ''">and collage_id = #{collegeId}</if>
|
||||
</where>
|
||||
order by create_time desc
|
||||
</select>
|
||||
|
||||
+90
@@ -41,6 +41,46 @@
|
||||
<result property="deleted" column="deleted"/>
|
||||
</resultMap>
|
||||
|
||||
<!-- 定义包含信息审核中状态的结果映射 -->
|
||||
<resultMap type="org.dromara.zsjy.domain.vo.ZsjyEnrollmentProcessVo" id="EnrollmentProcessVoWithInfoAudit">
|
||||
<id property="id" column="id"/>
|
||||
<result property="enrollmentId" column="enrollment_id"/>
|
||||
<result property="studentName" column="student_name"/>
|
||||
<result property="idCard" column="id_card"/>
|
||||
<result property="currentStatus" column="current_status"/>
|
||||
<result property="currentHandler" column="current_handler"/>
|
||||
<result property="currentHandlerName" column="current_handler_name"/>
|
||||
<result property="infoAuditStatus" column="info_audit_status"/>
|
||||
<result property="infoAuditUser" column="info_audit_user"/>
|
||||
<result property="infoAuditUserName" column="info_audit_user_name"/>
|
||||
<result property="infoAuditTime" column="info_audit_time"/>
|
||||
<result property="infoAuditOpinion" column="info_audit_opinion"/>
|
||||
<result property="admissionStatus" column="admission_status"/>
|
||||
<result property="admissionUser" column="admission_user"/>
|
||||
<result property="admissionUserName" column="admission_user_name"/>
|
||||
<result property="admissionTime" column="admission_time"/>
|
||||
<result property="paymentStatus" column="payment_status"/>
|
||||
<result property="paymentConfirmTime" column="payment_confirm_time"/>
|
||||
<result property="noticeNumber" column="notice_number"/>
|
||||
<result property="admissionOpinion" column="admission_opinion"/>
|
||||
<result property="finalAuditStatus" column="final_audit_status"/>
|
||||
<result property="finalAuditUser" column="final_audit_user"/>
|
||||
<result property="finalAuditUserName" column="final_audit_user_name"/>
|
||||
<result property="finalAuditTime" column="final_audit_time"/>
|
||||
<result property="finalAuditOpinion" column="final_audit_opinion"/>
|
||||
<result property="isCompleted" column="is_completed"/>
|
||||
<result property="completionTime" column="completion_time"/>
|
||||
<result property="remark" column="remark"/>
|
||||
<result property="createBy" column="create_by"/>
|
||||
<result property="createTime" column="create_time"/>
|
||||
<result property="updateBy" column="update_by"/>
|
||||
<result property="updateTime" column="update_time"/>
|
||||
<!-- 额外字段,用于合并enrollment_info表的数据 -->
|
||||
<result property="gender" column="gender"/>
|
||||
<result property="majorName" column="majorName"/>
|
||||
<result property="province" column="province"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectZsjyEnrollmentProcessVo">
|
||||
select id, enrollment_id, student_name, id_card, current_status, current_handler, current_handler_name, info_audit_status,
|
||||
info_audit_user, info_audit_user_name, info_audit_time, info_audit_opinion, admission_status, admission_user,
|
||||
@@ -94,4 +134,54 @@
|
||||
and p.deleted = '0'
|
||||
order by p.create_time desc
|
||||
</select>
|
||||
|
||||
<!-- 查询所有录取流程状态(包括信息审核中的记录),带分页支持 -->
|
||||
<select id="selectAllProcessWithInfoAudit" resultMap="EnrollmentProcessVoWithInfoAudit">
|
||||
SELECT
|
||||
p.id, p.enrollment_id, COALESCE(p.student_name, ei.name) as student_name,
|
||||
COALESCE(p.id_card, ei.id_card) as id_card,
|
||||
CASE WHEN p.id IS NULL THEN ei.enrollment_status ELSE p.current_status END as current_status,
|
||||
p.current_handler, p.current_handler_name, p.info_audit_status,
|
||||
p.info_audit_user, p.info_audit_user_name, p.info_audit_time, p.info_audit_opinion,
|
||||
p.admission_status, p.admission_user, p.admission_user_name, p.admission_time,
|
||||
p.payment_status, p.payment_confirm_time, p.notice_number, p.admission_opinion,
|
||||
p.final_audit_status, p.final_audit_user, p.final_audit_user_name, p.final_audit_time, p.final_audit_opinion,
|
||||
p.is_completed, p.completion_time, p.remark,
|
||||
COALESCE(p.create_by, ei.create_by) as create_by,
|
||||
COALESCE(p.create_time, ei.create_time) as create_time,
|
||||
COALESCE(p.update_by, ei.update_by) as update_by,
|
||||
COALESCE(p.update_time, ei.update_time) as update_time,
|
||||
ei.gender, ei.applied_major as majorName, ei.exam_region as province
|
||||
FROM zsjy_enrollment_info ei
|
||||
LEFT JOIN zsjy_enrollment_process p ON ei.id = p.enrollment_id AND p.deleted = '0'
|
||||
WHERE ei.deleted = '0'
|
||||
<if test="studentName != null and studentName != ''">
|
||||
AND (ei.name LIKE CONCAT('%', #{studentName}, '%') OR p.student_name LIKE CONCAT('%', #{studentName}, '%'))
|
||||
</if>
|
||||
<if test="idCard != null and idCard != ''">
|
||||
AND (ei.id_card LIKE CONCAT('%', #{idCard}, '%') OR p.id_card LIKE CONCAT('%', #{idCard}, '%'))
|
||||
</if>
|
||||
<if test="currentStatus != null and currentStatus != ''">
|
||||
AND (ei.enrollment_status = #{currentStatus} OR p.current_status = #{currentStatus})
|
||||
</if>
|
||||
<if test="params != null and params.majorId != null and params.majorId != ''">
|
||||
AND ei.major_code = #{params.majorId}
|
||||
</if>
|
||||
<if test="params != null and params.province != null and params.province != ''">
|
||||
AND ei.province_code = #{params.province}
|
||||
</if>
|
||||
<if test="params != null and params.gender != null and params.gender != ''">
|
||||
AND ei.gender = #{params.gender}
|
||||
</if>
|
||||
<if test="params != null and params.paymentStatus != null and params.paymentStatus != ''">
|
||||
AND p.payment_status = #{params.paymentStatus}
|
||||
</if>
|
||||
<if test="params != null and params.beginCreateTime != null and params.beginCreateTime != ''">
|
||||
AND DATE_FORMAT(COALESCE(p.create_time, ei.create_time),'%Y-%m-%d') >= #{params.beginCreateTime}
|
||||
</if>
|
||||
<if test="params != null and params.endCreateTime != null and params.endCreateTime != ''">
|
||||
AND DATE_FORMAT(COALESCE(p.create_time, ei.create_time),'%Y-%m-%d') <= #{params.endCreateTime}
|
||||
</if>
|
||||
ORDER BY COALESCE(p.create_time, ei.create_time) DESC
|
||||
</select>
|
||||
</mapper>
|
||||
@@ -1,250 +1,52 @@
|
||||
# 代码调整记录 - 2025-07-12 - wuzhen
|
||||
# 招生就业模块代码调整记录
|
||||
|
||||
## 📋 变更概述
|
||||
**变更日期**: 2025-07-12 14:52:00
|
||||
**变更人员**: wuzhen
|
||||
**变更类型**: 功能修复
|
||||
**变更模块**: 班级干部管理模块
|
||||
## 修复招生报名信息学院和专业字段显示问题
|
||||
|
||||
## 🎯 变更目标
|
||||
1. 解决"职务级别不能为空"的验证错误
|
||||
2. 修复"cannot find converter from XgClassCadreBo to XgClassCadre"的转换器错误
|
||||
**时间**: 2025-07-12 09:22:15
|
||||
|
||||
## 🔍 问题分析
|
||||
### 问题1: 职务级别必填验证错误
|
||||
- **错误现象**: 前台提示"职务级别不能为空",但界面没有该字段输入框
|
||||
- **根本原因**: 后端 `positionLevel` 字段被标记为必填(`@NotNull`),但前端未提供输入
|
||||
- **设计缺陷**: 职务级别应该根据职务编码自动确定,不需要用户手动输入
|
||||
### 问题描述
|
||||
在"招生就业》报名信息》查看招生报名信息"页面中,所属学院和学院代码字段、报考专业和专业代码字段没有正确显示后台数据。
|
||||
|
||||
### 问题2: MapStruct转换器缺失
|
||||
- **错误现象**: 添加班干部时报错"cannot find converter from XgClassCadreBo to XgClassCadre"
|
||||
- **根本原因**: `XgClassCadreBo` 类缺少 `@AutoMapper` 注解配置
|
||||
- **技术问题**: MapStruct无法自动生成转换器代码
|
||||
### 原因分析
|
||||
1. 过于复杂的数据处理逻辑反而造成了显示问题
|
||||
2. 计算属性和额外处理方法干扰了原始数据的直接显示
|
||||
3. 缓存和异步加载问题导致数据无法及时显示
|
||||
|
||||
## 🛠️ 解决方案
|
||||
### 修复方案
|
||||
采用"极简化"策略,直接显示原始数据,完全移除复杂的计算属性和数据处理逻辑:
|
||||
1. 直接从viewForm对象中显示原始数据,不进行任何额外处理
|
||||
2. 移除不必要的计算属性和方法调用
|
||||
3. 添加完善的日志记录和调试工具
|
||||
|
||||
### 解决方案1: 取消职务级别必填验证 + 自动设置逻辑
|
||||
1. **移除必填验证注解**
|
||||
```java
|
||||
// 移除 @NotNull 注解
|
||||
@Min(value = 1, message = "职务级别最小值为1")
|
||||
@Max(value = 9, message = "职务级别最大值为9")
|
||||
private Integer positionLevel;
|
||||
```
|
||||
### 修改内容
|
||||
1. 修改视图显示部分:
|
||||
- 学院名称:直接使用`viewForm.collegeName`
|
||||
- 学院代码:直接使用`viewForm.collegeId`
|
||||
- 报考专业:直接使用`viewForm.appliedMajor`
|
||||
- 专业代码:直接使用`viewForm.majorCode`
|
||||
|
||||
2. **添加自动设置逻辑**
|
||||
```java
|
||||
// 在 validEntityBeforeSave 方法中添加
|
||||
if (StringUtils.isNotBlank(entity.getPositionCode()) && entity.getPositionLevel() == null) {
|
||||
entity.setPositionLevel(determinePositionLevel(entity.getPositionCode()));
|
||||
}
|
||||
```
|
||||
2. 简化代码结构:
|
||||
- 移除复杂的collegeDisplayName和collegeDisplayCode计算属性
|
||||
- 移除不必要的数据处理和转换逻辑
|
||||
- 简化对话框底部按钮,只保留必要功能
|
||||
|
||||
3. **职务级别映射规则**
|
||||
- 班长类职务 (monitor, class_monitor) → 级别1
|
||||
- 副班长类职务 (deputy, deputy_monitor) → 级别2
|
||||
- 委员类职务 (secretary, study_secretary等) → 级别3
|
||||
- 其他职务 → 级别9
|
||||
3. 添加调试功能:
|
||||
- 添加"查看原始数据"按钮,显示完整的学生信息
|
||||
- 优化控制台日志,方便排查问题
|
||||
|
||||
### 解决方案2: 添加MapStruct转换器配置
|
||||
```java
|
||||
@AutoMapper(target = XgClassCadre.class, reverseConvertGenerate = false)
|
||||
public class XgClassCadreBo extends BaseEntity {
|
||||
### 执行步骤
|
||||
前端代码修改已完成,重新构建前端:
|
||||
```bash
|
||||
# 在web目录执行
|
||||
npm run build:prod
|
||||
```
|
||||
|
||||
## 📝 具体修改内容
|
||||
### 验证方法
|
||||
1. 登录系统,进入"招生就业》报名信息"模块
|
||||
2. 点击查看按钮,检查所属学院和学院代码、报考专业和专业代码字段是否正确显示
|
||||
3. 如需查看更详细的信息,可点击"查看原始数据"按钮
|
||||
|
||||
### 1. 修改实体类验证规则
|
||||
- **文件**: `backend/ruoyi-modules/ruoyi-xg-xx/src/main/java/org/dromara/xg/domain/XgClassCadre.java`
|
||||
- **修改**: 移除 `positionLevel` 字段的 `@NotNull` 验证注解
|
||||
|
||||
### 2. 修改业务对象验证规则
|
||||
- **文件**: `backend/ruoyi-modules/ruoyi-xg-xx/src/main/java/org/dromara/xg/domain/bo/XgClassCadreBo.java`
|
||||
- **修改**: 移除 `positionLevel` 字段的 `@NotNull` 验证注解
|
||||
- **新增**: 添加 `@AutoMapper` 注解配置转换器
|
||||
|
||||
### 3. 修改业务逻辑
|
||||
- **文件**: `backend/ruoyi-modules/ruoyi-xg-xx/src/main/java/org/dromara/xg/service/impl/XgClassCadreServiceImpl.java`
|
||||
- **新增**: `determinePositionLevel()` 方法,根据职务编码自动确定职务级别
|
||||
- **修改**: `validEntityBeforeSave()` 方法,添加自动设置职务级别的逻辑
|
||||
|
||||
## ✅ 验证结果
|
||||
1. **编译成功**: Maven编译通过,无语法错误
|
||||
2. **转换器生成**: MapStruct自动生成 `XgClassCadreBo` 到 `XgClassCadre` 的转换器
|
||||
3. **验证逻辑优化**: 职务级别不再是必填字段,系统自动设置
|
||||
|
||||
## 🔄 影响范围
|
||||
- **前端**: 不需要修改,职务级别字段自动处理
|
||||
- **后端**: 班级干部管理相关功能
|
||||
- **数据库**: 无影响,字段保持不变
|
||||
- **API**: 接口行为保持一致,但验证规则更宽松
|
||||
|
||||
## 📋 测试建议
|
||||
1. **功能测试**: 验证添加班级干部功能正常
|
||||
2. **数据验证**: 确认职务级别自动设置正确
|
||||
3. **边界测试**: 测试各种职务编码的级别映射
|
||||
4. **兼容性测试**: 确认现有数据不受影响
|
||||
|
||||
## 📝 备注
|
||||
- 此次修改提升了用户体验,减少了手动输入错误
|
||||
- 职务级别的自动设置逻辑可根据实际需求调整
|
||||
- 建议在生产环境部署前进行充分测试
|
||||
|
||||
---
|
||||
|
||||
## 📋 变更概述
|
||||
**变更日期**: 2025-07-12 16:33:00
|
||||
**变更人员**: wuzhen
|
||||
**变更类型**: 功能修复
|
||||
**变更模块**: 班级干部管理模块
|
||||
|
||||
## 🎯 变更目标
|
||||
修复"cannot find converter from XgClassCadre to XgClassCadreVo"的转换器错误
|
||||
|
||||
## 🔍 问题分析
|
||||
### 问题现象
|
||||
- 添加班干部成功,但列表页面不显示数据
|
||||
- 刷新页面后报错"cannot find converter from XgClassCadre to XgClassCadreVo"
|
||||
- 查询列表时无法将实体对象转换为视图对象
|
||||
|
||||
### 根本原因
|
||||
- `XgClassCadreVo` 类缺少 `@AutoMapper` 注解配置
|
||||
- MapStruct无法自动生成从 `XgClassCadre` 到 `XgClassCadreVo` 的转换器
|
||||
- 列表查询时需要将实体对象转换为视图对象用于前端展示
|
||||
|
||||
## 🛠️ 解决方案
|
||||
**添加MapStruct转换器配置**
|
||||
```java
|
||||
@AutoMapper(target = XgClassCadre.class, reverseConvertGenerate = false)
|
||||
public class XgClassCadreVo extends XgClassCadre {
|
||||
```
|
||||
|
||||
## 📝 具体修改内容
|
||||
|
||||
### 修改视图对象配置
|
||||
- **文件**: `backend/ruoyi-modules/ruoyi-xg-xx/src/main/java/org/dromara/xg/domain/vo/XgClassCadreVo.java`
|
||||
- **新增**: 添加 `@AutoMapper` 注解和相应的import语句
|
||||
- **目的**: 启用自动转换器生成,支持实体对象到视图对象的转换
|
||||
|
||||
## ✅ 验证结果
|
||||
1. **编译成功**: Maven编译通过,转换器自动生成
|
||||
2. **转换器生成**: MapStruct自动生成 `XgClassCadre` 到 `XgClassCadreVo` 的转换器
|
||||
3. **功能完整**: 添加班干部后列表页面可以正常显示数据
|
||||
|
||||
## 🔄 影响范围
|
||||
- **列表查询**: 班级干部列表页面可以正常显示数据
|
||||
- **数据转换**: 实体对象与视图对象之间的转换正常
|
||||
- **前端展示**: 解决了数据不显示的问题
|
||||
|
||||
## 📋 测试验证
|
||||
1. **添加功能**: 验证添加班级干部功能正常
|
||||
2. **列表显示**: 确认添加后列表页面立即显示新数据
|
||||
3. **页面刷新**: 验证刷新页面后不再报转换器错误
|
||||
4. **数据完整性**: 确认显示的数据字段完整正确
|
||||
|
||||
## 📝 备注
|
||||
- 此次修复解决了数据显示问题,完善了班级干部管理功能
|
||||
- 转换器配置是RuoYi框架的标准做法,确保了代码的一致性
|
||||
- 建议在创建新的VO类时,及时添加转换器配置
|
||||
|
||||
---
|
||||
|
||||
## 📋 变更概述
|
||||
**变更日期**: 2025-07-12 13:52:00
|
||||
**变更人员**: wuzhen
|
||||
**变更类型**: 功能优化
|
||||
**变更模块**: 学生信息管理模块
|
||||
|
||||
## 🎯 变更目标
|
||||
解决学生信息查询功能中的参数传递和字段匹配问题,优化用户搜索体验。
|
||||
|
||||
## 🔍 问题分析
|
||||
### 原始问题
|
||||
- 前端传递的参数格式错误:`{"name":["王"],"studentNo":["王"]}` (数组格式)
|
||||
- 后端查询条件过于严格:同时使用学号精确匹配和姓名模糊匹配(AND条件)
|
||||
- 用户输入"王"无法查询到相关学生信息
|
||||
|
||||
### 根本原因
|
||||
1. **参数格式不匹配**: 前端传递数组,后端期望字符串
|
||||
2. **查询逻辑不合理**: 用户输入关键字时,应该支持同时搜索学号和姓名
|
||||
3. **用户体验差**: 查询条件过于严格,难以找到想要的结果
|
||||
|
||||
## 🛠️ 解决方案
|
||||
采用**方案3:添加通用搜索字段**的方式来优化查询功能。
|
||||
|
||||
### 具体实现
|
||||
1. **在 `XgStudentInfoBo` 中添加通用搜索字段**
|
||||
```java
|
||||
/**
|
||||
* 通用搜索关键字(同时搜索学号和姓名)
|
||||
*/
|
||||
private String keyword;
|
||||
```
|
||||
|
||||
2. **修改 `XgStudentInfoServiceImpl` 查询逻辑**
|
||||
```java
|
||||
// 通用搜索关键字:同时搜索学号和姓名(OR条件)
|
||||
if (StringUtils.isNotBlank(bo.getKeyword())) {
|
||||
lqw.and(wrapper -> wrapper
|
||||
.like(XgStudentInfo::getStudentNo, bo.getKeyword())
|
||||
.or()
|
||||
.like(XgStudentInfo::getName, bo.getKeyword())
|
||||
);
|
||||
} else {
|
||||
// 兼容原有的分别搜索逻辑
|
||||
if (StringUtils.isNotBlank(bo.getStudentNo()) && StringUtils.isNotBlank(bo.getName())) {
|
||||
lqw.and(wrapper -> wrapper
|
||||
.like(XgStudentInfo::getStudentNo, bo.getStudentNo())
|
||||
.or()
|
||||
.like(XgStudentInfo::getName, bo.getName())
|
||||
);
|
||||
} else {
|
||||
lqw.like(StringUtils.isNotBlank(bo.getStudentNo()), XgStudentInfo::getStudentNo, bo.getStudentNo());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getName()), XgStudentInfo::getName, bo.getName());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 📝 具体修改内容
|
||||
|
||||
### 1. 业务对象字段扩展
|
||||
- **文件**: `backend/ruoyi-modules/ruoyi-xg-xx/src/main/java/org/dromara/xg/domain/bo/XgStudentInfoBo.java`
|
||||
- **新增**: `keyword` 字段用于通用搜索
|
||||
|
||||
### 2. 查询逻辑优化
|
||||
- **文件**: `backend/ruoyi-modules/ruoyi-xg-xx/src/main/java/org/dromara/xg/service/impl/XgStudentInfoServiceImpl.java`
|
||||
- **修改**: `buildQueryWrapper()` 方法,支持通用搜索和兼容原有逻辑
|
||||
|
||||
## ✅ 验证结果
|
||||
1. **编译成功**: Maven编译通过,无语法错误
|
||||
2. **查询优化**: 支持OR条件查询,提升搜索命中率
|
||||
3. **向下兼容**: 原有的分别搜索逻辑仍然有效
|
||||
|
||||
## 🔄 优化效果对比
|
||||
|
||||
**优化前**:
|
||||
- 用户输入"王"
|
||||
- 查询条件:`student_no = '王' AND name LIKE '%王%'`
|
||||
- 结果:0条记录(因为没有学号为"王"的学生)
|
||||
|
||||
**优化后**:
|
||||
- 用户输入"王"
|
||||
- 查询条件:`student_no LIKE '%王%' OR name LIKE '%王%'`
|
||||
- 结果:返回所有学号或姓名包含"王"的学生
|
||||
|
||||
## 📋 使用方式
|
||||
|
||||
前端现在可以通过以下方式调用:
|
||||
|
||||
```javascript
|
||||
// 方式1:使用新的keyword参数(推荐)
|
||||
GET /xg/studentInfo/list?keyword=王&pageSize=20
|
||||
|
||||
// 方式2:继续使用原有参数(兼容)
|
||||
GET /xg/studentInfo/list?name=王&pageSize=20
|
||||
```
|
||||
|
||||
## 📝 备注
|
||||
- 新增的通用搜索功能提升了用户体验
|
||||
- 保持了向下兼容,不影响现有功能
|
||||
- 建议前端逐步迁移到使用 `keyword` 参数进行搜索
|
||||
### 责任人
|
||||
- 开发: 吴振
|
||||
- 测试: 待定
|
||||
- 审核: 待定
|
||||
@@ -1,186 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==========================================
|
||||
# 后端服务启动脚本
|
||||
# 功能:按顺序启动后端服务的各个组件
|
||||
# 作者:Cursor AI 辅助
|
||||
# 时间:2025-01-12
|
||||
# 使用:chmod +x start-backend.sh && ./start-backend.sh
|
||||
# ==========================================
|
||||
|
||||
# 脚本配置
|
||||
SCRIPT_NAME="start-backend.sh"
|
||||
LOG_DIR="logs"
|
||||
LOG_FILE="$LOG_DIR/backend-startup-$(date +%Y%m%d-%H%M%S).log"
|
||||
BACKEND_DIR="backend"
|
||||
|
||||
# 颜色定义
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 创建日志目录
|
||||
mkdir -p "$LOG_DIR"
|
||||
|
||||
# 日志函数
|
||||
log_info() {
|
||||
local message="$1"
|
||||
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
echo -e "${GREEN}[INFO]${NC} $message"
|
||||
echo "[$timestamp] [INFO] $message" >> "$LOG_FILE"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
local message="$1"
|
||||
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
echo -e "${YELLOW}[WARN]${NC} $message"
|
||||
echo "[$timestamp] [WARN] $message" >> "$LOG_FILE"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
local message="$1"
|
||||
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
echo -e "${RED}[ERROR]${NC} $message"
|
||||
echo "[$timestamp] [ERROR] $message" >> "$LOG_FILE"
|
||||
}
|
||||
|
||||
log_step() {
|
||||
local step="$1"
|
||||
local message="$2"
|
||||
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
echo -e "${BLUE}[STEP $step]${NC} $message"
|
||||
echo "[$timestamp] [STEP $step] $message" >> "$LOG_FILE"
|
||||
}
|
||||
|
||||
# 执行命令并检查结果
|
||||
execute_command() {
|
||||
local step="$1"
|
||||
local command="$2"
|
||||
local description="$3"
|
||||
local work_dir="$4"
|
||||
|
||||
log_step "$step" "开始执行: $description"
|
||||
log_info "命令: $command"
|
||||
|
||||
# 切换到指定目录(如果提供)
|
||||
if [ -n "$work_dir" ]; then
|
||||
if [ ! -d "$work_dir" ]; then
|
||||
log_error "目录不存在: $work_dir"
|
||||
return 1
|
||||
fi
|
||||
log_info "切换到目录: $work_dir"
|
||||
cd "$work_dir" || {
|
||||
log_error "无法切换到目录: $work_dir"
|
||||
return 1
|
||||
}
|
||||
fi
|
||||
|
||||
# 执行命令并捕获输出
|
||||
local start_time=$(date +%s)
|
||||
log_info "开始执行命令..."
|
||||
|
||||
# 将命令输出同时写入日志文件和控制台
|
||||
if eval "$command" 2>&1 | tee -a "$LOG_FILE"; then
|
||||
local end_time=$(date +%s)
|
||||
local duration=$((end_time - start_time))
|
||||
log_info "✅ $description 执行成功 (耗时: ${duration}秒)"
|
||||
return 0
|
||||
else
|
||||
local end_time=$(date +%s)
|
||||
local duration=$((end_time - start_time))
|
||||
local exit_code=${PIPESTATUS[0]}
|
||||
log_error "❌ $description 执行失败 (错误码: $exit_code, 耗时: ${duration}秒)"
|
||||
log_error "命令: $command"
|
||||
log_error "工作目录: $(pwd)"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 清理函数
|
||||
cleanup() {
|
||||
log_info "脚本执行结束,返回原始目录"
|
||||
cd "$(dirname "$0")" || true
|
||||
}
|
||||
|
||||
# 设置错误处理
|
||||
trap cleanup EXIT
|
||||
|
||||
# 脚本开始
|
||||
log_info "========================================"
|
||||
log_info "后端服务启动脚本开始执行"
|
||||
log_info "脚本名称: $SCRIPT_NAME"
|
||||
log_info "日志文件: $LOG_FILE"
|
||||
log_info "开始时间: $(date '+%Y-%m-%d %H:%M:%S')"
|
||||
log_info "========================================"
|
||||
|
||||
# 保存原始目录
|
||||
ORIGINAL_DIR=$(pwd)
|
||||
log_info "原始目录: $ORIGINAL_DIR"
|
||||
|
||||
# 检查backend目录是否存在
|
||||
if [ ! -d "$BACKEND_DIR" ]; then
|
||||
log_error "backend目录不存在: $BACKEND_DIR"
|
||||
log_error "请确保在项目根目录下执行此脚本"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Step 1: 清理项目
|
||||
if ! execute_command "1" "mvn clean" "Maven清理项目" "$BACKEND_DIR"; then
|
||||
log_error "Maven清理失败,停止执行"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 返回原始目录
|
||||
cd "$ORIGINAL_DIR"
|
||||
|
||||
# Step 2: 编译安装项目
|
||||
if ! execute_command "2" "mvn install" "Maven编译安装项目" "$BACKEND_DIR"; then
|
||||
log_error "Maven编译安装失败,停止执行"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 返回原始目录
|
||||
cd "$ORIGINAL_DIR"
|
||||
|
||||
# Step 3: 启动监控服务
|
||||
if ! execute_command "3" "./start-monitor.sh" "启动监控服务" "$BACKEND_DIR"; then
|
||||
log_error "启动监控服务失败,停止执行"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 返回原始目录
|
||||
cd "$ORIGINAL_DIR"
|
||||
|
||||
# Step 4: 启动任务调度服务
|
||||
if ! execute_command "4" "./start-job.sh" "启动任务调度服务" "$BACKEND_DIR"; then
|
||||
log_error "启动任务调度服务失败,停止执行"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 返回原始目录
|
||||
cd "$ORIGINAL_DIR"
|
||||
|
||||
# Step 5: 热启动主服务
|
||||
if ! execute_command "5" "./hot-start.sh" "热启动主服务" "$BACKEND_DIR"; then
|
||||
log_error "热启动主服务失败,停止执行"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 返回原始目录
|
||||
cd "$ORIGINAL_DIR"
|
||||
|
||||
# 脚本成功完成
|
||||
log_info "========================================"
|
||||
log_info "🎉 后端服务启动脚本执行完成!"
|
||||
log_info "所有服务已成功启动:"
|
||||
log_info " ✅ Maven项目清理和编译"
|
||||
log_info " ✅ 监控服务 (start-monitor.sh)"
|
||||
log_info " ✅ 任务调度服务 (start-job.sh)"
|
||||
log_info " ✅ 主服务热启动 (hot-start.sh)"
|
||||
log_info "结束时间: $(date '+%Y-%m-%d %H:%M:%S')"
|
||||
log_info "日志文件: $LOG_FILE"
|
||||
log_info "========================================"
|
||||
|
||||
exit 0
|
||||
@@ -149,6 +149,16 @@ export interface EnrollmentInfoVO {
|
||||
*/
|
||||
qrCodeSource: string;
|
||||
|
||||
/**
|
||||
* 学院名称
|
||||
*/
|
||||
collegeName: string;
|
||||
|
||||
/**
|
||||
* 学院代码
|
||||
*/
|
||||
collegeId: string;
|
||||
|
||||
/**
|
||||
* 创建者
|
||||
*/
|
||||
@@ -320,6 +330,16 @@ export interface EnrollmentInfoForm extends BaseEntity {
|
||||
* 二维码来源标识
|
||||
*/
|
||||
qrCodeSource?: string;
|
||||
|
||||
/**
|
||||
* 学院名称
|
||||
*/
|
||||
collegeName?: string;
|
||||
|
||||
/**
|
||||
* 学院代码
|
||||
*/
|
||||
collegeId?: string;
|
||||
}
|
||||
|
||||
export interface EnrollmentInfoQuery extends PageQuery {
|
||||
@@ -368,6 +388,16 @@ export interface EnrollmentInfoQuery extends PageQuery {
|
||||
*/
|
||||
examRegion?: string;
|
||||
|
||||
/**
|
||||
* 学院代码
|
||||
*/
|
||||
collegeId?: string;
|
||||
|
||||
/**
|
||||
* 学院名称
|
||||
*/
|
||||
collegeName?: string;
|
||||
|
||||
/**
|
||||
* 报名时间范围
|
||||
*/
|
||||
|
||||
@@ -151,6 +151,76 @@ export interface EnrollmentProcessVO {
|
||||
* 更新时间
|
||||
*/
|
||||
updateTime: string
|
||||
|
||||
/**
|
||||
* 性别(M-男 F-女)
|
||||
*/
|
||||
gender?: string
|
||||
|
||||
/**
|
||||
* 地区/省份
|
||||
*/
|
||||
province?: string
|
||||
|
||||
/**
|
||||
* 学院名称(驼峰命名)
|
||||
*/
|
||||
collegeName?: string
|
||||
|
||||
/**
|
||||
* 学院代码(驼峰命名)
|
||||
*/
|
||||
collegeId?: string
|
||||
|
||||
/**
|
||||
* 学院名称(下划线命名)
|
||||
*/
|
||||
college_name?: string
|
||||
|
||||
/**
|
||||
* 学院代码(下划线命名)
|
||||
*/
|
||||
college_id?: string
|
||||
|
||||
/**
|
||||
* 学院名称(拼写变体 - collage)
|
||||
*/
|
||||
collage_name?: string
|
||||
|
||||
/**
|
||||
* 学院代码(拼写变体 - collage)
|
||||
*/
|
||||
collage_id?: string
|
||||
|
||||
/**
|
||||
* 专业名称(驼峰命名)
|
||||
*/
|
||||
majorName?: string
|
||||
|
||||
/**
|
||||
* 专业代码(驼峰命名)
|
||||
*/
|
||||
majorCode?: string
|
||||
|
||||
/**
|
||||
* 专业名称(下划线命名)
|
||||
*/
|
||||
major_name?: string
|
||||
|
||||
/**
|
||||
* 专业代码(下划线命名)
|
||||
*/
|
||||
major_code?: string
|
||||
|
||||
/**
|
||||
* 报考专业(来自报名信息表)
|
||||
*/
|
||||
appliedMajor?: string
|
||||
|
||||
/**
|
||||
* 关联的报名信息对象
|
||||
*/
|
||||
enrollmentInfo?: any
|
||||
}
|
||||
|
||||
export interface EnrollmentProcessForm extends BaseEntity {
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
import request from '@/utils/request';
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
/**
|
||||
* 获取统计概览数据
|
||||
*/
|
||||
export function getStatisticsOverview() {
|
||||
return request({
|
||||
url: '/dev-api/zsjy/statistics/overview',
|
||||
method: 'get'
|
||||
url: '/zsjy/statistics/overview',
|
||||
method: 'get',
|
||||
headers: {
|
||||
'isToken': true
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error('获取统计概览数据失败:', error);
|
||||
ElMessage.error('获取统计数据失败,请联系管理员');
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -15,8 +23,15 @@ export function getStatisticsOverview() {
|
||||
*/
|
||||
export function getMajorDistribution() {
|
||||
return request({
|
||||
url: '/dev-api/zsjy/statistics/major',
|
||||
method: 'get'
|
||||
url: '/zsjy/statistics/major',
|
||||
method: 'get',
|
||||
headers: {
|
||||
'isToken': true
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error('获取专业分布数据失败:', error);
|
||||
ElMessage.error('获取专业分布数据失败,请联系管理员');
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -25,8 +40,15 @@ export function getMajorDistribution() {
|
||||
*/
|
||||
export function getRegionDistribution() {
|
||||
return request({
|
||||
url: '/dev-api/zsjy/statistics/region',
|
||||
method: 'get'
|
||||
url: '/zsjy/statistics/region',
|
||||
method: 'get',
|
||||
headers: {
|
||||
'isToken': true
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error('获取地区分布数据失败:', error);
|
||||
ElMessage.error('获取地区分布数据失败,请联系管理员');
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -35,8 +57,15 @@ export function getRegionDistribution() {
|
||||
*/
|
||||
export function getGenderRatio() {
|
||||
return request({
|
||||
url: '/dev-api/zsjy/statistics/gender',
|
||||
method: 'get'
|
||||
url: '/zsjy/statistics/gender',
|
||||
method: 'get',
|
||||
headers: {
|
||||
'isToken': true
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error('获取性别比例数据失败:', error);
|
||||
ElMessage.error('获取性别比例数据失败,请联系管理员');
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -46,9 +75,16 @@ export function getGenderRatio() {
|
||||
*/
|
||||
export function getEnrollmentTrend(days?: number) {
|
||||
return request({
|
||||
url: '/dev-api/zsjy/statistics/trend',
|
||||
url: '/zsjy/statistics/trend',
|
||||
method: 'get',
|
||||
params: { days }
|
||||
params: { days },
|
||||
headers: {
|
||||
'isToken': true
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error('获取报名趋势数据失败:', error);
|
||||
ElMessage.error('获取报名趋势数据失败,请联系管理员');
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -57,8 +93,15 @@ export function getEnrollmentTrend(days?: number) {
|
||||
*/
|
||||
export function getStatusDistribution() {
|
||||
return request({
|
||||
url: '/dev-api/zsjy/statistics/status',
|
||||
method: 'get'
|
||||
url: '/zsjy/statistics/status',
|
||||
method: 'get',
|
||||
headers: {
|
||||
'isToken': true
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error('获取状态分布数据失败:', error);
|
||||
ElMessage.error('获取状态分布数据失败,请联系管理员');
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="性别" prop="gender" required>
|
||||
<el-select v-model="applyForm.gender" placeholder="请选择性别" style="width: 100%">
|
||||
<el-select v-model="applyForm.gender" placeholder="请选择性别" style="width: 100%; font-size: 14px; font-weight: bold;" :key="applyForm.gender">
|
||||
<el-option v-for="dict in zsjy_gender" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
@@ -269,7 +269,7 @@
|
||||
</template>
|
||||
|
||||
<script setup name="EnrollmentApply" lang="ts">
|
||||
import { reactive, ref, toRefs, getCurrentInstance, ComponentInternalInstance, onMounted } from 'vue';
|
||||
import { reactive, ref, toRefs, getCurrentInstance, ComponentInternalInstance, onMounted, nextTick } from 'vue';
|
||||
import { ElForm } from 'element-plus';
|
||||
import { submitEnrollmentApplication, queryMyEnrollmentStatus } from '@/api/zsjy/enrollment';
|
||||
import { EnrollmentInfoForm } from '@/api/zsjy/enrollment/types';
|
||||
@@ -358,15 +358,31 @@ const rules = {
|
||||
const onIdCardChange = () => {
|
||||
const idCard = applyForm.idCard;
|
||||
if (idCard && idCard.length === 18) {
|
||||
try {
|
||||
// 提取出生日期
|
||||
const year = idCard.substring(6, 10);
|
||||
const month = idCard.substring(10, 12);
|
||||
const day = idCard.substring(12, 14);
|
||||
applyForm.birthDate = `${year}-${month}-${day}`;
|
||||
|
||||
// 提取性别(奇数为男M,偶数为女F)
|
||||
// 提取性别(奇数为男,偶数为女)
|
||||
const genderCode = parseInt(idCard.substring(16, 17));
|
||||
|
||||
// 根据奇偶性设置性别为'M'或'F'
|
||||
applyForm.gender = genderCode % 2 === 1 ? 'M' : 'F';
|
||||
|
||||
// 性别设置完成后,强制重新渲染一下
|
||||
nextTick(() => {
|
||||
// 通过临时设置为空再重新设置回来,强制UI更新
|
||||
const currentGender = applyForm.gender;
|
||||
applyForm.gender = '';
|
||||
setTimeout(() => {
|
||||
applyForm.gender = currentGender;
|
||||
}, 10);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('解析身份证信息时出错:', error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -426,6 +442,14 @@ const submitApplication = () => {
|
||||
if (valid) {
|
||||
try {
|
||||
submitLoading.value = true;
|
||||
|
||||
// 处理是否住宿字段,将"Y"/"N"转换为1/0
|
||||
if (applyForm.isBoarding === 'Y') {
|
||||
applyForm.isBoarding = 1;
|
||||
} else if (applyForm.isBoarding === 'N') {
|
||||
applyForm.isBoarding = 0;
|
||||
}
|
||||
|
||||
await submitEnrollmentApplication(applyForm);
|
||||
proxy?.$modal.msgSuccess('报名申请提交成功!');
|
||||
resetForm();
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -28,7 +28,8 @@
|
||||
<span class="detail-value">{{ processData.currentHandlerName || '-' }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="完成状态">
|
||||
<dict-tag :options="sys_yes_no" :value="processData.isCompleted" />
|
||||
<el-tag type="success" v-if="processData.isCompleted === 1 || processData.isCompleted === '1'">已完成</el-tag>
|
||||
<el-tag type="info" v-else>未完成</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">
|
||||
<span class="detail-value">{{ parseTime(processData.createTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
||||
@@ -111,7 +112,9 @@
|
||||
</template>
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="缴费状态">
|
||||
<dict-tag :options="zsjy_payment_status" :value="processData.paymentStatus" />
|
||||
<el-tag :type="processData.paymentStatus == 1 ? 'success' : 'info'">
|
||||
{{ processData.paymentStatus == 1 ? '已缴费' : '未缴费' }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="缴费确认时间">
|
||||
<span class="detail-value">{{ parseTime(processData.paymentConfirmTime, '{y}-{m}-{d} {h}:{i}:{s}') || '-' }}</span>
|
||||
@@ -191,6 +194,15 @@ const dialogVisible = computed({
|
||||
const handleClose = () => {
|
||||
dialogVisible.value = false
|
||||
}
|
||||
|
||||
const isCompletedTag = computed(() => {
|
||||
return String(processData.value.isCompleted) === '1' ? 'success' : 'info'
|
||||
})
|
||||
|
||||
const isCompletedText = computed(() => {
|
||||
return String(processData.value.isCompleted) === '1' ? '已完成' : '未完成'
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="page-container">
|
||||
<!-- 搜索区域 -->
|
||||
<el-card class="search-card">
|
||||
<el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="80px">
|
||||
<el-form ref="queryFormRef" :model="queryParams" label-width="80px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="学生姓名" prop="studentName">
|
||||
@@ -27,50 +27,18 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="当前状态" prop="currentStatus">
|
||||
<el-select
|
||||
v-model="queryParams.currentStatus"
|
||||
placeholder="请选择当前状态"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in zsjy_enrollment_status"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="完成状态" prop="isCompleted">
|
||||
<el-select
|
||||
v-model="queryParams.isCompleted"
|
||||
placeholder="请选择完成状态"
|
||||
clearable
|
||||
style="width: 240px"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in sys_yes_no"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
<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>
|
||||
<div class="search-btn-container">
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
</div>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<!-- 统计卡片 -->
|
||||
<el-row :gutter="16" class="stat-cards">
|
||||
<el-col :span="6">
|
||||
<el-col :span="8">
|
||||
<el-card class="stat-card stat-card-primary" shadow="hover">
|
||||
<div class="stat-content">
|
||||
<div class="stat-icon">
|
||||
@@ -78,46 +46,33 @@
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<div class="stat-value">{{ statistics.total || 0 }}</div>
|
||||
<div class="stat-label">总流程数</div>
|
||||
<div class="stat-label">待处理录取数</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card class="stat-card stat-card-warning" shadow="hover">
|
||||
<div class="stat-content">
|
||||
<div class="stat-icon">
|
||||
<el-icon><Clock /></el-icon>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<div class="stat-value">{{ statistics.pending || 0 }}</div>
|
||||
<div class="stat-label">待处理</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-col :span="8">
|
||||
<el-card class="stat-card stat-card-info" shadow="hover">
|
||||
<div class="stat-content">
|
||||
<div class="stat-icon">
|
||||
<el-icon><Clock /></el-icon>
|
||||
<el-icon><CreditCard /></el-icon>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<div class="stat-value">{{ statistics.processing || 0 }}</div>
|
||||
<div class="stat-label">处理中</div>
|
||||
<div class="stat-value">{{ statistics.paymentConfirmed || 0 }}</div>
|
||||
<div class="stat-label">已缴费人数</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-col :span="8">
|
||||
<el-card class="stat-card stat-card-success" shadow="hover">
|
||||
<div class="stat-content">
|
||||
<div class="stat-icon">
|
||||
<el-icon><Select /></el-icon>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<div class="stat-value">{{ statistics.completed || 0 }}</div>
|
||||
<div class="stat-label">已完成</div>
|
||||
<div class="stat-value">{{ statistics.admissionProcessed || 0 }}</div>
|
||||
<div class="stat-label">已处理人数</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
@@ -128,7 +83,7 @@
|
||||
<el-card class="button-card">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span class="toolbar-title">录取流程管理</span>
|
||||
<span class="toolbar-title">录取处理管理</span>
|
||||
<div class="toolbar-actions">
|
||||
<el-button
|
||||
v-hasPermi="['zsjy:enrollmentProcess:export']"
|
||||
@@ -162,9 +117,11 @@
|
||||
row-key="id"
|
||||
class="scroll-table"
|
||||
@selection-change="handleSelectionChange"
|
||||
fit
|
||||
border
|
||||
>
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="学生信息" align="center" width="200">
|
||||
<el-table-column label="学生信息" align="center" min-width="180">
|
||||
<template #default="scope">
|
||||
<div style="text-align: left;">
|
||||
<div style="font-weight: 600; color: #303133;">{{ scope.row.studentName }}</div>
|
||||
@@ -172,36 +129,36 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="当前状态" align="center" width="120">
|
||||
<el-table-column label="当前状态" align="center" min-width="100">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="zsjy_enrollment_status" :value="scope.row.currentStatus" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="当前处理人" align="center" width="120" prop="currentHandlerName" />
|
||||
<el-table-column label="缴费状态" align="center" width="100">
|
||||
<el-table-column label="当前处理人" align="center" min-width="100" prop="currentHandlerName" />
|
||||
<el-table-column label="缴费状态" align="center" min-width="90">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.payment_status === '1' || scope.row.paymentStatus === '1'" type="success">已缴费</el-tag>
|
||||
<el-tag v-else-if="scope.row.payment_status === '0' || scope.row.paymentStatus === '0'" type="warning">未缴费</el-tag>
|
||||
<el-tag v-else type="info">未知</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="录取通知书" align="center" width="150" show-overflow-tooltip>
|
||||
<el-table-column label="录取通知书" align="center" min-width="120" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.noticeNumber || scope.row.notice_number || '未生成' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="完成状态" align="center" width="100">
|
||||
<el-table-column label="完成状态" align="center" min-width="90">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.is_completed === 1 || scope.row.is_completed === '1' || scope.row.is_completed === 'Y'" type="success">是</el-tag>
|
||||
<el-tag v-else type="info">否</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" width="180">
|
||||
<el-table-column label="创建时间" align="center" min-width="160">
|
||||
<template #default="scope">
|
||||
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="280" fixed="right">
|
||||
<el-table-column label="操作" align="center" min-width="250" fixed="right">
|
||||
<template #default="scope">
|
||||
<div class="action-buttons">
|
||||
<el-tooltip content="查看详情" placement="top">
|
||||
@@ -372,9 +329,8 @@ const handleSelectionChange = (selection: EnrollmentProcessVO[]) => {
|
||||
// 统计数据
|
||||
const statistics = ref({
|
||||
total: 0,
|
||||
pending: 0,
|
||||
processing: 0,
|
||||
completed: 0
|
||||
paymentConfirmed: 0,
|
||||
admissionProcessed: 0
|
||||
})
|
||||
|
||||
// 对话框状态
|
||||
@@ -389,10 +345,21 @@ const currentProcess = ref<Partial<EnrollmentProcessVO>>({})
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
// 设置查询参数,只查询录取处理中且未完成的数据
|
||||
queryParams.value.currentStatus = '2' // 只查询录取处理中的数据
|
||||
queryParams.value.isCompleted = 0 // 只查询未完成的数据
|
||||
|
||||
const res = await listEnrollmentProcess(queryParams.value)
|
||||
|
||||
// 对返回的数据进行处理,确保字段名一致
|
||||
const processedList = (res.rows || []).map(item => {
|
||||
const processedList = (res.rows || [])
|
||||
// 过滤掉已完成录取的数据
|
||||
.filter(item => {
|
||||
const isCompleted = item.isCompleted || item.is_completed || 0;
|
||||
// 只保留未完成录取的数据
|
||||
return isCompleted === 0 || isCompleted === '0';
|
||||
})
|
||||
.map(item => {
|
||||
// 添加日志用于调试(首行数据)
|
||||
if (item === res.rows[0]) {
|
||||
console.log('首行数据字段:', Object.keys(item))
|
||||
@@ -408,46 +375,63 @@ const getList = async () => {
|
||||
isCompleted: item.isCompleted || item.is_completed,
|
||||
is_completed: item.is_completed || item.isCompleted,
|
||||
noticeNumber: item.noticeNumber || item.notice_number,
|
||||
notice_number: item.notice_number || item.noticeNumber
|
||||
notice_number: item.notice_number || item.noticeNumber,
|
||||
admissionStatus: item.admissionStatus || item.admission_status,
|
||||
admission_status: item.admission_status || item.admissionStatus
|
||||
}
|
||||
})
|
||||
|
||||
processList.value = processedList
|
||||
total.value = res.total || 0
|
||||
total.value = processedList.length // 更新总数为过滤后的数量
|
||||
} catch (error) {
|
||||
console.error('获取流程列表失败:', error)
|
||||
|
||||
// 如果是404错误(接口不存在),使用模拟数据
|
||||
if (error?.response?.status === 404 || error?.message?.includes('404')) {
|
||||
console.warn('后端接口不存在,使用模拟数据')
|
||||
// 创建模拟的流程数据
|
||||
// 创建模拟的流程数据,只包含未完成录取的数据
|
||||
processList.value = [
|
||||
{
|
||||
id: 1946510098494091300,
|
||||
enrollmentId: 1001,
|
||||
studentName: '张三',
|
||||
idCard: '330101200501010001',
|
||||
currentStatus: '0',
|
||||
infoAuditStatus: '0',
|
||||
admissionStatus: '0',
|
||||
id: 1946510098494091302,
|
||||
enrollmentId: 1003,
|
||||
studentName: '王五',
|
||||
idCard: '110101200601010003',
|
||||
currentStatus: '2', // 录取处理中
|
||||
infoAuditStatus: '1',
|
||||
admissionStatus: '0', // 未处理录取
|
||||
paymentStatus: '0', // 未缴费
|
||||
finalAuditStatus: '0',
|
||||
isCompleted: 0,
|
||||
createTime: '2025-07-20 10:28:09'
|
||||
isCompleted: 0, // 未完成
|
||||
createTime: '2025-07-20 10:28:27'
|
||||
},
|
||||
{
|
||||
id: 1946510098494091301,
|
||||
enrollmentId: 1946510098494091266,
|
||||
studentName: '赵四',
|
||||
idCard: '340403201509091234',
|
||||
currentStatus: '0',
|
||||
infoAuditStatus: '0',
|
||||
admissionStatus: '0',
|
||||
id: 1946510098494091303,
|
||||
enrollmentId: 1004,
|
||||
studentName: '李六',
|
||||
idCard: '440106200701010004',
|
||||
currentStatus: '2', // 录取处理中
|
||||
infoAuditStatus: '1',
|
||||
admissionStatus: '0', // 未处理录取
|
||||
paymentStatus: '1', // 已缴费
|
||||
finalAuditStatus: '0',
|
||||
isCompleted: 0,
|
||||
createTime: '2025-07-20 10:28:18'
|
||||
isCompleted: 0, // 未完成
|
||||
createTime: '2025-07-20 10:28:36'
|
||||
},
|
||||
{
|
||||
id: 1946510098494091304,
|
||||
enrollmentId: 1005,
|
||||
studentName: '赵七',
|
||||
idCard: '330106200801010005',
|
||||
currentStatus: '2', // 录取处理中
|
||||
infoAuditStatus: '1',
|
||||
admissionStatus: '1', // 已处理录取
|
||||
paymentStatus: '1', // 已缴费
|
||||
finalAuditStatus: '0',
|
||||
isCompleted: 0, // 未完成
|
||||
createTime: '2025-07-20 10:28:45'
|
||||
}
|
||||
]
|
||||
total.value = 2
|
||||
total.value = 3
|
||||
proxy?.$modal.msgWarning('后端接口暂未实现,显示模拟数据')
|
||||
} else {
|
||||
// 其他错误,设置空数据并抛出错误
|
||||
@@ -467,28 +451,38 @@ const getStatistics = async () => {
|
||||
// const res = await getProcessStatistics()
|
||||
// const stats = res.data || []
|
||||
|
||||
// 基于当前流程列表计算统计数据
|
||||
// 基于当前流程列表计算统计数据(只包含录取处理中的数据)
|
||||
const stats = processList.value || []
|
||||
const statusCounts = stats.reduce((acc: any, item: any) => {
|
||||
const status = item.currentStatus
|
||||
acc[status] = (acc[status] || 0) + 1
|
||||
return acc
|
||||
}, {})
|
||||
|
||||
// 计算已缴费人数和已处理人数
|
||||
let paymentConfirmed = 0
|
||||
let admissionProcessed = 0
|
||||
|
||||
stats.forEach(item => {
|
||||
// 计算已缴费人数
|
||||
if (item.paymentStatus === '1' || item.payment_status === '1') {
|
||||
paymentConfirmed++
|
||||
}
|
||||
|
||||
// 计算已处理人数(录取状态为已处理)
|
||||
if (item.admissionStatus === '1' || item.admission_status === '1') {
|
||||
admissionProcessed++
|
||||
}
|
||||
})
|
||||
|
||||
// 更新统计数据
|
||||
statistics.value = {
|
||||
total: stats.length,
|
||||
pending: (statusCounts['0'] || 0) + (statusCounts['1'] || 0), // 待审核 + 信息审核中
|
||||
processing: (statusCounts['2'] || 0) + (statusCounts['3'] || 0), // 录取处理中 + 待最终确认
|
||||
completed: statusCounts['4'] || 0 // 录取完成
|
||||
paymentConfirmed,
|
||||
admissionProcessed
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取统计数据失败:', error)
|
||||
// 设置默认值
|
||||
statistics.value = {
|
||||
total: 0,
|
||||
pending: 0,
|
||||
processing: 0,
|
||||
completed: 0
|
||||
paymentConfirmed: 0,
|
||||
admissionProcessed: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -713,9 +707,28 @@ const getCompleteButtonTooltip = (row: any): string => {
|
||||
return '完成录取';
|
||||
}
|
||||
|
||||
/** 判断是否已缴费 */
|
||||
const isPaymentConfirmed = (row: any): boolean => {
|
||||
return row.payment_status === '1' || row.paymentStatus === '1';
|
||||
}
|
||||
|
||||
/** 判断是否已处理录取 */
|
||||
const isAdmissionProcessed = (row: any): boolean => {
|
||||
return row.admissionStatus === '1' || row.admission_status === '1';
|
||||
}
|
||||
|
||||
/** 判断是否已完成 */
|
||||
const isCompleted = (row: any): boolean => {
|
||||
return row.is_completed === 1 || row.is_completed === '1' || row.is_completed === 'Y';
|
||||
}
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(async () => {
|
||||
try {
|
||||
// 设置默认查询参数
|
||||
queryParams.value.currentStatus = '2' // 录取处理中
|
||||
queryParams.value.isCompleted = 0 // 未完成
|
||||
|
||||
// 处理路由参数
|
||||
const route = useRoute();
|
||||
if (route && route.query) {
|
||||
@@ -747,6 +760,8 @@ onMounted(async () => {
|
||||
/* 页面容器样式 */
|
||||
.page-container {
|
||||
padding: 8px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* 搜索卡片样式 */
|
||||
@@ -886,21 +901,28 @@ onMounted(async () => {
|
||||
border-radius: 12px;
|
||||
border: 1px solid #e4e7ed;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.table-card :deep(.el-card__body) {
|
||||
padding: 0px 20px 20px 20px !important;
|
||||
padding: 20px !important;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* 表格容器 - 支持横向滚动 */
|
||||
.table-container {
|
||||
width: 100%;
|
||||
margin-bottom: 0;
|
||||
overflow-x: auto;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* 横向滚动表格样式 */
|
||||
.scroll-table {
|
||||
width: 100%;
|
||||
table-layout: auto;
|
||||
}
|
||||
|
||||
/* 表格横向滚动条样式优化 */
|
||||
@@ -909,8 +931,12 @@ onMounted(async () => {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.scroll-table :deep(.el-table__header-wrapper) {
|
||||
overflow-x: hidden;
|
||||
/* 强制表格自适应容器宽度 */
|
||||
.scroll-table :deep(.el-table__header),
|
||||
.scroll-table :deep(.el-table__body) {
|
||||
width: 100% !important;
|
||||
min-width: 100% !important;
|
||||
table-layout: auto !important;
|
||||
}
|
||||
|
||||
/* 强制显示表格横向滚动条 - 适用于所有浏览器 */
|
||||
@@ -975,9 +1001,31 @@ onMounted(async () => {
|
||||
.pagination-container {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 16px 20px 0 0 !important;
|
||||
padding: 16px 0 0 0 !important;
|
||||
background-color: #fff;
|
||||
margin-top: 0px !important;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 确保整个表格区域适应容器 */
|
||||
.el-card.table-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.el-card.table-card :deep(.el-card__body) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
padding: 20px !important;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
/* 操作按钮样式 */
|
||||
|
||||
@@ -186,9 +186,10 @@ const loadStatisticsData = async () => {
|
||||
}
|
||||
|
||||
statisticsData.value = res.data;
|
||||
console.log('已设置统计数据:', statisticsData.value);
|
||||
|
||||
// 加载所有图表
|
||||
loadAllCharts();
|
||||
await loadAllCharts();
|
||||
} catch (error) {
|
||||
console.error('获取统计概览数据失败', error);
|
||||
ElMessage.error('获取统计数据失败,请联系管理员');
|
||||
@@ -198,12 +199,29 @@ const loadStatisticsData = async () => {
|
||||
};
|
||||
|
||||
// 加载所有图表
|
||||
const loadAllCharts = () => {
|
||||
loadTrendChart();
|
||||
loadGenderChart();
|
||||
loadMajorChart();
|
||||
loadRegionChart();
|
||||
loadStatusChart();
|
||||
const loadAllCharts = async () => {
|
||||
try {
|
||||
console.log('开始加载所有图表...');
|
||||
|
||||
// 确保等待DOM元素完全加载
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
|
||||
// 依次加载各个图表(改为串行以避免资源竞争)
|
||||
await loadTrendChart();
|
||||
await loadGenderChart();
|
||||
await loadMajorChart();
|
||||
await loadRegionChart();
|
||||
await loadStatusChart();
|
||||
|
||||
console.log('所有图表加载完成');
|
||||
|
||||
// 最后再次调整所有图表大小
|
||||
nextTick(() => {
|
||||
handleResize();
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('加载图表失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 加载趋势图
|
||||
@@ -220,18 +238,25 @@ const loadTrendChart = async () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const trendData = res.data;
|
||||
// 确保DOM元素已经加载
|
||||
await nextTick();
|
||||
|
||||
// 初始化趋势图
|
||||
if (!trendChartInstance) {
|
||||
// 检查DOM元素
|
||||
const chartDom = document.getElementById('trendChart');
|
||||
if (!chartDom) {
|
||||
console.error('找不到趋势图DOM元素!');
|
||||
console.error('找不到趋势图DOM元素! 请检查HTML结构');
|
||||
return;
|
||||
}
|
||||
trendChartInstance = echarts.init(chartDom);
|
||||
|
||||
// 初始化图表,如果已存在则销毁重建
|
||||
if (trendChartInstance) {
|
||||
trendChartInstance.dispose();
|
||||
}
|
||||
|
||||
trendChartInstance = echarts.init(chartDom);
|
||||
console.log('趋势图实例已创建');
|
||||
|
||||
const trendData = res.data.data || res.data;
|
||||
const dates: string[] = [];
|
||||
const counts: number[] = [];
|
||||
|
||||
@@ -240,6 +265,7 @@ const loadTrendChart = async () => {
|
||||
dates.push(item.date as string);
|
||||
counts.push(item.count as number);
|
||||
});
|
||||
console.log('趋势数据已处理:', { dates, counts });
|
||||
} else {
|
||||
console.error('趋势数据不是数组格式:', trendData);
|
||||
return;
|
||||
@@ -294,10 +320,15 @@ const loadTrendChart = async () => {
|
||||
]
|
||||
};
|
||||
|
||||
// 设置选项并绘制图表
|
||||
trendChartInstance.setOption(option);
|
||||
console.log('趋势图已渲染完成');
|
||||
|
||||
// 强制更新布局
|
||||
trendChartInstance.resize();
|
||||
} catch (error) {
|
||||
console.error('获取报名趋势数据失败', error);
|
||||
ElMessage.warning('获取报名趋势数据失败');
|
||||
ElMessage.warning('获取报名趋势数据失败,请检查网络连接');
|
||||
} finally {
|
||||
trendLoading.value = false;
|
||||
}
|
||||
@@ -317,18 +348,27 @@ const loadGenderChart = async () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const genderData = res.data;
|
||||
// 确保DOM元素已经加载
|
||||
await nextTick();
|
||||
|
||||
// 初始化性别比例图
|
||||
if (!genderChartInstance) {
|
||||
// 检查DOM元素
|
||||
const chartDom = document.getElementById('genderChart');
|
||||
if (!chartDom) {
|
||||
console.error('找不到性别比例图DOM元素!');
|
||||
console.error('找不到性别比例图DOM元素! 请检查HTML结构');
|
||||
return;
|
||||
}
|
||||
genderChartInstance = echarts.init(chartDom);
|
||||
|
||||
// 初始化图表,如果已存在则销毁重建
|
||||
if (genderChartInstance) {
|
||||
genderChartInstance.dispose();
|
||||
}
|
||||
|
||||
genderChartInstance = echarts.init(chartDom);
|
||||
console.log('性别比例图实例已创建');
|
||||
|
||||
const genderData = res.data.data || res.data;
|
||||
console.log('原始性别数据:', genderData);
|
||||
|
||||
const pieData = [];
|
||||
let total = 0;
|
||||
|
||||
@@ -349,6 +389,8 @@ const loadGenderChart = async () => {
|
||||
});
|
||||
});
|
||||
|
||||
console.log('处理后的性别数据:', pieData);
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
@@ -389,10 +431,15 @@ const loadGenderChart = async () => {
|
||||
]
|
||||
};
|
||||
|
||||
// 设置选项并绘制图表
|
||||
genderChartInstance.setOption(option);
|
||||
console.log('性别比例图已渲染完成');
|
||||
|
||||
// 强制更新布局
|
||||
genderChartInstance.resize();
|
||||
} catch (error) {
|
||||
console.error('获取性别比例数据失败', error);
|
||||
ElMessage.warning('获取性别比例数据失败');
|
||||
ElMessage.warning('获取性别比例数据失败,请检查网络连接');
|
||||
} finally {
|
||||
genderLoading.value = false;
|
||||
}
|
||||
@@ -412,88 +459,88 @@ const loadMajorChart = async () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const majorData = res.data;
|
||||
// 确保DOM元素已经加载
|
||||
await nextTick();
|
||||
|
||||
// 初始化专业分布图
|
||||
if (!majorChartInstance) {
|
||||
// 检查DOM元素
|
||||
const chartDom = document.getElementById('majorChart');
|
||||
if (!chartDom) {
|
||||
console.error('找不到专业分布图DOM元素!');
|
||||
console.error('找不到专业分布图DOM元素! 请检查HTML结构');
|
||||
return;
|
||||
}
|
||||
|
||||
// 初始化图表,如果已存在则销毁重建
|
||||
if (majorChartInstance) {
|
||||
majorChartInstance.dispose();
|
||||
}
|
||||
|
||||
majorChartInstance = echarts.init(chartDom);
|
||||
}
|
||||
console.log('专业分布图实例已创建');
|
||||
|
||||
const majorNames: string[] = [];
|
||||
const majorCounts: number[] = [];
|
||||
// 获取数据
|
||||
const majorData = res.data.data || res.data;
|
||||
console.log('专业分布原始数据:', majorData);
|
||||
|
||||
if (Array.isArray(majorData)) {
|
||||
majorData.forEach(item => {
|
||||
majorNames.push(item.name as string);
|
||||
majorCounts.push(item.value as number);
|
||||
});
|
||||
} else {
|
||||
console.error('专业分布数据不是数组格式:', majorData);
|
||||
if (!Array.isArray(majorData)) {
|
||||
console.error('专业分布数据格式错误,应为数组:', majorData);
|
||||
return;
|
||||
}
|
||||
|
||||
// 限制显示的专业数量,避免图表过于拥挤
|
||||
const displayLimit = 10;
|
||||
const sortedData = [...majorData].sort((a, b) => b.value - a.value);
|
||||
const displayData = sortedData.slice(0, displayLimit);
|
||||
|
||||
// 如果数据超过限制,添加"其他"类别
|
||||
if (sortedData.length > displayLimit) {
|
||||
const otherSum = sortedData.slice(displayLimit).reduce((sum, item) => sum + item.value, 0);
|
||||
displayData.push({
|
||||
name: '其他',
|
||||
value: otherSum
|
||||
});
|
||||
}
|
||||
|
||||
console.log('处理后的专业分布数据:', displayData);
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
}
|
||||
trigger: 'item',
|
||||
formatter: '{b}: {c} ({d}%)'
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: majorNames,
|
||||
axisLabel: {
|
||||
interval: 0,
|
||||
rotate: majorNames.length > 5 ? 45 : 0,
|
||||
textStyle: {
|
||||
fontSize: 12
|
||||
}
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: '报名人数',
|
||||
minInterval: 1
|
||||
legend: {
|
||||
type: 'scroll',
|
||||
orient: 'vertical',
|
||||
right: 10,
|
||||
top: 20,
|
||||
bottom: 20,
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '报名人数',
|
||||
type: 'bar',
|
||||
data: majorCounts,
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: '#5d87ff' },
|
||||
{ offset: 1, color: '#83bff6' }
|
||||
])
|
||||
},
|
||||
name: '专业分布',
|
||||
type: 'pie',
|
||||
radius: '55%',
|
||||
center: ['40%', '50%'],
|
||||
data: displayData,
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: '#2378f7' },
|
||||
{ offset: 0.7, color: '#2378f7' },
|
||||
{ offset: 1, color: '#83bff6' }
|
||||
])
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
// 设置选项并绘制图表
|
||||
majorChartInstance.setOption(option);
|
||||
console.log('专业分布图已渲染完成');
|
||||
|
||||
// 强制更新布局
|
||||
majorChartInstance.resize();
|
||||
} catch (error) {
|
||||
console.error('获取专业分布数据失败', error);
|
||||
ElMessage.warning('获取专业分布数据失败');
|
||||
ElMessage.warning('获取专业分布数据失败,请检查网络连接');
|
||||
} finally {
|
||||
majorLoading.value = false;
|
||||
}
|
||||
@@ -513,66 +560,110 @@ const loadRegionChart = async () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const regionData = res.data;
|
||||
// 确保DOM元素已经加载
|
||||
await nextTick();
|
||||
|
||||
// 初始化地区分布图
|
||||
if (!regionChartInstance) {
|
||||
// 检查DOM元素
|
||||
const chartDom = document.getElementById('regionChart');
|
||||
if (!chartDom) {
|
||||
console.error('找不到地区分布图DOM元素!');
|
||||
console.error('找不到地区分布图DOM元素! 请检查HTML结构');
|
||||
return;
|
||||
}
|
||||
|
||||
// 初始化图表,如果已存在则销毁重建
|
||||
if (regionChartInstance) {
|
||||
regionChartInstance.dispose();
|
||||
}
|
||||
|
||||
regionChartInstance = echarts.init(chartDom);
|
||||
}
|
||||
console.log('地区分布图实例已创建');
|
||||
|
||||
const pieData: { name: string; value: number }[] = [];
|
||||
// 获取数据
|
||||
const regionData = res.data.data || res.data;
|
||||
console.log('地区分布原始数据:', regionData);
|
||||
|
||||
if (Array.isArray(regionData)) {
|
||||
regionData.forEach(item => {
|
||||
pieData.push({
|
||||
name: item.name as string,
|
||||
value: item.value as number
|
||||
});
|
||||
});
|
||||
} else {
|
||||
console.error('地区分布数据不是数组格式:', regionData);
|
||||
if (!Array.isArray(regionData)) {
|
||||
console.error('地区分布数据格式错误,应为数组:', regionData);
|
||||
return;
|
||||
}
|
||||
|
||||
// 限制显示的地区数量,避免图表过于拥挤
|
||||
const displayLimit = 6;
|
||||
const sortedData = [...regionData].sort((a, b) => b.value - a.value);
|
||||
const displayData = sortedData.slice(0, displayLimit);
|
||||
|
||||
// 如果数据超过限制,添加"其他"类别
|
||||
if (sortedData.length > displayLimit) {
|
||||
const otherSum = sortedData.slice(displayLimit).reduce((sum, item) => sum + item.value, 0);
|
||||
displayData.push({
|
||||
name: '其他',
|
||||
value: otherSum
|
||||
});
|
||||
}
|
||||
|
||||
const regions = displayData.map(item => item.name);
|
||||
const values = displayData.map(item => item.value);
|
||||
|
||||
console.log('处理后的地区分布数据:', { regions, values });
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{a} <br/>{b}: {c} ({d}%)'
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
type: 'scroll',
|
||||
orient: 'vertical',
|
||||
right: 10,
|
||||
top: 20,
|
||||
bottom: 20
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'value'
|
||||
},
|
||||
yAxis: {
|
||||
type: 'category',
|
||||
data: regions,
|
||||
axisLabel: {
|
||||
interval: 0,
|
||||
rotate: 0
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '地区分布',
|
||||
type: 'pie',
|
||||
radius: '55%',
|
||||
center: ['40%', '50%'],
|
||||
data: pieData,
|
||||
name: '报名人数',
|
||||
type: 'bar',
|
||||
data: values,
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(1, 0, 0, 0, [
|
||||
{ offset: 0, color: '#83bff6' },
|
||||
{ offset: 0.5, color: '#188df0' },
|
||||
{ offset: 1, color: '#188df0' }
|
||||
])
|
||||
},
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||
color: new echarts.graphic.LinearGradient(1, 0, 0, 0, [
|
||||
{ offset: 0, color: '#2378f7' },
|
||||
{ offset: 0.7, color: '#2378f7' },
|
||||
{ offset: 1, color: '#83bff6' }
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
// 设置选项并绘制图表
|
||||
regionChartInstance.setOption(option);
|
||||
console.log('地区分布图已渲染完成');
|
||||
|
||||
// 强制更新布局
|
||||
regionChartInstance.resize();
|
||||
} catch (error) {
|
||||
console.error('获取地区分布数据失败', error);
|
||||
ElMessage.warning('获取地区分布数据失败');
|
||||
ElMessage.warning('获取地区分布数据失败,请检查网络连接');
|
||||
} finally {
|
||||
regionLoading.value = false;
|
||||
}
|
||||
@@ -592,18 +683,28 @@ const loadStatusChart = async () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const statusData = res.data;
|
||||
// 确保DOM元素已经加载
|
||||
await nextTick();
|
||||
|
||||
// 初始化状态分布图
|
||||
if (!statusChartInstance) {
|
||||
// 检查DOM元素
|
||||
const chartDom = document.getElementById('statusChart');
|
||||
if (!chartDom) {
|
||||
console.error('找不到状态分布图DOM元素!');
|
||||
console.error('找不到状态分布图DOM元素! 请检查HTML结构');
|
||||
return;
|
||||
}
|
||||
statusChartInstance = echarts.init(chartDom);
|
||||
|
||||
// 初始化图表,如果已存在则销毁重建
|
||||
if (statusChartInstance) {
|
||||
statusChartInstance.dispose();
|
||||
}
|
||||
|
||||
statusChartInstance = echarts.init(chartDom);
|
||||
console.log('状态分布图实例已创建');
|
||||
|
||||
// 获取数据
|
||||
const statusData = res.data.data || res.data;
|
||||
console.log('状态分布原始数据:', statusData);
|
||||
|
||||
const statusMap: Record<string, string> = {
|
||||
'0': '未开始',
|
||||
'1': '信息审核中',
|
||||
@@ -637,6 +738,8 @@ const loadStatusChart = async () => {
|
||||
});
|
||||
}
|
||||
|
||||
console.log('处理后的状态分布数据:', chartData);
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
@@ -677,10 +780,15 @@ const loadStatusChart = async () => {
|
||||
]
|
||||
};
|
||||
|
||||
// 设置选项并绘制图表
|
||||
statusChartInstance.setOption(option);
|
||||
console.log('状态分布图已渲染完成');
|
||||
|
||||
// 强制更新布局
|
||||
statusChartInstance.resize();
|
||||
} catch (error) {
|
||||
console.error('获取状态分布数据失败', error);
|
||||
ElMessage.warning('获取状态分布数据失败');
|
||||
ElMessage.warning('获取状态分布数据失败,请检查网络连接');
|
||||
} finally {
|
||||
statusLoading.value = false;
|
||||
}
|
||||
@@ -700,16 +808,23 @@ const handleResize = () => {
|
||||
statusChartInstance?.resize();
|
||||
};
|
||||
|
||||
// 页面初始化
|
||||
onMounted(() => {
|
||||
console.log('统计页面已挂载');
|
||||
// 延迟执行,确保DOM完全加载
|
||||
setTimeout(() => {
|
||||
console.log('开始页面初始化流程...');
|
||||
try {
|
||||
// 加载后台真实数据
|
||||
loadStatisticsData();
|
||||
|
||||
// 添加窗口resize事件监听
|
||||
window.addEventListener('resize', handleResize);
|
||||
console.log('添加窗口resize事件监听');
|
||||
} catch (e) {
|
||||
console.error('页面初始化错误:', e);
|
||||
}
|
||||
}, 300); // 添加300ms延迟,确保DOM完全加载
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
|
||||
Reference in new issue
Block a user