Merge branch 'master' of http://47.99.164.66:3000/hzdyjs-xg/all-project
No files matched your search
@@ -155,8 +155,8 @@ public class TeacherAssessmentController extends BaseController {
|
||||
*/
|
||||
@SaCheckPermission("bzrkh:teacherAssessment:detail")
|
||||
@GetMapping("/classStudentRecords")
|
||||
public R<ClassStudentRecordsVo> getClassStudentRecords(@RequestParam Long classId) {
|
||||
ClassStudentRecordsVo records = teacherAssessmentService.queryClassStudentRecords(classId);
|
||||
public R<ClassStudentRecordsVo> getClassStudentRecords(@RequestParam Long classId, @RequestParam Long planId) {
|
||||
ClassStudentRecordsVo records = teacherAssessmentService.queryClassStudentRecords(classId, planId);
|
||||
return R.ok(records);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -137,18 +138,30 @@ public interface TeacherAssessmentMapper extends BaseMapperPlus<Object, Object>
|
||||
String selectClassNameById(@Param("classId") Long classId);
|
||||
|
||||
/**
|
||||
* 根据班级ID查询学生违纪记录
|
||||
* 根据班级ID和时间范围查询学生违纪记录
|
||||
*
|
||||
* @param classId 班级ID
|
||||
* @param classId 班级ID
|
||||
* @param startTime 数据提取开始时间
|
||||
* @param endTime 数据提取结束时间
|
||||
* @return 学生违纪记录列表
|
||||
*/
|
||||
List<StudentDisciplineRecordVo> selectStudentDisciplineRecordsByClassId(@Param("classId") Long classId);
|
||||
List<StudentDisciplineRecordVo> selectStudentDisciplineRecordsByClassIdAndTimeRange(@Param("classId") Long classId, @Param("startTime") Date startTime, @Param("endTime") Date endTime);
|
||||
|
||||
/**
|
||||
* 根据班级ID查询学生奖励记录
|
||||
* 根据班级ID和时间范围查询学生奖励记录
|
||||
*
|
||||
* @param classId 班级ID
|
||||
* @param classId 班级ID
|
||||
* @param startTime 数据提取开始时间
|
||||
* @param endTime 数据提取结束时间
|
||||
* @return 学生奖励记录列表
|
||||
*/
|
||||
List<StudentRewardRecordVo> selectStudentRewardRecordsByClassId(@Param("classId") Long classId);
|
||||
List<StudentRewardRecordVo> selectStudentRewardRecordsByClassIdAndTimeRange(@Param("classId") Long classId, @Param("startTime") Date startTime, @Param("endTime") Date endTime);
|
||||
|
||||
/**
|
||||
* 根据计划ID查询数据提取时间范围
|
||||
*
|
||||
* @param planId 计划ID
|
||||
* @return 包含数据提取开始和结束时间的Map,key为startTime和endTime
|
||||
*/
|
||||
BmzrAssessmentPlanVo selectDataExtractTimeByPlanId(@Param("planId") Long planId);
|
||||
}
|
||||
@@ -99,7 +99,8 @@ public interface ITeacherAssessmentService {
|
||||
* 查询班级学生违纪记录和奖励记录
|
||||
*
|
||||
* @param classId 班级ID
|
||||
* @param planId 考核计划ID
|
||||
* @return 班级学生记录
|
||||
*/
|
||||
ClassStudentRecordsVo queryClassStudentRecords(Long classId);
|
||||
ClassStudentRecordsVo queryClassStudentRecords(Long classId, Long planId);
|
||||
}
|
||||
@@ -73,7 +73,7 @@ public class BmzrAssessmentIndicatorServiceImpl implements IBmzrAssessmentIndica
|
||||
private LambdaQueryWrapper<BmzrAssessmentIndicator> buildQueryWrapper(BmzrAssessmentIndicatorBo bo) {
|
||||
LambdaQueryWrapper<BmzrAssessmentIndicator> lqw = Wrappers.lambdaQuery();
|
||||
lqw.like(StringUtils.isNotBlank(bo.getIndicatorName()), BmzrAssessmentIndicator::getIndicatorName, bo.getIndicatorName());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getIndicatorCode()), BmzrAssessmentIndicator::getIndicatorCode, bo.getIndicatorCode());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getIndicatorCode()), BmzrAssessmentIndicator::getIndicatorCode, bo.getIndicatorCode());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getIndicatorType()), BmzrAssessmentIndicator::getIndicatorType, bo.getIndicatorType());
|
||||
lqw.eq(bo.getParentId() != null, BmzrAssessmentIndicator::getParentId, bo.getParentId());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getAllowSelfEvaluation()), BmzrAssessmentIndicator::getAllowSelfEvaluation, bo.getAllowSelfEvaluation());
|
||||
|
||||
@@ -97,10 +97,10 @@ public class BmzrAssessmentIndicatorTypeServiceImpl implements IBmzrAssessmentIn
|
||||
log.info("添加指标类型名称模糊查询条件: {}", bo.getIndicatorTypeName());
|
||||
}
|
||||
|
||||
// 指标类型编码精确查询
|
||||
// 指标类型编码模糊查询
|
||||
if (StringUtils.isNotBlank(bo.getIndicatorTypeCode())) {
|
||||
lqw.eq(BmzrAssessmentIndicatorType::getIndicatorTypeCode, bo.getIndicatorTypeCode());
|
||||
log.info("添加指标类型编码精确查询条件: {}", bo.getIndicatorTypeCode());
|
||||
lqw.like(BmzrAssessmentIndicatorType::getIndicatorTypeCode, bo.getIndicatorTypeCode());
|
||||
log.info("添加指标类型编码模糊查询条件: {}", bo.getIndicatorTypeCode());
|
||||
}
|
||||
|
||||
// 状态查询
|
||||
@@ -121,7 +121,7 @@ public class BmzrAssessmentIndicatorTypeServiceImpl implements IBmzrAssessmentIn
|
||||
// 禁用逻辑删除过滤器,查询包含已删除的记录
|
||||
lqw.in(BmzrAssessmentIndicatorType::getDeleted, 0, 1);
|
||||
lqw.like(StringUtils.isNotBlank(bo.getIndicatorTypeName()), BmzrAssessmentIndicatorType::getIndicatorTypeName, bo.getIndicatorTypeName());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getIndicatorTypeCode()), BmzrAssessmentIndicatorType::getIndicatorTypeCode, bo.getIndicatorTypeCode());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getIndicatorTypeCode()), BmzrAssessmentIndicatorType::getIndicatorTypeCode, bo.getIndicatorTypeCode());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getStatus()), BmzrAssessmentIndicatorType::getStatus, bo.getStatus());
|
||||
lqw.orderByAsc(BmzrAssessmentIndicatorType::getCreateTime);
|
||||
return lqw;
|
||||
|
||||
@@ -75,8 +75,8 @@ public class BmzrAssessmentSchemeServiceImpl implements IBmzrAssessmentSchemeSer
|
||||
Map<String, Object> params = bo.getParams();
|
||||
LambdaQueryWrapper<BmzrAssessmentScheme> lqw = Wrappers.lambdaQuery();
|
||||
lqw.like(StringUtils.isNotBlank(bo.getSchemeName()), BmzrAssessmentScheme::getSchemeName, bo.getSchemeName());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getSchemeCode()), BmzrAssessmentScheme::getSchemeCode, bo.getSchemeCode());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getAcademicYear()), BmzrAssessmentScheme::getAcademicYear, bo.getAcademicYear());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getSchemeCode()), BmzrAssessmentScheme::getSchemeCode, bo.getSchemeCode());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getAcademicYear()), BmzrAssessmentScheme::getAcademicYear, bo.getAcademicYear());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getSemester()), BmzrAssessmentScheme::getSemester, bo.getSemester());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getSelfEvaluationRequired()), BmzrAssessmentScheme::getSelfEvaluationRequired, bo.getSelfEvaluationRequired());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getSuperiorEvaluationRequired()), BmzrAssessmentScheme::getSuperiorEvaluationRequired, bo.getSuperiorEvaluationRequired());
|
||||
|
||||
@@ -665,18 +665,38 @@ public class TeacherAssessmentServiceImpl implements ITeacherAssessmentService {
|
||||
* 查询班级学生违纪记录和奖励记录
|
||||
*
|
||||
* @param classId 班级ID
|
||||
* @param planId 考核计划ID
|
||||
* @return 班级学生记录
|
||||
*/
|
||||
@Override
|
||||
public ClassStudentRecordsVo queryClassStudentRecords(Long classId) {
|
||||
public ClassStudentRecordsVo queryClassStudentRecords(Long classId, Long planId) {
|
||||
// 查询班级信息
|
||||
String className = teacherAssessmentMapper.selectClassNameById(classId);
|
||||
|
||||
// 查询班级学生违纪记录
|
||||
List<StudentDisciplineRecordVo> disciplineRecords = teacherAssessmentMapper.selectStudentDisciplineRecordsByClassId(classId);
|
||||
// 查询考核计划的数据提取时间范围
|
||||
BmzrAssessmentPlanVo plan = teacherAssessmentMapper.selectDataExtractTimeByPlanId(planId);
|
||||
if (plan == null) {
|
||||
throw new RuntimeException("考核计划不存在,计划ID:" + planId);
|
||||
}
|
||||
|
||||
// 查询班级学生奖励记录
|
||||
List<StudentRewardRecordVo> rewardRecords = teacherAssessmentMapper.selectStudentRewardRecordsByClassId(classId);
|
||||
Date startTime = plan.getDataExtractStartTime();
|
||||
Date endTime = plan.getDataExtractEndTime();
|
||||
|
||||
List<StudentDisciplineRecordVo> disciplineRecords = null;
|
||||
List<StudentRewardRecordVo> rewardRecords = null;
|
||||
|
||||
// 如果设置了数据提取时间范围,按时间范围查询;否则查询所有记录
|
||||
if (startTime != null && endTime != null) {
|
||||
// 查询指定时间范围内的班级学生违纪记录
|
||||
disciplineRecords = teacherAssessmentMapper.selectStudentDisciplineRecordsByClassIdAndTimeRange(classId, startTime, endTime);
|
||||
|
||||
// 查询指定时间范围内的班级学生奖励记录
|
||||
rewardRecords = teacherAssessmentMapper.selectStudentRewardRecordsByClassIdAndTimeRange(classId, startTime, endTime);
|
||||
} else {
|
||||
// 如果没有设置时间范围,查询所有记录(兼容旧数据)
|
||||
disciplineRecords = teacherAssessmentMapper.selectStudentDisciplineRecordsByClassIdAndTimeRange(classId, null, null);
|
||||
rewardRecords = teacherAssessmentMapper.selectStudentRewardRecordsByClassIdAndTimeRange(classId, null, null);
|
||||
}
|
||||
|
||||
// 构建返回对象
|
||||
ClassStudentRecordsVo result = new ClassStudentRecordsVo();
|
||||
|
||||
@@ -376,13 +376,13 @@
|
||||
<!-- 根据班级ID查询班级名称 -->
|
||||
<select id="selectClassNameById" resultType="java.lang.String">
|
||||
SELECT class_name
|
||||
FROM sys_class
|
||||
FROM xg_class_msg
|
||||
WHERE id = #{classId}
|
||||
AND deleted = 0
|
||||
</select>
|
||||
|
||||
<!-- 根据班级ID查询学生违纪记录 -->
|
||||
<select id="selectStudentDisciplineRecordsByClassId" resultType="org.dromara.bzrkh.domain.vo.StudentDisciplineRecordVo">
|
||||
<!-- 根据班级ID和时间范围查询学生违纪记录 -->
|
||||
<select id="selectStudentDisciplineRecordsByClassIdAndTimeRange" resultType="org.dromara.bzrkh.domain.vo.StudentDisciplineRecordVo">
|
||||
SELECT
|
||||
id,
|
||||
student_id,
|
||||
@@ -410,11 +410,15 @@
|
||||
WHERE class_id = #{classId}
|
||||
AND deleted = 0
|
||||
AND status = '1'
|
||||
<if test="startTime != null and endTime != null">
|
||||
AND discipline_date >= #{startTime}
|
||||
AND discipline_date <= #{endTime}
|
||||
</if>
|
||||
ORDER BY discipline_date DESC
|
||||
</select>
|
||||
|
||||
<!-- 根据班级ID查询学生奖励记录 -->
|
||||
<select id="selectStudentRewardRecordsByClassId" resultType="org.dromara.bzrkh.domain.vo.StudentRewardRecordVo">
|
||||
<!-- 根据班级ID和时间范围查询学生奖励记录 -->
|
||||
<select id="selectStudentRewardRecordsByClassIdAndTimeRange" resultType="org.dromara.bzrkh.domain.vo.StudentRewardRecordVo">
|
||||
SELECT
|
||||
id,
|
||||
student_id,
|
||||
@@ -441,7 +445,22 @@
|
||||
WHERE class_id = #{classId}
|
||||
AND deleted = 0
|
||||
AND status = '1'
|
||||
<if test="startTime != null and endTime != null">
|
||||
AND reward_date >= #{startTime}
|
||||
AND reward_date <= #{endTime}
|
||||
</if>
|
||||
ORDER BY reward_date DESC
|
||||
</select>
|
||||
|
||||
<!-- 根据计划ID查询数据提取时间范围 -->
|
||||
<select id="selectDataExtractTimeByPlanId" resultType="org.dromara.bzrkh.domain.vo.BmzrAssessmentPlanVo">
|
||||
SELECT
|
||||
id,
|
||||
data_extract_start_time,
|
||||
data_extract_end_time
|
||||
FROM bmzr_assessment_plan
|
||||
WHERE id = #{planId}
|
||||
AND deleted = 0
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -27,6 +27,7 @@ import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.dromara.system.domain.vo.SysUserVo;
|
||||
import org.dromara.system.service.ISysUserService;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
|
||||
/**
|
||||
* 培训申请控制器
|
||||
@@ -48,7 +49,7 @@ public class RsTrainingApplicationController extends BaseController {
|
||||
private ISysUserService sysUserService;
|
||||
|
||||
/**
|
||||
* 查询培训申请列表
|
||||
* 查询培训申请列表(供管理员和审批人使用)
|
||||
*/
|
||||
@SaCheckPermission("rs:trainingapplication:list")
|
||||
@GetMapping("/list")
|
||||
@@ -249,9 +250,31 @@ public class RsTrainingApplicationController extends BaseController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询我的培训申请列表
|
||||
* 查询待审批的培训申请列表(供审批人使用)
|
||||
*/
|
||||
@SaCheckPermission("rs:trainingapplication:list")
|
||||
@SaCheckPermission("rs:trainingapproval:process")
|
||||
@GetMapping("/pending/list")
|
||||
public TableDataInfo<RsTrainingApplication> pendingList(RsTrainingApplication application, PageQuery pageQuery) {
|
||||
// 设置租户ID
|
||||
if (application.getTenantId() == null || application.getTenantId().isEmpty()) {
|
||||
application.setTenantId(LoginHelper.getTenantId());
|
||||
}
|
||||
|
||||
// 只查询待审核状态的申请(排除已通过状态)
|
||||
application.setExcludeStatus("1"); // 排除已通过状态
|
||||
|
||||
log.info("查询待审批培训申请列表 - 用户: {}, 角色: {}, 查询条件: {}",
|
||||
LoginHelper.getUsername(),
|
||||
StpUtil.getRoleList(),
|
||||
application);
|
||||
|
||||
return trainingApplicationService.selectPageRsTrainingApplicationList(application, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询我的培训申请列表(供申请人使用)
|
||||
*/
|
||||
@SaCheckPermission("rs:trainingapplication:query")
|
||||
@GetMapping("/my")
|
||||
public TableDataInfo<RsTrainingApplication> listMyApplications(RsTrainingApplication application, PageQuery pageQuery) {
|
||||
return trainingApplicationService.selectPageMyApplications(StpUtil.getLoginIdAsLong(), application, pageQuery);
|
||||
|
||||
@@ -40,7 +40,17 @@ public class RsTrainingApprovalController extends BaseController {
|
||||
*/
|
||||
@SaCheckPermission("rs:trainingapproval:list")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<RsTrainingApproval> list(RsTrainingApproval approval, PageQuery pageQuery) {
|
||||
public TableDataInfo<RsTrainingApproval> list(RsTrainingApproval approval, PageQuery pageQuery,
|
||||
@RequestParam(value = "courseName", required = false) String courseName,
|
||||
@RequestParam(value = "userName", required = false) String userName) {
|
||||
// 参数转换:将前端传递的参数名转换为后端Mapper期望的参数名
|
||||
if (courseName != null && !courseName.trim().isEmpty()) {
|
||||
approval.setCourseTitle(courseName.trim());
|
||||
}
|
||||
if (userName != null && !userName.trim().isEmpty()) {
|
||||
approval.setApplicationUserName(userName.trim());
|
||||
}
|
||||
|
||||
// 使用带详细信息的查询方法,返回申请人姓名和课程名称
|
||||
return trainingApprovalService.selectPageRsTrainingApprovalWithDetailsList(approval, pageQuery);
|
||||
}
|
||||
|
||||
@@ -26,8 +26,8 @@ import java.util.Map;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import org.dromara.rs.service.impl.RsTrainingRecordServiceImpl;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 培训记录控制器
|
||||
@@ -35,6 +35,7 @@ import org.dromara.rs.service.impl.RsTrainingRecordServiceImpl;
|
||||
* @author weizzzz
|
||||
* @date 2025-07-16
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/rs/training/record")
|
||||
public class RsTrainingRecordController extends BaseController {
|
||||
@@ -51,6 +52,34 @@ public class RsTrainingRecordController extends BaseController {
|
||||
return trainingRecordService.selectPageRsTrainingRecordList(record, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试培训地点搜索功能
|
||||
*/
|
||||
@SaCheckPermission("rs:trainingrecord:list")
|
||||
@GetMapping("/test-location")
|
||||
public R<List<RsTrainingRecord>> testLocationSearch(@RequestParam String location) {
|
||||
log.info("测试培训地点搜索 - location参数: {}", location);
|
||||
|
||||
// 创建查询对象
|
||||
RsTrainingRecord record = new RsTrainingRecord();
|
||||
record.setLocation(location);
|
||||
|
||||
log.info("创建的查询对象 - location: {}", record.getLocation());
|
||||
|
||||
// 直接调用Mapper方法
|
||||
List<RsTrainingRecord> list = trainingRecordService.selectRsTrainingRecordList(record);
|
||||
|
||||
log.info("查询结果数量: {}", list.size());
|
||||
if (!list.isEmpty()) {
|
||||
log.info("前3条记录的location值:");
|
||||
list.stream().limit(3).forEach(r ->
|
||||
log.info(" - recordId: {}, location: {}", r.getRecordId(), r.getLocation())
|
||||
);
|
||||
}
|
||||
|
||||
return R.ok(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出培训记录列表
|
||||
*/
|
||||
|
||||
@@ -86,7 +86,7 @@ public class RsJob extends BaseEntity {
|
||||
* 浏览次数
|
||||
*/
|
||||
@ExcelProperty(value = "浏览次数")
|
||||
@TableField(value = "views")
|
||||
@TableField(value = "view_count")
|
||||
private Integer viewCount;
|
||||
|
||||
/**
|
||||
|
||||
@@ -118,4 +118,10 @@ public class RsTrainingApplication extends BaseEntity {
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date rejectTime;
|
||||
|
||||
/**
|
||||
* 排除状态(查询时排除指定状态,不存储到数据库)
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private String excludeStatus;
|
||||
}
|
||||
@@ -84,6 +84,9 @@ public class RsTrainingApplicationServiceImpl implements IRsTrainingApplicationS
|
||||
} else if (StpUtil.hasRole("rs-admin")) {
|
||||
// 人事管理员可以看到所有申请(包括审批和部门管理功能)
|
||||
// 不设置额外过滤条件,让管理员能看到所有申请
|
||||
} else if (StpUtil.hasRole("rs-Approver")) {
|
||||
// 审批人角色:可以看到所有申请(用于审批)
|
||||
// 不设置过滤条件,允许查看所有申请
|
||||
} else {
|
||||
// 默认情况:只能看到自己创建的申请
|
||||
application.setCreateBy(currentUserId);
|
||||
@@ -196,6 +199,10 @@ public class RsTrainingApplicationServiceImpl implements IRsTrainingApplicationS
|
||||
lqw.eq(application.getStatus() != null, RsTrainingApplication::getStatus, application.getStatus());
|
||||
lqw.like(application.getLocation() != null, RsTrainingApplication::getLocation, application.getLocation());
|
||||
lqw.eq(application.getDuration() != null, RsTrainingApplication::getDuration, application.getDuration());
|
||||
// 课程名称搜索:搜索自定义课程名称字段
|
||||
lqw.like(application.getCourseName() != null, RsTrainingApplication::getCourseName, application.getCourseName());
|
||||
// 排除指定状态的条件(用于待审批接口)
|
||||
lqw.ne(application.getExcludeStatus() != null, RsTrainingApplication::getStatus, application.getExcludeStatus());
|
||||
return lqw;
|
||||
}
|
||||
|
||||
|
||||
@@ -78,9 +78,9 @@ public class RsTrainingApprovalServiceImpl implements IRsTrainingApprovalService
|
||||
// 申请人只能看到与自己相关的审批记录
|
||||
// 这里需要通过申请表关联查询
|
||||
lqw.exists("SELECT 1 FROM rs_training_application app WHERE app.application_id = rs_training_approval.application_id AND app.user_id = " + currentUserId);
|
||||
} else if (StpUtil.hasRole("rs-admin")) {
|
||||
// 人事管理员可以看到所有审批记录
|
||||
// 不设置额外过滤条件,让管理员能看到所有审批
|
||||
} else if (StpUtil.hasRole("rs-admin") || StpUtil.hasRole("rs-Approver")) {
|
||||
// 人事管理员和审批人可以看到所有审批记录
|
||||
// 不设置额外过滤条件,让管理员和审批人能看到所有审批
|
||||
} else {
|
||||
// 默认情况:只能看到自己创建或处理的审批
|
||||
lqw.and(wrapper -> wrapper.eq(RsTrainingApproval::getCreateBy, currentUserId)
|
||||
@@ -126,7 +126,15 @@ public class RsTrainingApprovalServiceImpl implements IRsTrainingApprovalService
|
||||
@Override
|
||||
public TableDataInfo<RsTrainingApproval> selectPagePendingApprovals(Long approverId, RsTrainingApproval approval, PageQuery pageQuery) {
|
||||
LambdaQueryWrapper<RsTrainingApproval> lqw = buildQueryWrapper(approval);
|
||||
lqw.eq(RsTrainingApproval::getApproverId, approverId);
|
||||
|
||||
// 简化权限模型:审批人和管理员可以看到所有待审核记录
|
||||
if (StpUtil.hasRole("rs-admin") || StpUtil.hasRole("rs-Approver")) {
|
||||
// 管理员和审批人可以看到所有待审核记录,不限制approverId
|
||||
} else {
|
||||
// 其他角色只能看到指派给自己的待审核记录
|
||||
lqw.eq(RsTrainingApproval::getApproverId, approverId);
|
||||
}
|
||||
|
||||
lqw.eq(RsTrainingApproval::getStatus, "0"); // 待审核状态
|
||||
com.baomidou.mybatisplus.extension.plugins.pagination.Page<RsTrainingApproval> page = trainingApprovalMapper.selectPage(pageQuery.build(), lqw);
|
||||
return TableDataInfo.build(page);
|
||||
|
||||
@@ -21,6 +21,8 @@ import org.slf4j.LoggerFactory;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.dromara.common.satoken.utils.LoginHelper;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
|
||||
/**
|
||||
* 培训记录服务实现类
|
||||
@@ -61,9 +63,49 @@ public class RsTrainingRecordServiceImpl implements IRsTrainingRecordService, Ap
|
||||
*/
|
||||
@Override
|
||||
public TableDataInfo<RsTrainingRecord> selectPageRsTrainingRecordList(RsTrainingRecord record, PageQuery pageQuery) {
|
||||
// ===== 调试日志:检查location参数 =====
|
||||
log.info("培训记录查询参数 - location: {}", record.getLocation());
|
||||
log.info("培训记录查询参数 - courseName: {}", record.getCourseName());
|
||||
log.info("培训记录查询参数 - department: {}", record.getDepartment());
|
||||
|
||||
// ===== 业务逻辑层权限过滤 =====
|
||||
// 根据用户角色进行数据权限过滤
|
||||
if (!LoginHelper.isSuperAdmin()) {
|
||||
// 获取当前登录用户信息
|
||||
Long currentUserId = LoginHelper.getUserId();
|
||||
|
||||
// 检查用户角色并应用相应的数据过滤
|
||||
if (StpUtil.hasRole("rs-Applicant")) {
|
||||
// 申请人只能看到自己的培训记录
|
||||
record.setUserId(currentUserId);
|
||||
} else if (StpUtil.hasRole("rs-admin")) {
|
||||
// 人事管理员可以看到所有培训记录
|
||||
// 不设置额外过滤条件,让管理员能看到所有记录
|
||||
} else if (StpUtil.hasRole("rs-Approver")) {
|
||||
// 审批人角色:可以看到所有培训记录(用于管理)
|
||||
// 不设置过滤条件,允许查看所有记录
|
||||
} else {
|
||||
// 默认情况:只能看到自己的培训记录
|
||||
record.setUserId(currentUserId);
|
||||
}
|
||||
}
|
||||
|
||||
// ===== 调试日志:检查权限过滤后的参数 =====
|
||||
log.info("权限过滤后 - location: {}", record.getLocation());
|
||||
log.info("权限过滤后 - userId: {}", record.getUserId());
|
||||
|
||||
// 使用手动分页方式,参考培训申请的实现
|
||||
List<RsTrainingRecord> allList = trainingRecordMapper.selectRsTrainingRecordList(record);
|
||||
|
||||
// ===== 调试日志:检查查询结果 =====
|
||||
log.info("查询结果总数: {}", allList.size());
|
||||
if (!allList.isEmpty() && record.getLocation() != null && !record.getLocation().isEmpty()) {
|
||||
long matchCount = allList.stream()
|
||||
.filter(r -> r.getLocation() != null && r.getLocation().contains(record.getLocation()))
|
||||
.count();
|
||||
log.info("包含location[{}]的记录数: {}", record.getLocation(), matchCount);
|
||||
}
|
||||
|
||||
// 创建分页对象
|
||||
Page<RsTrainingRecord> page = pageQuery.build();
|
||||
long current = page.getCurrent();
|
||||
|
||||
@@ -60,6 +60,16 @@
|
||||
<if test="tenantId != null and tenantId != ''">and a.tenant_id = #{tenantId}</if>
|
||||
<if test="location != null and location != ''">and a.location like concat('%', #{location}, '%')</if>
|
||||
<if test="duration != null">and a.duration = #{duration}</if>
|
||||
<!-- 课程名称搜索条件:支持自定义课程名称和标准课程标题 -->
|
||||
<if test="courseName != null and courseName != ''">
|
||||
and (
|
||||
(a.course_id = 0 and a.course_name like concat('%', #{courseName}, '%'))
|
||||
or
|
||||
(a.course_id != 0 and c.title like concat('%', #{courseName}, '%'))
|
||||
)
|
||||
</if>
|
||||
<!-- 排除指定状态的条件(用于待审批接口) -->
|
||||
<if test="excludeStatus != null and excludeStatus != ''">and a.status != #{excludeStatus}</if>
|
||||
</where>
|
||||
ORDER BY a.create_time DESC
|
||||
</select>
|
||||
@@ -80,6 +90,18 @@
|
||||
<if test="app.applyTime != null ">and a.apply_time = #{app.applyTime}</if>
|
||||
<if test="app.currentApprover != null and app.currentApprover != ''">and a.current_approver = #{app.currentApprover}</if>
|
||||
<if test="app.tenantId != null and app.tenantId != ''">and a.tenant_id = #{app.tenantId}</if>
|
||||
<if test="app.location != null and app.location != ''">and a.location like concat('%', #{app.location}, '%')</if>
|
||||
<if test="app.duration != null">and a.duration = #{app.duration}</if>
|
||||
<!-- 课程名称搜索条件:支持自定义课程名称和标准课程标题 -->
|
||||
<if test="app.courseName != null and app.courseName != ''">
|
||||
and (
|
||||
(a.course_id = 0 and a.course_name like concat('%', #{app.courseName}, '%'))
|
||||
or
|
||||
(a.course_id != 0 and c.title like concat('%', #{app.courseName}, '%'))
|
||||
)
|
||||
</if>
|
||||
<!-- 排除指定状态的条件(用于待审批接口) -->
|
||||
<if test="app.excludeStatus != null and app.excludeStatus != ''">and a.status != #{app.excludeStatus}</if>
|
||||
</where>
|
||||
ORDER BY a.create_time DESC
|
||||
LIMIT #{offset}, #{limit}
|
||||
|
||||
@@ -60,6 +60,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="completionStatus != null and completionStatus != ''">and r.completion_status = #{completionStatus}</if>
|
||||
<if test="status != null and status != ''">and r.status = #{status}</if>
|
||||
<if test="department != null and department != ''">and r.department like concat('%', #{department}, '%')</if>
|
||||
<if test="location != null and location != ''">and r.location like concat('%', #{location}, '%')</if>
|
||||
<if test="params.beginTime != null and params.beginTime != ''">and date_format(r.create_time,'%y%m%d') >= date_format(#{params.beginTime},'%y%m%d')</if>
|
||||
<if test="params.endTime != null and params.endTime != ''">and date_format(r.create_time,'%y%m%d') <= date_format(#{params.endTime},'%y%m%d')</if>
|
||||
</where>
|
||||
|
||||
@@ -1,670 +0,0 @@
|
||||
# 物业报修管理 API
|
||||
|
||||
## 📋 文档说明
|
||||
|
||||
本文档记录物业报修管理相关 API 接口,包括维修分类管理、报修工单管理、工单进度跟踪等功能。
|
||||
|
||||
**依赖文档**: 请先阅读 [API 文档基础规范.md](./API文档基础规范.md) 了解通用规范。
|
||||
|
||||
---
|
||||
|
||||
## 🔧 维修分类管理
|
||||
|
||||
### 查询维修分类列表
|
||||
|
||||
**接口地址**: `GET /hq/pm/repairCategory/list`
|
||||
|
||||
**接口描述**: 分页查询维修分类列表,支持权限过滤
|
||||
|
||||
**权限要求**: `pm:category:list`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
| ------------ | ------ | ---- | --------------------- |
|
||||
| pageNum | int | 否 | 页码,默认 1 |
|
||||
| pageSize | int | 否 | 页大小,默认 10 |
|
||||
| categoryName | string | 否 | 分类名称 |
|
||||
| categoryCode | string | 否 | 分类编码 |
|
||||
| parentId | long | 否 | 父级分类 ID |
|
||||
| skillLevel | string | 否 | 技能等级要求 |
|
||||
| status | string | 否 | 状态(0-停用,1-正常) |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"total": 6,
|
||||
"rows": [
|
||||
{
|
||||
"id": 1,
|
||||
"categoryName": "电路维修",
|
||||
"categoryCode": "ELECTRIC",
|
||||
"categoryDescription": "电路、照明、开关、插座等电气设备维修",
|
||||
"parentId": 0,
|
||||
"ancestorIds": "0",
|
||||
"levelDepth": 1,
|
||||
"childrenCount": 0,
|
||||
"requiredSkills": "电工技能、安全操作",
|
||||
"skillLevel": "2",
|
||||
"estimatedDuration": 120,
|
||||
"standardCost": 200.0,
|
||||
"iconUrl": null,
|
||||
"colorCode": "#FF6B35",
|
||||
"sortOrder": 1,
|
||||
"status": "1",
|
||||
"usageCount": 0,
|
||||
"lastUsedTime": null,
|
||||
"remark": null,
|
||||
"createTime": "2025-01-23 14:30:00"
|
||||
}
|
||||
],
|
||||
"code": 200,
|
||||
"msg": "查询成功"
|
||||
}
|
||||
```
|
||||
|
||||
### 获取维修分类树形结构
|
||||
|
||||
**接口地址**: `GET /hq/pm/repairCategory/tree`
|
||||
|
||||
**接口描述**: 获取维修分类的树形结构数据
|
||||
|
||||
**权限要求**: `pm:category:tree`
|
||||
|
||||
**请求参数**: 无
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "操作成功",
|
||||
"data": [
|
||||
{
|
||||
"id": 1,
|
||||
"categoryName": "电路维修",
|
||||
"categoryCode": "ELECTRIC",
|
||||
"categoryDescription": "电路、照明、开关、插座等电气设备维修",
|
||||
"parentId": 0,
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 获取维修分类下拉选项
|
||||
|
||||
**接口地址**: `GET /hq/pm/repairCategory/options`
|
||||
|
||||
**接口描述**: 获取维修分类的下拉选项数据,用于表单选择器
|
||||
|
||||
**权限要求**: `pm:category:list`
|
||||
|
||||
**请求参数**: 无
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "操作成功",
|
||||
"data": [
|
||||
{
|
||||
"value": 1,
|
||||
"label": "电路维修"
|
||||
},
|
||||
{
|
||||
"value": 2,
|
||||
"label": "水管维修"
|
||||
},
|
||||
{
|
||||
"value": 3,
|
||||
"label": "门窗维修"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**数据格式说明**:
|
||||
|
||||
- **value**: 分类 ID,用于表单提交
|
||||
- **label**: 分类名称,用于显示
|
||||
- **扁平化处理**: 将树形结构扁平化,便于下拉选择
|
||||
|
||||
**使用场景**:
|
||||
|
||||
- 报修工单创建时选择维修类型
|
||||
- 表单编辑时的分类选择
|
||||
- 搜索条件中的分类筛选
|
||||
|
||||
### 获取维修分类详情
|
||||
|
||||
**接口地址**: `GET /hq/pm/repairCategory/{id}`
|
||||
|
||||
**接口描述**: 根据 ID 获取维修分类详细信息
|
||||
|
||||
**权限要求**: `pm:category:query`
|
||||
|
||||
**路径参数**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
| ------ | ---- | ---- | ------- |
|
||||
| id | long | 是 | 分类 ID |
|
||||
|
||||
### 新增维修分类
|
||||
|
||||
**接口地址**: `POST /hq/pm/repairCategory`
|
||||
|
||||
**接口描述**: 创建新的维修分类
|
||||
|
||||
**权限要求**: `pm:category:add`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
```json
|
||||
{
|
||||
"categoryName": "测试维修类型",
|
||||
"categoryCode": "TEST_REPAIR",
|
||||
"categoryDescription": "这是一个测试的维修类型",
|
||||
"parentId": 0,
|
||||
"skillLevel": "1",
|
||||
"estimatedDuration": 60,
|
||||
"standardCost": 100.0,
|
||||
"colorCode": "#1890FF",
|
||||
"sortOrder": 99,
|
||||
"status": "1",
|
||||
"remark": "测试数据"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ 报修工单管理
|
||||
|
||||
### 查询报修工单列表
|
||||
|
||||
**接口地址**: `GET /hq/pm/repairOrder/list`
|
||||
|
||||
**接口描述**: 分页查询报修工单列表,支持多种条件筛选,根据用户角色进行数据权限过滤
|
||||
|
||||
**权限要求**: `pm:repairOrder:list`
|
||||
|
||||
**数据权限说明**:
|
||||
|
||||
- **超级管理员**: 可查看所有报修工单
|
||||
- **物业维修管理员** (role_key: property_maintenance_admin): 可查看所有报修工单
|
||||
- **维修人员** (role_key: maintenance_worker): 只能查看自己创建的工单和分配给自己的工单
|
||||
- **普通用户**: 只能查看自己创建的工单
|
||||
|
||||
**请求参数**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
| --------------- | ------ | ---- | --------------- |
|
||||
| pageNum | int | 否 | 页码,默认 1 |
|
||||
| pageSize | int | 否 | 页大小,默认 10 |
|
||||
| orderNo | string | 否 | 工单编号 |
|
||||
| title | string | 否 | 报修标题 |
|
||||
| repairLocation | string | 否 | 报修地点 |
|
||||
| categoryId | long | 否 | 维修类型 ID |
|
||||
| priorityLevel | int | 否 | 紧急程度 |
|
||||
| reporterName | string | 否 | 报修人姓名 |
|
||||
| reporterPhone | string | 否 | 报修人电话 |
|
||||
| orderStatus | string | 否 | 工单状态 |
|
||||
| assigneeId | long | 否 | 分派维修员 ID |
|
||||
| assigneeName | string | 否 | 分派维修员姓名 |
|
||||
| createTimeRange | array | 否 | 创建时间范围 |
|
||||
| finishTimeRange | array | 否 | 完成时间范围 |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"total": 1,
|
||||
"rows": [
|
||||
{
|
||||
"id": "1939510596285673473",
|
||||
"tenantId": "000000",
|
||||
"orderNo": "RO202506300002",
|
||||
"title": "详细测试工单 - 会议室投影仪故障",
|
||||
"description": "会议室投影仪无法正常启动,显示器黑屏,影响会议正常进行。设备型号:EPSON EB-U05,购买时间:2023年6月。",
|
||||
"repairLocation": "行政楼三楼305会议室",
|
||||
"categoryId": 1,
|
||||
"categoryName": "电路维修",
|
||||
"priorityLevel": 2,
|
||||
"expectedFinishTime": "2025-07-02 02:25:34",
|
||||
"reporterId": 1,
|
||||
"reporterName": "测试用户",
|
||||
"reporterPhone": "13900139001",
|
||||
"reporterDept": "信息技术部",
|
||||
"orderStatus": "1",
|
||||
"assignTime": null,
|
||||
"assigneeId": null,
|
||||
"assigneeName": null,
|
||||
"assignRemark": null,
|
||||
"startTime": null,
|
||||
"finishTime": null,
|
||||
"totalDuration": null,
|
||||
"repairResult": null,
|
||||
"usedMaterials": null,
|
||||
"followSuggestions": null,
|
||||
"evaluationScore": null,
|
||||
"evaluationTime": null,
|
||||
"attachmentCount": 0,
|
||||
"progressCount": 1,
|
||||
"cancelReason": null,
|
||||
"remark": "设备在保修期内,请联系厂家技术支持",
|
||||
"createTime": "2025-06-30 02:25:34",
|
||||
"updateTime": "2025-06-30 02:25:34"
|
||||
}
|
||||
],
|
||||
"code": 200,
|
||||
"msg": "查询成功"
|
||||
}
|
||||
```
|
||||
|
||||
### 生成工单编号
|
||||
|
||||
**接口地址**: `GET /hq/pm/repairOrder/generateOrderNo`
|
||||
|
||||
**接口描述**: 自动生成报修工单编号
|
||||
|
||||
**权限要求**: 无需特殊权限
|
||||
|
||||
**请求参数**: 无
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "RO202506300002",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
**说明**: 工单编号格式为`RO + 年月日 + 序号`,如`RO202506300002`
|
||||
|
||||
### 获取报修统计数据
|
||||
|
||||
**接口地址**: `GET /hq/pm/repairOrder/statistics`
|
||||
|
||||
**接口描述**: 获取报修工单的统计数据,用于仪表板展示
|
||||
|
||||
**权限要求**: `pm:repairOrder:statistics`
|
||||
|
||||
**访问权限说明**:
|
||||
|
||||
- **超级管理员**: 可以访问统计数据
|
||||
- **物业维修管理员** (role_key: property_maintenance_admin): 可以访问统计数据
|
||||
- **维修人员** (role_key: maintenance_worker): **无权限访问**
|
||||
- **普通用户**: **无权限访问**
|
||||
|
||||
**请求参数**: 无
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "操作成功",
|
||||
"data": {
|
||||
"totalOrders": 156,
|
||||
"pendingAssign": 23,
|
||||
"inProgress": 45,
|
||||
"completed": 88,
|
||||
"cancelled": 0,
|
||||
"avgProcessTime": 2.5,
|
||||
"avgEvaluationScore": 4.3,
|
||||
"monthlyNewOrders": 67,
|
||||
"monthlyCompletedOrders": 52,
|
||||
"statusDistribution": [
|
||||
{
|
||||
"status": "待分派",
|
||||
"count": 23,
|
||||
"percentage": 14.7
|
||||
},
|
||||
{
|
||||
"status": "进行中",
|
||||
"count": 45,
|
||||
"percentage": 28.8
|
||||
},
|
||||
{
|
||||
"status": "已完成",
|
||||
"count": 88,
|
||||
"percentage": 56.4
|
||||
}
|
||||
],
|
||||
"categoryDistribution": [
|
||||
{
|
||||
"categoryName": "电路维修",
|
||||
"count": 45,
|
||||
"percentage": 28.8
|
||||
},
|
||||
{
|
||||
"categoryName": "水管维修",
|
||||
"count": 38,
|
||||
"percentage": 24.4
|
||||
}
|
||||
],
|
||||
"priorityDistribution": [
|
||||
{
|
||||
"priorityLevel": 1,
|
||||
"count": 12,
|
||||
"percentage": 7.7
|
||||
},
|
||||
{
|
||||
"priorityLevel": 2,
|
||||
"count": 34,
|
||||
"percentage": 21.8
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**统计字段说明**:
|
||||
|
||||
| 字段名 | 类型 | 说明 |
|
||||
| ---------------------- | ----- | ---------------------- |
|
||||
| totalOrders | int | 总工单数量 |
|
||||
| pendingAssign | int | 待分派工单数 |
|
||||
| inProgress | int | 进行中工单数 |
|
||||
| completed | int | 已完成工单数 |
|
||||
| cancelled | int | 已取消工单数 |
|
||||
| avgProcessTime | float | 平均处理时长(小时) |
|
||||
| avgEvaluationScore | float | 平均评价得分(1-5 分) |
|
||||
| monthlyNewOrders | int | 本月新增工单数 |
|
||||
| monthlyCompletedOrders | int | 本月完成工单数 |
|
||||
| statusDistribution | array | 各状态分布统计 |
|
||||
| categoryDistribution | array | 各类型分布统计 |
|
||||
| priorityDistribution | array | 紧急程度分布统计 |
|
||||
|
||||
### 分派工单
|
||||
|
||||
**接口地址**: `PUT /hq/pm/repairOrder/{orderId}/assign`
|
||||
|
||||
**接口描述**: 分派工单给维修员
|
||||
|
||||
**权限要求**: `pm:repairOrder:assign`
|
||||
|
||||
**路径参数**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 | 示例值 |
|
||||
| ------- | ---- | ---- | ------- | ------ |
|
||||
| orderId | Long | 是 | 工单 ID | 1 |
|
||||
|
||||
**请求参数**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 | 示例值 |
|
||||
| ------------ | ------ | ---- | --------- | ---------- |
|
||||
| assigneeId | Long | 是 | 维修员 ID | 2 |
|
||||
| assignRemark | String | 否 | 分派备注 | "紧急处理" |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "分派成功",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
**错误码**:
|
||||
|
||||
| 错误码 | 说明 | 解决方案 |
|
||||
| ------ | ------------------ | ------------- |
|
||||
| 400001 | 工单不存在 | 检查工单 ID |
|
||||
| 400002 | 维修员不存在 | 检查维修员 ID |
|
||||
| 400003 | 工单状态不允许分派 | 检查工单状态 |
|
||||
|
||||
### 获取工单进度列表
|
||||
|
||||
**接口地址**: `GET /hq/pm/repairOrder/{orderId}/progress`
|
||||
|
||||
**接口描述**: 获取指定工单的进度记录列表
|
||||
|
||||
**权限要求**: `pm:repairOrder:progress`
|
||||
|
||||
**路径参数**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 | 示例值 |
|
||||
| ------- | ---- | ---- | ------- | ------ |
|
||||
| orderId | Long | 是 | 工单 ID | 1 |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "查询成功",
|
||||
"data": [
|
||||
{
|
||||
"id": 1,
|
||||
"orderId": 1,
|
||||
"progressType": "5",
|
||||
"progressStatus": "正在维修",
|
||||
"progressDetail": "已到达现场,开始检查设备",
|
||||
"operatorId": 2,
|
||||
"operatorName": "张维修",
|
||||
"operatorRole": "worker",
|
||||
"locationInfo": "教学楼A座301",
|
||||
"estimatedDuration": 60,
|
||||
"operationTime": "2025-06-30 14:30:00",
|
||||
"nextStep": "更换零件",
|
||||
"issuesEncountered": "发现零件老化",
|
||||
"materialsNeeded": "电路板x1",
|
||||
"remark": "需要采购新零件"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 更新维修进度
|
||||
|
||||
**接口地址**: `POST /hq/pm/repairOrder/progress`
|
||||
|
||||
**接口描述**: 添加工单维修进度记录
|
||||
|
||||
**权限要求**: `pm:repairOrder:updateProgress`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 | 示例值 |
|
||||
| ----------------- | ------- | ---- | ---------------------------------------------------------------------------------------------------------------- | -------------------------- |
|
||||
| orderId | Long | 是 | 工单 ID | 1 |
|
||||
| progressType | String | 是 | 进度类型:1-工单分派,2-接收确认,3-到达现场,4-开始维修,5-进度更新,6-完成维修,7-用户评价,8-工单归档,9-其他 | "5" |
|
||||
| progressStatus | String | 是 | 进度状态描述 | "正在维修" |
|
||||
| progressDetail | String | 是 | 进度详细描述 | "已到达现场,开始检查设备" |
|
||||
| locationInfo | String | 否 | 操作位置信息 | "教学楼 A 座 301" |
|
||||
| estimatedDuration | Integer | 否 | 预计耗时(分钟) | 60 |
|
||||
| nextStep | String | 否 | 下一步计划 | "更换零件" |
|
||||
| issuesEncountered | String | 否 | 遇到的问题 | "发现零件老化" |
|
||||
| materialsNeeded | String | 否 | 需要的材料 | "电路板 x1" |
|
||||
| remark | String | 否 | 备注信息 | "需要采购新零件" |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "进度更新成功",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
### 完成工单
|
||||
|
||||
**接口地址**: `PUT /hq/pm/repairOrder/{orderId}/finish`
|
||||
|
||||
**接口描述**: 完成工单维修,填写维修结果
|
||||
|
||||
**权限要求**: `pm:repairOrder:complete`
|
||||
|
||||
**路径参数**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 | 示例值 |
|
||||
| ------- | ---- | ---- | ------- | ------ |
|
||||
| orderId | Long | 是 | 工单 ID | 1 |
|
||||
|
||||
**请求参数**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 | 示例值 |
|
||||
| ----------------- | ------ | ---- | ------------ | -------------------------------- |
|
||||
| repairResult | String | 是 | 维修结果描述 | "已更换故障电路板,设备恢复正常" |
|
||||
| usedMaterials | String | 否 | 使用材料清单 | "电路板 x1, 螺丝 x4" |
|
||||
| followSuggestions | String | 否 | 后续建议 | "建议定期检查电路板状态" |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "工单完成成功",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⭐ 服务评价管理
|
||||
|
||||
### 创建服务评价
|
||||
|
||||
**接口地址**: `POST /hq/pm/repairOrder/evaluation`
|
||||
|
||||
**接口描述**: 创建工单服务评价
|
||||
|
||||
**权限要求**: `pm:repairOrder:evaluate`
|
||||
|
||||
**请求参数**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 | 示例值 |
|
||||
| ---------------------- | ------- | ---- | -------------------------------------------------------------- | ---------------------------- |
|
||||
| orderId | Long | 是 | 工单 ID | 1 |
|
||||
| evaluatedWorkerId | Long | 是 | 被评价维修员 ID | 2 |
|
||||
| overallScore | Number | 是 | 综合评价得分(1.0-5.0) | 4.5 |
|
||||
| qualityScore | Number | 是 | 维修质量得分(1.0-5.0) | 4.8 |
|
||||
| attitudeScore | Number | 是 | 服务态度得分(1.0-5.0) | 4.5 |
|
||||
| responseSpeedScore | Number | 是 | 响应速度得分(1.0-5.0) | 4.2 |
|
||||
| communicationScore | Number | 是 | 沟通效果得分(1.0-5.0) | 4.6 |
|
||||
| evaluationContent | String | 是 | 文字评价内容 | "维修师傅技术过硬,态度很好" |
|
||||
| satisfactionLevel | String | 是 | 满意度等级:1-非常满意,2-满意,3-一般,4-不满意,5-非常不满意 | "1" |
|
||||
| isAnonymous | Integer | 否 | 是否匿名评价:0-否,1-是 | 0 |
|
||||
| isPublic | Integer | 否 | 是否公开显示:0-否,1-是 | 1 |
|
||||
| positiveTags | String | 否 | 正面标签 | "技术专业,态度友好" |
|
||||
| negativeTags | String | 否 | 负面标签 | "" |
|
||||
| improvementSuggestions | String | 否 | 改进建议 | "希望能提前通知到达时间" |
|
||||
| remark | String | 否 | 备注信息 | "" |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "评价提交成功",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
### 获取工单评价
|
||||
|
||||
**接口地址**: `GET /hq/pm/repairOrder/{orderId}/evaluation`
|
||||
|
||||
**接口描述**: 获取指定工单的服务评价信息
|
||||
|
||||
**权限要求**: `pm:repairOrder:query`
|
||||
|
||||
**路径参数**:
|
||||
|
||||
| 参数名 | 类型 | 必填 | 说明 | 示例值 |
|
||||
| ------- | ---- | ---- | ------- | ------ |
|
||||
| orderId | Long | 是 | 工单 ID | 1 |
|
||||
|
||||
**响应示例**:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "查询成功",
|
||||
"data": {
|
||||
"id": 1,
|
||||
"orderId": 1,
|
||||
"evaluatorId": 3,
|
||||
"evaluatorName": "张老师",
|
||||
"evaluatedWorkerId": 2,
|
||||
"evaluatedWorkerName": "张维修",
|
||||
"overallScore": 4.5,
|
||||
"qualityScore": 4.8,
|
||||
"attitudeScore": 4.5,
|
||||
"responseSpeedScore": 4.2,
|
||||
"communicationScore": 4.6,
|
||||
"evaluationContent": "维修师傅技术过硬,态度很好",
|
||||
"satisfactionLevel": "1",
|
||||
"isAnonymous": 0,
|
||||
"isPublic": 1,
|
||||
"positiveTags": "技术专业,态度友好",
|
||||
"improvementSuggestions": "希望能提前通知到达时间",
|
||||
"evaluationTime": "2025-06-30 16:30:00"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 数据字典
|
||||
|
||||
### 工单状态
|
||||
|
||||
| 值 | 说明 | 备注 |
|
||||
| --- | ------ | ------------ |
|
||||
| 1 | 待分派 | 已创建待分派 |
|
||||
| 2 | 进行中 | 已分派进行中 |
|
||||
| 3 | 已完成 | 维修完成 |
|
||||
| 4 | 已取消 | 工单取消 |
|
||||
|
||||
### 紧急程度
|
||||
|
||||
| 值 | 说明 | 备注 |
|
||||
| --- | ---- | ------------ |
|
||||
| 1 | 低 | 一般维修 |
|
||||
| 2 | 中 | 较重要维修 |
|
||||
| 3 | 高 | 紧急维修 |
|
||||
| 4 | 紧急 | 非常紧急维修 |
|
||||
|
||||
### 进度类型
|
||||
|
||||
| 值 | 说明 | 备注 |
|
||||
| --- | -------- | -------------- |
|
||||
| 1 | 工单分派 | 分派给维修员 |
|
||||
| 2 | 接收确认 | 维修员确认接收 |
|
||||
| 3 | 到达现场 | 到达维修现场 |
|
||||
| 4 | 开始维修 | 开始进行维修 |
|
||||
| 5 | 进度更新 | 维修进度更新 |
|
||||
| 6 | 完成维修 | 维修工作完成 |
|
||||
| 7 | 用户评价 | 用户评价完成 |
|
||||
| 8 | 工单归档 | 工单归档完成 |
|
||||
| 9 | 其他 | 其他操作 |
|
||||
|
||||
### 满意度等级
|
||||
|
||||
| 值 | 说明 | 备注 |
|
||||
| --- | ---------- | ---- |
|
||||
| 1 | 非常满意 | 5 分 |
|
||||
| 2 | 满意 | 4 分 |
|
||||
| 3 | 一般 | 3 分 |
|
||||
| 4 | 不满意 | 2 分 |
|
||||
| 5 | 非常不满意 | 1 分 |
|
||||
|
||||
---
|
||||
|
||||
## 📞 联系方式
|
||||
|
||||
如有物业报修相关问题,请联系开发团队或查看项目仓库的 Issues。
|
||||
|
||||
---
|
||||
|
||||
**📝 维护**: 物业报修管理 API 文档
|
||||
**🔄 更新**: 2025-07-01
|
||||
**�� 文件**: 物业报修管理 API.md
|
||||
@@ -4,7 +4,7 @@
|
||||
<!-- Logo和标题区域 -->
|
||||
<view class="login-header">
|
||||
<view class="login-logo">
|
||||
<image class="logo-img" src="/static/logo-2.png" mode="aspectFit" />
|
||||
<image class="logo-img" src="/static/logo.png" mode="aspectFit" />
|
||||
</view>
|
||||
<view class="login-text">
|
||||
<view class="login-title">智慧校园</view>
|
||||
|
||||
@@ -10,6 +10,12 @@
|
||||
<!-- 加载中状态 -->
|
||||
<u-loading-page :loading="loading" loading-text="加载中..."></u-loading-page>
|
||||
|
||||
<!-- 后台数据加载提示 -->
|
||||
<view v-if="!loading && !backgroundDataLoaded" class="background-loading-tip">
|
||||
<u-icon name="clock" size="16" color="#909399"></u-icon>
|
||||
<text class="tip-text">部分辅助数据正在后台加载中...</text>
|
||||
</view>
|
||||
|
||||
<!-- 内容区域 -->
|
||||
<view v-if="!loading" class="content-wrapper">
|
||||
<!-- 学生基本信息卡片 -->
|
||||
@@ -95,7 +101,7 @@
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">籍贯</text>
|
||||
<text class="value">{{ getRegionFullPath(form.nativePlace) || '-' }}</text>
|
||||
<text class="value">{{ nativePlaceDisplayName || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">政治面貌</text>
|
||||
@@ -172,7 +178,7 @@
|
||||
<view class="info-item long-content">
|
||||
<text class="label">生源地区</text>
|
||||
<text class="value long-text">
|
||||
{{ getRegionFullPath(form.sourceRegion) || '-' }}
|
||||
{{ sourceRegionDisplayName || '-' }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="info-item long-content">
|
||||
@@ -782,38 +788,32 @@ export default {
|
||||
// 显示名称(异步加载)
|
||||
roomDisplayName: '',
|
||||
bedDisplayName: '',
|
||||
nativePlaceDisplayName: '', // 籍贯显示名称
|
||||
sourceRegionDisplayName: '', // 生源地区显示名称
|
||||
|
||||
// 加载状态
|
||||
regionLoadingPromise: null,
|
||||
backgroundDataLoaded: false, // 后台数据是否加载完成
|
||||
};
|
||||
},
|
||||
|
||||
async created() {
|
||||
console.log('=== 学生信息页面初始化开始 ===');
|
||||
try {
|
||||
await this.loadDictData();
|
||||
console.log('✓ 字典数据加载完成');
|
||||
} catch (error) {
|
||||
console.error('字典数据加载出错(不影响后续流程):', error);
|
||||
}
|
||||
|
||||
try {
|
||||
await this.loadRegionAndDormData();
|
||||
console.log('✓ 区域和宿舍数据加载完成');
|
||||
} catch (error) {
|
||||
console.error('区域和宿舍数据加载出错:', error);
|
||||
}
|
||||
|
||||
try {
|
||||
await this.loadDormRoomAndBedData();
|
||||
console.log('✓ 房间和床位数据加载完成');
|
||||
} catch (error) {
|
||||
console.error('房间和床位数据加载出错:', error);
|
||||
}
|
||||
|
||||
// 第一阶段:立即加载核心个人信息,让页面内容快速显示
|
||||
try {
|
||||
this.loading = true;
|
||||
await this.getPersonalInfoData();
|
||||
console.log('✓ 个人信息加载完成');
|
||||
console.log('✓ 核心个人信息加载完成,页面可以显示');
|
||||
} catch (error) {
|
||||
console.error('个人信息加载出错:', error);
|
||||
console.error('个人信息加载失败:', error);
|
||||
// 即使个人信息加载失败,也要设置loading为false,避免页面一直loading
|
||||
this.loading = false;
|
||||
}
|
||||
|
||||
// 第二阶段:并行加载辅助数据(不阻塞页面显示)
|
||||
this.loadSupportingDataInBackground();
|
||||
|
||||
console.log('=== 学生信息页面初始化完成 ===');
|
||||
},
|
||||
|
||||
@@ -1054,6 +1054,256 @@ export default {
|
||||
return getDictTagType(this.studentStatusOptions, this.form.studentStatus, fallbackMap);
|
||||
},
|
||||
|
||||
// 后台并行加载辅助数据
|
||||
async loadSupportingDataInBackground() {
|
||||
console.log('开始后台加载辅助数据...');
|
||||
|
||||
// 第一阶段:并行加载所有非核心数据,互不阻塞
|
||||
const loadPromises = [
|
||||
this.loadDictDataSafely(),
|
||||
this.loadDormDataSafely(),
|
||||
this.loadDormRoomAndBedDataSafely(),
|
||||
];
|
||||
|
||||
// 使用Promise.allSettled确保即使某些请求失败也不影响其他
|
||||
const results = await Promise.allSettled(loadPromises);
|
||||
|
||||
results.forEach((result, index) => {
|
||||
const taskNames = ['字典数据', '宿舍数据', '房间床位数据'];
|
||||
if (result.status === 'fulfilled') {
|
||||
console.log(`✓ ${taskNames[index]}后台加载完成`);
|
||||
} else {
|
||||
console.warn(`✗ ${taskNames[index]}后台加载失败:`, result.reason);
|
||||
}
|
||||
});
|
||||
|
||||
// 第二阶段:更新宿舍相关显示名称(不依赖区域数据)
|
||||
try {
|
||||
await this.updateDisplayNamesAfterBackgroundLoad();
|
||||
console.log('✓ 后台数据加载后显示名称更新完成');
|
||||
} catch (error) {
|
||||
console.warn('后台数据加载后显示名称更新失败:', error);
|
||||
}
|
||||
|
||||
// 第三阶段:尝试加载区域数据并更新区域显示名称(可选,不阻塞整体完成)
|
||||
this.loadRegionDataAndUpdateDisplay();
|
||||
|
||||
console.log('=== 后台辅助数据加载完成 ===');
|
||||
this.backgroundDataLoaded = true;
|
||||
},
|
||||
|
||||
// 安全加载字典数据(出错不影响页面)
|
||||
async loadDictDataSafely() {
|
||||
try {
|
||||
await this.loadDictData();
|
||||
} catch (error) {
|
||||
console.warn('字典数据加载失败,使用默认值:', error);
|
||||
// 确保字典数组不为undefined
|
||||
this.ensureDictArraysExist();
|
||||
}
|
||||
},
|
||||
|
||||
// 安全加载宿舍数据(出错不影响页面)
|
||||
async loadDormDataSafely() {
|
||||
try {
|
||||
await this.loadDormBuildingData();
|
||||
} catch (error) {
|
||||
console.warn('宿舍数据加载失败,使用默认值:', error);
|
||||
this.dormBuildingOptions = [];
|
||||
}
|
||||
},
|
||||
|
||||
// 安全加载房间床位数据(出错不影响页面)
|
||||
async loadDormRoomAndBedDataSafely() {
|
||||
try {
|
||||
await this.loadDormRoomAndBedData();
|
||||
} catch (error) {
|
||||
console.warn('房间床位数据加载失败,使用默认值:', error);
|
||||
this.dormRoomOptions = [];
|
||||
this.dormBedOptions = [];
|
||||
}
|
||||
},
|
||||
|
||||
// 设置临时显示名称,避免页面空白
|
||||
setTemporaryDisplayNames() {
|
||||
// 设置临时的区域显示名称
|
||||
if (this.form.nativePlace) {
|
||||
this.nativePlaceDisplayName = `区域${this.form.nativePlace}`;
|
||||
}
|
||||
if (this.form.sourceRegion) {
|
||||
this.sourceRegionDisplayName = `区域${this.form.sourceRegion}`;
|
||||
}
|
||||
|
||||
// 房间和床位显示名称暂时留空,将在后台数据加载完成后更新
|
||||
console.log('临时显示名称已设置');
|
||||
},
|
||||
|
||||
// 异步加载区域数据并更新显示名称(不阻塞主流程)
|
||||
async loadRegionDataAndUpdateDisplay() {
|
||||
try {
|
||||
console.log('开始异步加载区域数据...');
|
||||
await this.loadRegionDataLazily();
|
||||
|
||||
// 区域数据加载完成后,更新区域显示名称
|
||||
if (this.form.nativePlace) {
|
||||
const nativePlaceName = this.buildRegionPath(this.form.nativePlace);
|
||||
if (nativePlaceName !== `区域${this.form.nativePlace}`) {
|
||||
this.nativePlaceDisplayName = nativePlaceName;
|
||||
console.log('✓ 异步更新籍贯名称:', nativePlaceName);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.form.sourceRegion) {
|
||||
const sourceRegionName = this.buildRegionPath(this.form.sourceRegion);
|
||||
if (sourceRegionName !== `区域${this.form.sourceRegion}`) {
|
||||
this.sourceRegionDisplayName = sourceRegionName;
|
||||
console.log('✓ 异步更新生源地区名称:', sourceRegionName);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('✓ 区域数据加载和显示名称更新完成');
|
||||
} catch (error) {
|
||||
console.warn('区域数据加载失败,保持临时显示名称:', error);
|
||||
}
|
||||
},
|
||||
|
||||
// 后台数据加载完成后更新显示名称(仅处理宿舍相关)
|
||||
async updateDisplayNamesAfterBackgroundLoad() {
|
||||
const updatePromises = [];
|
||||
|
||||
// 更新房间和床位名称(需要宿舍数据)
|
||||
if (this.form.dormitoryRoom) {
|
||||
updatePromises.push(
|
||||
this.getRoomName(this.form.dormitoryRoom)
|
||||
.then((name) => {
|
||||
this.roomDisplayName = name;
|
||||
console.log('✓ 后台更新房间名称:', name);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.warn('后台更新房间名称失败:', error);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (this.form.extField1) {
|
||||
updatePromises.push(
|
||||
this.getBedName(this.form.extField1)
|
||||
.then((name) => {
|
||||
this.bedDisplayName = name;
|
||||
console.log('✓ 后台更新床位名称:', name);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.warn('后台更新床位名称失败:', error);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// 等待所有宿舍相关更新完成
|
||||
await Promise.allSettled(updatePromises);
|
||||
console.log('宿舍相关显示名称更新完成');
|
||||
},
|
||||
|
||||
// 确保字典数组存在,避免页面报错
|
||||
ensureDictArraysExist() {
|
||||
const dictArrays = [
|
||||
'genderOptions',
|
||||
'nationalityOptions',
|
||||
'politicalStatusOptions',
|
||||
'educationLevelOptions',
|
||||
'householdRegisterTypeOptions',
|
||||
'maritalStatusOptions',
|
||||
'yesNoOptions',
|
||||
'familyEconomicOptions',
|
||||
'bloodTypeOptions',
|
||||
'disabilityTypeOptions',
|
||||
'academicLevelOptions',
|
||||
'trainingModeOptions',
|
||||
'admissionCategoryOptions',
|
||||
'studentStatusOptions',
|
||||
];
|
||||
|
||||
dictArrays.forEach((arrayName) => {
|
||||
if (!this[arrayName] || !Array.isArray(this[arrayName])) {
|
||||
this[arrayName] = [];
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 单独加载宿舍楼栋数据
|
||||
async loadDormBuildingData() {
|
||||
console.log('开始加载宿舍楼栋数据...');
|
||||
try {
|
||||
const dormRes = await request({
|
||||
url: '/xgss/dormBuildingTree/listAll',
|
||||
method: 'GET',
|
||||
});
|
||||
|
||||
if (dormRes && dormRes.data) {
|
||||
this.dormBuildingOptions = this.processDormTreeData(dormRes.data);
|
||||
console.log('宿舍楼栋数据处理完成,条目数:', this.dormBuildingOptions.length);
|
||||
} else {
|
||||
console.warn('宿舍楼栋数据加载失败或为空');
|
||||
this.dormBuildingOptions = [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载宿舍楼栋数据失败:', error);
|
||||
this.dormBuildingOptions = [];
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// 懒加载行政区划数据(仅在需要时加载)
|
||||
async loadRegionDataLazily() {
|
||||
// 如果已经加载过,直接返回
|
||||
if (this.regionOptions && this.regionOptions.length > 0) {
|
||||
return this.regionOptions;
|
||||
}
|
||||
|
||||
// 如果正在加载中,等待加载完成
|
||||
if (this.regionLoadingPromise) {
|
||||
return await this.regionLoadingPromise;
|
||||
}
|
||||
|
||||
// 开始加载
|
||||
this.regionLoadingPromise = this.doLoadRegionData();
|
||||
|
||||
try {
|
||||
await this.regionLoadingPromise;
|
||||
return this.regionOptions;
|
||||
} finally {
|
||||
this.regionLoadingPromise = null;
|
||||
}
|
||||
},
|
||||
|
||||
// 实际执行行政区划数据加载
|
||||
async doLoadRegionData() {
|
||||
console.log('懒加载行政区划数据...');
|
||||
try {
|
||||
const regionRes = await request({
|
||||
url: '/system/SysRegion/tree',
|
||||
method: 'GET',
|
||||
});
|
||||
|
||||
if (regionRes && regionRes.data) {
|
||||
this.regionOptions = regionRes.data;
|
||||
console.log('行政区划数据懒加载完成,条目数:', regionRes.data.length);
|
||||
} else {
|
||||
console.warn('行政区划数据懒加载失败或为空');
|
||||
this.regionOptions = [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('懒加载行政区划数据失败:', error);
|
||||
this.regionOptions = [];
|
||||
// 显示用户友好的错误提示
|
||||
uni.showToast({
|
||||
title: '区域数据加载失败',
|
||||
icon: 'none',
|
||||
duration: 2000,
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// 加载字典数据
|
||||
async loadDictData() {
|
||||
console.log('开始加载字典数据...');
|
||||
@@ -1163,105 +1413,6 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
// 加载行政区划和宿舍楼栋数据
|
||||
async loadRegionAndDormData() {
|
||||
console.log('开始加载区域和宿舍数据...');
|
||||
try {
|
||||
console.log('发起并行请求: 行政区划 & 宿舍楼栋');
|
||||
const [regionRes, dormRes] = await Promise.all([
|
||||
request({
|
||||
url: '/system/SysRegion/tree',
|
||||
method: 'GET',
|
||||
})
|
||||
.then((res) => {
|
||||
console.log('✓ 行政区划请求完成');
|
||||
return res;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('✗ 行政区划请求失败:', err);
|
||||
throw err;
|
||||
}),
|
||||
request({
|
||||
url: '/xgss/dormBuildingTree/listAll',
|
||||
method: 'GET',
|
||||
})
|
||||
.then((res) => {
|
||||
console.log('✓ 宿舍楼栋请求完成');
|
||||
return res;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('✗ 宿舍楼栋请求失败:', err);
|
||||
throw err;
|
||||
}),
|
||||
]);
|
||||
|
||||
// 处理行政区划数据
|
||||
if (regionRes && regionRes.data) {
|
||||
this.regionOptions = regionRes.data;
|
||||
console.log('行政区划数据处理完成,条目数:', regionRes.data.length);
|
||||
} else {
|
||||
console.warn('行政区划数据加载失败或为空');
|
||||
}
|
||||
|
||||
// 处理宿舍楼栋数据,添加性别标识
|
||||
if (dormRes && dormRes.data) {
|
||||
console.log('dormRes', dormRes);
|
||||
this.dormBuildingOptions = this.processDormTreeData(dormRes.data);
|
||||
console.log('宿舍楼栋数据处理完成,条目数:', this.dormBuildingOptions.length);
|
||||
} else {
|
||||
console.warn('宿舍楼栋数据加载失败或为空');
|
||||
this.dormBuildingOptions = [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载区域和宿舍数据失败:', error);
|
||||
uni.showToast({
|
||||
title: '加载基础数据失败,部分功能可能受影响',
|
||||
icon: 'none',
|
||||
duration: 3000,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// 处理宿舍楼栋树形数据,为楼栋添加性别标识
|
||||
processDormTreeData(nodes) {
|
||||
console.log('processDormTreeData - nodes', nodes);
|
||||
if (!nodes || !Array.isArray(nodes)) {
|
||||
console.warn('宿舍楼栋数据格式不正确');
|
||||
return [];
|
||||
}
|
||||
|
||||
return nodes.map((node) => {
|
||||
const processedNode = { ...node };
|
||||
|
||||
// 如果是楼栋类型,添加性别标识
|
||||
if (node.type === 'building' && node.buildingType) {
|
||||
console.log('node.buildingType', node.buildingType);
|
||||
const genderSuffix =
|
||||
node.buildingType === '1'
|
||||
? '(男)'
|
||||
: node.buildingType === '2'
|
||||
? '(女)'
|
||||
: '';
|
||||
|
||||
// 确保 label 和 name 存在后再添加后缀
|
||||
if (node.label) {
|
||||
processedNode.label = node.label + genderSuffix;
|
||||
}
|
||||
if (node.name) {
|
||||
processedNode.name = node.name + genderSuffix;
|
||||
}
|
||||
}
|
||||
|
||||
// 递归处理子节点
|
||||
if (node.children && node.children.length > 0) {
|
||||
processedNode.children = this.processDormTreeData(node.children);
|
||||
}
|
||||
console.log('processedNode', processedNode);
|
||||
|
||||
return processedNode;
|
||||
});
|
||||
},
|
||||
|
||||
// 加载房间和床位数据(改为按需加载)
|
||||
async loadDormRoomAndBedData() {
|
||||
console.log('房间和床位数据改为按需加载模式,不再预加载所有数据');
|
||||
@@ -1270,29 +1421,70 @@ export default {
|
||||
this.dormBedOptions = [];
|
||||
},
|
||||
|
||||
// 异步加载房间和床位显示名称
|
||||
async loadRoomAndBedDisplayNames() {
|
||||
// 异步加载各种显示名称(房间、床位、区域)
|
||||
async loadDisplayNames() {
|
||||
try {
|
||||
// 加载房间名称
|
||||
// 并行加载房间和床位名称
|
||||
const loadPromises = [];
|
||||
|
||||
if (this.form.dormitoryRoom) {
|
||||
this.roomDisplayName = await this.getRoomName(this.form.dormitoryRoom);
|
||||
console.log('房间名称加载完成:', this.roomDisplayName);
|
||||
loadPromises.push(
|
||||
this.getRoomName(this.form.dormitoryRoom).then((name) => {
|
||||
this.roomDisplayName = name;
|
||||
console.log('房间名称加载完成:', name);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// 加载床位名称
|
||||
if (this.form.extField1) {
|
||||
this.bedDisplayName = await this.getBedName(this.form.extField1);
|
||||
console.log('床位名称加载完成:', this.bedDisplayName);
|
||||
loadPromises.push(
|
||||
this.getBedName(this.form.extField1).then((name) => {
|
||||
this.bedDisplayName = name;
|
||||
console.log('床位名称加载完成:', name);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// 并行加载区域显示名称(不阻塞房间床位显示)
|
||||
if (this.form.nativePlace) {
|
||||
loadPromises.push(
|
||||
this.getRegionFullPathAsync(this.form.nativePlace)
|
||||
.then((name) => {
|
||||
this.nativePlaceDisplayName = name;
|
||||
console.log('籍贯显示名称加载完成:', name);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.warn('籍贯显示名称加载失败:', error);
|
||||
this.nativePlaceDisplayName = `区域${this.form.nativePlace}`;
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (this.form.sourceRegion) {
|
||||
loadPromises.push(
|
||||
this.getRegionFullPathAsync(this.form.sourceRegion)
|
||||
.then((name) => {
|
||||
this.sourceRegionDisplayName = name;
|
||||
console.log('生源地区显示名称加载完成:', name);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.warn('生源地区显示名称加载失败:', error);
|
||||
this.sourceRegionDisplayName = `区域${this.form.sourceRegion}`;
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// 等待所有显示名称加载完成(使用 allSettled 确保部分失败不影响其他)
|
||||
await Promise.allSettled(loadPromises);
|
||||
console.log('所有显示名称加载完成');
|
||||
} catch (error) {
|
||||
console.error('加载房间和床位显示名称失败:', error);
|
||||
console.error('加载显示名称失败:', error);
|
||||
}
|
||||
},
|
||||
|
||||
// 获取学生个人信息
|
||||
async getPersonalInfoData() {
|
||||
try {
|
||||
this.loading = true;
|
||||
const response = await request({
|
||||
url: '/studentMsg/studentMsg/personalInfo',
|
||||
method: 'GET',
|
||||
@@ -1331,8 +1523,10 @@ export default {
|
||||
extField1Type: typeof this.form.extField1,
|
||||
});
|
||||
|
||||
// 异步加载房间和床位信息
|
||||
await this.loadRoomAndBedDisplayNames();
|
||||
// 立即设置临时显示名称,避免空白
|
||||
this.setTemporaryDisplayNames();
|
||||
|
||||
console.log('✓ 个人信息加载完成,页面可以显示');
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: response.msg || '获取个人信息失败',
|
||||
@@ -1437,9 +1631,63 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
// 根据行政区划代码获取完整路径(省/市/区)
|
||||
getRegionFullPath(code) {
|
||||
if (!code || !this.regionOptions) return '';
|
||||
// 处理宿舍楼栋树形数据,为楼栋添加性别标识
|
||||
processDormTreeData(nodes) {
|
||||
console.log('processDormTreeData - nodes', nodes);
|
||||
if (!nodes || !Array.isArray(nodes)) {
|
||||
console.warn('宿舍楼栋数据格式不正确');
|
||||
return [];
|
||||
}
|
||||
|
||||
return nodes.map((node) => {
|
||||
const processedNode = { ...node };
|
||||
|
||||
// 如果是楼栋类型,添加性别标识
|
||||
if (node.type === 'building' && node.buildingType) {
|
||||
console.log('node.buildingType', node.buildingType);
|
||||
const genderSuffix =
|
||||
node.buildingType === '1'
|
||||
? '(男)'
|
||||
: node.buildingType === '2'
|
||||
? '(女)'
|
||||
: '';
|
||||
|
||||
// 确保 label 和 name 存在后再添加后缀
|
||||
if (node.label) {
|
||||
processedNode.label = node.label + genderSuffix;
|
||||
}
|
||||
if (node.name) {
|
||||
processedNode.name = node.name + genderSuffix;
|
||||
}
|
||||
}
|
||||
|
||||
// 递归处理子节点
|
||||
if (node.children && node.children.length > 0) {
|
||||
processedNode.children = this.processDormTreeData(node.children);
|
||||
}
|
||||
console.log('processedNode', processedNode);
|
||||
|
||||
return processedNode;
|
||||
});
|
||||
},
|
||||
|
||||
// 异步获取区域完整路径(用于后台加载)
|
||||
async getRegionFullPathAsync(code) {
|
||||
if (!code) return '';
|
||||
|
||||
// 懒加载区域数据
|
||||
await this.loadRegionDataLazily();
|
||||
|
||||
// 获取完整路径
|
||||
return this.buildRegionPath(code);
|
||||
},
|
||||
|
||||
// 构建区域路径(纯计算,不触发加载)
|
||||
buildRegionPath(code) {
|
||||
if (!code || !this.regionOptions || this.regionOptions.length === 0) {
|
||||
return `区域${code}`;
|
||||
}
|
||||
|
||||
const path = [];
|
||||
const findPath = (nodes, targetCode) => {
|
||||
for (const node of nodes) {
|
||||
@@ -1456,8 +1704,58 @@ export default {
|
||||
}
|
||||
return false;
|
||||
};
|
||||
findPath(this.regionOptions, code);
|
||||
return path.join(' / ');
|
||||
|
||||
if (findPath(this.regionOptions, code)) {
|
||||
return path.join(' / ');
|
||||
}
|
||||
|
||||
return `区域${code}`;
|
||||
},
|
||||
|
||||
// 根据行政区划代码获取完整路径(省/市/区)- 同步版本,用于模板(已弃用)
|
||||
getRegionFullPath(code) {
|
||||
if (!code) return '';
|
||||
|
||||
// 如果行政区划数据未加载,触发懒加载并返回临时显示
|
||||
if (!this.regionOptions || this.regionOptions.length === 0) {
|
||||
// 异步加载区域数据,不阻塞当前显示
|
||||
this.loadRegionDataLazily()
|
||||
.then(() => {
|
||||
// 加载完成后触发页面更新
|
||||
this.$forceUpdate();
|
||||
})
|
||||
.catch(() => {
|
||||
// 加载失败时显示代码
|
||||
console.warn('行政区划数据懒加载失败,显示原始代码');
|
||||
});
|
||||
|
||||
// 返回代码作为临时显示
|
||||
return `区域${code}`;
|
||||
}
|
||||
|
||||
const path = [];
|
||||
const findPath = (nodes, targetCode) => {
|
||||
for (const node of nodes) {
|
||||
path.push(node.name);
|
||||
if (node.code === targetCode) {
|
||||
return true;
|
||||
}
|
||||
if (node.children && node.children.length > 0) {
|
||||
if (findPath(node.children, targetCode)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
path.pop();
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
if (findPath(this.regionOptions, code)) {
|
||||
return path.join(' / ');
|
||||
}
|
||||
|
||||
// 如果找不到,返回代码
|
||||
return `区域${code}`;
|
||||
},
|
||||
|
||||
// 根据楼栋ID获取楼栋名称(含性别标识)
|
||||
@@ -1726,6 +2024,21 @@ export default {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.background-loading-tip {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 16rpx 30rpx;
|
||||
background-color: #f0f9ff;
|
||||
border-bottom: 1px solid #e1ecf4;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.tip-text {
|
||||
font-size: 24rpx;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
padding: 20rpx;
|
||||
padding-bottom: 40rpx;
|
||||
|
||||
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 359 KiB |
@@ -107,7 +107,7 @@ export const userManager = {
|
||||
|
||||
return {
|
||||
name: this.getDisplayName(),
|
||||
avatar: user.avatar || '/static/logo.svg',
|
||||
avatar: user.avatar || '/static/logo.png',
|
||||
deptName: user.deptName || '未设置部门',
|
||||
status: user.status,
|
||||
loginDate: user.loginDate,
|
||||
|
||||
@@ -98,12 +98,13 @@ export const submitAssessmentScore = (data: AssessmentSubmitVO): AxiosPromise<vo
|
||||
/**
|
||||
* 查询班级学生违纪记录和奖励记录
|
||||
* @param classId 班级ID
|
||||
* @param planId 考核计划ID
|
||||
*/
|
||||
export const getClassStudentRecords = (classId: string | number): AxiosPromise<any> => {
|
||||
export const getClassStudentRecords = (classId: string | number, planId: string | number): AxiosPromise<any> => {
|
||||
return request({
|
||||
url: '/bzrkh/teacherAssessment/classStudentRecords',
|
||||
method: 'get',
|
||||
params: { classId }
|
||||
params: { classId, planId }
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ export interface ApplicationQuery {
|
||||
pageSize: number;
|
||||
courseId?: number | string;
|
||||
courseName?: string;
|
||||
location?: string; // 培训地点
|
||||
userId?: number | string;
|
||||
userName?: string;
|
||||
department?: string;
|
||||
@@ -175,6 +176,15 @@ export function listTrainingApplication(query: ApplicationQuery) {
|
||||
});
|
||||
}
|
||||
|
||||
// 查询待审批的培训申请列表(供审批人使用)
|
||||
export function listPendingTrainingApplication(query: ApplicationQuery) {
|
||||
return request({
|
||||
url: '/rs/training/application/pending/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
|
||||
// 获取培训申请详情
|
||||
export function getTrainingApplication(applicationId: string | number) {
|
||||
return request({
|
||||
@@ -245,6 +255,15 @@ export function listTrainingRecord(query: RecordQuery) {
|
||||
});
|
||||
}
|
||||
|
||||
// 获取我的培训记录列表
|
||||
export function listMyTrainingRecord(query: RecordQuery) {
|
||||
return request({
|
||||
url: '/rs/training/record/my',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
}
|
||||
|
||||
// 获取培训记录详情
|
||||
export function getTrainingRecord(recordId: string | number) {
|
||||
return request({
|
||||
|
||||
|
Before Width: | Height: | Size: 1.9 MiB After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 1.9 MiB After Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 359 KiB |
@@ -7,7 +7,7 @@
|
||||
<!-- 左侧Logo和系统名称 -->
|
||||
<div class="left-menu flex align-center">
|
||||
<div class="logo-container">
|
||||
<img src="@/assets/logo/logo.svg" alt="学校Logo" class="school-logo" />
|
||||
<img src="@/assets/logo/logo.png" alt="学校Logo" class="school-logo" />
|
||||
</div>
|
||||
<div class="system-title">
|
||||
<span class="title-text">杭州第一技师学院智慧校园管理系统</span>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="计划名称" prop="planName">
|
||||
<el-input v-model="queryParams.planName" placeholder="请输入计划名称" clearable @keyup.enter="handleQuery" />
|
||||
<el-input v-model="queryParams.planName" placeholder="请输入计划名称(支持模糊搜索)" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
@@ -31,12 +31,12 @@
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="班级名称" prop="className">
|
||||
<el-input v-model="queryParams.className" placeholder="请输入班级名称" clearable @keyup.enter="handleQuery" />
|
||||
<el-input v-model="queryParams.className" placeholder="请输入班级名称(支持模糊搜索)" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="班主任姓名" prop="teacherName">
|
||||
<el-input v-model="queryParams.teacherName" placeholder="请输入班主任姓名" clearable @keyup.enter="handleQuery" />
|
||||
<el-input v-model="queryParams.teacherName" placeholder="请输入班主任姓名(支持模糊搜索)" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -71,7 +71,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="班级名称" align="center" prop="className" min-width="120" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="班主任姓名" align="center" prop="evaluatedUserName" min-width="120" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="班主任姓名" align="center" prop="teacherName" min-width="120" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="申请时间" align="center" min-width="180" sortable="custom" prop="submitTime">
|
||||
<template #default="scope">
|
||||
<span>{{ parseTime(scope.row.submitTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="指标编码" prop="indicatorCode">
|
||||
<el-input v-model="queryParams.indicatorCode" placeholder="请输入指标编码" clearable @keyup.enter="handleQuery" />
|
||||
<el-input v-model="queryParams.indicatorCode" placeholder="请输入指标编码(支持模糊搜索)" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="指标类型编码" prop="indicatorTypeCode">
|
||||
<el-input v-model="queryParams.indicatorTypeCode" placeholder="请输入指标类型编码" clearable @keyup.enter="handleQuery" />
|
||||
<el-input v-model="queryParams.indicatorTypeCode" placeholder="请输入指标类型编码(支持模糊搜索)" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="指标类型名称" prop="indicatorTypeName">
|
||||
<el-input v-model="queryParams.indicatorTypeName" placeholder="请输入指标类型名称" clearable @keyup.enter="handleQuery" />
|
||||
<el-input v-model="queryParams.indicatorTypeName" placeholder="请输入指标类型名称(支持模糊搜索)" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
@@ -203,16 +203,22 @@ const handleQuery = () => {
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
|
||||
// 构建查询参数,过滤掉undefined和null值
|
||||
// 构建查询参数,正确处理空字符串和undefined/null值
|
||||
const queryData = {
|
||||
pageNum: queryParams.value.pageNum,
|
||||
pageSize: queryParams.value.pageSize,
|
||||
...(queryParams.value.indicatorTypeCode && { indicatorTypeCode: queryParams.value.indicatorTypeCode }),
|
||||
...(queryParams.value.indicatorTypeName && { indicatorTypeName: queryParams.value.indicatorTypeName }),
|
||||
...(queryParams.value.status && { status: queryParams.value.status })
|
||||
// 修复:正确处理空字符串,只过滤undefined和null
|
||||
...(queryParams.value.indicatorTypeCode !== undefined && queryParams.value.indicatorTypeCode !== null && { indicatorTypeCode: queryParams.value.indicatorTypeCode }),
|
||||
...(queryParams.value.indicatorTypeName !== undefined && queryParams.value.indicatorTypeName !== null && { indicatorTypeName: queryParams.value.indicatorTypeName }),
|
||||
...(queryParams.value.status !== undefined && queryParams.value.status !== null && { status: queryParams.value.status })
|
||||
};
|
||||
|
||||
console.log('📋 发送查询请求,参数:', queryData);
|
||||
console.log('🔍 原始查询参数:', {
|
||||
indicatorTypeCode: `"${queryParams.value.indicatorTypeCode}"`,
|
||||
indicatorTypeName: `"${queryParams.value.indicatorTypeName}"`,
|
||||
status: `"${queryParams.value.status}"`
|
||||
});
|
||||
|
||||
try {
|
||||
const res = await listAssessmentIndicatorType(queryData);
|
||||
@@ -251,7 +257,7 @@ const resetQuery = () => {
|
||||
// 重置表单字段
|
||||
queryFormRef.value?.resetFields();
|
||||
|
||||
// 手动重置查询参数对象
|
||||
// 手动重置查询参数对象,确保清空所有搜索条件
|
||||
queryParams.value.indicatorTypeCode = undefined;
|
||||
queryParams.value.indicatorTypeName = undefined;
|
||||
queryParams.value.status = undefined;
|
||||
|
||||
@@ -24,11 +24,6 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="学年" prop="academicYear">
|
||||
<el-input v-model="queryParams.academicYear" placeholder="请输入学年" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="学期" prop="semester">
|
||||
<el-select v-model="queryParams.semester" placeholder="请选择学期" clearable style="width: 100%">
|
||||
@@ -44,22 +39,6 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="创建时间">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
value-format="YYYY-MM-DD"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
style="width: 100%"
|
||||
@change="handleQuery"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="24" class="search-btn-container">
|
||||
<el-form-item>
|
||||
@@ -312,7 +291,6 @@ const ids = ref<Array<string | number>>([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
|
||||
const schemeOptions = ref<BmzrAssessmentSchemeVO[]>([]);
|
||||
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
@@ -347,7 +325,6 @@ const data = reactive<PageData<AssessmentPlanForm, AssessmentPlanQuery>>({
|
||||
planName: undefined,
|
||||
planCode: undefined,
|
||||
schemeId: undefined,
|
||||
academicYear: undefined,
|
||||
semester: undefined,
|
||||
planStatus: undefined,
|
||||
params: {}
|
||||
@@ -370,11 +347,6 @@ const { queryParams, form, rules } = toRefs(data);
|
||||
/** 查询考核计划列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true;
|
||||
queryParams.value.params = {};
|
||||
if (dateRange.value && dateRange.value.length === 2 && dateRange.value[0] && dateRange.value[1]) {
|
||||
queryParams.value.params['beginCreateTime'] = dateRange.value[0] as string;
|
||||
queryParams.value.params['endCreateTime'] = dateRange.value[1] as string;
|
||||
}
|
||||
const res = await listAssessmentPlan(queryParams.value);
|
||||
assessmentPlanList.value = res.rows;
|
||||
total.value = res.total;
|
||||
@@ -411,7 +383,6 @@ const handleQuery = () => {
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
dateRange.value = ['', ''];
|
||||
queryFormRef.value?.resetFields();
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
@@ -12,12 +12,12 @@
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="方案编码" prop="schemeCode">
|
||||
<el-input v-model="queryParams.schemeCode" placeholder="请输入方案编码" clearable @keyup.enter="handleQuery" />
|
||||
<el-input v-model="queryParams.schemeCode" placeholder="请输入方案编码(支持模糊搜索)" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="学年" prop="academicYear">
|
||||
<el-input v-model="queryParams.academicYear" placeholder="请输入学年" clearable @keyup.enter="handleQuery" />
|
||||
<el-input v-model="queryParams.academicYear" placeholder="请输入学年(支持模糊搜索)" clearable @keyup.enter="handleQuery" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
@@ -337,7 +337,7 @@
|
||||
</el-card>
|
||||
|
||||
<!-- Tab切换 -->
|
||||
<el-tabs type="border-card" style="margin-top: 16px">
|
||||
<el-tabs v-model="studentRecordsActiveTab" type="border-card" style="margin-top: 16px">
|
||||
<!-- 违纪记录Tab -->
|
||||
<el-tab-pane label="违纪记录" name="discipline">
|
||||
<el-table :data="studentRecordsData?.disciplineRecords || []" border style="width: 100%" max-height="400" empty-text="暂无违纪记录">
|
||||
@@ -464,6 +464,7 @@ const monthlyScoreForm = reactive({
|
||||
const studentRecordsVisible = ref(false);
|
||||
const studentRecordsLoading = ref(false);
|
||||
const studentRecordsData = ref<ClassStudentRecordsVO>();
|
||||
const studentRecordsActiveTab = ref('discipline'); // 默认激活违纪记录标签页
|
||||
|
||||
const dialogTitle = computed(() => {
|
||||
const className = assessmentDetail.value?.classDetail?.className || '';
|
||||
@@ -654,14 +655,17 @@ const loadAssessmentDetail = async () => {
|
||||
|
||||
/** 查看学生违规处分信息 */
|
||||
const handleViewStudentViolations = async () => {
|
||||
if (!props.classId) {
|
||||
proxy?.$modal.msgError('缺少班级ID参数');
|
||||
if (!props.classId || !props.planId) {
|
||||
proxy?.$modal.msgError('缺少必要参数(班级ID或计划ID)');
|
||||
return;
|
||||
}
|
||||
|
||||
// 重置为默认激活的违纪记录标签页
|
||||
studentRecordsActiveTab.value = 'discipline';
|
||||
|
||||
studentRecordsLoading.value = true;
|
||||
try {
|
||||
const res = await getClassStudentRecords(props.classId);
|
||||
const res = await getClassStudentRecords(props.classId, props.planId);
|
||||
studentRecordsData.value = res.data;
|
||||
studentRecordsVisible.value = true;
|
||||
} catch (error) {
|
||||
|
||||
@@ -362,7 +362,7 @@
|
||||
</el-card>
|
||||
|
||||
<!-- Tab切换 -->
|
||||
<el-tabs type="border-card" style="margin-top: 16px">
|
||||
<el-tabs v-model="studentRecordsActiveTab" type="border-card" style="margin-top: 16px">
|
||||
<!-- 违纪记录Tab -->
|
||||
<el-tab-pane label="违纪记录" name="discipline">
|
||||
<el-table :data="studentRecordsData?.disciplineRecords || []" border style="width: 100%" max-height="400" empty-text="暂无违纪记录">
|
||||
@@ -571,6 +571,7 @@ const specialWorkForm = reactive({
|
||||
const studentRecordsVisible = ref(false);
|
||||
const studentRecordsLoading = ref(false);
|
||||
const studentRecordsData = ref<ClassStudentRecordsVO>();
|
||||
const studentRecordsActiveTab = ref('discipline'); // 默认激活违纪记录标签页
|
||||
|
||||
const dialogTitle = computed(() => {
|
||||
const className = getCurrentClassDetail()?.className || '';
|
||||
@@ -992,14 +993,17 @@ const loadAssessmentDetail = async () => {
|
||||
|
||||
/** 查看学生违规处分信息 */
|
||||
const handleViewStudentViolations = async () => {
|
||||
if (!props.classId) {
|
||||
proxy?.$modal.msgError('缺少班级ID参数');
|
||||
if (!props.classId || !props.planId) {
|
||||
proxy?.$modal.msgError('缺少必要参数(班级ID或计划ID)');
|
||||
return;
|
||||
}
|
||||
|
||||
// 重置为默认激活的违纪记录标签页
|
||||
studentRecordsActiveTab.value = 'discipline';
|
||||
|
||||
studentRecordsLoading.value = true;
|
||||
try {
|
||||
const res = await getClassStudentRecords(props.classId);
|
||||
const res = await getClassStudentRecords(props.classId, props.planId);
|
||||
studentRecordsData.value = res.data;
|
||||
studentRecordsVisible.value = true;
|
||||
} catch (error) {
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
<el-table-column label="报告厅图片" align="center" prop="hallImage" width="200">
|
||||
<template #default="scope">
|
||||
<div v-if="scope.row.hallImage" class="table-image">
|
||||
<image-preview :src="scope.row.hallImage" :width="50" :height="50" />
|
||||
<image-preview :src="getFirstImage(scope.row.hallImage)" :width="50" :height="50" />
|
||||
</div>
|
||||
<div v-else class="table-no-image">
|
||||
<span class="table-no-image-text">暂无图片</span>
|
||||
@@ -382,6 +382,14 @@ const cancelView = () => {
|
||||
viewDialog.visible = false;
|
||||
};
|
||||
|
||||
/** 获取第一张图片 */
|
||||
const getFirstImage = (hallImage: string) => {
|
||||
if (!hallImage) return '';
|
||||
// 如果有多个图片用逗号分隔,只取第一张
|
||||
const images = hallImage.split(',').filter((img) => img.trim() !== '');
|
||||
return images.length > 0 ? images[0].trim() : '';
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
|
||||
@@ -136,6 +136,7 @@ interface MenuAction {
|
||||
status?: string;
|
||||
moduleOrder?: number; // 新增:用于模块排序
|
||||
menuOrder?: number; // 新增:用于菜单项排序
|
||||
orderNum?: number; // 新增:用于存储菜单的原始排序号
|
||||
logoUrl?: string; // 新增:用于存储随机logo图片路径
|
||||
}
|
||||
|
||||
@@ -379,10 +380,11 @@ const flattenMenus = (menus: any[], parentChain: string[] = [], parentPath = '',
|
||||
// 1. 组件为Layout或ParentView字符串
|
||||
// 2. 组件为Vue对象且没有子菜单(可能是目录)
|
||||
// 3. menuType为'M'(目录类型)
|
||||
const isDirectoryMenu = menu.component === 'Layout' ||
|
||||
menu.component === 'ParentView' ||
|
||||
(typeof menu.component === 'object' && !hasVisibleChildren) ||
|
||||
menu.menuType === 'M';
|
||||
const isDirectoryMenu =
|
||||
menu.component === 'Layout' ||
|
||||
menu.component === 'ParentView' ||
|
||||
(typeof menu.component === 'object' && !hasVisibleChildren) ||
|
||||
menu.menuType === 'M';
|
||||
|
||||
// 添加工作流菜单的详细调试信息
|
||||
if (menu.meta?.title === '工作流' || menu.name === 'Workflow11616') {
|
||||
@@ -424,12 +426,13 @@ const flattenMenus = (menus: any[], parentChain: string[] = [], parentPath = '',
|
||||
status: menu.status || '0',
|
||||
moduleOrder: moduleOrder, // 添加模块顺序
|
||||
menuOrder: menuIndex, // 添加菜单在当前层级的顺序
|
||||
orderNum: menu.orderNum || 0, // 添加菜单的原始排序号
|
||||
logoUrl: getIconPath(iconKey) // 根据icon名称获取对应图标
|
||||
};
|
||||
|
||||
// 添加调试日志来验证路径构建
|
||||
// 添加调试日志来验证路径构建和排序
|
||||
console.log(
|
||||
`菜单项路径构建: ${menu.meta.title} - 原始path: ${menu.path}, 构建后fullPath: ${fullPath}, parentPath: ${parentPath}, level: ${level}`
|
||||
`菜单项路径构建: ${menu.meta.title} - 原始path: ${menu.path}, 构建后fullPath: ${fullPath}, orderNum: ${menu.orderNum}, parentPath: ${parentPath}, level: ${level}`
|
||||
);
|
||||
|
||||
result.push(menuAction);
|
||||
@@ -498,13 +501,27 @@ const groupMenusByModule = (menus: MenuAction[]): MenuModule[] => {
|
||||
return;
|
||||
}
|
||||
|
||||
// 按orderNum排序前的日志
|
||||
console.log(
|
||||
`${moduleName}模块排序前:`,
|
||||
moduleMenus.map((m) => `${m.title}(${m.orderNum})`)
|
||||
);
|
||||
|
||||
const sortedMenus = moduleMenus.sort((a, b) => (a.orderNum || 0) - (b.orderNum || 0));
|
||||
|
||||
// 按orderNum排序后的日志
|
||||
console.log(
|
||||
`${moduleName}模块排序后:`,
|
||||
sortedMenus.map((m) => `${m.title}(${m.orderNum})`)
|
||||
);
|
||||
|
||||
const module: MenuModule = {
|
||||
id: `module-${moduleName}`,
|
||||
title: moduleName,
|
||||
description: `${moduleName}模块包含${moduleMenus.length}个功能`,
|
||||
color: moduleColors[colorIndex % moduleColors.length],
|
||||
// 菜单项按原始顺序排序,而不是按名称排序
|
||||
menus: moduleMenus.sort((a, b) => (a.menuOrder || 0) - (b.menuOrder || 0))
|
||||
// 菜单项按原始orderNum排序,确保显示顺序正确
|
||||
menus: sortedMenus
|
||||
};
|
||||
modules.push(module);
|
||||
colorIndex++;
|
||||
|
||||
@@ -403,36 +403,47 @@
|
||||
</el-dialog>
|
||||
|
||||
<!-- 简历记录对话框 -->
|
||||
<el-dialog title="简历记录" v-model="applicationOpen" width="900px" append-to-body>
|
||||
<el-dialog title="简历记录" v-model="applicationOpen" width="1200px" append-to-body>
|
||||
<div v-if="currentJob">
|
||||
<div class="job-info-header">
|
||||
<el-descriptions :column="3" border size="small">
|
||||
<el-descriptions-item label="职位名称">{{ currentJob.title }}</el-descriptions-item>
|
||||
<el-descriptions-item label="部门">{{ getDepartmentLabel(currentJob.department) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="申请人数">{{ currentJob.applicantCount }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<div class="job-info-compact">
|
||||
<div class="info-item">
|
||||
<span class="info-label">职位名称:</span>
|
||||
<span class="info-value">{{ currentJob.title }}</span>
|
||||
</div>
|
||||
<div class="info-divider">|</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">部门:</span>
|
||||
<span class="info-value">{{ getDepartmentLabel(currentJob.department) }}</span>
|
||||
</div>
|
||||
<div class="info-divider">|</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">申请人数:</span>
|
||||
<span class="info-value highlight">{{ currentJob.applicantCount }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="application-table mt-4">
|
||||
<el-table v-loading="applicationLoading" :data="applicationList" border>
|
||||
<el-table-column label="应聘者" align="center" prop="applicantName" />
|
||||
<el-table-column label="申请状态" align="center" prop="status" width="120">
|
||||
<div class="application-table mt-4">
|
||||
<el-table v-loading="applicationLoading" :data="applicationList" border size="small" class="compact-table" :row-style="{ height: '32px' }" :cell-style="{ padding: '2px 0' }">
|
||||
<el-table-column label="应聘者" align="center" prop="applicantName" width="110" />
|
||||
<el-table-column label="申请状态" align="center" prop="status" width="110">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getStatusType(scope.row.status)">
|
||||
<el-tag :type="getStatusType(scope.row.status)" size="small">
|
||||
{{ getStatusLabel(scope.row.status) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="联系电话" align="center" prop="contactPhone" />
|
||||
<el-table-column label="联系邮箱" align="center" prop="contactEmail" />
|
||||
<el-table-column label="自我介绍" align="center" prop="selfDescription" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="HR备注" align="center" prop="hrNotes" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="申请时间" align="center" prop="applyTime" width="180">
|
||||
<el-table-column label="联系电话" align="center" prop="contactPhone" width="130" />
|
||||
<el-table-column label="联系邮箱" align="center" prop="contactEmail" width="200" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="自我介绍" align="center" prop="selfDescription" width="180" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="申请时间" align="center" prop="applyTime" width="170">
|
||||
<template #default="scope">
|
||||
<span>{{ parseTime(scope.row.applyTime) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="300">
|
||||
<el-table-column label="HR备注" align="center" prop="hrNotes" width="150" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="130" fixed="right">
|
||||
<template #default="scope">
|
||||
<div class="action-buttons">
|
||||
<el-tooltip content="查看详情" placement="top">
|
||||
@@ -442,8 +453,8 @@
|
||||
<el-button link type="primary" icon="Download" size="small" @click="downloadResume(scope.row.resumePath)"></el-button>
|
||||
</el-tooltip>
|
||||
|
||||
<!-- 状态选择器 -->
|
||||
<div class="status-selector" v-hasPermi="['rs:application:edit']">
|
||||
<!-- 状态选择器已隐藏 -->
|
||||
<!-- <div class="status-selector" v-hasPermi="['rs:application:edit']">
|
||||
<el-select
|
||||
:model-value="scope.row.status"
|
||||
@change="(newStatus: string) => handleStatusChange(scope.row, newStatus)"
|
||||
@@ -452,7 +463,7 @@
|
||||
>
|
||||
<el-option v-for="option in applicationStatusOptions" :key="option.value" :label="option.label" :value="option.value" />
|
||||
</el-select>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -465,6 +476,9 @@
|
||||
v-model:page="applicationQueryParams.pageNum"
|
||||
v-model:limit="applicationQueryParams.pageSize"
|
||||
@pagination="getApplicationList"
|
||||
:page-sizes="[5, 10, 15, 20]"
|
||||
layout="sizes, prev, pager, next, jumper"
|
||||
:small="true"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1839,11 +1853,24 @@ onMounted(() => {
|
||||
.pagination-container {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 16px 20px 0 0 !important;
|
||||
padding: 8px 20px 0 0 !important;
|
||||
background-color: #fff;
|
||||
margin-top: 0px !important;
|
||||
}
|
||||
|
||||
.pagination-container :deep(.el-pagination) {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.pagination-container :deep(.el-pagination .btn-prev),
|
||||
.pagination-container :deep(.el-pagination .btn-next),
|
||||
.pagination-container :deep(.el-pagination .el-pager li) {
|
||||
min-width: 28px;
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* 搜索按钮容器样式 */
|
||||
.search-btn-container {
|
||||
display: flex;
|
||||
@@ -1857,11 +1884,76 @@ onMounted(() => {
|
||||
|
||||
/* 申请记录样式 */
|
||||
.job-info-header {
|
||||
margin-bottom: 16px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
/* 紧凑型职位信息样式 */
|
||||
.job-info-compact {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 6px 10px;
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #e1e8ed;
|
||||
border-radius: 4px;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
min-height: 36px;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
font-size: 13px;
|
||||
color: #606266;
|
||||
font-weight: 500;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
font-size: 13px;
|
||||
color: #303133;
|
||||
font-weight: 600;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.info-value.highlight {
|
||||
color: #409eff;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.info-divider {
|
||||
color: #dcdfe6;
|
||||
font-size: 13px;
|
||||
margin: 0 4px;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.application-table {
|
||||
margin-top: 16px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.application-table .el-table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.application-table .el-table__body-wrapper {
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* 弹窗内容区域优化 */
|
||||
.el-dialog__body {
|
||||
padding: 15px 20px 0 20px !important;
|
||||
}
|
||||
|
||||
.el-dialog__footer {
|
||||
padding: 10px 20px 15px 20px !important;
|
||||
}
|
||||
|
||||
.mt-4 {
|
||||
@@ -1872,12 +1964,15 @@ onMounted(() => {
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
gap: 2px;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
|
||||
.el-button {
|
||||
margin: 0;
|
||||
padding: 2px 6px;
|
||||
font-size: 11px;
|
||||
min-height: 20px;
|
||||
}
|
||||
|
||||
.status-selector {
|
||||
@@ -1897,4 +1992,45 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 紧凑表格样式 */
|
||||
.compact-table {
|
||||
font-size: 13px;
|
||||
|
||||
:deep(.el-table__header) {
|
||||
th {
|
||||
padding: 4px 0;
|
||||
font-size: 13px;
|
||||
height: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-table__body) {
|
||||
td {
|
||||
padding: 4px 0;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.cell) {
|
||||
padding: 0 4px;
|
||||
line-height: 1.3;
|
||||
word-break: keep-all;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* 固定列样式优化 */
|
||||
:deep(.el-table__fixed-right) {
|
||||
box-shadow: -2px 0 6px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
:deep(.el-table__fixed-right-patch) {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
/* 确保固定列正常显示 */
|
||||
:deep(.el-table__fixed-right .el-table__fixed-body-wrapper) {
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -3,8 +3,8 @@
|
||||
<el-card class="box-card">
|
||||
<!-- 审批管理选项卡 -->
|
||||
<el-tabs v-model="activeTab" type="border-card" @tab-click="handleTabClick">
|
||||
<!-- 待审批申请 -->
|
||||
<el-tab-pane label="待审批申请" name="pending">
|
||||
<!-- 出国审批 -->
|
||||
<el-tab-pane label="出国审批" name="pending">
|
||||
<div class="tab-content">
|
||||
<!-- 搜索区域 -->
|
||||
<transition :enter-active-class="animate.searchAnimate.enter" :leave-active-class="animate.searchAnimate.leave">
|
||||
@@ -42,20 +42,6 @@
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="9">
|
||||
<el-form-item label="出国时间">
|
||||
<el-date-picker
|
||||
v-model="pendingDateRange"
|
||||
style="width: 100%"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
value-format="YYYY-MM-DD"
|
||||
@change="handlePendingDateRangeChange"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row>
|
||||
@@ -185,20 +171,6 @@
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="9">
|
||||
<el-form-item label="审批时间">
|
||||
<el-date-picker
|
||||
v-model="recordDateRange"
|
||||
style="width: 100%"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
value-format="YYYY-MM-DD"
|
||||
@change="handleRecordDateRangeChange"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row>
|
||||
@@ -228,9 +200,9 @@
|
||||
<!-- 审批记录表格 -->
|
||||
<el-table v-loading="recordLoading" border :data="recordList" @row-dblclick="handleViewRecord" v-hasPermi="['rs:overseas:list']">
|
||||
<el-table-column label="申请人" align="center" prop="applicantName" width="100" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="所属部门" align="center" prop="department" width="140" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="出国目的" align="center" prop="purposeText" width="100" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="目的地" align="center" prop="destination" width="120" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="所属部门" align="center" prop="department" width="140" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="出国时间" align="center" min-width="220" :show-overflow-tooltip="true">
|
||||
<template #default="scope"> {{ formatDate(scope.row.startDate) }} 至 {{ formatDate(scope.row.endDate) }} </template>
|
||||
</el-table-column>
|
||||
@@ -246,15 +218,7 @@
|
||||
<el-tooltip content="查看详情" placement="top">
|
||||
<el-button link type="primary" icon="View" @click="handleViewRecord(scope.row)" v-hasPermi="['rs:overseas:query']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="查看审批历史" placement="top">
|
||||
<el-button
|
||||
link
|
||||
type="info"
|
||||
icon="List"
|
||||
@click.stop="handleViewApprovalHistory(scope.row)"
|
||||
v-hasPermi="['rs:overseas:query']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<!-- 查看审批历史按钮已隐藏 -->
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -435,16 +399,13 @@ const state = reactive({
|
||||
pendingList: [] as OverseasApplicationVO[],
|
||||
pendingTotal: 0,
|
||||
showPendingSearch: true,
|
||||
pendingDateRange: [] as string[],
|
||||
pendingQueryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
applicantName: '',
|
||||
department: '',
|
||||
destination: '',
|
||||
status: '0', // 只查询待审核状态
|
||||
startDateBegin: '',
|
||||
startDateEnd: ''
|
||||
status: '0' // 只查询待审核状态
|
||||
} as OverseasApplicationQuery,
|
||||
|
||||
// 审批记录列表相关
|
||||
@@ -452,7 +413,6 @@ const state = reactive({
|
||||
recordList: [] as OverseasApprovalVO[],
|
||||
recordTotal: 0,
|
||||
showRecordSearch: true,
|
||||
recordDateRange: [] as string[],
|
||||
recordQueryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
@@ -461,9 +421,7 @@ const state = reactive({
|
||||
approvalStatus: '',
|
||||
applicantName: '',
|
||||
department: '',
|
||||
destination: '',
|
||||
approvalTimeBegin: '',
|
||||
approvalTimeEnd: ''
|
||||
destination: ''
|
||||
} as OverseasApprovalQuery,
|
||||
|
||||
// 查看详情
|
||||
@@ -520,13 +478,11 @@ const {
|
||||
pendingList,
|
||||
pendingTotal,
|
||||
showPendingSearch,
|
||||
pendingDateRange,
|
||||
pendingQueryParams,
|
||||
recordLoading,
|
||||
recordList,
|
||||
recordTotal,
|
||||
showRecordSearch,
|
||||
recordDateRange,
|
||||
recordQueryParams,
|
||||
viewDialog,
|
||||
viewData,
|
||||
@@ -631,28 +587,54 @@ const handleRecordQuery = () => {
|
||||
|
||||
// 重置搜索
|
||||
const resetPendingQuery = () => {
|
||||
proxy.resetForm('pendingQueryForm');
|
||||
state.pendingDateRange = [];
|
||||
state.pendingQueryParams.status = '0'; // 保持查询待审核状态
|
||||
handlePendingQuery();
|
||||
try {
|
||||
// 重置查询参数
|
||||
Object.assign(state.pendingQueryParams, {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
applicantName: '',
|
||||
department: '',
|
||||
destination: '',
|
||||
status: '0' // 保持查询待审核状态
|
||||
});
|
||||
// 重置表单字段
|
||||
const form = proxy.$refs.pendingQueryForm;
|
||||
if (form && typeof form.resetFields === 'function') {
|
||||
form.resetFields();
|
||||
}
|
||||
handlePendingQuery();
|
||||
} catch (error) {
|
||||
console.error('重置查询条件时发生错误:', error);
|
||||
handlePendingQuery();
|
||||
}
|
||||
};
|
||||
|
||||
const resetRecordQuery = () => {
|
||||
proxy.resetForm('recordQueryForm');
|
||||
handleRecordQuery();
|
||||
};
|
||||
|
||||
// 处理日期范围变化
|
||||
const handlePendingDateRangeChange = (dates: string[] | null) => {
|
||||
if (dates && dates.length === 2) {
|
||||
state.pendingQueryParams.startDateBegin = dates[0];
|
||||
state.pendingQueryParams.startDateEnd = dates[1];
|
||||
} else {
|
||||
state.pendingQueryParams.startDateBegin = '';
|
||||
state.pendingQueryParams.startDateEnd = '';
|
||||
try {
|
||||
// 重置查询参数
|
||||
Object.assign(state.recordQueryParams, {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
applicationId: undefined,
|
||||
approvalLevel: '',
|
||||
approvalStatus: '',
|
||||
applicantName: '',
|
||||
department: '',
|
||||
destination: ''
|
||||
});
|
||||
// 重置表单字段
|
||||
const form = proxy.$refs.recordQueryForm;
|
||||
if (form && typeof form.resetFields === 'function') {
|
||||
form.resetFields();
|
||||
}
|
||||
handleRecordQuery();
|
||||
} catch (error) {
|
||||
console.error('重置查询条件时发生错误:', error);
|
||||
handleRecordQuery();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// 查看申请详情
|
||||
const handleViewApplication = async (row: OverseasApplicationVO) => {
|
||||
try {
|
||||
@@ -840,16 +822,7 @@ const getApprovalStatusTagType = (status: string): 'success' | 'warning' | 'dang
|
||||
return typeMap[status] || 'info';
|
||||
};
|
||||
|
||||
// 审批记录日期范围变化处理
|
||||
const handleRecordDateRangeChange = (dates: [string, string] | null) => {
|
||||
if (dates && dates.length === 2) {
|
||||
state.recordQueryParams.approvalTimeBegin = dates[0];
|
||||
state.recordQueryParams.approvalTimeEnd = dates[1];
|
||||
} else {
|
||||
state.recordQueryParams.approvalTimeBegin = '';
|
||||
state.recordQueryParams.approvalTimeEnd = '';
|
||||
}
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -31,19 +31,7 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="9">
|
||||
<el-form-item label="申请时间">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
style="width: 100%"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
@change="handleDateRangeChange"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
</el-row>
|
||||
|
||||
<el-row>
|
||||
@@ -483,6 +471,8 @@ const userStore = useUserStore();
|
||||
|
||||
// 表单引用
|
||||
const applicationFormRef = ref<any>(null);
|
||||
const applicationQueryForm = ref<any>(null);
|
||||
const recordQueryForm = ref<any>(null);
|
||||
|
||||
const state = reactive({
|
||||
// 选项卡相关
|
||||
@@ -498,9 +488,7 @@ const state = reactive({
|
||||
pageSize: 10,
|
||||
destination: '',
|
||||
status: '',
|
||||
purpose: '',
|
||||
applyTimeBegin: '',
|
||||
applyTimeEnd: ''
|
||||
purpose: ''
|
||||
} as OverseasApplicationQuery,
|
||||
|
||||
// 记录列表相关
|
||||
@@ -555,8 +543,7 @@ const state = reactive({
|
||||
status: '0'
|
||||
},
|
||||
|
||||
// 时间范围
|
||||
dateRange: [],
|
||||
|
||||
|
||||
// 字典数据
|
||||
statusOptions: [
|
||||
@@ -603,7 +590,6 @@ const {
|
||||
approvalHistoryLoading,
|
||||
approvalHistoryList,
|
||||
currentApplication,
|
||||
dateRange,
|
||||
statusOptions,
|
||||
purposeOptions,
|
||||
completionStatusOptions
|
||||
@@ -706,30 +692,38 @@ const handleRecordQuery = () => {
|
||||
getRecordList();
|
||||
};
|
||||
|
||||
// 重置搜索
|
||||
// 重置搜索(参考培训审批的正确实现)
|
||||
const resetApplicationQuery = () => {
|
||||
proxy.resetForm('applicationQueryForm');
|
||||
state.dateRange = [];
|
||||
state.applicationQueryParams.applyTimeBegin = '';
|
||||
state.applicationQueryParams.applyTimeEnd = '';
|
||||
handleApplicationQuery();
|
||||
// 重置表单字段显示
|
||||
applicationQueryForm.value?.resetFields();
|
||||
// 重置查询参数
|
||||
Object.assign(state.applicationQueryParams, {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
destination: '',
|
||||
status: '',
|
||||
purpose: '',
|
||||
params: {}
|
||||
});
|
||||
getApplicationList();
|
||||
};
|
||||
|
||||
const resetRecordQuery = () => {
|
||||
proxy.resetForm('recordQueryForm');
|
||||
handleRecordQuery();
|
||||
// 重置表单字段显示
|
||||
recordQueryForm.value?.resetFields();
|
||||
// 重置查询参数
|
||||
Object.assign(state.recordQueryParams, {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
applicantName: '',
|
||||
destination: '',
|
||||
completionStatus: '',
|
||||
params: {}
|
||||
});
|
||||
getRecordList();
|
||||
};
|
||||
|
||||
// 时间范围改变
|
||||
const handleDateRangeChange = (dates: any) => {
|
||||
if (dates && dates.length === 2) {
|
||||
state.applicationQueryParams.applyTimeBegin = dates[0];
|
||||
state.applicationQueryParams.applyTimeEnd = dates[1];
|
||||
} else {
|
||||
state.applicationQueryParams.applyTimeBegin = '';
|
||||
state.applicationQueryParams.applyTimeEnd = '';
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// 新增申请
|
||||
const handleAddApplication = () => {
|
||||
|
||||
@@ -32,9 +32,9 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="9">
|
||||
<el-form-item label="审批时间">
|
||||
<el-form-item label="审批时间" prop="dateRange">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
v-model="queryParams.dateRange"
|
||||
style="width: 100%"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
@@ -369,6 +369,11 @@ const { rs_approval_status, rs_approval_level, rs_overseas_purpose } = proxy.use
|
||||
// 选项卡相关
|
||||
const activeTab = ref('approval');
|
||||
|
||||
// 表单ref
|
||||
const queryFormRef = ref();
|
||||
const recordQueryForm = ref();
|
||||
const approvalRef = ref();
|
||||
|
||||
// 出国审批相关
|
||||
const approvalList = ref([]);
|
||||
const open = ref(false);
|
||||
@@ -381,7 +386,6 @@ const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const title = ref('');
|
||||
const detailData = ref({});
|
||||
const dateRange = ref([]);
|
||||
|
||||
// 审批记录相关
|
||||
const recordList = ref([]);
|
||||
@@ -404,7 +408,8 @@ const data = reactive({
|
||||
approvalTimeBegin: null,
|
||||
approvalTimeEnd: null,
|
||||
applicantName: null, // 申请人姓名搜索
|
||||
purpose: null // 出国目的搜索
|
||||
purpose: null, // 出国目的搜索
|
||||
dateRange: [] // 日期范围
|
||||
},
|
||||
recordQueryParams: {
|
||||
pageNum: 1,
|
||||
@@ -505,11 +510,32 @@ function handleQuery() {
|
||||
|
||||
/** 重置按钮操作 */
|
||||
function resetQuery() {
|
||||
dateRange.value = [];
|
||||
queryParams.value.approvalTimeBegin = null;
|
||||
queryParams.value.approvalTimeEnd = null;
|
||||
proxy.resetForm('queryFormRef');
|
||||
try {
|
||||
// 先手动清空查询参数
|
||||
queryParams.value = {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
applicationId: null,
|
||||
approverId: null,
|
||||
approverName: null,
|
||||
approvalLevel: null,
|
||||
approvalStatus: null,
|
||||
approvalTimeBegin: null,
|
||||
approvalTimeEnd: null,
|
||||
applicantName: null,
|
||||
purpose: null,
|
||||
dateRange: []
|
||||
};
|
||||
// 再重置表单
|
||||
if (queryFormRef.value && typeof queryFormRef.value.resetFields === 'function') {
|
||||
queryFormRef.value.resetFields();
|
||||
}
|
||||
handleQuery();
|
||||
} catch (error) {
|
||||
console.error('重置查询条件时发生错误:', error);
|
||||
// 即使出错也要重新查询
|
||||
handleQuery();
|
||||
}
|
||||
}
|
||||
|
||||
/** 审批记录搜索按钮操作 */
|
||||
@@ -520,8 +546,27 @@ function handleRecordQuery() {
|
||||
|
||||
/** 审批记录重置按钮操作 */
|
||||
function resetRecordQuery() {
|
||||
proxy.resetForm('recordQueryForm');
|
||||
try {
|
||||
// 先手动清空查询参数
|
||||
recordQueryParams.value = {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
applicantName: null,
|
||||
approverName: null,
|
||||
approvalStatus: null,
|
||||
approvalTimeBegin: null,
|
||||
approvalTimeEnd: null
|
||||
};
|
||||
// 再重置表单
|
||||
if (recordQueryForm.value && typeof recordQueryForm.value.resetFields === 'function') {
|
||||
recordQueryForm.value.resetFields();
|
||||
}
|
||||
handleRecordQuery();
|
||||
} catch (error) {
|
||||
console.error('重置查询条件时发生错误:', error);
|
||||
// 即使出错也要重新查询
|
||||
handleRecordQuery();
|
||||
}
|
||||
}
|
||||
|
||||
// 取消按钮
|
||||
@@ -542,7 +587,7 @@ function reset() {
|
||||
approvalOpinion: null,
|
||||
approvalTime: null
|
||||
};
|
||||
proxy.resetForm('approvalRef');
|
||||
approvalRef.value?.resetFields();
|
||||
}
|
||||
|
||||
// 多选框选中数据
|
||||
@@ -596,7 +641,7 @@ function handleViewRecord(row) {
|
||||
|
||||
/** 提交按钮 */
|
||||
function submitForm() {
|
||||
proxy.$refs['approvalRef'].validate((valid) => {
|
||||
approvalRef.value?.validate((valid) => {
|
||||
if (valid) {
|
||||
if (form.value.approvalId != null) {
|
||||
updateOverseasApproval(form.value).then((response) => {
|
||||
|
||||
@@ -994,6 +994,7 @@ const showRecordSearch = ref(true);
|
||||
courseName: '',
|
||||
userId: undefined as string | number | undefined,
|
||||
userName: '',
|
||||
location: '',
|
||||
department: '',
|
||||
completionStatus: '',
|
||||
includeApprovedApplications: true, // 包含已通过审批的申请数据
|
||||
|
||||
@@ -147,14 +147,6 @@
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="审批结果" prop="status">
|
||||
<el-select v-model="approvalRecordQueryParams.status" placeholder="请选择审批结果" clearable style="width: 100%">
|
||||
<el-option label="已通过" value="1" />
|
||||
<el-option label="已拒绝" value="2" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row>
|
||||
@@ -206,14 +198,11 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审批意见" align="center" prop="opinion" :show-overflow-tooltip="true" min-width="150" />
|
||||
<el-table-column label="操作" align="center" width="160">
|
||||
<el-table-column label="操作" align="center" width="80">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="查看详情" placement="top">
|
||||
<el-button link type="primary" icon="View" @click="handleViewApprovalRecord(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="查看审批历史" placement="top">
|
||||
<el-button link type="info" icon="List" @click.stop="handleViewApprovalHistory(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -600,7 +589,6 @@ const approvalRecordQueryParams = reactive({
|
||||
pageSize: 10,
|
||||
courseName: '',
|
||||
userName: '',
|
||||
status: '',
|
||||
includeApprovedApplications: true, // 包含已通过审批的申请数据
|
||||
params: {}
|
||||
});
|
||||
@@ -794,7 +782,7 @@ const getApprovalRecordList = async () => {
|
||||
// 获取所有审批记录(不限于当前用户审批过的)
|
||||
const queryParams = {
|
||||
...approvalRecordQueryParams,
|
||||
status: approvalRecordQueryParams.status || '1,2', // 默认获取已通过和已拒绝的记录
|
||||
status: '1,2', // 获取已通过和已拒绝的记录
|
||||
includeApprovedApplications: true // 确保包含已通过状态的培训申请数据
|
||||
};
|
||||
const response = await listAllApprovalRecords(queryParams);
|
||||
@@ -823,7 +811,6 @@ const resetApprovalRecordQuery = () => {
|
||||
pageSize: 10,
|
||||
courseName: '',
|
||||
userName: '',
|
||||
status: '',
|
||||
includeApprovedApplications: true, // 保持包含已通过审批的申请数据
|
||||
params: {}
|
||||
});
|
||||
@@ -874,7 +861,7 @@ const handleExportApprovalRecord = async () => {
|
||||
try {
|
||||
const queryParams = {
|
||||
...approvalRecordQueryParams,
|
||||
status: approvalRecordQueryParams.status || '1,2', // 默认导出已通过和已拒绝的记录
|
||||
status: '1,2', // 导出已通过和已拒绝的记录
|
||||
includeApprovedApplications: true // 确保包含已通过状态的培训申请数据
|
||||
};
|
||||
const response = await exportApprovalRecord(queryParams);
|
||||
@@ -916,10 +903,11 @@ const handleViewApprovalHistory = async (row: any) => {
|
||||
}
|
||||
|
||||
// 设置当前应用信息
|
||||
// 兼容两种数据结构:培训申请(courseName, userName) 和 培训记录(courseTitle, applicationUserName)
|
||||
Object.assign(currentApplication, {
|
||||
applicationId: applicationId,
|
||||
courseName: row.courseName || '未知课程',
|
||||
userName: row.userName || '未知用户',
|
||||
courseName: row.courseName || row.courseTitle || '未知课程',
|
||||
userName: row.userName || row.applicationUserName || '未知用户',
|
||||
applyTime: row.applyTime || '',
|
||||
status: row.status || '0'
|
||||
});
|
||||
|
||||