wuzhen update 招生就业相关修改

This commit is contained in:
“wuzhen” committed 2026-06-22 14:50:19 +08:00
1 parent 80cdde97a1
commit 6546732bb2
29 files changed
+1389 -287

No files matched your search

@@ -199,4 +199,21 @@ public class XgDormBedDetailVo implements Serializable {
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
// =============== 请假信息 ===============
private LeaveInfo leaveInfo;
@Data
public static class LeaveInfo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private Long leaveId;
private String leaveTypeName;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date leaveStartTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date leaveEndTime;
}
}
@@ -16,12 +16,13 @@ import org.dromara.xg.domain.vo.XgDormBedDetailVo;
import org.dromara.xg.domain.XgDormBed;
import org.dromara.xg.domain.XgDormRoom;
import org.dromara.xg.domain.XgDormBedStudent;
import org.dromara.xg.domain.XgSysDept;
import org.dromara.xg.domain.vo.XgStudentLeaveVo;
import org.dromara.xg.mapper.XgDormBedMapper;
import org.dromara.xg.mapper.XgDormRoomMapper;
import org.dromara.xg.mapper.XgDormBedStudentMapper;
import org.dromara.xg.mapper.XgSysDeptMapper;
import org.dromara.xg.service.IXgDormBedService;
import org.dromara.xg.service.IXgStudentLeaveService;
import org.dromara.common.satoken.utils.LoginHelper;
import java.util.List;
@@ -31,6 +32,8 @@ import java.util.Set;
import java.util.stream.Collectors;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Objects;
import java.time.LocalDateTime;
/**
* 宿舍床位信息Service业务层处理
@@ -47,6 +50,7 @@ public class XgDormBedServiceImpl implements IXgDormBedService {
private final XgDormRoomMapper xgDormRoomMapper;
private final XgDormBedStudentMapper xgDormBedStudentMapper;
private final XgSysDeptMapper xgSysDeptMapper;
private final IXgStudentLeaveService xgStudentLeaveService;
/**
* 查询宿舍床位信息
@@ -160,9 +164,25 @@ public class XgDormBedServiceImpl implements IXgDormBedService {
public List<XgDormBedDetailVo> queryBedDetailsByRoomId(Long roomId) {
List<XgDormBedDetailVo> result = new ArrayList<>();
// 第一步:从 xg_dorm_bed_student 为主表查有学生的床位
List<Map<String, Object>> studentBeds = xgDormBedStudentMapper.selectRoomStudents(roomId);
List<Long> studentIds = studentBeds.stream()
.map(row -> {
Object studentId = row.get("student_id");
if (studentId instanceof Number) {
return ((Number) studentId).longValue();
}
return null;
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
Map<Long, XgStudentLeaveVo> leaveMap = Map.of();
if (!studentIds.isEmpty()) {
leaveMap = xgStudentLeaveService.queryStudentLeavesByCheckDate(
studentIds, LocalDateTime.now());
}
Set<Long> occupiedBedIds = new HashSet<>();
for (Map<String, Object> row : studentBeds) {
XgDormBedDetailVo vo = new XgDormBedDetailVo();
@@ -184,10 +204,24 @@ public class XgDormBedServiceImpl implements IXgDormBedService {
vo.setMajorName(row.get("major_name") != null ? row.get("major_name").toString() : null);
vo.setAssignmentStatus("0");
vo.setBedStatus("1");
Object studentIdObj = row.get("student_id");
if (studentIdObj instanceof Number) {
Long studentIdValue = ((Number) studentIdObj).longValue();
XgStudentLeaveVo leaveVo = leaveMap.get(studentIdValue);
if (leaveVo != null) {
XgDormBedDetailVo.LeaveInfo leaveInfo = new XgDormBedDetailVo.LeaveInfo();
leaveInfo.setLeaveId(leaveVo.getId());
leaveInfo.setLeaveTypeName(leaveVo.getLeaveTypeName());
leaveInfo.setLeaveStartTime(leaveVo.getStartTime());
leaveInfo.setLeaveEndTime(leaveVo.getEndTime());
vo.setLeaveInfo(leaveInfo);
}
}
result.add(vo);
}
// 第二步:补查 xg_dorm_bed 中未被分配的空床位
List<XgDormBed> emptyBeds = baseMapper.selectList(
Wrappers.<XgDormBed>lambdaQuery()
.eq(XgDormBed::getRoomId, roomId)
@@ -33,11 +33,14 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.Objects;
import java.util.Collections;
import java.time.LocalDateTime;
import org.dromara.xg.domain.bo.XgDormBedBatchRecallBo;
import org.dromara.xg.domain.bo.XgDormSelectiveAllocationBo;
import org.dromara.xg.mapper.XgStudentMsgMapper;
import org.dromara.xg.domain.XgStudentMsg;
import org.dromara.xg.service.IXgStudentLeaveService;
import org.dromara.xg.domain.vo.XgStudentLeaveVo;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.xg.domain.vo.DormBuildingTreeVo;
@@ -75,6 +78,7 @@ public class XgDormBedStudentServiceImpl implements IXgDormBedStudentService {
private final XgDormAreaMapper xgDormAreaMapper;
private final XgSysDeptMapper xgSysDeptMapper;
private final XgClassMsgMapper xgClassMsgMapper;
private final IXgStudentLeaveService xgStudentLeaveService;
/**
* 查询床位学生分配关系
@@ -2457,6 +2461,7 @@ public class XgDormBedStudentServiceImpl implements IXgDormBedStudentService {
/**
* 根据房间ID获取住在当前房间的所有学生信息
* 包含学生姓名、学号、性别、学院名称、专业名称、班级名称、床位编码、床位ID
* 同时查询当前时间有效的请假信息
*/
@Override
public List<Object> getRoomStudents(Long roomId) {
@@ -2466,6 +2471,23 @@ public class XgDormBedStudentServiceImpl implements IXgDormBedStudentService {
// 使用SQL关联查询,一次性获取所有信息
List<Map<String, Object>> studentDetails = baseMapper.selectRoomStudents(roomId);
List<Long> studentIds = studentDetails.stream()
.map(detail -> {
Object studentId = detail.get("student_id");
if (studentId instanceof Number) {
return ((Number) studentId).longValue();
}
return null;
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
Map<Long, XgStudentLeaveVo> leaveMap = Map.of();
if (!studentIds.isEmpty()) {
leaveMap = xgStudentLeaveService.queryStudentLeavesByCheckDate(
studentIds, LocalDateTime.now());
}
List<Object> result = new ArrayList<>();
for (Map<String, Object> detailMap : studentDetails) {
@@ -2488,6 +2510,20 @@ public class XgDormBedStudentServiceImpl implements IXgDormBedStudentService {
studentInfo.put("bedCode", detailMap.get("bed_code"));
studentInfo.put("bedId", detailMap.get("bed_id"));
Object studentIdObj = detailMap.get("student_id");
if (studentIdObj instanceof Number) {
Long studentIdValue = ((Number) studentIdObj).longValue();
XgStudentLeaveVo leaveInfo = leaveMap.get(studentIdValue);
if (leaveInfo != null) {
Map<String, Object> leaveInfoMap = new LinkedHashMap<>();
leaveInfoMap.put("leaveId", leaveInfo.getId());
leaveInfoMap.put("leaveTypeName", leaveInfo.getLeaveTypeName());
leaveInfoMap.put("leaveStartTime", leaveInfo.getStartTime());
leaveInfoMap.put("leaveEndTime", leaveInfo.getEndTime());
studentInfo.put("leaveInfo", leaveInfoMap);
}
}
result.add(studentInfo);
}
@@ -22,9 +22,9 @@ import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.zsjy.domain.vo.ZsjyEnrollmentInfoVo;
import org.dromara.zsjy.domain.bo.ZsjyEnrollmentInfoBo;
import org.dromara.zsjy.service.IZsjyEnrollmentInfoService;
import org.dromara.zsjy.service.IZsjyEnrollmentMajorService;
import org.dromara.zsjy.domain.vo.ZsjyEnrollmentDeptVo;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.xg.service.IXgSysDeptService;
import org.dromara.xg.domain.vo.XgSysDeptVo;
/**
* 招生报名信息Controller
@@ -39,7 +39,7 @@ import org.dromara.xg.domain.vo.XgSysDeptVo;
public class ZsjyEnrollmentInfoController extends BaseController {
private final IZsjyEnrollmentInfoService enrollmentInfoService;
private final IXgSysDeptService xgSysDeptService;
private final IZsjyEnrollmentMajorService enrollmentMajorService;
/**
* 查询招生报名信息列表
@@ -164,10 +164,9 @@ public class ZsjyEnrollmentInfoController extends BaseController {
*/
@SaIgnore
@GetMapping("/colleges")
public R<List<XgSysDeptVo>> getColleges() {
public R<List<ZsjyEnrollmentDeptVo>> getColleges() {
try {
// 调用学院部门服务获取学院列表
List<XgSysDeptVo> colleges = xgSysDeptService.selectCollegeList();
List<ZsjyEnrollmentDeptVo> colleges = enrollmentMajorService.selectOpenColleges();
return R.ok(colleges);
} catch (Exception e) {
return R.fail("获取学院列表异常: " + e.getMessage());
@@ -179,10 +178,9 @@ public class ZsjyEnrollmentInfoController extends BaseController {
*/
@SaIgnore
@GetMapping("/majors/{collegeId}")
public R<List<XgSysDeptVo>> getMajorsByCollegeId(@PathVariable Long collegeId) {
public R<List<ZsjyEnrollmentDeptVo>> getMajorsByCollegeId(@PathVariable Long collegeId) {
try {
// 调用学院部门服务获取专业列表
List<XgSysDeptVo> majors = xgSysDeptService.selectMajorListByCollegeId(collegeId);
List<ZsjyEnrollmentDeptVo> majors = enrollmentMajorService.selectOpenMajorsByCollegeId(collegeId);
return R.ok(majors);
} catch (Exception e) {
return R.fail("获取专业列表异常: " + e.getMessage());
@@ -0,0 +1,54 @@
package org.dromara.zsjy.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaMode;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.web.core.BaseController;
import org.dromara.zsjy.domain.bo.ZsjyEnrollmentMajorSaveBo;
import org.dromara.zsjy.domain.vo.ZsjyEnrollmentDeptVo;
import org.dromara.zsjy.service.IZsjyEnrollmentMajorService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* 招生报名开放专业配置
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/zsjy/enrollment/major")
public class ZsjyEnrollmentMajorController extends BaseController {
private final IZsjyEnrollmentMajorService enrollmentMajorService;
@SaCheckPermission(value = {"zsjy:enrollmentMajor:list", "zsjy:enrollmentMajor:query"}, mode = SaMode.OR)
@GetMapping("/colleges")
public R<List<ZsjyEnrollmentDeptVo>> colleges() {
return R.ok(enrollmentMajorService.selectCollegeList());
}
@SaCheckPermission(value = {"zsjy:enrollmentMajor:list", "zsjy:enrollmentMajor:query"}, mode = SaMode.OR)
@GetMapping("/list")
public R<List<ZsjyEnrollmentDeptVo>> list(@RequestParam Long collegeDeptId) {
return R.ok(enrollmentMajorService.selectMajorConfigByCollegeId(collegeDeptId));
}
@SaCheckPermission("zsjy:enrollmentMajor:save")
@Log(title = "招生报名专业配置", businessType = BusinessType.UPDATE)
@RepeatSubmit
@PostMapping("/save")
public R<Void> save(@Validated @RequestBody ZsjyEnrollmentMajorSaveBo bo) {
return toAjax(enrollmentMajorService.saveCollegeConfig(bo));
}
}
@@ -21,6 +21,7 @@ import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.zsjy.domain.vo.ZsjyEnrollmentProcessVo;
import org.dromara.zsjy.domain.vo.ZsjyEnrollmentRegisterExportVo;
import org.dromara.zsjy.domain.bo.ZsjyEnrollmentProcessBo;
import org.dromara.zsjy.service.IZsjyEnrollmentProcessService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
@@ -59,6 +60,16 @@ public class ZsjyEnrollmentProcessController extends BaseController {
ExcelUtil.exportExcel(list, "招生录取流程状态", ZsjyEnrollmentProcessVo.class, response);
}
/**
* 导出新生录取名册(与纸质录取名册字段一致)
*/
@Log(title = "新生录取名册", businessType = BusinessType.EXPORT)
@PostMapping("/exportRegister")
public void exportRegister(ZsjyEnrollmentProcessBo bo, HttpServletResponse response) {
List<ZsjyEnrollmentRegisterExportVo> list = enrollmentProcessService.queryRegisterExportList(bo);
ExcelUtil.exportExcel(list, "新生录取名册", ZsjyEnrollmentRegisterExportVo.class, response);
}
/**
* 获取招生录取流程状态详细信息
*/
@@ -0,0 +1,43 @@
package org.dromara.zsjy.domain;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.tenant.core.TenantEntity;
import java.io.Serial;
/**
* 招生报名开放专业配置 zsjy_enrollment_major
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("zsjy_enrollment_major")
public class ZsjyEnrollmentMajor extends TenantEntity {
@Serial
private static final long serialVersionUID = 1L;
@TableId(value = "id")
private Long id;
private Long collegeDeptId;
private Long majorDeptId;
private String collegeName;
private String majorName;
private String majorCode;
private Integer sortOrder;
/** 0正常 1停用 */
private String status;
@TableLogic
private Integer deleted;
}
@@ -53,11 +53,13 @@ public class ZsjyEnrollmentInfoBo extends BaseEntity {
/**
* 是否住校(0-否 1-是)
*/
@NotNull(message = "是否住宿不能为空", groups = { AddGroup.class, EditGroup.class })
private Integer isBoarding;
/**
* 出生年月
*/
@NotNull(message = "出生日期不能为空", groups = { AddGroup.class, EditGroup.class })
private Date birthDate;
/**
@@ -68,39 +70,46 @@ public class ZsjyEnrollmentInfoBo extends BaseEntity {
private String phone1;
/**
* 联系手机号码2(非必填,填写时需符合手机号格式)
* 联系手机号码2
*/
@Pattern(regexp = "^$|^1[3-9]\\d{9}$", message = "手机号码格式不正确", groups = { AddGroup.class, EditGroup.class })
@NotBlank(message = "备用电话不能为空", groups = { AddGroup.class, EditGroup.class })
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号码格式不正确", groups = { AddGroup.class, EditGroup.class })
private String phone2;
/**
* 民族
*/
@NotBlank(message = "民族不能为空", groups = { AddGroup.class, EditGroup.class })
private String nation;
/**
* 家庭地址
*/
@NotBlank(message = "家庭住址不能为空", groups = { AddGroup.class, EditGroup.class })
private String homeAddress;
/**
* 初中毕业学校
*/
@NotBlank(message = "毕业中学不能为空", groups = { AddGroup.class, EditGroup.class })
private String middleSchool;
/**
* 准考证号码
*/
@NotBlank(message = "准考证号不能为空", groups = { AddGroup.class, EditGroup.class })
private String examNumber;
/**
* 参加中考地区
*/
@NotBlank(message = "中考地区不能为空", groups = { AddGroup.class, EditGroup.class })
private String examRegion;
/**
* 当地中考满分
*/
@NotNull(message = "中考满分不能为空", groups = { AddGroup.class, EditGroup.class })
@Min(value = 100, message = "中考满分不能小于100", groups = { AddGroup.class, EditGroup.class })
@Max(value = 1000, message = "中考满分不能大于1000", groups = { AddGroup.class, EditGroup.class })
private Integer totalScoreFull;
@@ -108,42 +117,49 @@ public class ZsjyEnrollmentInfoBo extends BaseEntity {
/**
* 考生中考成绩(含体育)
*/
@NotNull(message = "中考成绩不能为空", groups = { AddGroup.class, EditGroup.class })
@Min(value = 0, message = "中考成绩不能小于0", groups = { AddGroup.class, EditGroup.class })
private Integer totalScore;
/**
* 语文成绩
*/
@NotNull(message = "语文成绩不能为空", groups = { AddGroup.class, EditGroup.class })
@DecimalMin(value = "0.0", message = "语文成绩不能小于0", groups = { AddGroup.class, EditGroup.class })
private BigDecimal chineseScore;
/**
* 数学成绩
*/
@NotNull(message = "数学成绩不能为空", groups = { AddGroup.class, EditGroup.class })
@DecimalMin(value = "0.0", message = "数学成绩不能小于0", groups = { AddGroup.class, EditGroup.class })
private BigDecimal mathScore;
/**
* 英语成绩
*/
@NotNull(message = "英语成绩不能为空", groups = { AddGroup.class, EditGroup.class })
@DecimalMin(value = "0.0", message = "英语成绩不能小于0", groups = { AddGroup.class, EditGroup.class })
private BigDecimal englishScore;
/**
* 科学成绩
*/
@NotNull(message = "科学成绩不能为空", groups = { AddGroup.class, EditGroup.class })
@DecimalMin(value = "0.0", message = "科学成绩不能小于0", groups = { AddGroup.class, EditGroup.class })
private BigDecimal scienceScore;
/**
* 社会成绩
*/
@NotNull(message = "社会成绩不能为空", groups = { AddGroup.class, EditGroup.class })
@DecimalMin(value = "0.0", message = "社会成绩不能小于0", groups = { AddGroup.class, EditGroup.class })
private BigDecimal socialScore;
/**
* 体育成绩
*/
@NotNull(message = "体育成绩不能为空", groups = { AddGroup.class, EditGroup.class })
@DecimalMin(value = "0.0", message = "体育成绩不能小于0", groups = { AddGroup.class, EditGroup.class })
private BigDecimal peScore;
@@ -156,16 +172,19 @@ public class ZsjyEnrollmentInfoBo extends BaseEntity {
/**
* 专业代码
*/
@NotBlank(message = "专业代码不能为空", groups = { AddGroup.class, EditGroup.class })
private String majorCode;
/**
* 校服尺码
*/
@NotBlank(message = "校服尺码不能为空", groups = { AddGroup.class, EditGroup.class })
private String uniformSize;
/**
* 健康情况
*/
@NotBlank(message = "健康状况不能为空", groups = { AddGroup.class, EditGroup.class })
private String healthStatus;
/**
@@ -196,11 +215,13 @@ public class ZsjyEnrollmentInfoBo extends BaseEntity {
/**
* 学院名称
*/
@NotBlank(message = "所属学院不能为空", groups = { AddGroup.class, EditGroup.class })
private String collegeName;
/**
* 学院代码
*/
@NotBlank(message = "学院代码不能为空", groups = { AddGroup.class, EditGroup.class })
private String collegeId;
// 添加明确的getter和setter方法以解决编译问题
@@ -0,0 +1,19 @@
package org.dromara.zsjy.domain.bo;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.util.List;
/**
* 按学院批量保存招生开放专业
*/
@Data
public class ZsjyEnrollmentMajorSaveBo {
@NotNull(message = "学院ID不能为空")
private Long collegeDeptId;
/** 勾选的专业 dept_id 列表,可为空表示清空该学院开放专业 */
private List<Long> majorDeptIds;
}
@@ -0,0 +1,59 @@
package org.dromara.zsjy.domain.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 招生报名学院/专业选项
*/
@Data
@Schema(description = "招生报名学院/专业选项")
public class ZsjyEnrollmentDeptVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@JsonSerialize(using = ToStringSerializer.class)
private Long deptId;
@JsonSerialize(using = ToStringSerializer.class)
private Long parentId;
private String deptName;
private String deptCategory;
private Integer orderNum;
/** 配置页:是否已勾选 */
private Boolean selected;
@JsonSerialize(using = ToStringSerializer.class)
private Long majorDeptId;
private String majorName;
private String majorCode;
@JsonSerialize(using = ToStringSerializer.class)
private Long collegeDeptId;
@JsonSerialize(using = ToStringSerializer.class)
private Long createBy;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
@JsonSerialize(using = ToStringSerializer.class)
private Long updateBy;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
}
@@ -234,6 +234,11 @@ public class ZsjyEnrollmentProcessVo implements Serializable {
*/
private String majorName;
/**
* 是否住校(0-否 1-是)- 从enrollment_info表获取
*/
private Integer isBoarding;
/**
* 省份名称 - 从enrollment_info表获取
*/
@@ -0,0 +1,40 @@
package org.dromara.zsjy.domain.vo;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 新生录取名册导出VO(与纸质录取名册字段一致)
*/
@Data
@ExcelIgnoreUnannotated
public class ZsjyEnrollmentRegisterExportVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@ExcelProperty(value = "序号")
private Integer serialNumber;
@ExcelProperty(value = "姓名")
private String studentName;
@ExcelProperty(value = "录取专业")
private String admittedMajor;
@ExcelProperty(value = "通知书号码")
private String noticeNumber;
@ExcelProperty(value = "是否住校")
private String boardingFlag;
@ExcelProperty(value = "预交学费")
private String prepaidTuitionFlag;
@ExcelProperty(value = "备注(时间、经办人)")
private String remarkInfo;
}
@@ -0,0 +1,28 @@
package org.dromara.zsjy.mapper;
import org.apache.ibatis.annotations.Param;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.dromara.zsjy.domain.ZsjyEnrollmentMajor;
import org.dromara.zsjy.domain.vo.ZsjyEnrollmentDeptVo;
import java.util.List;
/**
* 招生报名开放专业配置 Mapper
*/
public interface ZsjyEnrollmentMajorMapper extends BaseMapperPlus<ZsjyEnrollmentMajor, ZsjyEnrollmentDeptVo> {
List<ZsjyEnrollmentDeptVo> selectCollegeList();
List<ZsjyEnrollmentDeptVo> selectMajorConfigByCollegeId(@Param("collegeDeptId") Long collegeDeptId);
List<ZsjyEnrollmentDeptVo> selectOpenColleges();
List<ZsjyEnrollmentDeptVo> selectOpenMajorsByCollegeId(@Param("collegeDeptId") Long collegeDeptId);
int countValidCollege(@Param("collegeDeptId") Long collegeDeptId);
int countValidMajorUnderCollege(@Param("collegeDeptId") Long collegeDeptId, @Param("majorDeptId") Long majorDeptId);
int physicalDeleteByCollegeDeptId(@Param("collegeDeptId") Long collegeDeptId);
}
@@ -0,0 +1,22 @@
package org.dromara.zsjy.service;
import org.dromara.zsjy.domain.bo.ZsjyEnrollmentMajorSaveBo;
import org.dromara.zsjy.domain.vo.ZsjyEnrollmentDeptVo;
import java.util.List;
/**
* 招生报名开放专业配置 Service
*/
public interface IZsjyEnrollmentMajorService {
List<ZsjyEnrollmentDeptVo> selectCollegeList();
List<ZsjyEnrollmentDeptVo> selectMajorConfigByCollegeId(Long collegeDeptId);
Boolean saveCollegeConfig(ZsjyEnrollmentMajorSaveBo bo);
List<ZsjyEnrollmentDeptVo> selectOpenColleges();
List<ZsjyEnrollmentDeptVo> selectOpenMajorsByCollegeId(Long collegeDeptId);
}
@@ -1,6 +1,7 @@
package org.dromara.zsjy.service;
import org.dromara.zsjy.domain.vo.ZsjyEnrollmentProcessVo;
import org.dromara.zsjy.domain.vo.ZsjyEnrollmentRegisterExportVo;
import org.dromara.zsjy.domain.bo.ZsjyEnrollmentProcessBo;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
@@ -139,4 +140,12 @@ public interface IZsjyEnrollmentProcessService {
* @return 待我审核分页列表
*/
TableDataInfo<ZsjyEnrollmentProcessVo> queryMyAuditList(PageQuery pageQuery);
/**
* 查询新生录取名册导出数据(与列表筛选条件一致)
*
* @param bo 查询条件
* @return 录取名册导出列表
*/
List<ZsjyEnrollmentRegisterExportVo> queryRegisterExportList(ZsjyEnrollmentProcessBo bo);
}
@@ -0,0 +1,120 @@
package org.dromara.zsjy.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.zsjy.domain.ZsjyEnrollmentMajor;
import org.dromara.zsjy.domain.bo.ZsjyEnrollmentMajorSaveBo;
import org.dromara.zsjy.domain.vo.ZsjyEnrollmentDeptVo;
import org.dromara.zsjy.mapper.ZsjyEnrollmentMajorMapper;
import org.dromara.zsjy.service.IZsjyEnrollmentMajorService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 招生报名开放专业配置 Service 实现
*/
@RequiredArgsConstructor
@Service
public class ZsjyEnrollmentMajorServiceImpl implements IZsjyEnrollmentMajorService {
private final ZsjyEnrollmentMajorMapper enrollmentMajorMapper;
@Override
public List<ZsjyEnrollmentDeptVo> selectCollegeList() {
return enrollmentMajorMapper.selectCollegeList();
}
@Override
public List<ZsjyEnrollmentDeptVo> selectMajorConfigByCollegeId(Long collegeDeptId) {
if (collegeDeptId == null) {
throw new ServiceException("学院ID不能为空");
}
if (enrollmentMajorMapper.countValidCollege(collegeDeptId) == 0) {
throw new ServiceException("学院不存在或已停用");
}
return enrollmentMajorMapper.selectMajorConfigByCollegeId(collegeDeptId);
}
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean saveCollegeConfig(ZsjyEnrollmentMajorSaveBo bo) {
Long collegeDeptId = bo.getCollegeDeptId();
if (enrollmentMajorMapper.countValidCollege(collegeDeptId) == 0) {
throw new ServiceException("学院不存在或已停用");
}
List<Long> majorDeptIds = bo.getMajorDeptIds() == null ? List.of() : bo.getMajorDeptIds();
for (Long majorDeptId : majorDeptIds) {
if (enrollmentMajorMapper.countValidMajorUnderCollege(collegeDeptId, majorDeptId) == 0) {
throw new ServiceException("存在不属于该学院的专业,无法保存");
}
}
List<ZsjyEnrollmentDeptVo> allMajors = enrollmentMajorMapper.selectMajorConfigByCollegeId(collegeDeptId);
Map<Long, ZsjyEnrollmentDeptVo> majorMap = new HashMap<>();
for (ZsjyEnrollmentDeptVo major : allMajors) {
majorMap.put(major.getMajorDeptId(), major);
}
String collegeName = resolveCollegeName(collegeDeptId);
enrollmentMajorMapper.physicalDeleteByCollegeDeptId(collegeDeptId);
if (CollUtil.isEmpty(majorDeptIds)) {
return true;
}
List<ZsjyEnrollmentMajor> insertList = new ArrayList<>();
int sort = 0;
for (Long majorDeptId : majorDeptIds) {
ZsjyEnrollmentDeptVo major = majorMap.get(majorDeptId);
if (major == null) {
continue;
}
ZsjyEnrollmentMajor entity = new ZsjyEnrollmentMajor();
entity.setId(IdUtil.getSnowflakeNextId());
entity.setCollegeDeptId(collegeDeptId);
entity.setMajorDeptId(majorDeptId);
entity.setCollegeName(collegeName);
entity.setMajorName(major.getMajorName());
entity.setMajorCode(major.getMajorCode());
entity.setSortOrder(++sort);
entity.setStatus("0");
entity.setDeleted(0);
insertList.add(entity);
}
if (CollUtil.isNotEmpty(insertList)) {
enrollmentMajorMapper.insertBatch(insertList);
}
return true;
}
@Override
public List<ZsjyEnrollmentDeptVo> selectOpenColleges() {
return enrollmentMajorMapper.selectOpenColleges();
}
@Override
public List<ZsjyEnrollmentDeptVo> selectOpenMajorsByCollegeId(Long collegeDeptId) {
if (collegeDeptId == null) {
throw new ServiceException("学院ID不能为空");
}
return enrollmentMajorMapper.selectOpenMajorsByCollegeId(collegeDeptId);
}
private String resolveCollegeName(Long collegeDeptId) {
return enrollmentMajorMapper.selectCollegeList().stream()
.filter(item -> collegeDeptId.equals(item.getDeptId()))
.map(ZsjyEnrollmentDeptVo::getDeptName)
.findFirst()
.orElse("");
}
}
@@ -14,6 +14,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.dromara.zsjy.domain.bo.ZsjyEnrollmentProcessBo;
import org.dromara.zsjy.domain.vo.ZsjyEnrollmentProcessVo;
import org.dromara.zsjy.domain.vo.ZsjyEnrollmentRegisterExportVo;
import org.dromara.zsjy.domain.ZsjyEnrollmentProcess;
import org.dromara.zsjy.mapper.ZsjyEnrollmentProcessMapper;
import org.dromara.zsjy.service.IZsjyEnrollmentProcessService;
@@ -22,6 +23,8 @@ import java.util.List;
import java.util.Collection;
import java.util.Date;
import java.util.Set;
import java.util.ArrayList;
import java.text.SimpleDateFormat;
/**
* 招生录取流程状态Service业务层处理
@@ -430,4 +433,56 @@ public class ZsjyEnrollmentProcessServiceImpl implements IZsjyEnrollmentProcessS
Page<ZsjyEnrollmentProcessVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
@Override
public List<ZsjyEnrollmentRegisterExportVo> queryRegisterExportList(ZsjyEnrollmentProcessBo bo) {
List<ZsjyEnrollmentProcessVo> sourceList = baseMapper.selectAllProcessWithInfoAudit(bo);
List<ZsjyEnrollmentRegisterExportVo> exportList = new ArrayList<>();
if (sourceList == null || sourceList.isEmpty()) {
return exportList;
}
int index = 1;
for (ZsjyEnrollmentProcessVo item : sourceList) {
ZsjyEnrollmentRegisterExportVo exportVo = new ZsjyEnrollmentRegisterExportVo();
exportVo.setSerialNumber(index++);
exportVo.setStudentName(StringUtils.blankToDefault(item.getStudentName(), ""));
exportVo.setAdmittedMajor(StringUtils.blankToDefault(item.getMajorName(), ""));
exportVo.setNoticeNumber(StringUtils.blankToDefault(item.getNoticeNumber(), ""));
exportVo.setBoardingFlag(formatBoardingFlag(item.getIsBoarding()));
exportVo.setPrepaidTuitionFlag(formatPrepaidTuitionFlag(item.getPaymentStatus()));
exportVo.setRemarkInfo(buildRegisterRemark(item));
exportList.add(exportVo);
}
return exportList;
}
private String formatBoardingFlag(Integer isBoarding) {
return isBoarding != null && isBoarding == 1 ? "√" : "";
}
private String formatPrepaidTuitionFlag(String paymentStatus) {
return "1".equals(paymentStatus) ? "√" : "×";
}
private String buildRegisterRemark(ZsjyEnrollmentProcessVo item) {
if (StringUtils.isNotBlank(item.getRemark())) {
return item.getRemark();
}
StringBuilder remark = new StringBuilder();
Date remarkTime = item.getPaymentConfirmTime() != null ? item.getPaymentConfirmTime()
: (item.getAdmissionTime() != null ? item.getAdmissionTime() : item.getFinalAuditTime());
if (remarkTime != null) {
remark.append(new SimpleDateFormat("M.d").format(remarkTime));
}
String handler = StringUtils.blankToDefault(item.getAdmissionUserName(),
StringUtils.blankToDefault(item.getFinalAuditUserName(),
StringUtils.blankToDefault(item.getInfoAuditUserName(), item.getCurrentHandlerName())));
if (StringUtils.isNotBlank(handler)) {
if (!remark.isEmpty()) {
remark.append(" ");
}
remark.append(handler);
}
return remark.toString();
}
}
@@ -0,0 +1,88 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.zsjy.mapper.ZsjyEnrollmentMajorMapper">
<select id="selectCollegeList" resultType="org.dromara.zsjy.domain.vo.ZsjyEnrollmentDeptVo">
SELECT d.dept_id AS deptId,
d.dept_name AS deptName,
d.dept_category AS deptCategory,
d.order_num AS orderNum
FROM sys_dept d
WHERE d.del_flag = '0'
AND d.status = '0'
AND d.parent_id = 101
ORDER BY d.order_num
</select>
<select id="selectMajorConfigByCollegeId" resultType="org.dromara.zsjy.domain.vo.ZsjyEnrollmentDeptVo">
SELECT m.dept_id AS majorDeptId,
m.dept_name AS majorName,
m.dept_category AS majorCode,
m.parent_id AS collegeDeptId,
CASE WHEN em.id IS NOT NULL THEN 1 ELSE 0 END AS selected
FROM sys_dept m
LEFT JOIN zsjy_enrollment_major em
ON em.major_dept_id = m.dept_id AND em.deleted = 0 AND em.status = '0'
WHERE m.parent_id = #{collegeDeptId}
AND m.del_flag = '0'
AND m.status = '0'
AND m.dept_category LIKE 'zy-%'
ORDER BY m.order_num
</select>
<select id="selectOpenColleges" resultType="org.dromara.zsjy.domain.vo.ZsjyEnrollmentDeptVo">
SELECT DISTINCT c.dept_id AS deptId,
c.dept_name AS deptName,
c.dept_category AS deptCategory,
c.order_num AS orderNum
FROM sys_dept c
INNER JOIN sys_dept m ON m.parent_id = c.dept_id
INNER JOIN zsjy_enrollment_major em ON em.major_dept_id = m.dept_id
WHERE c.parent_id = 101
AND c.del_flag = '0'
AND c.status = '0'
AND m.del_flag = '0'
AND m.status = '0'
AND em.deleted = 0
AND em.status = '0'
ORDER BY c.order_num
</select>
<select id="selectOpenMajorsByCollegeId" resultType="org.dromara.zsjy.domain.vo.ZsjyEnrollmentDeptVo">
SELECT m.dept_id AS deptId,
m.dept_name AS deptName,
m.dept_category AS deptCategory,
em.sort_order AS orderNum
FROM sys_dept m
INNER JOIN zsjy_enrollment_major em ON em.major_dept_id = m.dept_id
WHERE m.parent_id = #{collegeDeptId}
AND m.del_flag = '0'
AND m.status = '0'
AND em.deleted = 0
AND em.status = '0'
ORDER BY em.sort_order, m.order_num
</select>
<select id="countValidCollege" resultType="int">
SELECT COUNT(1)
FROM sys_dept d
WHERE d.dept_id = #{collegeDeptId}
AND d.parent_id = 101
AND d.del_flag = '0'
AND d.status = '0'
</select>
<select id="countValidMajorUnderCollege" resultType="int">
SELECT COUNT(1)
FROM sys_dept m
WHERE m.dept_id = #{majorDeptId}
AND m.parent_id = #{collegeDeptId}
AND m.del_flag = '0'
AND m.status = '0'
AND m.dept_category LIKE 'zy-%'
</select>
<delete id="physicalDeleteByCollegeDeptId">
DELETE FROM zsjy_enrollment_major WHERE college_dept_id = #{collegeDeptId}
</delete>
</mapper>
@@ -78,6 +78,7 @@
<!-- 额外字段,用于合并enrollment_info表的数据 -->
<result property="gender" column="gender"/>
<result property="majorName" column="majorName"/>
<result property="isBoarding" column="isBoarding"/>
<result property="province" column="province"/>
</resultMap>
@@ -143,7 +144,7 @@
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
ei.gender, ei.applied_major as majorName, ei.is_boarding as isBoarding, 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'
+3 -3
View File
@@ -208,7 +208,7 @@
}
},
{
"path": "pages/xgss/dmdl/dmdl",
"path": "pages/xgss/ssdm/ssdm",
"style": {
"navigationBarTitleText": "宿舍点名",
"navigationBarBackgroundColor": "#0051d9",
@@ -216,14 +216,14 @@
}
},
{
"path": "pages/xgss/dmdl/dmdlCheck",
"path": "pages/xgss/ssdm/ssdmCheck",
"style": {
"navigationBarTitleText": "宿舍点名",
"navigationStyle": "custom"
}
},
{
"path": "pages/xgss/dmdl/dmdlDetail",
"path": "pages/xgss/ssdm/ssdmDetail",
"style": {
"navigationBarTitleText": "点名详情",
"navigationBarBackgroundColor": "#0051d9",
+73 -63
View File
@@ -57,7 +57,7 @@
></u-picker>
</u-form-item>
<u-form-item label="出生日期" prop="birthDate">
<u-form-item label="出生日期" prop="birthDate" required>
<u-input
v-model="formData.birthDate"
placeholder="选择出生日期"
@@ -71,15 +71,15 @@
<u-input v-model="formData.phone1" placeholder="请输入联系电话" />
</u-form-item>
<u-form-item label="备用电话" prop="phone2">
<u-form-item label="备用电话" prop="phone2" required>
<u-input v-model="formData.phone2" placeholder="请输入备用电话" />
</u-form-item>
<u-form-item label="民族" prop="nation">
<u-form-item label="民族" prop="nation" required>
<u-input v-model="formData.nation" placeholder="请输入民族" />
</u-form-item>
<u-form-item label="是否住宿" prop="isBoarding">
<u-form-item label="是否住宿" prop="isBoarding" required>
<view
@click="showBoardingPicker"
@tap="showBoardingPicker"
@@ -102,11 +102,11 @@
></u-picker>
</u-form-item>
<u-form-item label="健康状况" prop="healthStatus">
<u-form-item label="健康状况" prop="healthStatus" required>
<u-input v-model="formData.healthStatus" placeholder="请输入健康状况" />
</u-form-item>
<u-form-item label="家庭住址" prop="homeAddress">
<u-form-item label="家庭住址" prop="homeAddress" required>
<u-input
v-model="formData.homeAddress"
type="textarea"
@@ -123,19 +123,19 @@
<text class="title-text">学业信息</text>
</view>
<u-form-item label="毕业中学" prop="middleSchool">
<u-form-item label="毕业中学" prop="middleSchool" required>
<u-input v-model="formData.middleSchool" placeholder="请输入毕业中学全称" />
</u-form-item>
<u-form-item label="准考证号" prop="examNumber">
<u-form-item label="准考证号" prop="examNumber" required>
<u-input v-model="formData.examNumber" placeholder="请输入准考证号" />
</u-form-item>
<u-form-item label="中考地区" prop="examRegion">
<u-form-item label="中考地区" prop="examRegion" required>
<u-input v-model="formData.examRegion" placeholder="请输入中考地区" />
</u-form-item>
<u-form-item label="中考满分" prop="totalScoreFull">
<u-form-item label="中考满分" prop="totalScoreFull" required>
<u-number-box
v-model="formData.totalScoreFull"
:min="0"
@@ -144,7 +144,7 @@
></u-number-box>
</u-form-item>
<u-form-item label="中考成绩" prop="totalScore">
<u-form-item label="中考成绩" prop="totalScore" required>
<u-number-box
v-model="formData.totalScore"
:min="0"
@@ -153,7 +153,7 @@
></u-number-box>
</u-form-item>
<u-form-item label="语文成绩" prop="chineseScore">
<u-form-item label="语文成绩" prop="chineseScore" required>
<u-number-box
v-model="formData.chineseScore"
:min="0"
@@ -162,7 +162,7 @@
></u-number-box>
</u-form-item>
<u-form-item label="数学成绩" prop="mathScore">
<u-form-item label="数学成绩" prop="mathScore" required>
<u-number-box
v-model="formData.mathScore"
:min="0"
@@ -171,7 +171,7 @@
></u-number-box>
</u-form-item>
<u-form-item label="英语成绩" prop="englishScore">
<u-form-item label="英语成绩" prop="englishScore" required>
<u-number-box
v-model="formData.englishScore"
:min="0"
@@ -180,7 +180,7 @@
></u-number-box>
</u-form-item>
<u-form-item label="科学成绩" prop="scienceScore">
<u-form-item label="科学成绩" prop="scienceScore" required>
<u-number-box
v-model="formData.scienceScore"
:min="0"
@@ -189,7 +189,7 @@
></u-number-box>
</u-form-item>
<u-form-item label="社会成绩" prop="socialScore">
<u-form-item label="社会成绩" prop="socialScore" required>
<u-number-box
v-model="formData.socialScore"
:min="0"
@@ -198,7 +198,7 @@
></u-number-box>
</u-form-item>
<u-form-item label="体育成绩" prop="peScore">
<u-form-item label="体育成绩" prop="peScore" required>
<u-number-box
v-model="formData.peScore"
:min="0"
@@ -238,7 +238,7 @@
></u-picker>
</u-form-item>
<u-form-item label="学院代码" prop="collegeId">
<u-form-item label="学院代码" prop="collegeId" required>
<u-input
v-model="formData.collegeId"
placeholder="学院代码将自动填入"
@@ -265,7 +265,7 @@
></u-picker>
</u-form-item>
<u-form-item label="专业代码" prop="majorCode">
<u-form-item label="专业代码" prop="majorCode" required>
<u-input
v-model="formData.majorCode"
placeholder="专业代码将自动填入"
@@ -273,7 +273,7 @@
/>
</u-form-item>
<u-form-item label="校服尺码" prop="uniformSize">
<u-form-item label="校服尺码" prop="uniformSize" required>
<view
@click="showUniformSizePicker"
@tap="showUniformSizePicker"
@@ -444,6 +444,28 @@ import { getDict } from '@/common/dict.js';
import { request } from '@/common/request.js';
import { getCollegeList, getMajorsByCollegeId } from '@/api/enrollment.js';
const idCardPattern = /^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/;
const phonePattern = /^1[3-9]\d{9}$/;
const requiredText = (label) => [{ required: true, message: `${label}不能为空`, trigger: ['blur', 'change'] }];
const requiredNumber = (label, min = 0, max) => [
{
validator: (rule, value, callback) => {
if (value === null || value === undefined || value === '') {
callback(new Error(`${label}不能为空`));
} else if (Number(value) < min) {
callback(new Error(`${label}不能小于${min}`));
} else if (max !== undefined && Number(value) > max) {
callback(new Error(`${label}不能大于${max}`));
} else {
callback();
}
},
trigger: ['blur', 'change'],
},
];
export default {
name: 'EnrollmentIndex',
data() {
@@ -535,28 +557,43 @@ export default {
majorPickerColumns: [],
uniformSizePickerColumns: [],
// 表单验证规则
// 表单验证规则(特长爱好、获奖情况除外)
rules: {
name: [{ required: true, message: '姓名不能为空', trigger: 'blur' }],
name: requiredText('姓名'),
idCard: [
{ required: true, message: '身份证号不能为空', trigger: 'blur' },
{
pattern:
/^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/,
message: '身份证号格式不正确',
trigger: 'blur',
},
{ pattern: idCardPattern, message: '身份证号格式不正确', trigger: 'blur' },
],
gender: [{ required: true, message: '性别不能为空', trigger: 'change' }],
gender: requiredText('性别'),
birthDate: requiredText('出生日期'),
phone1: [
{ required: true, message: '联系电话不能为空', trigger: 'blur' },
{ pattern: /^1[3-9]\d{9}$/, message: '手机号码格式不正确', trigger: 'blur' },
{ pattern: phonePattern, message: '手机号码格式不正确', trigger: 'blur' },
],
phone2: [
{ pattern: /^$|^1[3-9]\d{9}$/, message: '手机号码格式不正确', trigger: 'blur' },
{ required: true, message: '备用电话不能为空', trigger: 'blur' },
{ pattern: phonePattern, message: '手机号码格式不正确', trigger: 'blur' },
],
collegeName: [{ required: true, message: '所属学院不能为空', trigger: 'change' }],
appliedMajor: [{ required: true, message: '报考专业不能为空', trigger: 'change' }],
nation: requiredText('民族'),
isBoarding: requiredText('是否住宿'),
healthStatus: requiredText('健康状况'),
homeAddress: requiredText('家庭住址'),
middleSchool: requiredText('毕业中学'),
examNumber: requiredText('准考证号'),
examRegion: requiredText('中考地区'),
totalScoreFull: requiredNumber('中考满分', 100, 1000),
totalScore: requiredNumber('中考成绩', 0),
chineseScore: requiredNumber('语文成绩', 0),
mathScore: requiredNumber('数学成绩', 0),
englishScore: requiredNumber('英语成绩', 0),
scienceScore: requiredNumber('科学成绩', 0),
socialScore: requiredNumber('社会成绩', 0),
peScore: requiredNumber('体育成绩', 0),
collegeName: requiredText('所属学院'),
collegeId: requiredText('学院代码'),
appliedMajor: requiredText('报考专业'),
majorCode: requiredText('专业代码'),
uniformSize: requiredText('校服尺码'),
},
};
},
@@ -565,21 +602,10 @@ export default {
try {
console.log('🚀 开始页面初始化...');
// 先初始化兜底数据,确保页面能正常显示
this.initDefaultDictData();
this.initDefaultCollegeData();
console.log('✅ 兜底数据初始化完成:', {
genderOptions: this.genderOptions.length,
collegeOptions: this.collegeOptions.length,
majorOptions: this.majorOptions.length,
});
// 并行加载数据,即使部分失败也不影响页面显示
// 并行加载数据
const results = await Promise.allSettled([
this.initDictData(),
this.loadCollegeOptions(),
this.initMajorOptions(),
]);
console.log('📋 异步数据加载结果:', results);
@@ -606,9 +632,7 @@ export default {
console.log('✅ 页面初始化完成,准备就绪');
} catch (error) {
console.error('❌ 页面初始化失败:', error);
// 确保使用兜底数据
this.initDefaultDictData();
this.initDefaultCollegeData();
this.$forceUpdate();
}
},
@@ -798,15 +822,8 @@ export default {
}
} catch (error) {
console.error('学院数据加载失败:', error);
// 设置兜底学院数据
this.collegeOptions = [
{ deptId: 1, deptName: '机械工程学院', deptCategory: 'JX' },
{ deptId: 2, deptName: '电气工程学院', deptCategory: 'DQ' },
{ deptId: 3, deptName: '信息工程学院', deptCategory: 'XX' },
{ deptId: 4, deptName: '经济管理学院', deptCategory: 'JG' },
{ deptId: 5, deptName: '建筑工程学院', deptCategory: 'JZ' },
];
console.log('✅ 已使用默认学院数据(兜底方案)');
this.collegeOptions = [];
uni.showToast({ title: '学院数据加载失败', icon: 'none' });
}
// 更新学院picker的columns数据
@@ -1174,13 +1191,6 @@ export default {
this.majorOptions = [];
}
// 如果API加载失败或无数据,使用兜底专业数据
if (!this.majorOptions || this.majorOptions.length === 0) {
const defaultMajors = this.getDefaultMajorsByCollegeId(collegeId);
this.majorOptions = defaultMajors;
console.log('✅ 已使用默认专业数据(兜底方案):', defaultMajors);
}
// 更新专业picker的columns数据
this.majorPickerColumns = [this.majorOptions.map((item) => item.label)];
console.log('✅ 专业picker columns更新完成:', {
+1 -1
View File
@@ -1220,7 +1220,7 @@ export default {
// 跳转到宿舍点名页面
gotoDormRollCall() {
uni.navigateTo({
url: '/pages/xgss/dmdl/dmdl',
url: '/pages/xgss/ssdm/ssdm',
});
},
+30 -53
View File
@@ -39,7 +39,7 @@
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="出生日期" prop="birthDate">
<el-form-item label="出生日期" prop="birthDate" required>
<el-date-picker v-model="applyForm.birthDate" type="date" placeholder="选择出生日期" value-format="YYYY-MM-DD" style="width: 100%" />
</el-form-item>
</el-col>
@@ -49,33 +49,33 @@
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="备用电话" prop="phone2">
<el-form-item label="备用电话" prop="phone2" required>
<el-input v-model="applyForm.phone2" placeholder="请输入备用电话" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="民族" prop="nation">
<el-form-item label="民族" prop="nation" required>
<el-input v-model="applyForm.nation" placeholder="请输入民族" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="是否住宿" prop="isBoarding">
<el-form-item label="是否住宿" prop="isBoarding" required>
<el-select v-model="applyForm.isBoarding" placeholder="请选择是否住宿" style="width: 100%">
<el-option v-for="dict in sys_yes_no" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="健康状况" prop="healthStatus">
<el-form-item label="健康状况" prop="healthStatus" required>
<el-input v-model="applyForm.healthStatus" placeholder="请输入健康状况" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="家庭住址" prop="homeAddress">
<el-form-item label="家庭住址" prop="homeAddress" required>
<el-input v-model="applyForm.homeAddress" placeholder="请输入详细的家庭住址" />
</el-form-item>
</el-col>
@@ -90,63 +90,63 @@
</h3>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="毕业中学" prop="middleSchool">
<el-form-item label="毕业中学" prop="middleSchool" required>
<el-input v-model="applyForm.middleSchool" placeholder="请输入毕业中学全称" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="准考证号" prop="examNumber">
<el-form-item label="准考证号" prop="examNumber" required>
<el-input v-model="applyForm.examNumber" placeholder="请输入准考证号" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="中考地区" prop="examRegion">
<el-form-item label="中考地区" prop="examRegion" required>
<el-input v-model="applyForm.examRegion" placeholder="请输入中考地区" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="中考满分" prop="totalScoreFull">
<el-form-item label="中考满分" prop="totalScoreFull" required>
<el-input-number v-model="applyForm.totalScoreFull" placeholder="请输入中考满分" :min="100" :max="1000" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="中考成绩" prop="totalScore">
<el-form-item label="中考成绩" prop="totalScore" required>
<el-input-number v-model="applyForm.totalScore" placeholder="请输入中考成绩" :min="0" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="语文成绩" prop="chineseScore">
<el-form-item label="语文成绩" prop="chineseScore" required>
<el-input-number v-model="applyForm.chineseScore" placeholder="请输入语文成绩" :min="0" style="width: 100%" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="数学成绩" prop="mathScore">
<el-form-item label="数学成绩" prop="mathScore" required>
<el-input-number v-model="applyForm.mathScore" placeholder="请输入数学成绩" :min="0" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="英语成绩" prop="englishScore">
<el-form-item label="英语成绩" prop="englishScore" required>
<el-input-number v-model="applyForm.englishScore" placeholder="请输入英语成绩" :min="0" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="科学成绩" prop="scienceScore">
<el-form-item label="科学成绩" prop="scienceScore" required>
<el-input-number v-model="applyForm.scienceScore" placeholder="请输入科学成绩" :min="0" style="width: 100%" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="社会成绩" prop="socialScore">
<el-form-item label="社会成绩" prop="socialScore" required>
<el-input-number v-model="applyForm.socialScore" placeholder="请输入社会成绩" :min="0" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="体育成绩" prop="peScore">
<el-form-item label="体育成绩" prop="peScore" required>
<el-input-number v-model="applyForm.peScore" placeholder="请输入体育成绩" :min="0" style="width: 100%" />
</el-form-item>
</el-col>
@@ -168,7 +168,7 @@
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="学院代码" prop="collegeId">
<el-form-item label="学院代码" prop="collegeId" required>
<el-input v-model="applyForm.collegeId" placeholder="学院代码将自动填入" readonly />
</el-form-item>
</el-col>
@@ -182,16 +182,16 @@
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="专业代码" prop="majorCode">
<el-form-item label="专业代码" prop="majorCode" required>
<el-input v-model="applyForm.majorCode" placeholder="专业代码将自动填入" readonly />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="校服尺码" prop="uniformSize">
<el-form-item label="校服尺码" prop="uniformSize" required>
<el-select v-model="applyForm.uniformSize" placeholder="请选择校服尺码" style="width: 100%">
<el-option v-for="dict in zsjy_uniform_size" :key="dict.value" :label="dict.label" :value="dict.value" />
<el-option v-for="dict in uniformSizeOptions" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
</el-col>
@@ -282,11 +282,13 @@
</template>
<script setup name="EnrollmentApply" lang="ts">
import { reactive, ref, toRefs, getCurrentInstance, ComponentInternalInstance, onMounted, nextTick } from 'vue';
import { reactive, ref, toRefs, getCurrentInstance, ComponentInternalInstance, onMounted, nextTick, computed } from 'vue';
import { ElForm } from 'element-plus';
import { submitEnrollmentApplication, queryMyEnrollmentStatus } from '@/api/zsjy/enrollment';
import type { EnrollmentInfoForm } from '@/api/zsjy/enrollment/types';
import { listColleges, listMajorsByCollegeId } from '@/api/xg/xsjcxx/classMsg/college';
import { listOpenColleges, listOpenMajorsByCollegeId } from '@/api/zsjy/enrollmentMajor';
import { enrollmentFormRules } from './enrollmentFormRules';
import { resolveUniformSizeOptions } from './uniformSizeOptions';
import { User, School, Star, Document, Check, Refresh } from '@element-plus/icons-vue';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@@ -294,6 +296,8 @@ const { zsjy_gender, sys_yes_no, zsjy_major, zsjy_uniform_size } = toRefs<any>(
(proxy as any)?.useDict('zsjy_gender', 'sys_yes_no', 'zsjy_major', 'zsjy_uniform_size')
);
const uniformSizeOptions = computed(() => resolveUniformSizeOptions(zsjy_uniform_size.value));
const applyFormRef = ref<InstanceType<typeof ElForm>>();
const submitLoading = ref(false);
const queryLoading = ref(false);
@@ -343,33 +347,7 @@ const queryForm = reactive({
examNumber: ''
});
const rules = {
name: [{ required: true, message: '姓名不能为空', trigger: 'blur' }],
idCard: [
{ required: true, message: '身份证号不能为空', trigger: 'blur' },
{
pattern: /^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/,
message: '身份证号格式不正确',
trigger: 'blur'
}
],
gender: [{ required: true, message: '性别不能为空', trigger: 'change' }],
phone1: [
{ required: true, message: '联系电话不能为空', trigger: 'blur' },
{ pattern: /^1[3-9]\d{9}$/, message: '手机号码格式不正确', trigger: 'blur' }
],
phone2: [{ pattern: /^$|^1[3-9]\d{9}$/, message: '手机号码格式不正确', trigger: 'blur' }],
collegeName: [{ required: true, message: '所属学院不能为空', trigger: 'change' }],
appliedMajor: [{ required: true, message: '报考专业不能为空', trigger: 'change' }],
totalScoreFull: [{ type: 'number', min: 100, max: 1000, message: '中考满分应在100-1000之间', trigger: 'blur' }],
totalScore: [{ type: 'number', min: 0, message: '中考成绩不能小于0', trigger: 'blur' }],
chineseScore: [{ type: 'number', min: 0, message: '语文成绩不能小于0', trigger: 'blur' }],
mathScore: [{ type: 'number', min: 0, message: '数学成绩不能小于0', trigger: 'blur' }],
englishScore: [{ type: 'number', min: 0, message: '英语成绩不能小于0', trigger: 'blur' }],
scienceScore: [{ type: 'number', min: 0, message: '科学成绩不能小于0', trigger: 'blur' }],
socialScore: [{ type: 'number', min: 0, message: '社会成绩不能小于0', trigger: 'blur' }],
peScore: [{ type: 'number', min: 0, message: '体育成绩不能小于0', trigger: 'blur' }]
};
const rules = enrollmentFormRules;
/** 身份证号变化处理 */
const onIdCardChange = () => {
@@ -438,7 +416,7 @@ const onCollegeChange = async (selectedCollegeId: number) => {
/** 根据学院ID加载专业 */
const loadMajorsByCollegeId = async (collegeId: number) => {
try {
const response = await listMajorsByCollegeId(collegeId);
const response = await listOpenMajorsByCollegeId(collegeId);
if (response.data) {
majorOptions.value = response.data.map((major: any) => ({
value: major.deptId.toString(),
@@ -566,7 +544,7 @@ const queryStatus = async () => {
/** 获取学院选项 */
const getCollegeOptions = async () => {
try {
const response = await listColleges();
const response = await listOpenColleges();
if (response.data) {
collegeOptions.value = response.data;
console.log('学院数据加载成功:', collegeOptions.value);
@@ -579,7 +557,6 @@ const getCollegeOptions = async () => {
onMounted(() => {
getCollegeOptions();
getMajorOptions();
});
</script>
@@ -0,0 +1,58 @@
import type { FormRules } from 'element-plus';
const idCardPattern = /^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/;
const phonePattern = /^1[3-9]\d{9}$/;
const requiredText = (label: string, trigger: 'blur' | 'change' = 'blur') => [
{ required: true, message: `${label}不能为空`, trigger }
];
const requiredNumber = (label: string, min: number, max?: number) => {
const rules: FormRules[string] = [
{ required: true, message: `${label}不能为空`, trigger: 'blur' },
{ type: 'number', min, message: `${label}不能小于${min}`, trigger: 'blur' }
];
if (max !== undefined) {
rules.push({ type: 'number', max, message: `${label}不能大于${max}`, trigger: 'blur' });
}
return rules;
};
/** 招生报名信息表单校验(特长爱好、获奖情况除外) */
export const enrollmentFormRules: FormRules = {
name: requiredText('姓名'),
idCard: [
{ required: true, message: '身份证号不能为空', trigger: 'blur' },
{ pattern: idCardPattern, message: '身份证号格式不正确', trigger: 'blur' }
],
gender: requiredText('性别', 'change'),
birthDate: requiredText('出生日期', 'change'),
phone1: [
{ required: true, message: '联系电话不能为空', trigger: 'blur' },
{ pattern: phonePattern, message: '手机号码格式不正确', trigger: 'blur' }
],
phone2: [
{ required: true, message: '备用电话不能为空', trigger: 'blur' },
{ pattern: phonePattern, message: '手机号码格式不正确', trigger: 'blur' }
],
nation: requiredText('民族'),
isBoarding: requiredText('是否住宿', 'change'),
healthStatus: requiredText('健康状况'),
homeAddress: requiredText('家庭住址'),
middleSchool: requiredText('毕业中学'),
examNumber: requiredText('准考证号'),
examRegion: requiredText('中考地区'),
totalScoreFull: requiredNumber('中考满分', 100, 1000),
totalScore: requiredNumber('中考成绩', 0),
chineseScore: requiredNumber('语文成绩', 0),
mathScore: requiredNumber('数学成绩', 0),
englishScore: requiredNumber('英语成绩', 0),
scienceScore: requiredNumber('科学成绩', 0),
socialScore: requiredNumber('社会成绩', 0),
peScore: requiredNumber('体育成绩', 0),
collegeName: requiredText('所属学院', 'change'),
collegeId: requiredText('学院代码'),
appliedMajor: requiredText('报考专业', 'change'),
majorCode: requiredText('专业代码'),
uniformSize: requiredText('校服尺码', 'change')
};
@@ -0,0 +1,147 @@
export interface EnrollmentPrintField {
label: string;
value?: string | number | null;
/** 占满一行(如地址、特长等) */
fullWidth?: boolean;
}
export interface EnrollmentPrintSection {
title: string;
fields: EnrollmentPrintField[];
}
const formatValue = (value?: string | number | null) => {
if (value === undefined || value === null || value === '') {
return '-';
}
return String(value);
};
const escapeHtml = (text: string) =>
text
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
const buildSectionHtml = (section: EnrollmentPrintSection) => {
const fieldsHtml = section.fields
.map((field) => {
const cssClass = field.fullWidth ? 'field full' : 'field';
return `<div class="${cssClass}"><span class="label">${escapeHtml(field.label)}:</span><span class="value">${escapeHtml(formatValue(field.value))}</span></div>`;
})
.join('');
return `<section class="section"><h2>${escapeHtml(section.title)}</h2><div class="fields">${fieldsHtml}</div></section>`;
};
/** 打印招生报名信息 */
export const printEnrollmentInfo = (documentTitle: string, sections: EnrollmentPrintSection[], printTime?: string) => {
const printWindow = window.open('', '_blank');
if (!printWindow) {
throw new Error('无法打开打印窗口,请检查浏览器是否拦截弹窗');
}
const sectionsHtml = sections.map(buildSectionHtml).join('');
const timeText = printTime || new Date().toLocaleString('zh-CN');
const html = `<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<title>${escapeHtml(documentTitle)}</title>
<style>
* { box-sizing: border-box; }
body {
margin: 0;
padding: 24px 32px;
font-family: "Microsoft YaHei", "PingFang SC", Arial, sans-serif;
color: #303133;
font-size: 14px;
line-height: 1.6;
}
.print-header {
text-align: center;
margin-bottom: 24px;
padding-bottom: 12px;
border-bottom: 2px solid #409eff;
}
.print-title {
margin: 0 0 8px;
font-size: 22px;
font-weight: 700;
}
.print-meta {
margin: 0;
color: #909399;
font-size: 13px;
}
.section {
margin-bottom: 20px;
page-break-inside: avoid;
}
.section h2 {
margin: 0 0 12px;
padding-left: 10px;
font-size: 16px;
border-left: 4px solid #409eff;
}
.fields {
display: flex;
flex-wrap: wrap;
border: 1px solid #ebeef5;
border-radius: 4px;
overflow: hidden;
}
.field {
width: 33.33%;
display: flex;
padding: 10px 12px;
border-bottom: 1px solid #ebeef5;
border-right: 1px solid #ebeef5;
min-height: 42px;
}
.field:nth-child(3n) {
border-right: none;
}
.field.full {
width: 100%;
border-right: none;
}
.label {
flex: 0 0 auto;
color: #606266;
font-weight: 600;
white-space: nowrap;
}
.value {
flex: 1;
color: #303133;
word-break: break-all;
}
@media print {
body { padding: 12px 16px; }
@page { margin: 12mm; }
}
</style>
</head>
<body>
<div class="print-header">
<h1 class="print-title">${escapeHtml(documentTitle)}</h1>
<p class="print-meta">打印时间:${escapeHtml(timeText)}</p>
</div>
${sectionsHtml}
<script>
window.onload = function () {
window.print();
window.onafterprint = function () { window.close(); };
};
<\/script>
</body>
</html>`;
printWindow.document.open();
printWindow.document.write(html);
printWindow.document.close();
};
+140 -149
View File
@@ -189,12 +189,12 @@
<h3 class="form-section-title">基本信息</h3>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="姓名" prop="name">
<el-form-item label="姓名" prop="name" required>
<el-input v-model="form.name" placeholder="请输入姓名" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="身份证号" prop="idCard">
<el-form-item label="身份证号" prop="idCard" required>
<el-input
v-model="form.idCard"
placeholder="请输入身份证号"
@@ -205,7 +205,7 @@
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="性别" prop="gender">
<el-form-item label="性别" prop="gender" required>
<el-select
v-model="form.gender"
placeholder="请选择性别"
@@ -220,43 +220,43 @@
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="出生日期" prop="birthDate">
<el-form-item label="出生日期" prop="birthDate" required>
<el-date-picker v-model="form.birthDate" type="date" placeholder="选择出生日期" value-format="YYYY-MM-DD" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="联系电话" prop="phone1">
<el-form-item label="联系电话" prop="phone1" required>
<el-input v-model="form.phone1" placeholder="请输入联系电话" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="备用电话" prop="phone2">
<el-form-item label="备用电话" prop="phone2" required>
<el-input v-model="form.phone2" placeholder="请输入备用电话" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="民族" prop="nation">
<el-form-item label="民族" prop="nation" required>
<el-input v-model="form.nation" placeholder="请输入民族" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="是否住宿" prop="isBoarding">
<el-form-item label="是否住宿" prop="isBoarding" required>
<el-select v-model="form.isBoarding" placeholder="请选择是否住宿" style="width: 100%">
<el-option v-for="dict in sys_yes_no" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="健康状况" prop="healthStatus">
<el-form-item label="健康状况" prop="healthStatus" required>
<el-input v-model="form.healthStatus" placeholder="请输入健康状况" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="家庭住址" prop="homeAddress">
<el-form-item label="家庭住址" prop="homeAddress" required>
<el-input v-model="form.homeAddress" placeholder="请输入家庭住址" />
</el-form-item>
</el-col>
@@ -268,63 +268,63 @@
<h3 class="form-section-title">学业信息</h3>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="毕业中学" prop="middleSchool">
<el-form-item label="毕业中学" prop="middleSchool" required>
<el-input v-model="form.middleSchool" placeholder="请输入毕业中学" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="准考证号" prop="examNumber">
<el-form-item label="准考证号" prop="examNumber" required>
<el-input v-model="form.examNumber" placeholder="请输入准考证号" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="中考地区" prop="examRegion">
<el-form-item label="中考地区" prop="examRegion" required>
<el-input v-model="form.examRegion" placeholder="请输入中考地区" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="中考满分" prop="totalScoreFull">
<el-form-item label="中考满分" prop="totalScoreFull" required>
<el-input-number v-model="form.totalScoreFull" placeholder="请输入中考满分" :min="100" :max="1000" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="中考成绩" prop="totalScore">
<el-form-item label="中考成绩" prop="totalScore" required>
<el-input-number v-model="form.totalScore" placeholder="请输入中考成绩" :min="0" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="语文成绩" prop="chineseScore">
<el-form-item label="语文成绩" prop="chineseScore" required>
<el-input-number v-model="form.chineseScore" placeholder="请输入语文成绩" :min="0" style="width: 100%" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="数学成绩" prop="mathScore">
<el-form-item label="数学成绩" prop="mathScore" required>
<el-input-number v-model="form.mathScore" placeholder="请输入数学成绩" :min="0" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="英语成绩" prop="englishScore">
<el-form-item label="英语成绩" prop="englishScore" required>
<el-input-number v-model="form.englishScore" placeholder="请输入英语成绩" :min="0" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="科学成绩" prop="scienceScore">
<el-form-item label="科学成绩" prop="scienceScore" required>
<el-input-number v-model="form.scienceScore" placeholder="请输入科学成绩" :min="0" style="width: 100%" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="社会成绩" prop="socialScore">
<el-form-item label="社会成绩" prop="socialScore" required>
<el-input-number v-model="form.socialScore" placeholder="请输入社会成绩" :min="0" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="体育成绩" prop="peScore">
<el-form-item label="体育成绩" prop="peScore" required>
<el-input-number v-model="form.peScore" placeholder="请输入体育成绩" :min="0" style="width: 100%" />
</el-form-item>
</el-col>
@@ -336,7 +336,7 @@
<h3 class="form-section-title">报考信息</h3>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="所属学院" prop="collegeName">
<el-form-item label="所属学院" prop="collegeName" required>
<el-select
v-model="(form as EnrollmentInfoFormExtended).selectCollegeId"
placeholder="请选择所属学院"
@@ -348,30 +348,30 @@
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="学院代码" prop="collegeId">
<el-form-item label="学院代码" prop="collegeId" required>
<el-input v-model="form.collegeId" placeholder="学院代码将自动填入" readonly />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="报考专业" prop="appliedMajor">
<el-form-item label="报考专业" prop="appliedMajor" required>
<el-select v-model="form.appliedMajor" placeholder="请选择报考专业" style="width: 100%" @change="onMajorChange">
<el-option v-for="major in majorOptions" :key="major.value" :label="major.label" :value="major.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="专业代码" prop="majorCode">
<el-form-item label="专业代码" prop="majorCode" required>
<el-input v-model="form.majorCode" placeholder="专业代码将自动填入" readonly />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="校服尺码" prop="uniformSize">
<el-form-item label="校服尺码" prop="uniformSize" required>
<el-select v-model="form.uniformSize" placeholder="请选择校服尺码" style="width: 100%">
<el-option v-for="dict in zsjy_uniform_size" :key="dict.value" :label="dict.label" :value="dict.value" />
<el-option v-for="dict in uniformSizeOptions" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
</el-col>
@@ -605,6 +605,7 @@
<template #footer>
<div class="dialog-footer">
<el-button @click="cancelView">关 闭</el-button>
<el-button type="success" @click="printViewForm">打 印</el-button>
<el-button type="primary" @click="showRawData">查看原始数据</el-button>
</div>
</template>
@@ -824,7 +825,10 @@ import { createProcessFromEnrollment } from '@/api/zsjy/enrollmentProcess';
import { EnrollmentInfoVO, EnrollmentInfoQuery, EnrollmentInfoForm } from '@/api/zsjy/enrollment/types';
import { ComponentInternalInstance, getCurrentInstance, reactive, ref, toRefs, onMounted, watch, computed, nextTick } from 'vue';
import { FormInstance } from 'element-plus';
import { listDept } from '@/api/system/dept';
import { listOpenColleges, listOpenMajorsByCollegeId } from '@/api/zsjy/enrollmentMajor';
import { enrollmentFormRules } from './enrollmentFormRules';
import { getUniformSizeLabel as resolveUniformSizeLabel, resolveUniformSizeOptions } from './uniformSizeOptions';
import { printEnrollmentInfo } from './enrollmentPrint';
import { DeptVO } from '@/api/system/dept/types';
// 状态常量
@@ -1040,39 +1044,16 @@ const data = reactive<PageData<EnrollmentInfoForm, EnrollmentInfoQuery>>({
majorCode: undefined,
phone1: undefined,
examRegion: undefined,
enrollmentStatus: ENROLLMENT_STATUS.AUDITING, // 默认只查询"信息审核中"状态的学生
enrollmentStatus: undefined,
params: {}
},
rules: {
name: [{ required: true, message: '姓名不能为空', trigger: 'blur' }],
idCard: [
{ required: true, message: '身份证号不能为空', trigger: 'blur' },
{
pattern: /^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/,
message: '身份证号格式不正确',
trigger: 'blur'
}
],
gender: [{ required: true, message: '性别不能为空', trigger: 'change' }],
phone1: [
{ required: true, message: '联系电话不能为空', trigger: 'blur' },
{ pattern: /^1[3-9]\d{9}$/, message: '手机号码格式不正确', trigger: 'blur' }
],
phone2: [{ pattern: /^$|^1[3-9]\d{9}$/, message: '手机号码格式不正确', trigger: 'blur' }],
appliedMajor: [{ required: true, message: '报考专业不能为空', trigger: 'change' }],
totalScoreFull: [{ type: 'number', min: 100, max: 1000, message: '中考满分应在100-1000之间', trigger: 'blur' }],
totalScore: [{ type: 'number', min: 0, message: '中考成绩不能小于0', trigger: 'blur' }],
chineseScore: [{ type: 'number', min: 0, message: '语文成绩不能小于0', trigger: 'blur' }],
mathScore: [{ type: 'number', min: 0, message: '数学成绩不能小于0', trigger: 'blur' }],
englishScore: [{ type: 'number', min: 0, message: '英语成绩不能小于0', trigger: 'blur' }],
scienceScore: [{ type: 'number', min: 0, message: '科学成绩不能小于0', trigger: 'blur' }],
socialScore: [{ type: 'number', min: 0, message: '社会成绩不能小于0', trigger: 'blur' }],
peScore: [{ type: 'number', min: 0, message: '体育成绩不能小于0', trigger: 'blur' }]
}
rules: enrollmentFormRules
});
const { queryParams, form, rules } = toRefs(data);
const uniformSizeOptions = computed(() => resolveUniformSizeOptions(zsjy_uniform_size.value));
/** 身份证号变化处理 */
const onIdCardChange = () => {
const idCard = form.value.idCard;
@@ -1267,12 +1248,7 @@ const getCollegeNameEnhanced = (student: any) => {
};
/** 获取校服尺码显示名称 */
const getUniformSizeLabel = (value: string | undefined) => {
if (!value) return '-';
// 使用字典数据查找校服尺码
const size = zsjy_uniform_size.value?.find((item: any) => item.value === value);
return size ? size.label : value;
};
const getUniformSizeLabel = (value: string | undefined) => resolveUniformSizeLabel(value, zsjy_uniform_size.value);
/** 获取是否住宿显示文本 */
const getBoardingDisplay = (value: any) => {
@@ -1360,8 +1336,6 @@ const handleQuery = () => {
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
// 确保重置后状态过滤条件依然生效
queryParams.value.enrollmentStatus = ENROLLMENT_STATUS.AUDITING;
handleQuery();
};
@@ -1733,6 +1707,82 @@ const cancelView = () => {
viewDialog.visible = false;
};
/** 打印查看的报名信息 */
const printViewForm = () => {
const form = viewForm;
const display = (value: unknown) => (value === undefined || value === null || value === '' ? '-' : String(value));
const genderText =
form.gender === 'M' ? '男' : form.gender === 'F' ? '女' : getGenderDisplay(form.gender, form.idCard);
const statusText = form.enrollmentStatus
? proxy?.selectDictLabel(zsjy_enrollment_status.value, form.enrollmentStatus) || form.enrollmentStatus
: '-';
const applicationTimeText = form.applicationTime
? proxy?.parseTime(form.applicationTime, '{y}-{m}-{d} {h}:{i}:{s}')
: '-';
try {
printEnrollmentInfo(
`招生报名信息${form.name ? ' - ' + form.name : ''}`,
[
{
title: '基本信息',
fields: [
{ label: '姓名', value: display(form.name) },
{ label: '身份证号', value: display(form.idCard) },
{ label: '性别', value: genderText },
{ label: '出生日期', value: display(form.birthDate) },
{ label: '联系电话', value: display(form.phone1) },
{ label: '备用电话', value: display(form.phone2) },
{ label: '民族', value: display(form.nation) },
{ label: '是否住宿', value: getBoardingDisplay(form.isBoarding) },
{ label: '健康状况', value: display(form.healthStatus) },
{ label: '家庭住址', value: display(form.homeAddress), fullWidth: true }
]
},
{
title: '学业信息',
fields: [
{ label: '毕业中学', value: display(form.middleSchool) },
{ label: '准考证号', value: display(form.examNumber) },
{ label: '中考地区', value: display(form.examRegion) },
{ label: '中考满分', value: display(form.totalScoreFull) },
{ label: '中考成绩', value: display(form.totalScore) },
{ label: '语文成绩', value: display(form.chineseScore) },
{ label: '数学成绩', value: display(form.mathScore) },
{ label: '英语成绩', value: display(form.englishScore) },
{ label: '科学成绩', value: display(form.scienceScore) },
{ label: '社会成绩', value: display(form.socialScore) },
{ label: '体育成绩', value: display(form.peScore) }
]
},
{
title: '报考信息',
fields: [
{ label: '所属学院', value: display(form.collegeName) },
{ label: '学院代码', value: display(form.collegeId) },
{ label: '报考专业', value: display(form.appliedMajor) },
{ label: '专业代码', value: display(form.majorCode) },
{ label: '校服尺码', value: getUniformSizeLabel(form.uniformSize) },
{ label: '报名状态', value: statusText },
{ label: '报名时间', value: applicationTimeText }
]
},
{
title: '其他信息',
fields: [
{ label: '特长爱好', value: display(form.specialties), fullWidth: true },
{ label: '获奖情况', value: display(form.awards), fullWidth: true }
]
}
],
proxy?.parseTime(new Date(), '{y}-{m}-{d} {h}:{i}:{s}')
);
} catch (error) {
console.error('打印报名信息失败:', error);
proxy?.$modal.msgError('无法打开打印窗口,请检查浏览器是否拦截弹窗');
}
};
/** 显示原始数据 */
const showRawData = () => {
// 显示所有表单数据
@@ -1833,93 +1883,40 @@ const collegeOptions = ref<DeptVO[]>([]);
const majorOptionsByCollege = ref<{ [key: string]: DeptVO[] }>({});
// 添加获取部门数据的方法
/** 获取学院和专业数据 */
/** 获取学院和专业数据(仅开放报名的专业) */
const getCollegeAndMajorData = async () => {
try {
console.log('开始加载学院和专业数据...');
// 强制刷新字典数据
console.log('加载学院字典数据...');
try {
const dictResponse = await proxy?.getDicts('zsjy_college');
console.log('学院字典加载结果:', dictResponse);
} catch (dictError) {
console.error('加载字典数据出错:', dictError);
}
// 获取所有部门数据
console.log('加载部门数据...');
const res = await listDept();
console.log('开始加载开放报名的学院和专业数据...');
const res = await listOpenColleges();
if (res.code === 200 && res.data) {
// 找出学院 - 类别编码以xy开头的部门
const colleges = res.data.filter((dept: DeptVO) => {
return dept.deptCategory && dept.deptCategory.startsWith('xy');
});
const colleges = res.data.map((item: any) => ({
deptId: item.deptId,
deptName: item.deptName,
deptCategory: item.deptCategory,
parentId: item.parentId
})) as DeptVO[];
collegeOptions.value = colleges;
console.log(
'获取到的学院数据:',
colleges.map((c) => ({
deptId: c.deptId,
deptName: c.deptName,
deptCategory: c.deptCategory
}))
majorOptionsByCollege.value = {};
await Promise.all(
colleges.map(async (college) => {
const majorRes = await listOpenMajorsByCollegeId(college.deptId);
const majors = (majorRes.data || []).map((major: any) => ({
deptId: major.deptId,
deptName: major.deptName,
deptCategory: major.deptCategory,
parentId: college.deptId
})) as DeptVO[];
majorOptionsByCollege.value[college.deptId] = majors;
})
);
// 如果没有找到学院数据,尝试其他可能的过滤条件
if (colleges.length === 0) {
console.log('未找到xy开头的学院,尝试其他可能的部门...');
// 尝试查找所有非叶子节点部门
const possibleColleges = res.data.filter((dept: DeptVO) => {
return !dept.isLeaf;
});
if (possibleColleges.length > 0) {
collegeOptions.value = possibleColleges;
console.log(
'找到可能的学院:',
possibleColleges.map((c) => ({
deptId: c.deptId,
deptName: c.deptName,
deptCategory: c.deptCategory
}))
);
}
}
// 找出专业 - 类别编码以zy开头的部门
const allMajors = res.data.filter((dept: DeptVO) => {
return dept.deptCategory && dept.deptCategory.startsWith('zy');
});
console.log('获取到的所有专业数据:', allMajors.length);
// 按照上下级关系组织专业
colleges.forEach((college: DeptVO) => {
// 找出该学院下的所有专业
const majors = allMajors.filter(
(dept: DeptVO) =>
// 使用上下级关系或其他关联关系确定专业归属哪个学院
dept.parentId === college.deptId
);
majorOptionsByCollege.value[college.deptId] = majors;
if (majors.length > 0) {
console.log(`学院 ${college.deptName} 的专业数量: ${majors.length}`);
}
});
// 如果当前有学生信息且缺少学院名称,尝试找到匹配
if (viewForm.collegeId && !viewForm.collegeName) {
// 查找学院名称
const college = collegeOptions.value.find((c) => c.deptCategory === viewForm.collegeId);
if (college) {
console.log(`根据学院代码 ${viewForm.collegeId} 找到学院名称: ${college.deptName}`);
viewForm.collegeName = college.deptName;
} else {
console.warn(`未找到匹配学院代码 ${viewForm.collegeId} 的学院名称`);
}
}
@@ -1927,20 +1924,14 @@ const getCollegeAndMajorData = async () => {
success: true,
collegeCount: collegeOptions.value.length
};
} else {
console.error('部门数据返回异常:', res);
return {
success: false,
error: '部门数据返回异常'
};
}
console.error('开放学院数据返回异常:', res);
return { success: false, error: '开放学院数据返回异常' };
} catch (error) {
console.error('获取部门数据出错:', error);
console.error('获取开放学院和专业数据出错:', error);
proxy?.$modal.msgError('获取学院和专业数据失败');
return {
success: false,
error: error
};
return { success: false, error };
}
};
@@ -0,0 +1,28 @@
/** 招生报名校服尺码标准选项(与移动端兜底数据一致) */
export const DEFAULT_UNIFORM_SIZE_OPTIONS: DictDataOption[] = [
{ value: '150/84A', label: '150/84A(身高150cm)' },
{ value: '155/86A', label: '155/86A(身高155cm)' },
{ value: '160/88A', label: '160/88A(身高160cm)' },
{ value: '165/90A', label: '165/90A(身高165cm)' },
{ value: '170/92A', label: '170/92A(身高170cm)' },
{ value: '175/94A', label: '175/94A(身高175cm)' },
{ value: '180/96A', label: '180/96A(身高180cm)' },
{ value: '185/98A', label: '185/98A(身高185cm)' },
{ value: '190/100A', label: '190/100A(身高190cm)' }
];
/** 优先使用字典数据,字典为空时使用标准尺码兜底 */
export const resolveUniformSizeOptions = (dictOptions?: DictDataOption[] | null): DictDataOption[] => {
if (dictOptions && dictOptions.length > 0) {
return dictOptions;
}
return DEFAULT_UNIFORM_SIZE_OPTIONS;
};
/** 获取校服尺码显示名称 */
export const getUniformSizeLabel = (value: string | undefined, dictOptions?: DictDataOption[] | null): string => {
if (!value) return '-';
const options = resolveUniformSizeOptions(dictOptions);
const size = options.find((item) => item.value === value);
return size ? size.label : value;
};
@@ -0,0 +1,227 @@
<template>
<div class="p-2 page-container">
<div class="flex h-full">
<div class="w-72 mr-4">
<el-card shadow="never" class="h-full">
<template #header>
<div class="flex items-center justify-between">
<span class="text-base font-medium">二级学院</span>
<el-button type="primary" link icon="Plus" @click="goAddMajor">新增专业</el-button>
</div>
</template>
<el-tree
v-loading="collegeLoading"
:data="collegeTreeData"
:props="treeProps"
node-key="deptId"
highlight-current
:current-node-key="activeCollegeId"
@node-click="handleCollegeClick"
>
<template #default="{ node, data }">
<span class="text-sm">{{ node.label }}</span>
<span v-if="data.deptCategory" class="text-xs text-gray-400 ml-2">({{ data.deptCategory }})</span>
</template>
</el-tree>
</el-card>
</div>
<div class="flex-1">
<el-card shadow="never">
<template #header>
<div class="flex items-center justify-between">
<span class="text-base font-medium">{{ activeCollegeName || '招生报名专业管理' }}</span>
<el-button type="primary" :loading="saving" @click="handleSave" v-hasPermi="['zsjy:enrollmentMajor:save']">
保存本学院配置
</el-button>
</div>
</template>
<div v-loading="loading" class="major-config-panel">
<el-empty v-if="!activeCollegeId" description="请在左侧选择学院" />
<template v-else>
<div class="toolbar">
<el-checkbox v-model="checkAll" :indeterminate="isIndeterminate" @change="handleCheckAllChange">全选</el-checkbox>
<span class="selected-count">已选 {{ checkedMajorIds.length }} / {{ majorList.length }}</span>
</div>
<el-empty v-if="majorList.length === 0" description="该学院暂无专业,请前往专业信息维护" />
<el-checkbox-group v-else v-model="checkedMajorIds" class="major-checkbox-group" @change="handleCheckedChange">
<el-checkbox v-for="item in majorList" :key="item.majorDeptId" :label="String(item.majorDeptId)" class="major-checkbox-item">
{{ item.majorName }}
<span class="major-code">({{ item.majorCode }})</span>
</el-checkbox>
</el-checkbox-group>
</template>
</div>
</el-card>
</div>
</div>
</div>
</template>
<script setup lang="ts" name="EnrollmentMajor">
import { ref, computed, onMounted, getCurrentInstance } from 'vue';
import router from '@/router';
import {
listEnrollmentMajorColleges,
listEnrollmentMajorConfig,
saveEnrollmentMajorConfig,
EnrollmentDeptVO
} from '@/api/zsjy/enrollmentMajor';
const { proxy } = getCurrentInstance() as any;
const collegeList = ref<EnrollmentDeptVO[]>([]);
const majorList = ref<EnrollmentDeptVO[]>([]);
const activeCollegeId = ref('');
const checkedMajorIds = ref<string[]>([]);
const collegeLoading = ref(false);
const loading = ref(false);
const saving = ref(false);
const checkAll = ref(false);
const isIndeterminate = ref(false);
const treeProps = {
label: 'deptName',
children: 'children'
};
const collegeTreeData = computed(() => collegeList.value);
const activeCollegeName = computed(() => {
const college = collegeList.value.find((item) => String(item.deptId) === activeCollegeId.value);
return college?.deptName || '';
});
const syncCheckAllState = () => {
const total = majorList.value.length;
const checked = checkedMajorIds.value.length;
checkAll.value = total > 0 && checked === total;
isIndeterminate.value = checked > 0 && checked < total;
};
const loadColleges = async () => {
collegeLoading.value = true;
try {
const res = await listEnrollmentMajorColleges();
collegeList.value = res.data || [];
if (collegeList.value.length === 0) {
proxy?.$modal.msgWarning('未加载到学院数据,请确认后端已更新并分配菜单权限');
return;
}
activeCollegeId.value = String(collegeList.value[0].deptId);
await loadMajorConfig(activeCollegeId.value);
} catch (error) {
console.error('加载学院失败', error);
proxy?.$modal.msgError('加载学院数据失败,请确认后端服务已重启');
} finally {
collegeLoading.value = false;
}
};
const loadMajorConfig = async (collegeDeptId: string) => {
if (!collegeDeptId) return;
loading.value = true;
try {
const res = await listEnrollmentMajorConfig(collegeDeptId);
majorList.value = res.data || [];
checkedMajorIds.value = majorList.value
.filter((item) => item.selected === true || item.selected === 1)
.map((item) => String(item.majorDeptId));
syncCheckAllState();
} catch (error) {
console.error('加载专业配置失败', error);
proxy?.$modal.msgError('加载专业配置失败');
majorList.value = [];
checkedMajorIds.value = [];
} finally {
loading.value = false;
}
};
const handleCollegeClick = (data: EnrollmentDeptVO) => {
activeCollegeId.value = String(data.deptId);
loadMajorConfig(activeCollegeId.value);
};
const handleCheckAllChange = (val: boolean) => {
checkedMajorIds.value = val ? majorList.value.map((item) => String(item.majorDeptId)) : [];
isIndeterminate.value = false;
};
const handleCheckedChange = () => {
syncCheckAllState();
};
const handleSave = async () => {
if (!activeCollegeId.value) return;
saving.value = true;
try {
await saveEnrollmentMajorConfig({
collegeDeptId: activeCollegeId.value,
majorDeptIds: checkedMajorIds.value
});
proxy?.$modal.msgSuccess('保存成功');
await loadMajorConfig(activeCollegeId.value);
} catch (error) {
console.error('保存失败', error);
} finally {
saving.value = false;
}
};
const resolveMajorManagePath = () => {
const matched = router.getRoutes().find((route) => {
const component = route.meta?.component as string | undefined;
return (component && component.includes('majormsg/index')) || route.path.endsWith('/majormsg');
});
return matched?.path || '/student/classmsg/majormsg';
};
const goAddMajor = () => {
const path = resolveMajorManagePath();
router.push(path).catch(() => {
proxy?.$modal.msgError('未找到专业信息页面,请联系管理员配置菜单');
});
};
onMounted(() => {
loadColleges();
});
</script>
<style scoped>
.major-config-panel {
min-height: 360px;
}
.toolbar {
display: flex;
align-items: center;
gap: 16px;
margin-bottom: 16px;
}
.selected-count {
color: #909399;
font-size: 13px;
}
.major-checkbox-group {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 8px 16px;
}
.major-checkbox-item {
margin-right: 0;
}
.major-code {
color: #909399;
font-size: 12px;
margin-left: 4px;
}
</style>
@@ -170,7 +170,6 @@
import { ref, reactive, onMounted, getCurrentInstance } from 'vue';
import {
listEnrollmentProcess,
exportEnrollmentProcess,
EnrollmentProcessQuery,
EnrollmentProcessVO,
getEnrollmentProcess
@@ -498,9 +497,14 @@ const resetQuery = () => {
handleQuery();
};
// 处理导出
// 处理导出(新生录取名册格式)
const handleExport = () => {
exportEnrollmentProcess(queryParams);
const year = new Date().getFullYear();
proxy?.download(
'/zsjy/enrollment/process/exportRegister',
{ ...queryParams },
`${year}年新生录取名册_${new Date().getTime()}.xlsx`
);
};
// 查看详情