Merge branch 'master' of http://47.99.164.66:3000/hzdyjs-xg/all-project
This commit is contained in:
commit
f4bb5e3cdd
57 files changed
+1508
-1532
No files matched your search
@@ -414,6 +414,7 @@
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- 社团管理模块 -->
|
||||
<!-- <dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
|
||||
@@ -112,6 +112,7 @@
|
||||
<groupId>org.dromara</groupId>
|
||||
<artifactId>ruoyi-xg</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 学工管理-学生信息管理模块 -->
|
||||
<!-- <dependency>
|
||||
<groupId>org.dromara</groupId>
|
||||
|
||||
+4
@@ -4,6 +4,8 @@ import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.dromara.common.mybatis.core.domain.BaseEntity;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
@@ -25,6 +27,7 @@ public class JcbtScholarshipBatch extends BaseEntity {
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId(value = "id")
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
@@ -101,6 +104,7 @@ public class JcbtScholarshipBatch extends BaseEntity {
|
||||
* 租户ID
|
||||
*/
|
||||
@TableField("tenant_id")
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long tenantId;
|
||||
|
||||
/**
|
||||
|
||||
-6
@@ -48,12 +48,6 @@ public class JcbtWorkStudyPosition extends BaseEntity {
|
||||
*/
|
||||
private String workTime;
|
||||
|
||||
/**
|
||||
* 工作地点
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private String workLocation;
|
||||
|
||||
/**
|
||||
* 岗位有效期开始
|
||||
*/
|
||||
|
||||
+4
@@ -8,6 +8,8 @@ import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import jakarta.validation.constraints.*;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
@@ -27,6 +29,7 @@ public class JcbtScholarshipBatchBo extends BaseEntity {
|
||||
* 主键ID
|
||||
*/
|
||||
@NotNull(message = "主键ID不能为空", groups = { EditGroup.class })
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
@@ -121,6 +124,7 @@ public class JcbtScholarshipBatchBo extends BaseEntity {
|
||||
/**
|
||||
* 租户ID
|
||||
*/
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long tenantId;
|
||||
|
||||
}
|
||||
-7
@@ -56,13 +56,6 @@ public class JcbtWorkStudyPositionBo extends BaseEntity {
|
||||
@Size(max = 200, message = "工作时间描述长度不能超过200个字符")
|
||||
private String workTime;
|
||||
|
||||
/**
|
||||
* 工作地点
|
||||
*/
|
||||
@NotBlank(message = "工作地点不能为空", groups = { AddGroup.class, EditGroup.class })
|
||||
@Size(max = 200, message = "工作地点描述长度不能超过200个字符")
|
||||
private String workLocation;
|
||||
|
||||
/**
|
||||
* 岗位有效期开始
|
||||
*/
|
||||
|
||||
+6
@@ -8,6 +8,8 @@ import org.dromara.common.excel.convert.ExcelDictConvert;
|
||||
import io.github.linpeilie.annotations.AutoMapper;
|
||||
import lombok.Data;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
@@ -32,6 +34,7 @@ public class JcbtScholarshipBatchVo implements Serializable {
|
||||
* 主键ID
|
||||
*/
|
||||
@ExcelProperty(value = "主键ID")
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
@@ -130,6 +133,7 @@ public class JcbtScholarshipBatchVo implements Serializable {
|
||||
/**
|
||||
* 创建者
|
||||
*/
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long createBy;
|
||||
|
||||
/**
|
||||
@@ -141,6 +145,7 @@ public class JcbtScholarshipBatchVo implements Serializable {
|
||||
/**
|
||||
* 更新者
|
||||
*/
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long updateBy;
|
||||
|
||||
/**
|
||||
@@ -157,6 +162,7 @@ public class JcbtScholarshipBatchVo implements Serializable {
|
||||
/**
|
||||
* 租户ID
|
||||
*/
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long tenantId;
|
||||
|
||||
}
|
||||
+172
-4
@@ -3,9 +3,11 @@ package org.dromara.jcbt.service.impl;
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.dromara.common.core.utils.MapstructUtils;
|
||||
import org.dromara.common.core.utils.StringUtils;
|
||||
@@ -30,6 +32,7 @@ import java.util.List;
|
||||
* @author ruoyi
|
||||
* @date 2025-01-27
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class JcbtScholarshipBatchServiceImpl implements IJcbtScholarshipBatchService {
|
||||
@@ -112,9 +115,52 @@ public class JcbtScholarshipBatchServiceImpl implements IJcbtScholarshipBatchSer
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean updateByBo(JcbtScholarshipBatchBo bo) {
|
||||
// 添加调试日志 - 检查BO的ID
|
||||
log.info("=== 修改操作调试 ===");
|
||||
log.info("输入BO的ID: {}, 类型: {}", bo.getId(), bo.getId() != null ? bo.getId().getClass().getSimpleName() : "null");
|
||||
log.info("输入BO的批次编码: {}", bo.getBatchCode());
|
||||
|
||||
JcbtScholarshipBatch update = MapstructUtils.convert(bo, JcbtScholarshipBatch.class);
|
||||
|
||||
// 添加调试日志 - 检查转换后实体的ID
|
||||
log.info("转换后实体的ID: {}, 类型: {}", update.getId(), update.getId() != null ? update.getId().getClass().getSimpleName() : "null");
|
||||
log.info("转换后实体的批次编码: {}", update.getBatchCode());
|
||||
|
||||
validEntityBeforeSave(update);
|
||||
boolean flag = baseMapper.updateById(update) > 0;
|
||||
|
||||
// 添加详细调试 - 检查updateById前的状态
|
||||
log.info("=== updateById执行前检查 ===");
|
||||
log.info("实体ID是否为null: {}", update.getId() == null);
|
||||
log.info("实体ID值: {}", update.getId());
|
||||
log.info("实体批次编码: {}", update.getBatchCode());
|
||||
|
||||
// 先检查数据库中是否存在该记录
|
||||
JcbtScholarshipBatch existingRecord = baseMapper.selectById(update.getId());
|
||||
log.info("数据库中是否存在该记录: {}", existingRecord != null);
|
||||
if (existingRecord != null) {
|
||||
log.info("数据库中的记录: ID={}, 批次编码={}", existingRecord.getId(), existingRecord.getBatchCode());
|
||||
}
|
||||
|
||||
log.info("=== 开始执行updateById ===");
|
||||
|
||||
// 使用更安全的更新策略,避免唯一键约束冲突
|
||||
boolean flag;
|
||||
try {
|
||||
flag = baseMapper.updateById(update) > 0;
|
||||
log.info("updateById执行成功: {}", flag);
|
||||
} catch (Exception e) {
|
||||
log.error("updateById执行失败,错误信息: {}", e.getMessage());
|
||||
|
||||
// 如果是唯一键冲突,尝试使用自定义更新SQL
|
||||
if (e.getMessage().contains("Duplicate entry") || e.getMessage().contains("duplicate key")) {
|
||||
log.info("检测到唯一键冲突,尝试使用自定义更新策略");
|
||||
flag = updateByIdSafely(update);
|
||||
log.info("自定义更新策略执行结果: {}", flag);
|
||||
} else {
|
||||
// 其他异常直接抛出
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
if (flag) {
|
||||
// 更新批次类型关联关系
|
||||
@@ -129,8 +175,17 @@ public class JcbtScholarshipBatchServiceImpl implements IJcbtScholarshipBatchSer
|
||||
* 保存前的数据校验
|
||||
*/
|
||||
private void validEntityBeforeSave(JcbtScholarshipBatch entity){
|
||||
// 添加调试日志 - 检查校验时的ID
|
||||
log.info("=== 数据校验调试 ===");
|
||||
log.info("校验时实体的ID: {}, 类型: {}", entity.getId(), entity.getId() != null ? entity.getId().getClass().getSimpleName() : "null");
|
||||
log.info("校验时实体的批次编码: {}", entity.getBatchCode());
|
||||
|
||||
// 批次编码重复性校验
|
||||
if (!checkBatchCodeUnique(entity.getBatchCode(), entity.getId())) {
|
||||
boolean isUnique = checkBatchCodeUnique(entity.getBatchCode(), entity.getId());
|
||||
log.info("唯一性校验结果: {}", isUnique);
|
||||
|
||||
if (!isUnique) {
|
||||
log.error("批次编码唯一性校验失败 - 编码: {}, ID: {}", entity.getBatchCode(), entity.getId());
|
||||
throw new RuntimeException("批次编码已存在");
|
||||
}
|
||||
|
||||
@@ -208,12 +263,125 @@ public class JcbtScholarshipBatchServiceImpl implements IJcbtScholarshipBatchSer
|
||||
*/
|
||||
@Override
|
||||
public Boolean checkBatchCodeUnique(String batchCode, Long id) {
|
||||
boolean exist = baseMapper.exists(Wrappers.<JcbtScholarshipBatch>lambdaQuery()
|
||||
// 添加调试日志 - 检查唯一性校验逻辑
|
||||
log.info("=== 唯一性校验调试 ===");
|
||||
log.info("校验参数 - 批次编码: {}, ID: {}", batchCode, id);
|
||||
log.info("ID是否非空: {}", ObjectUtil.isNotNull(id));
|
||||
|
||||
// 获取当前租户ID
|
||||
String tenantId = LoginHelper.getTenantId();
|
||||
log.info("当前租户ID: {}", tenantId);
|
||||
|
||||
LambdaQueryWrapper<JcbtScholarshipBatch> queryWrapper = Wrappers.<JcbtScholarshipBatch>lambdaQuery()
|
||||
.eq(JcbtScholarshipBatch::getBatchCode, batchCode)
|
||||
.ne(ObjectUtil.isNotNull(id), JcbtScholarshipBatch::getId, id));
|
||||
.eq(JcbtScholarshipBatch::getDeleted, 0); // 关键修复:只检查未删除的记录
|
||||
|
||||
// 如果是修改操作(ID不为空),排除当前记录
|
||||
if (ObjectUtil.isNotNull(id)) {
|
||||
queryWrapper.ne(JcbtScholarshipBatch::getId, id);
|
||||
log.info("修改操作 - 排除当前记录ID: {}", id);
|
||||
} else {
|
||||
log.info("新增操作 - 不排除任何记录");
|
||||
}
|
||||
|
||||
// 查询数量而不是存在性,以便调试
|
||||
long count = baseMapper.selectCount(queryWrapper);
|
||||
boolean exist = count > 0;
|
||||
|
||||
log.info("数据库查询结果 - 相同编码的其他未删除记录数量: {}", count);
|
||||
log.info("数据库查询结果 - 存在相同编码的其他未删除记录: {}", exist);
|
||||
log.info("唯一性校验最终结果: {}", !exist);
|
||||
|
||||
// 详细分析重复记录情况
|
||||
if (count > 0) {
|
||||
log.error("=== 发现重复记录,详细查询 ===");
|
||||
List<JcbtScholarshipBatch> duplicateRecords = baseMapper.selectList(queryWrapper);
|
||||
for (JcbtScholarshipBatch record : duplicateRecords) {
|
||||
log.error("重复记录详情 - ID: {}, 批次编码: {}, 是否删除: {}",
|
||||
record.getId(), record.getBatchCode(), record.getDeleted());
|
||||
}
|
||||
|
||||
// 检查是否为ID不匹配问题
|
||||
if (ObjectUtil.isNotNull(id)) {
|
||||
boolean foundExactMatch = duplicateRecords.stream()
|
||||
.anyMatch(record -> record.getId().equals(id));
|
||||
if (!foundExactMatch) {
|
||||
log.error("=== ID不匹配问题 ===");
|
||||
log.error("请求修改的ID: {}", id);
|
||||
log.error("数据库中实际存在的ID: {}",
|
||||
duplicateRecords.stream().map(JcbtScholarshipBatch::getId).toList());
|
||||
throw new RuntimeException(String.format(
|
||||
"记录ID不匹配!请求ID: %s,数据库实际ID: %s。请刷新页面重新获取最新数据。",
|
||||
id, duplicateRecords.get(0).getId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return !exist;
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全更新方法 - 避免唯一键约束冲突
|
||||
* 使用指定字段更新,绕过可能的唯一键约束问题
|
||||
*/
|
||||
private Boolean updateByIdSafely(JcbtScholarshipBatch update) {
|
||||
log.info("=== 执行安全更新策略 ===");
|
||||
|
||||
// 构建更新条件
|
||||
LambdaUpdateWrapper<JcbtScholarshipBatch> updateWrapper = Wrappers.lambdaUpdate();
|
||||
updateWrapper.eq(JcbtScholarshipBatch::getId, update.getId());
|
||||
|
||||
// 只更新非唯一键相关的字段,避免约束冲突
|
||||
if (update.getBatchName() != null) {
|
||||
updateWrapper.set(JcbtScholarshipBatch::getBatchName, update.getBatchName());
|
||||
}
|
||||
if (update.getAcademicYear() != null) {
|
||||
updateWrapper.set(JcbtScholarshipBatch::getAcademicYear, update.getAcademicYear());
|
||||
}
|
||||
if (update.getSemester() != null) {
|
||||
updateWrapper.set(JcbtScholarshipBatch::getSemester, update.getSemester());
|
||||
}
|
||||
if (update.getBatchType() != null) {
|
||||
updateWrapper.set(JcbtScholarshipBatch::getBatchType, update.getBatchType());
|
||||
}
|
||||
if (update.getStartTime() != null) {
|
||||
updateWrapper.set(JcbtScholarshipBatch::getStartTime, update.getStartTime());
|
||||
}
|
||||
if (update.getEndTime() != null) {
|
||||
updateWrapper.set(JcbtScholarshipBatch::getEndTime, update.getEndTime());
|
||||
}
|
||||
if (update.getApplyStartTime() != null) {
|
||||
updateWrapper.set(JcbtScholarshipBatch::getApplyStartTime, update.getApplyStartTime());
|
||||
}
|
||||
if (update.getApplyEndTime() != null) {
|
||||
updateWrapper.set(JcbtScholarshipBatch::getApplyEndTime, update.getApplyEndTime());
|
||||
}
|
||||
if (update.getReviewStartTime() != null) {
|
||||
updateWrapper.set(JcbtScholarshipBatch::getReviewStartTime, update.getReviewStartTime());
|
||||
}
|
||||
if (update.getReviewEndTime() != null) {
|
||||
updateWrapper.set(JcbtScholarshipBatch::getReviewEndTime, update.getReviewEndTime());
|
||||
}
|
||||
if (update.getStatus() != null) {
|
||||
updateWrapper.set(JcbtScholarshipBatch::getStatus, update.getStatus());
|
||||
}
|
||||
if (update.getDescription() != null) {
|
||||
updateWrapper.set(JcbtScholarshipBatch::getDescription, update.getDescription());
|
||||
}
|
||||
if (update.getNoticeContent() != null) {
|
||||
updateWrapper.set(JcbtScholarshipBatch::getNoticeContent, update.getNoticeContent());
|
||||
}
|
||||
|
||||
// 更新基础字段
|
||||
updateWrapper.set(JcbtScholarshipBatch::getUpdateBy, update.getUpdateBy());
|
||||
updateWrapper.set(JcbtScholarshipBatch::getUpdateTime, update.getUpdateTime());
|
||||
|
||||
int result = baseMapper.update(null, updateWrapper);
|
||||
log.info("安全更新策略影响行数: {}", result);
|
||||
|
||||
return result > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更改批次状态
|
||||
*/
|
||||
|
||||
+40
-10
@@ -45,33 +45,63 @@ public class JcbtScholarshipBatchTypeServiceImpl implements IJcbtScholarshipBatc
|
||||
return true; // 空列表视为成功
|
||||
}
|
||||
|
||||
// 先删除原有关联关系
|
||||
// 先逻辑删除原有关联关系
|
||||
deleteByBatchId(batchId);
|
||||
|
||||
// 批量插入新的关联关系
|
||||
List<JcbtScholarshipBatchType> batchTypes = new ArrayList<>();
|
||||
// 智能插入新的关联关系(处理逻辑删除冲突)
|
||||
int sortOrder = 1;
|
||||
for (Long typeId : scholarshipTypeIds) {
|
||||
insertOrRestoreBatchType(batchId, typeId, tenantId, sortOrder++);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 智能插入或恢复批次类型关联记录
|
||||
* 如果存在已逻辑删除的记录,则恢复;否则插入新记录
|
||||
*
|
||||
* 核心逻辑:
|
||||
* 1. 先查询是否存在已逻辑删除的记录(deleted=1)
|
||||
* 2. 如果存在,则更新该记录为未删除状态(deleted=0),避免唯一键冲突
|
||||
* 3. 如果不存在,则正常插入新记录
|
||||
*/
|
||||
private void insertOrRestoreBatchType(Long batchId, Long typeId, String tenantId, int sortOrder) {
|
||||
// 查询是否存在已逻辑删除的记录(关键:包含deleted=1的记录)
|
||||
LambdaQueryWrapper<JcbtScholarshipBatchType> queryWrapper = Wrappers.lambdaQuery();
|
||||
queryWrapper.eq(JcbtScholarshipBatchType::getBatchId, batchId);
|
||||
queryWrapper.eq(JcbtScholarshipBatchType::getScholarshipTypeId, typeId);
|
||||
queryWrapper.eq(JcbtScholarshipBatchType::getTenantId, tenantId);
|
||||
queryWrapper.eq(JcbtScholarshipBatchType::getDeleted, 1); // 仅查询已删除的记录
|
||||
queryWrapper.last("LIMIT 1"); // 提升性能,只取一条记录
|
||||
|
||||
JcbtScholarshipBatchType deletedRecord = baseMapper.selectOne(queryWrapper);
|
||||
|
||||
if (deletedRecord != null) {
|
||||
// 策略1:恢复已删除的记录,避免唯一键冲突
|
||||
deletedRecord.setIsActive(1);
|
||||
deletedRecord.setSortOrder(sortOrder);
|
||||
deletedRecord.setUpdateBy(LoginHelper.getUserId());
|
||||
deletedRecord.setUpdateTime(new Date());
|
||||
deletedRecord.setDeleted(0); // 关键:恢复为未删除状态
|
||||
|
||||
baseMapper.updateById(deletedRecord);
|
||||
} else {
|
||||
// 策略2:不存在已删除记录,正常插入新记录
|
||||
JcbtScholarshipBatchType batchType = new JcbtScholarshipBatchType();
|
||||
batchType.setBatchId(batchId);
|
||||
batchType.setScholarshipTypeId(typeId);
|
||||
batchType.setIsActive(1);
|
||||
batchType.setSortOrder(sortOrder++);
|
||||
batchType.setSortOrder(sortOrder);
|
||||
batchType.setTenantId(tenantId);
|
||||
batchType.setCreateBy(LoginHelper.getUserId());
|
||||
batchType.setCreateTime(new Date());
|
||||
batchType.setUpdateBy(LoginHelper.getUserId());
|
||||
batchType.setUpdateTime(new Date());
|
||||
batchType.setDeleted(0);
|
||||
batchTypes.add(batchType);
|
||||
}
|
||||
|
||||
// 批量插入
|
||||
for (JcbtScholarshipBatchType batchType : batchTypes) {
|
||||
baseMapper.insert(batchType);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+7
-9
@@ -6,7 +6,6 @@ CREATE TABLE jcbt_work_study_position (
|
||||
position_scope VARCHAR(20) DEFAULT 'COLLEGE' COMMENT '岗位发布范围(COLLEGE-本学院,UNIVERSITY-全校)',
|
||||
required_count INT NOT NULL COMMENT '需求人数',
|
||||
work_time VARCHAR(200) NOT NULL COMMENT '工作时间',
|
||||
work_location VARCHAR(200) NOT NULL COMMENT '工作地点',
|
||||
start_date DATETIME NOT NULL COMMENT '岗位有效期开始',
|
||||
end_date DATETIME NOT NULL COMMENT '岗位有效期结束',
|
||||
position_requirements TEXT COMMENT '岗位要求',
|
||||
@@ -37,45 +36,44 @@ CREATE TABLE jcbt_work_study_position (
|
||||
|
||||
-- 插入测试数据
|
||||
INSERT INTO jcbt_work_study_position (
|
||||
position_name, position_scope, required_count, work_time, work_location,
|
||||
position_name, position_scope, required_count, work_time,
|
||||
start_date, end_date, position_requirements, salary_info,
|
||||
contact_teacher, contact_phone, college_id, college_name, status, current_count
|
||||
) VALUES
|
||||
(
|
||||
'图书馆助理', 'UNIVERSITY', 5, '周一至周五 14:00-17:00', '图书馆一楼服务台',
|
||||
'图书馆助理', 'UNIVERSITY', 5, '周一至周五 14:00-17:00',
|
||||
'2025-01-01 00:00:00', '2025-06-30 23:59:59',
|
||||
'1. 熟悉图书馆基本业务流程;2. 责任心强,工作认真细致;3. 具备良好的沟通能力',
|
||||
'15元/小时,按月发放',
|
||||
'张老师', '13800138001', 1, '信息工程学院', 'PUBLISHED', 2
|
||||
),
|
||||
(
|
||||
'实验室管理员', 'COLLEGE', 3, '周一、三、五 16:00-18:00', '信息楼A301实验室',
|
||||
'实验室管理员', 'COLLEGE', 3, '周一、三、五 16:00-18:00',
|
||||
'2025-01-01 00:00:00', '2025-06-30 23:59:59',
|
||||
'1. 计算机相关专业优先;2. 熟悉实验室设备操作;3. 有相关工作经验者优先',
|
||||
'18元/小时,按月发放',
|
||||
'李老师', '13800138002', 1, '信息工程学院', 'RECRUITING', 1
|
||||
),
|
||||
(
|
||||
'教学助理', 'UNIVERSITY', 8, '根据课程安排,灵活调整', '各教学楼教室',
|
||||
'教学助理', 'UNIVERSITY', 8, '根据课程安排,灵活调整',
|
||||
'2025-01-01 00:00:00', '2025-06-30 23:59:59',
|
||||
'1. 学习成绩优良;2. 有较强的组织协调能力;3. 能够辅助教师完成教学任务',
|
||||
'12元/小时,按月发放',
|
||||
'王老师', '13800138003', 2, '经济管理学院', 'PUBLISHED', 5
|
||||
),
|
||||
(
|
||||
'食堂服务员', 'UNIVERSITY', 10, '11:00-13:00, 17:00-19:00', '学生食堂各楼层',
|
||||
'食堂服务员', 'UNIVERSITY', 10, '11:00-13:00, 17:00-19:00',
|
||||
'2025-01-01 00:00:00', '2025-06-30 23:59:59',
|
||||
'1. 身体健康,无传染性疾病;2. 工作积极主动;3. 能够承受一定的工作强度',
|
||||
'14元/小时,按月发放',
|
||||
'刘老师', '13800138004', 3, '后勤服务处', 'RECRUITING', 7
|
||||
),
|
||||
(
|
||||
'宿舍管理助理', 'COLLEGE', 6, '18:00-22:00(工作日)', '学生宿舍各楼栋',
|
||||
'宿舍管理助理', 'COLLEGE', 6, '18:00-22:00(工作日)',
|
||||
'2025-01-01 00:00:00', '2025-06-30 23:59:59',
|
||||
'1. 住校学生优先;2. 有责任心,能够处理突发情况;3. 沟通能力强',
|
||||
'13元/小时,按月发放',
|
||||
'陈老师', '13800138005', 4, '学生工作处', 'PUBLISHED', 3
|
||||
);
|
||||
|
||||
-- 如果表已存在,添加工作地点字段的ALTER语句
|
||||
-- ALTER TABLE jcbt_work_study_position ADD COLUMN work_location VARCHAR(200) NOT NULL COMMENT '工作地点' AFTER work_time;
|
||||
-- 注意:工作地点字段已从表结构中移除,不再需要
|
||||
+10
@@ -144,16 +144,19 @@ public class ZsjyGraduateInfoBo extends BaseEntity {
|
||||
/**
|
||||
* 邮箱地址
|
||||
*/
|
||||
@Size(max = 100, message = "邮箱地址长度不能超过100个字符", groups = { AddGroup.class, EditGroup.class })
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* QQ号码
|
||||
*/
|
||||
@Size(max = 20, message = "QQ号码长度不能超过20个字符", groups = { AddGroup.class, EditGroup.class })
|
||||
private String qqNumber;
|
||||
|
||||
/**
|
||||
* 微信号码
|
||||
*/
|
||||
@Size(max = 30, message = "微信号码长度不能超过30个字符", groups = { AddGroup.class, EditGroup.class })
|
||||
private String wechatNumber;
|
||||
|
||||
/**
|
||||
@@ -174,11 +177,14 @@ public class ZsjyGraduateInfoBo extends BaseEntity {
|
||||
/**
|
||||
* 详细地址
|
||||
*/
|
||||
@Size(max = 200, message = "详细地址长度不能超过200个字符", groups = { AddGroup.class, EditGroup.class })
|
||||
private String detailAddress;
|
||||
|
||||
/**
|
||||
* 邮政编码
|
||||
*/
|
||||
@Size(max = 10, message = "邮政编码长度不能超过10个字符", groups = { AddGroup.class, EditGroup.class })
|
||||
@Pattern(regexp = "^\\d{6}$", message = "邮政编码格式不正确,应为6位数字", groups = { AddGroup.class, EditGroup.class })
|
||||
private String postalCode;
|
||||
|
||||
/**
|
||||
@@ -190,6 +196,7 @@ public class ZsjyGraduateInfoBo extends BaseEntity {
|
||||
/**
|
||||
* 就业单位名称
|
||||
*/
|
||||
@Size(max = 100, message = "就业单位名称长度不能超过100个字符", groups = { AddGroup.class, EditGroup.class })
|
||||
private String employmentCompany;
|
||||
|
||||
/**
|
||||
@@ -200,6 +207,7 @@ public class ZsjyGraduateInfoBo extends BaseEntity {
|
||||
/**
|
||||
* 工作职位
|
||||
*/
|
||||
@Size(max = 50, message = "工作职位长度不能超过50个字符", groups = { AddGroup.class, EditGroup.class })
|
||||
private String jobPosition;
|
||||
|
||||
/**
|
||||
@@ -220,11 +228,13 @@ public class ZsjyGraduateInfoBo extends BaseEntity {
|
||||
/**
|
||||
* 升学院校
|
||||
*/
|
||||
@Size(max = 100, message = "升学院校长度不能超过100个字符", groups = { AddGroup.class, EditGroup.class })
|
||||
private String furtherSchool;
|
||||
|
||||
/**
|
||||
* 升学专业
|
||||
*/
|
||||
@Size(max = 50, message = "升学专业长度不能超过50个字符", groups = { AddGroup.class, EditGroup.class })
|
||||
private String furtherMajor;
|
||||
|
||||
/**
|
||||
|
||||
@@ -90,74 +90,72 @@
|
||||
</view>
|
||||
|
||||
<!-- 位置信息卡片 -->
|
||||
<view class="location-card" v-if="currentLocation" style="display: none;">
|
||||
<view class="location-card">
|
||||
<view class="card-header">
|
||||
<view class="header-left">
|
||||
<u-icon name="map-pin" size="18" color="#f56c6c"></u-icon>
|
||||
<text class="card-title">当前位置</text>
|
||||
</view>
|
||||
<u-button
|
||||
type="info"
|
||||
size="mini"
|
||||
text="重新定位"
|
||||
@click="refreshLocation"
|
||||
:loading="locationLoading"
|
||||
class="refresh-btn"
|
||||
></u-button>
|
||||
</view>
|
||||
|
||||
<!-- 地址信息 -->
|
||||
<view class="location-address">
|
||||
<u-icon name="location" size="14" color="#909399" class="address-icon"></u-icon>
|
||||
<text class="address-text">{{ currentLocation.address }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 坐标信息 -->
|
||||
<view class="coordinates-section">
|
||||
<view class="coord-row">
|
||||
<view class="coord-item">
|
||||
<text class="coord-label">经度</text>
|
||||
<text class="coord-value">{{ currentLocation.longitude.toFixed(6) }}</text>
|
||||
</view>
|
||||
<view class="coord-divider"></view>
|
||||
<view class="coord-item">
|
||||
<text class="coord-label">纬度</text>
|
||||
<text class="coord-value">{{ currentLocation.latitude.toFixed(6) }}</text>
|
||||
</view>
|
||||
|
||||
<text class="card-title">签到位置</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 精度信息 -->
|
||||
<view class="accuracy-section" v-if="currentLocation.accuracy">
|
||||
<view class="accuracy-info">
|
||||
<u-icon name="target" size="14" color="#67c23a" class="accuracy-icon"></u-icon>
|
||||
<text class="accuracy-text">定位精度 {{ currentLocation.accuracy.toFixed(0) }}米</text>
|
||||
<u-tag
|
||||
:text="getAccuracyStatus(currentLocation.accuracy).text"
|
||||
:type="getAccuracyStatus(currentLocation.accuracy).type"
|
||||
size="mini"
|
||||
class="accuracy-tag"
|
||||
></u-tag>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<view class="location-actions">
|
||||
<!-- 定位按钮 -->
|
||||
<view class="location-row">
|
||||
<u-button
|
||||
type="primary"
|
||||
size="default"
|
||||
plain
|
||||
@click="openLocationInMap"
|
||||
class="map-btn"
|
||||
plain
|
||||
@click="handleGetLocation"
|
||||
:loading="locationLoading"
|
||||
class="location-btn"
|
||||
>
|
||||
<u-icon name="map" size="16" color="#2979ff" />
|
||||
<text class="btn-text">在地图中查看</text>
|
||||
<u-icon name="map-fill" size="20" color="#2979ff" />
|
||||
<text style="margin-left: 8rpx">
|
||||
{{ checkIsWechatBrowser() ? '微信定位' : '定位' }}
|
||||
</text>
|
||||
</u-button>
|
||||
</view>
|
||||
|
||||
<!-- 位置信息显示 -->
|
||||
<view v-if="currentLocation" class="location-info-section">
|
||||
<!-- 坐标信息 -->
|
||||
<view class="location-coords-container">
|
||||
<text class="location-coords">
|
||||
经纬度:{{ currentLocation.latitude.toFixed(6) }},{{ currentLocation.longitude.toFixed(6) }}
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<!-- 精度信息 -->
|
||||
<view class="accuracy-section" v-if="currentLocation.accuracy">
|
||||
<view class="accuracy-info">
|
||||
<u-icon name="target" size="14" color="#67c23a" class="accuracy-icon"></u-icon>
|
||||
<text class="accuracy-text">定位精度 {{ currentLocation.accuracy.toFixed(0) }}米</text>
|
||||
<u-tag
|
||||
:text="getAccuracyStatus(currentLocation.accuracy).text"
|
||||
:type="getAccuracyStatus(currentLocation.accuracy).type"
|
||||
size="mini"
|
||||
class="accuracy-tag"
|
||||
></u-tag>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 查看位置按钮 -->
|
||||
<view class="location-action-container">
|
||||
<u-button
|
||||
size="default"
|
||||
type="primary"
|
||||
plain
|
||||
@click="openLocationInMap"
|
||||
class="view-map-btn"
|
||||
>
|
||||
<u-icon name="eye" size="16" color="#2979ff" />
|
||||
<text>查看位置</text>
|
||||
</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 位置获取失败提示 -->
|
||||
<view class="location-error-card" v-else-if="locationError">
|
||||
<view class="location-error-card" v-if="locationError && !currentLocation">
|
||||
<view class="card-header">
|
||||
<view class="header-left">
|
||||
<u-icon name="map-pin" size="18" color="#f56c6c"></u-icon>
|
||||
@@ -265,6 +263,7 @@ import { getInternshipInfo, getStudentInternshipInfo, checkIn, getDailyList, get
|
||||
import { getCurrentLocation, checkLocationPermission, openLocationSettings } from '@/utils/location.js';
|
||||
import { userManager } from '@/utils/user.js';
|
||||
import weChatSDK from '@/utils/wechat.js';
|
||||
import { request } from '@/common/request.js';
|
||||
|
||||
export default {
|
||||
name: 'InternshipIndex',
|
||||
@@ -289,7 +288,6 @@ export default {
|
||||
|
||||
// 微信SDK相关(新增)
|
||||
wechatSDK: weChatSDK,
|
||||
isWechatBrowser: false,
|
||||
wechatInitializing: false,
|
||||
|
||||
// 统计数据
|
||||
@@ -319,19 +317,6 @@ export default {
|
||||
};
|
||||
},
|
||||
|
||||
async onLoad() {
|
||||
// 初始化微信环境检查并自动初始化SDK
|
||||
await this.checkWechatEnvironment();
|
||||
|
||||
this.initPage();
|
||||
},
|
||||
|
||||
onShow() {
|
||||
// 页面显示时刷新数据
|
||||
this.loadTodayRecord();
|
||||
this.loadStatistics();
|
||||
},
|
||||
|
||||
methods: {
|
||||
// === 微信SDK相关方法 ===
|
||||
|
||||
@@ -339,9 +324,9 @@ export default {
|
||||
async checkWechatEnvironment() {
|
||||
try {
|
||||
// 检查是否在微信浏览器中
|
||||
this.isWechatBrowser = this.wechatSDK.isWeChatBrowser();
|
||||
const isWechatBrowser = this.wechatSDK.isWeChatBrowser();
|
||||
|
||||
if (this.isWechatBrowser) {
|
||||
if (isWechatBrowser) {
|
||||
// 在微信环境中,自动初始化SDK
|
||||
await this.initWechatSDK();
|
||||
}
|
||||
@@ -407,10 +392,10 @@ export default {
|
||||
}
|
||||
|
||||
// 初始化微信环境检测
|
||||
this.isWechatBrowser = this.wechatSDK.isWeChatBrowser();
|
||||
const isWechatBrowser = this.wechatSDK.isWeChatBrowser();
|
||||
|
||||
// 如果在微信环境,初始化微信SDK
|
||||
if (this.isWechatBrowser) {
|
||||
if (isWechatBrowser) {
|
||||
await this.initWechatSDK();
|
||||
}
|
||||
|
||||
@@ -419,10 +404,14 @@ export default {
|
||||
this.loadInternshipInfo(),
|
||||
this.loadTodayRecord(),
|
||||
this.loadRecentRecords(),
|
||||
this.loadStatistics(),
|
||||
this.initLocation()
|
||||
this.loadStatistics()
|
||||
]);
|
||||
|
||||
// 初始化位置信息(不阻塞页面加载)
|
||||
this.initLocation().catch(error => {
|
||||
console.error('位置信息初始化失败:', error);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('初始化页面失败:', error);
|
||||
uni.showToast({
|
||||
@@ -608,8 +597,8 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取当前位置
|
||||
await this.getCurrentLocationInfo();
|
||||
// 不自动获取位置,等待用户手动点击定位按钮
|
||||
console.log('位置权限检查完成,等待用户手动定位');
|
||||
} catch (error) {
|
||||
console.error('初始化位置失败:', error);
|
||||
this.canCheckin = false;
|
||||
@@ -645,16 +634,16 @@ export default {
|
||||
},
|
||||
|
||||
/**
|
||||
* 智能定位:根据环境自动选择最适合的定位方式(增强版)
|
||||
* 智能定位:根据环境自动选择最适合的定位方式(参考请假页面实现)
|
||||
*/
|
||||
async getCurrentLocationInfo() {
|
||||
async handleGetLocation() {
|
||||
try {
|
||||
this.locationLoading = true;
|
||||
this.locationError = '';
|
||||
uni.showLoading({ title: '定位中...', mask: true });
|
||||
|
||||
const envInfo = {
|
||||
isWechatBrowser: this.isWechatBrowser,
|
||||
isWechatBrowser: this.checkIsWechatBrowser(),
|
||||
isWechatSdkReady: this.wechatSDK.isInitialized,
|
||||
isH5Web: this.isH5Web(),
|
||||
isBrowserGeolocationSupported: this.isBrowserGeolocationSupported(),
|
||||
@@ -663,7 +652,7 @@ export default {
|
||||
console.log('环境检测结果:', envInfo);
|
||||
|
||||
// 优先级1:微信SDK定位(如果在微信环境且SDK已就绪)
|
||||
if (this.isWechatBrowser && this.wechatSDK.isInitialized) {
|
||||
if (this.checkIsWechatBrowser() && this.wechatSDK.isInitialized) {
|
||||
const wxLocationSuccess = await this.tryWeChatLocationWithSDK();
|
||||
if (wxLocationSuccess) {
|
||||
return;
|
||||
@@ -671,7 +660,7 @@ export default {
|
||||
}
|
||||
|
||||
// 优先级2:微信环境但SDK未就绪,尝试初始化后重试
|
||||
if (this.isWechatBrowser && !this.wechatSDK.isInitialized) {
|
||||
if (this.checkIsWechatBrowser() && !this.wechatSDK.isInitialized) {
|
||||
try {
|
||||
await this.initWechatSDK();
|
||||
if (this.wechatSDK.isInitialized) {
|
||||
@@ -710,7 +699,7 @@ export default {
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
// 用户选择重试
|
||||
this.getCurrentLocationInfo();
|
||||
this.handleGetLocation();
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -720,7 +709,14 @@ export default {
|
||||
},
|
||||
|
||||
/**
|
||||
* 使用微信SDK进行定位(增强版)
|
||||
* 检测浏览器是否支持 Geolocation API
|
||||
*/
|
||||
isBrowserGeolocationSupported() {
|
||||
return typeof navigator !== 'undefined' && 'geolocation' in navigator;
|
||||
},
|
||||
|
||||
/**
|
||||
* 使用微信SDK进行定位(参考请假页面实现)
|
||||
*/
|
||||
async tryWeChatLocationWithSDK() {
|
||||
try {
|
||||
@@ -768,7 +764,7 @@ export default {
|
||||
},
|
||||
|
||||
/**
|
||||
* H5 web端原生定位(增强版)
|
||||
* H5 web端原生定位(参考请假页面实现)
|
||||
*/
|
||||
async handleH5WebLocation() {
|
||||
return new Promise((resolve) => {
|
||||
@@ -792,6 +788,7 @@ export default {
|
||||
|
||||
const { latitude, longitude, accuracy } = position.coords;
|
||||
|
||||
// 将WGS84坐标转换为GCJ02坐标系(中国标准坐标系)
|
||||
this.currentLocation = {
|
||||
latitude: latitude,
|
||||
longitude: longitude,
|
||||
@@ -834,12 +831,12 @@ export default {
|
||||
content: `${errorMessage}。是否手动输入位置信息?`,
|
||||
showCancel: true,
|
||||
cancelText: '取消',
|
||||
confirmText: '稍后重试',
|
||||
confirmText: '手动输入',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
// 用户选择重试
|
||||
// 可以在这里添加手动输入位置的逻辑
|
||||
uni.showToast({
|
||||
title: '请稍后重试定位',
|
||||
title: '手动输入功能开发中',
|
||||
icon: 'none',
|
||||
duration: 2000,
|
||||
});
|
||||
@@ -935,23 +932,24 @@ export default {
|
||||
* 刷新位置
|
||||
*/
|
||||
refreshLocation() {
|
||||
this.getCurrentLocationInfo();
|
||||
this.handleGetLocation();
|
||||
},
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 打开位置信息在地图中查看
|
||||
* 打开位置信息在地图中查看(参考请假页面实现)
|
||||
*/
|
||||
async openLocationInMap() {
|
||||
if (!this.currentLocation) {
|
||||
uni.showToast({
|
||||
title: '无法获取位置信息',
|
||||
icon: 'none'
|
||||
title: '暂无位置信息',
|
||||
icon: 'none',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 从坐标信息解析经纬度
|
||||
const { latitude, longitude } = this.currentLocation;
|
||||
|
||||
if (!latitude || !longitude || isNaN(latitude) || isNaN(longitude)) {
|
||||
@@ -962,26 +960,72 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.isWechatBrowser && this.wechatSDK.isInitialized) {
|
||||
try {
|
||||
await this.wechatSDK.openLocation({
|
||||
latitude: latitude,
|
||||
// 声明变量在外部作用域,确保finally块可以访问
|
||||
let formattedAddress = '';
|
||||
|
||||
// 调用高德sdk逆地理编码
|
||||
try {
|
||||
// 调用后台API进行逆地理编码
|
||||
const geocodingResponse = await request({
|
||||
url: '/system/SysRegion/reverse-geocode',
|
||||
method: 'POST',
|
||||
data: {
|
||||
longitude: longitude,
|
||||
name: '实习签到位置',
|
||||
address: this.currentLocation.address || '当前位置',
|
||||
scale: 16,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('微信地图打开失败:', error);
|
||||
latitude: latitude,
|
||||
},
|
||||
});
|
||||
|
||||
if (geocodingResponse.code === 200 && geocodingResponse.data) {
|
||||
formattedAddress = geocodingResponse.data.regeocode?.formattedAddress || '';
|
||||
if (formattedAddress) {
|
||||
console.log('逆地理编码成功:', formattedAddress);
|
||||
uni.showToast({
|
||||
title: formattedAddress,
|
||||
icon: 'none',
|
||||
duration: 2000,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
console.warn('逆地理编码失败:', geocodingResponse.msg);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('逆地理编码请求失败:', error);
|
||||
// 逆地理编码失败不影响地图打开功能
|
||||
} finally {
|
||||
console.log('=== finally块开始执行 ===');
|
||||
console.log('formattedAddress:', formattedAddress);
|
||||
console.log('isWechatBrowser:', this.checkIsWechatBrowser());
|
||||
console.log('wechatSDK.isInitialized:', this.wechatSDK?.isInitialized);
|
||||
|
||||
if (this.checkIsWechatBrowser() && this.wechatSDK?.isInitialized) {
|
||||
try {
|
||||
console.log('尝试使用微信SDK打开地图...');
|
||||
await this.wechatSDK.openLocation({
|
||||
latitude: latitude,
|
||||
longitude: longitude,
|
||||
name: formattedAddress || '实习签到位置',
|
||||
address: formattedAddress || '当前位置',
|
||||
scale: 16,
|
||||
});
|
||||
console.log('微信地图打开成功');
|
||||
} catch (error) {
|
||||
console.error('微信地图打开失败:', error);
|
||||
this.fallbackOpenMap(latitude, longitude);
|
||||
}
|
||||
} else {
|
||||
console.log('使用备用地图打开方式...');
|
||||
this.fallbackOpenMap(latitude, longitude);
|
||||
}
|
||||
} else {
|
||||
this.fallbackOpenMap(latitude, longitude);
|
||||
console.log('=== finally块执行完成 ===');
|
||||
}
|
||||
},
|
||||
|
||||
// 备用地图打开方式
|
||||
// 备用地图打开方式(参考请假页面实现)
|
||||
fallbackOpenMap(latitude, longitude) {
|
||||
const url = `https://apis.map.qq.com/uri/v1/marker?marker=coord:${latitude},${longitude};title:实习签到位置;addr:${encodeURIComponent(
|
||||
this.currentLocation.address || '当前位置',
|
||||
)}&referer=智慧校园`;
|
||||
|
||||
// 首先尝试使用uni.openLocation(原生地图)
|
||||
if (typeof uni.openLocation === 'function') {
|
||||
uni.showLoading({
|
||||
@@ -1059,7 +1103,7 @@ export default {
|
||||
confirmText: '重新获取',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
this.initLocation();
|
||||
this.handleGetLocation();
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1269,6 +1313,24 @@ export default {
|
||||
checkIsWechatBrowser() {
|
||||
const ua = navigator.userAgent.toLowerCase();
|
||||
return ua.includes('micromessenger');
|
||||
},
|
||||
|
||||
/**
|
||||
* 刷新数据
|
||||
*/
|
||||
async refreshData() {
|
||||
try {
|
||||
// 刷新位置信息
|
||||
if (!this.currentLocation) {
|
||||
await this.getCurrentLocationInfo();
|
||||
}
|
||||
|
||||
// 检查今日签到状态
|
||||
await this.checkTodayCheckInStatus();
|
||||
|
||||
} catch (error) {
|
||||
console.error('数据刷新失败:', error);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1295,8 +1357,8 @@ export default {
|
||||
// 设置当前日期
|
||||
this.currentDate = this.formatDate(new Date());
|
||||
|
||||
// 检测微信环境
|
||||
this.isWechatBrowser = this.checkIsWechatBrowser();
|
||||
// 检测微信环境(在methods中有相应方法)
|
||||
// this.checkIsWechatBrowser() 在需要时调用
|
||||
|
||||
// 获取用户信息(不阻塞)
|
||||
this.loadUserInfo().catch(error => {
|
||||
@@ -1308,33 +1370,15 @@ export default {
|
||||
console.error('实习信息加载失败:', error);
|
||||
});
|
||||
|
||||
// 获取位置信息(核心功能,但不阻塞页面显示)
|
||||
this.getCurrentLocationInfo().catch(error => {
|
||||
console.error('位置信息获取失败:', error);
|
||||
});
|
||||
// 初始化位置权限检查(不阻塞页面显示)
|
||||
this.initLocation().catch(error => {
|
||||
console.error('位置信息初始化失败:', error);
|
||||
});
|
||||
|
||||
// 检查今日签到状态(不阻塞)
|
||||
this.checkTodayCheckInStatus().catch(error => {
|
||||
console.error('签到状态检查失败:', error);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 刷新数据
|
||||
*/
|
||||
async refreshData() {
|
||||
try {
|
||||
// 刷新位置信息
|
||||
if (!this.currentLocation) {
|
||||
await this.getCurrentLocationInfo();
|
||||
}
|
||||
|
||||
// 检查今日签到状态
|
||||
await this.checkTodayCheckInStatus();
|
||||
|
||||
} catch (error) {
|
||||
console.error('数据刷新失败:', error);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -1517,68 +1561,56 @@ export default {
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
/* 位置信息 */
|
||||
.location-address {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 16px;
|
||||
padding: 12px;
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
border-left: 3px solid #67c23a;
|
||||
}
|
||||
|
||||
.address-icon {
|
||||
margin-right: 8px;
|
||||
margin-top: 2px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.address-text {
|
||||
font-size: 14px;
|
||||
color: #303133;
|
||||
line-height: 1.5;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* 坐标信息 */
|
||||
.coordinates-section {
|
||||
/* 位置信息(参考请假页面样式) */
|
||||
.location-row {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.coord-row {
|
||||
.location-btn {
|
||||
width: 100%;
|
||||
height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #2979ff;
|
||||
background: #fff;
|
||||
color: #2979ff;
|
||||
}
|
||||
|
||||
.location-info-section {
|
||||
margin-top: 16px;
|
||||
padding: 16px;
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
border: 1px solid #e4e7ed;
|
||||
}
|
||||
|
||||
.coord-item {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
.location-coords-container {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.coord-divider {
|
||||
width: 1px;
|
||||
height: 32px;
|
||||
background: #e4e7ed;
|
||||
margin: 0 16px;
|
||||
}
|
||||
|
||||
.coord-label {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.coord-value {
|
||||
display: block;
|
||||
.location-coords {
|
||||
font-size: 14px;
|
||||
color: #303133;
|
||||
font-weight: 500;
|
||||
font-family: monospace;
|
||||
line-height: 1.5;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.location-action-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.view-map-btn {
|
||||
min-width: 120px;
|
||||
height: 36px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
/* 精度信息 */
|
||||
|
||||
@@ -4,7 +4,7 @@ import { AssessmentIndicatorVO, AssessmentIndicatorQuery, AssessmentIndicatorFor
|
||||
/**
|
||||
* 查询班主任考核指标体系列表
|
||||
* @param query 查询条件
|
||||
* @returns
|
||||
* @returns
|
||||
*/
|
||||
export function listBmzrAssessmentIndicator(query?: AssessmentIndicatorQuery) {
|
||||
return request({
|
||||
@@ -17,7 +17,7 @@ export function listBmzrAssessmentIndicator(query?: AssessmentIndicatorQuery) {
|
||||
/**
|
||||
* 查询班主任考核指标体系详细
|
||||
* @param id 主键
|
||||
* @returns
|
||||
* @returns
|
||||
*/
|
||||
export function getBmzrAssessmentIndicator(id: string | number) {
|
||||
return request({
|
||||
@@ -29,7 +29,7 @@ export function getBmzrAssessmentIndicator(id: string | number) {
|
||||
/**
|
||||
* 新增班主任考核指标体系
|
||||
* @param data 表单数据
|
||||
* @returns
|
||||
* @returns
|
||||
*/
|
||||
export function addBmzrAssessmentIndicator(data: AssessmentIndicatorForm) {
|
||||
return request({
|
||||
@@ -42,7 +42,7 @@ export function addBmzrAssessmentIndicator(data: AssessmentIndicatorForm) {
|
||||
/**
|
||||
* 修改班主任考核指标体系
|
||||
* @param data 表单数据
|
||||
* @returns
|
||||
* @returns
|
||||
*/
|
||||
export function updateBmzrAssessmentIndicator(data: AssessmentIndicatorForm) {
|
||||
return request({
|
||||
@@ -55,7 +55,7 @@ export function updateBmzrAssessmentIndicator(data: AssessmentIndicatorForm) {
|
||||
/**
|
||||
* 删除班主任考核指标体系
|
||||
* @param ids 主键串
|
||||
* @returns
|
||||
* @returns
|
||||
*/
|
||||
export function delBmzrAssessmentIndicator(ids: string | number | Array<string | number>) {
|
||||
return request({
|
||||
@@ -66,7 +66,7 @@ export function delBmzrAssessmentIndicator(ids: string | number | Array<string |
|
||||
|
||||
/**
|
||||
* 获取指标类型选项
|
||||
* @returns
|
||||
* @returns
|
||||
*/
|
||||
export function getIndicatorTypeOptions() {
|
||||
return request({
|
||||
@@ -77,11 +77,11 @@ export function getIndicatorTypeOptions() {
|
||||
|
||||
/**
|
||||
* 获取父级指标选项
|
||||
* @returns
|
||||
* @returns
|
||||
*/
|
||||
export function getParentIndicatorOptions() {
|
||||
return request({
|
||||
url: '/bzrkh/assessmentIndicator/treeselect',
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -234,4 +234,4 @@ export interface AssessmentIndicatorTypeOption {
|
||||
* 状态(0正常 1停用)
|
||||
*/
|
||||
status?: string;
|
||||
}
|
||||
}
|
||||
@@ -72,4 +72,4 @@ export const delAssessmentIndicatorType = (id: string | number | Array<string |
|
||||
url: '/bzrkh/assessmentIndicatorType/' + id,
|
||||
method: 'delete'
|
||||
});
|
||||
};
|
||||
};
|
||||
@@ -107,4 +107,4 @@ export interface AssessmentIndicatorTypeQuery extends PageQuery {
|
||||
* 日期范围参数
|
||||
*/
|
||||
params?: any;
|
||||
}
|
||||
}
|
||||
@@ -97,4 +97,4 @@ export const changeAssessmentPlanStatus = (id: string | number, status: string)
|
||||
planStatus: status
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
@@ -237,4 +237,4 @@ export interface AssessmentSchemeOption {
|
||||
* 状态
|
||||
*/
|
||||
status?: string;
|
||||
}
|
||||
}
|
||||
@@ -71,4 +71,4 @@ export const exportBmzrAssessmentScheme = (query?: BmzrAssessmentSchemeQuery): A
|
||||
method: 'post',
|
||||
data: query
|
||||
});
|
||||
};
|
||||
};
|
||||
@@ -182,4 +182,4 @@ export interface BmzrAssessmentSchemeQuery extends PageQuery {
|
||||
* 参数
|
||||
*/
|
||||
params?: any;
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import { BmzrAssessmentSchemeIndicatorVO, BmzrAssessmentSchemeIndicatorQuery, Bm
|
||||
/**
|
||||
* 查询班主任考核方案指标关联列表
|
||||
* @param query 查询条件
|
||||
* @returns
|
||||
* @returns
|
||||
*/
|
||||
export function listBmzrAssessmentSchemeIndicator(query?: BmzrAssessmentSchemeIndicatorQuery) {
|
||||
return request({
|
||||
@@ -17,7 +17,7 @@ export function listBmzrAssessmentSchemeIndicator(query?: BmzrAssessmentSchemeIn
|
||||
/**
|
||||
* 根据方案ID查询指标关联列表
|
||||
* @param schemeId 方案ID
|
||||
* @returns
|
||||
* @returns
|
||||
*/
|
||||
export function listBySchemeId(schemeId: string | number) {
|
||||
return request({
|
||||
@@ -29,7 +29,7 @@ export function listBySchemeId(schemeId: string | number) {
|
||||
/**
|
||||
* 查询班主任考核方案指标关联详细
|
||||
* @param id 主键
|
||||
* @returns
|
||||
* @returns
|
||||
*/
|
||||
export function getBmzrAssessmentSchemeIndicator(id: string | number) {
|
||||
return request({
|
||||
@@ -41,7 +41,7 @@ export function getBmzrAssessmentSchemeIndicator(id: string | number) {
|
||||
/**
|
||||
* 新增班主任考核方案指标关联
|
||||
* @param data 表单数据
|
||||
* @returns
|
||||
* @returns
|
||||
*/
|
||||
export function addBmzrAssessmentSchemeIndicator(data: BmzrAssessmentSchemeIndicatorForm) {
|
||||
return request({
|
||||
@@ -54,7 +54,7 @@ export function addBmzrAssessmentSchemeIndicator(data: BmzrAssessmentSchemeIndic
|
||||
/**
|
||||
* 修改班主任考核方案指标关联
|
||||
* @param data 表单数据
|
||||
* @returns
|
||||
* @returns
|
||||
*/
|
||||
export function updateBmzrAssessmentSchemeIndicator(data: BmzrAssessmentSchemeIndicatorForm) {
|
||||
return request({
|
||||
@@ -67,7 +67,7 @@ export function updateBmzrAssessmentSchemeIndicator(data: BmzrAssessmentSchemeIn
|
||||
/**
|
||||
* 删除班主任考核方案指标关联
|
||||
* @param ids 主键串
|
||||
* @returns
|
||||
* @returns
|
||||
*/
|
||||
export function delBmzrAssessmentSchemeIndicator(ids: string | number | Array<string | number>) {
|
||||
return request({
|
||||
@@ -80,7 +80,7 @@ export function delBmzrAssessmentSchemeIndicator(ids: string | number | Array<st
|
||||
* 批量保存方案指标关联
|
||||
* @param schemeId 方案ID
|
||||
* @param indicatorList 指标列表
|
||||
* @returns
|
||||
* @returns
|
||||
*/
|
||||
export function batchSaveSchemeIndicators(schemeId: string | number, indicatorList: BmzrAssessmentSchemeIndicatorForm[]) {
|
||||
return request({
|
||||
@@ -88,4 +88,4 @@ export function batchSaveSchemeIndicators(schemeId: string | number, indicatorLi
|
||||
method: 'post',
|
||||
data: indicatorList
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -147,4 +147,4 @@ export interface BmzrAssessmentSchemeIndicatorQuery extends PageQuery {
|
||||
* 参数
|
||||
*/
|
||||
params?: any;
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import { AxiosPromise } from 'axios';
|
||||
|
||||
export interface ScholarshipBatchVO {
|
||||
/** 主键ID */
|
||||
id?: string | number;
|
||||
id?: string;
|
||||
/** 批次编码 */
|
||||
batchCode?: string;
|
||||
/** 批次名称 */
|
||||
@@ -33,7 +33,7 @@ export interface ScholarshipBatchVO {
|
||||
/** 通知内容 */
|
||||
noticeContent?: string;
|
||||
/** 关联的奖学金类型ID列表 */
|
||||
scholarshipTypeIds?: Array<string | number>;
|
||||
scholarshipTypeIds?: Array<string>;
|
||||
/** 创建者 */
|
||||
createBy?: number;
|
||||
/** 创建时间 */
|
||||
@@ -46,7 +46,7 @@ export interface ScholarshipBatchVO {
|
||||
|
||||
export interface ScholarshipBatchForm {
|
||||
/** 主键ID */
|
||||
id?: string | number;
|
||||
id?: string;
|
||||
/** 批次编码 */
|
||||
batchCode?: string;
|
||||
/** 批次名称 */
|
||||
@@ -76,7 +76,7 @@ export interface ScholarshipBatchForm {
|
||||
/** 通知内容 */
|
||||
noticeContent?: string;
|
||||
/** 关联的奖学金类型ID列表 */
|
||||
scholarshipTypeIds?: Array<string | number>;
|
||||
scholarshipTypeIds?: Array<string>;
|
||||
}
|
||||
|
||||
export interface ScholarshipBatchQuery extends PageQuery {
|
||||
@@ -106,7 +106,7 @@ export function listScholarshipBatch(query: ScholarshipBatchQuery): AxiosPromise
|
||||
}
|
||||
|
||||
// 查询奖学金评定批次详细
|
||||
export function getScholarshipBatch(id: string | number): AxiosPromise<ScholarshipBatchVO> {
|
||||
export function getScholarshipBatch(id: string): AxiosPromise<ScholarshipBatchVO> {
|
||||
return request({
|
||||
url: '/jcbt/scholarshipBatch/' + id,
|
||||
method: 'get'
|
||||
@@ -132,7 +132,7 @@ export function updateScholarshipBatch(data: ScholarshipBatchForm) {
|
||||
}
|
||||
|
||||
// 删除奖学金评定批次
|
||||
export function delScholarshipBatch(id: string | number | Array<string | number>) {
|
||||
export function delScholarshipBatch(id: string | Array<string>) {
|
||||
return request({
|
||||
url: '/jcbt/scholarshipBatch/' + id,
|
||||
method: 'delete'
|
||||
@@ -177,7 +177,7 @@ export function getActiveBatches(): AxiosPromise<ScholarshipBatchVO[]> {
|
||||
}
|
||||
|
||||
// 检查批次编码是否唯一
|
||||
export function checkBatchCodeUnique(batchCode: string, id?: string | number): AxiosPromise<boolean> {
|
||||
export function checkBatchCodeUnique(batchCode: string, id?: string): AxiosPromise<boolean> {
|
||||
return request({
|
||||
url: '/jcbt/scholarshipBatch/checkBatchCodeUnique',
|
||||
method: 'get',
|
||||
@@ -189,7 +189,7 @@ export function checkBatchCodeUnique(batchCode: string, id?: string | number): A
|
||||
}
|
||||
|
||||
// 更改批次状态
|
||||
export function changeScholarshipBatchStatus(id: string | number, status: string) {
|
||||
export function changeScholarshipBatchStatus(id: string, status: string) {
|
||||
return request({
|
||||
url: '/jcbt/scholarshipBatch/changeStatus',
|
||||
method: 'put',
|
||||
|
||||
@@ -27,8 +27,6 @@ export interface WorkStudyApplicationQuery extends PageQuery {
|
||||
positionName?: string;
|
||||
/** 岗位类型 */
|
||||
positionType?: string;
|
||||
/** 工作地点 */
|
||||
workLocation?: string;
|
||||
/** 申请状态 */
|
||||
status?: string;
|
||||
/** 学院审核状态 */
|
||||
@@ -76,8 +74,6 @@ export interface WorkStudyApplicationForm {
|
||||
positionName: string;
|
||||
/** 岗位类型 */
|
||||
positionType: string;
|
||||
/** 工作地点 */
|
||||
workLocation: string;
|
||||
/** 计划工作时长 */
|
||||
plannedWorkHours?: number;
|
||||
/** 家庭经济情况 */
|
||||
|
||||
@@ -113,7 +113,6 @@ export interface DormHygieneCheckVO {
|
||||
* 检查备注
|
||||
*/
|
||||
checkRemarks: string;
|
||||
|
||||
}
|
||||
|
||||
export interface DormHygieneCheckForm extends BaseEntity {
|
||||
@@ -231,11 +230,9 @@ export interface DormHygieneCheckForm extends BaseEntity {
|
||||
* 检查备注
|
||||
*/
|
||||
checkRemarks?: string;
|
||||
|
||||
}
|
||||
|
||||
export interface DormHygieneCheckQuery extends PageQuery {
|
||||
|
||||
/**
|
||||
* 删除标志(0代表存在 1代表删除)
|
||||
*/
|
||||
@@ -346,10 +343,10 @@ export interface DormHygieneCheckQuery extends PageQuery {
|
||||
*/
|
||||
checkRemarks?: string;
|
||||
|
||||
/**
|
||||
* 日期范围参数
|
||||
*/
|
||||
params?: any;
|
||||
/**
|
||||
* 日期范围参数
|
||||
*/
|
||||
params?: any;
|
||||
}
|
||||
|
||||
// 宿舍级联选择相关类型
|
||||
@@ -643,6 +640,3 @@ export interface DormHygieneCheckCompleteForm extends DormHygieneCheckForm {
|
||||
*/
|
||||
studentScores?: DormHygieneStudentScoreForm[];
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -98,7 +98,6 @@ export interface DormHygieneStudentScoreVO {
|
||||
* 改进意见
|
||||
*/
|
||||
improvementNotes: string;
|
||||
|
||||
}
|
||||
|
||||
export interface DormHygieneStudentScoreForm extends BaseEntity {
|
||||
@@ -201,11 +200,9 @@ export interface DormHygieneStudentScoreForm extends BaseEntity {
|
||||
* 改进意见
|
||||
*/
|
||||
improvementNotes?: string;
|
||||
|
||||
}
|
||||
|
||||
export interface DormHygieneStudentScoreQuery extends PageQuery {
|
||||
|
||||
/**
|
||||
* 删除标志(0代表存在 1代表删除)
|
||||
*/
|
||||
@@ -301,11 +298,8 @@ export interface DormHygieneStudentScoreQuery extends PageQuery {
|
||||
*/
|
||||
improvementNotes?: string;
|
||||
|
||||
/**
|
||||
* 日期范围参数
|
||||
*/
|
||||
params?: any;
|
||||
/**
|
||||
* 日期范围参数
|
||||
*/
|
||||
params?: any;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -138,7 +138,6 @@ export interface DormViolationVO {
|
||||
* 备注
|
||||
*/
|
||||
remark: string;
|
||||
|
||||
}
|
||||
|
||||
export interface DormViolationForm extends BaseEntity {
|
||||
@@ -281,11 +280,9 @@ export interface DormViolationForm extends BaseEntity {
|
||||
* 备注
|
||||
*/
|
||||
remark?: string;
|
||||
|
||||
}
|
||||
|
||||
export interface DormViolationQuery extends PageQuery {
|
||||
|
||||
/**
|
||||
* 学生ID
|
||||
*/
|
||||
@@ -416,10 +413,10 @@ export interface DormViolationQuery extends PageQuery {
|
||||
*/
|
||||
deleted?: string;
|
||||
|
||||
/**
|
||||
* 日期范围参数
|
||||
*/
|
||||
params?: any;
|
||||
/**
|
||||
* 日期范围参数
|
||||
*/
|
||||
params?: any;
|
||||
}
|
||||
|
||||
// 学生选择相关类型
|
||||
@@ -478,6 +475,3 @@ export interface BedOptionVO {
|
||||
id: string | number;
|
||||
bedName: string;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -67,7 +67,9 @@ export const delHolidayStay = (id: string | number | Array<string | number>) =>
|
||||
* @param query 查询参数
|
||||
* @returns 学生选项列表
|
||||
*/
|
||||
export const getStudentOptions = (query?: StudentOptionQuery): AxiosPromise<{
|
||||
export const getStudentOptions = (
|
||||
query?: StudentOptionQuery
|
||||
): AxiosPromise<{
|
||||
total: number;
|
||||
rows: StudentOptionVO[];
|
||||
}> => {
|
||||
|
||||
@@ -173,7 +173,6 @@ export interface HolidayStayVO {
|
||||
* 备注信息
|
||||
*/
|
||||
remark: string;
|
||||
|
||||
}
|
||||
|
||||
export interface HolidayStayForm extends BaseEntity {
|
||||
@@ -351,11 +350,9 @@ export interface HolidayStayForm extends BaseEntity {
|
||||
* 备注信息
|
||||
*/
|
||||
remark?: string;
|
||||
|
||||
}
|
||||
|
||||
export interface HolidayStayQuery extends PageQuery {
|
||||
|
||||
/**
|
||||
* 删除标志(0代表存在 1代表删除)
|
||||
*/
|
||||
@@ -526,10 +523,10 @@ export interface HolidayStayQuery extends PageQuery {
|
||||
*/
|
||||
status?: string;
|
||||
|
||||
/**
|
||||
* 日期范围参数
|
||||
*/
|
||||
params?: any;
|
||||
/**
|
||||
* 日期范围参数
|
||||
*/
|
||||
params?: any;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -752,6 +749,3 @@ export interface BedOptionVO {
|
||||
*/
|
||||
bedStatus?: string;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -245,7 +245,6 @@ export interface DisciplineRecordForm {
|
||||
}
|
||||
|
||||
export interface DisciplineRecordQuery extends PageQuery {
|
||||
|
||||
/**
|
||||
* 学生ID
|
||||
*/
|
||||
@@ -346,10 +345,10 @@ export interface DisciplineRecordQuery extends PageQuery {
|
||||
*/
|
||||
approvalRemark?: string;
|
||||
|
||||
/**
|
||||
* 日期范围参数
|
||||
*/
|
||||
params?: any;
|
||||
/**
|
||||
* 日期范围参数
|
||||
*/
|
||||
params?: any;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -38,7 +38,6 @@ export interface DisciplineTypeVO {
|
||||
* 删除标志(0-未删除 1-已删除)
|
||||
*/
|
||||
deleted: number;
|
||||
|
||||
}
|
||||
|
||||
export interface DisciplineTypeForm extends BaseEntity {
|
||||
@@ -81,11 +80,9 @@ export interface DisciplineTypeForm extends BaseEntity {
|
||||
* 删除标志(0-未删除 1-已删除)
|
||||
*/
|
||||
deleted?: number;
|
||||
|
||||
}
|
||||
|
||||
export interface DisciplineTypeQuery extends PageQuery {
|
||||
|
||||
/**
|
||||
* 类型编码
|
||||
*/
|
||||
@@ -116,11 +113,8 @@ export interface DisciplineTypeQuery extends PageQuery {
|
||||
*/
|
||||
deleted?: number;
|
||||
|
||||
/**
|
||||
* 日期范围参数
|
||||
*/
|
||||
params?: any;
|
||||
/**
|
||||
* 日期范围参数
|
||||
*/
|
||||
params?: any;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@ export interface RewardTypeVO {
|
||||
* 删除标志(0-未删除 1-已删除)
|
||||
*/
|
||||
deleted: number;
|
||||
|
||||
}
|
||||
|
||||
export interface RewardTypeForm extends BaseEntity {
|
||||
@@ -71,11 +70,9 @@ export interface RewardTypeForm extends BaseEntity {
|
||||
* 删除标志(0-未删除 1-已删除)
|
||||
*/
|
||||
deleted?: number;
|
||||
|
||||
}
|
||||
|
||||
export interface RewardTypeQuery extends PageQuery {
|
||||
|
||||
/**
|
||||
* 类型编码
|
||||
*/
|
||||
@@ -106,11 +103,8 @@ export interface RewardTypeQuery extends PageQuery {
|
||||
*/
|
||||
deleted?: number;
|
||||
|
||||
/**
|
||||
* 日期范围参数
|
||||
*/
|
||||
params?: any;
|
||||
/**
|
||||
* 日期范围参数
|
||||
*/
|
||||
params?: any;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -245,7 +245,6 @@ export interface DisciplineRecordForm {
|
||||
}
|
||||
|
||||
export interface DisciplineRecordQuery extends PageQuery {
|
||||
|
||||
/**
|
||||
* 学生ID
|
||||
*/
|
||||
@@ -346,10 +345,10 @@ export interface DisciplineRecordQuery extends PageQuery {
|
||||
*/
|
||||
approvalRemark?: string;
|
||||
|
||||
/**
|
||||
* 日期范围参数
|
||||
*/
|
||||
params?: any;
|
||||
/**
|
||||
* 日期范围参数
|
||||
*/
|
||||
params?: any;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import request from '@/utils/request'
|
||||
import request from '@/utils/request';
|
||||
|
||||
// 查询学生月度总结列表
|
||||
export function listInternshipMonthly(query: any) {
|
||||
@@ -6,7 +6,7 @@ export function listInternshipMonthly(query: any) {
|
||||
url: '/zsjy/internship/monthly/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// 获取学生月度总结详细信息
|
||||
@@ -14,7 +14,7 @@ export function getInternshipMonthly(monthlyId: string | number) {
|
||||
return request({
|
||||
url: '/zsjy/internship/monthly/' + monthlyId,
|
||||
method: 'get'
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// 新增学生月度总结
|
||||
@@ -23,7 +23,7 @@ export function addInternshipMonthly(data: any) {
|
||||
url: '/zsjy/internship/monthly',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// 修改学生月度总结
|
||||
@@ -32,7 +32,7 @@ export function updateInternshipMonthly(data: any) {
|
||||
url: '/zsjy/internship/monthly',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// 删除学生月度总结
|
||||
@@ -40,7 +40,7 @@ export function delInternshipMonthly(monthlyIds: string | number | Array<string
|
||||
return request({
|
||||
url: '/zsjy/internship/monthly/' + monthlyIds,
|
||||
method: 'delete'
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// 提交月度总结审批
|
||||
@@ -48,7 +48,7 @@ export function submitInternshipMonthly(monthlyId: string | number) {
|
||||
return request({
|
||||
url: '/zsjy/internship/monthly/submit/' + monthlyId,
|
||||
method: 'put'
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// 自动保存草稿
|
||||
@@ -57,7 +57,7 @@ export function autosaveInternshipMonthly(data: any) {
|
||||
url: '/zsjy/internship/monthly/autosave',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// 检查月份重复
|
||||
@@ -65,7 +65,7 @@ export function checkSummaryMonth(summaryMonth: string) {
|
||||
return request({
|
||||
url: '/zsjy/internship/monthly/check/' + summaryMonth,
|
||||
method: 'get'
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// 获取月度总结模板
|
||||
@@ -73,7 +73,7 @@ export function getMonthlyTemplate(summaryMonth: string) {
|
||||
return request({
|
||||
url: '/zsjy/internship/monthly/template/' + summaryMonth,
|
||||
method: 'get'
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// 获取状态统计
|
||||
@@ -81,7 +81,7 @@ export function getMonthlyStatistics() {
|
||||
return request({
|
||||
url: '/zsjy/internship/monthly/statistics',
|
||||
method: 'get'
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// 导出月度总结数据
|
||||
@@ -90,7 +90,7 @@ export function exportInternshipMonthly(query: any) {
|
||||
url: '/zsjy/internship/monthly/export',
|
||||
method: 'post',
|
||||
params: query
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// 获取学生当前实习信息
|
||||
@@ -98,6 +98,5 @@ export function getCurrentInternshipInfo() {
|
||||
return request({
|
||||
url: '/zsjy/internship/monthly/current-internship',
|
||||
method: 'get'
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
+13
-13
@@ -1,4 +1,4 @@
|
||||
import request from '@/utils/request'
|
||||
import request from '@/utils/request';
|
||||
|
||||
/**
|
||||
* 二维码管理 VO
|
||||
@@ -156,7 +156,7 @@ export function listQrCode(query?: QrCodeQuery) {
|
||||
url: '/zsjy/qrcode/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -166,7 +166,7 @@ export function getQrCode(id: string | number) {
|
||||
return request({
|
||||
url: '/zsjy/qrcode/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -177,7 +177,7 @@ export function addQrCode(data: QrCodeBO) {
|
||||
url: '/zsjy/qrcode',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -188,7 +188,7 @@ export function updateQrCode(data: QrCodeBO) {
|
||||
url: '/zsjy/qrcode',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -198,7 +198,7 @@ export function delQrCode(ids: string | number | Array<string | number>) {
|
||||
return request({
|
||||
url: '/zsjy/qrcode/' + ids,
|
||||
method: 'delete'
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -210,7 +210,7 @@ export function exportQrCode(query?: QrCodeQuery) {
|
||||
method: 'post',
|
||||
data: query,
|
||||
responseType: 'blob'
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -220,7 +220,7 @@ export function validateToken(token: string) {
|
||||
return request({
|
||||
url: '/zsjy/qrcode/validate/' + token,
|
||||
method: 'get'
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -230,7 +230,7 @@ export function getQrCodeByToken(token: string) {
|
||||
return request({
|
||||
url: '/zsjy/qrcode/token/' + token,
|
||||
method: 'get'
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -240,7 +240,7 @@ export function incrementUsage(id: string | number) {
|
||||
return request({
|
||||
url: '/zsjy/qrcode/increment/' + id,
|
||||
method: 'post'
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -250,7 +250,7 @@ export function generateToken() {
|
||||
return request({
|
||||
url: '/zsjy/qrcode/generate-token',
|
||||
method: 'post'
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -261,7 +261,7 @@ export const qrCodeTypeOptions = [
|
||||
label: '招生报名',
|
||||
value: 1
|
||||
}
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* 状态选项
|
||||
@@ -275,4 +275,4 @@ export const statusOptions = [
|
||||
label: '启用',
|
||||
value: 1
|
||||
}
|
||||
]
|
||||
];
|
||||
@@ -140,13 +140,12 @@ service.interceptors.response.use(
|
||||
code === 0 ||
|
||||
code === 'SUCCESS' ||
|
||||
resData.success === true ||
|
||||
(res.status === 200 && (
|
||||
resData.rows !== undefined ||
|
||||
resData.total !== undefined ||
|
||||
(resData.data && (resData.data.rows !== undefined || resData.data.total !== undefined || resData.data.list !== undefined)) ||
|
||||
resData.list !== undefined
|
||||
)) ||
|
||||
(res.status === 200 && (res.config?.method === 'get'))
|
||||
(res.status === 200 &&
|
||||
(resData.rows !== undefined ||
|
||||
resData.total !== undefined ||
|
||||
(resData.data && (resData.data.rows !== undefined || resData.data.total !== undefined || resData.data.list !== undefined)) ||
|
||||
resData.list !== undefined)) ||
|
||||
(res.status === 200 && res.config?.method === 'get')
|
||||
) {
|
||||
return Promise.resolve(resData);
|
||||
}
|
||||
@@ -179,8 +178,8 @@ service.interceptors.response.use(
|
||||
} else if (code === 403) {
|
||||
// 特殊处理403权限错误,提供更明确的错误提示
|
||||
const permissionMsg = '无访问权限,可能原因:\n1. 用户权限不足\n2. 权限配置问题\n请联系管理员检查权限配置';
|
||||
ElMessage({
|
||||
message: permissionMsg,
|
||||
ElMessage({
|
||||
message: permissionMsg,
|
||||
type: 'warning',
|
||||
duration: 8000,
|
||||
showClose: true
|
||||
|
||||
@@ -41,24 +41,10 @@
|
||||
<el-button type="primary" icon="Plus" @click="handleAdd" v-hasPermi="['bzrkh:assessmentIndicator:add']">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
icon="Edit"
|
||||
:disabled="single"
|
||||
@click="handleUpdate()"
|
||||
v-hasPermi="['bzrkh:assessmentIndicator:edit']"
|
||||
>
|
||||
修改
|
||||
</el-button>
|
||||
<el-button icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['bzrkh:assessmentIndicator:edit']"> 修改 </el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
icon="Delete"
|
||||
:disabled="multiple"
|
||||
@click="handleDelete()"
|
||||
v-hasPermi="['bzrkh:assessmentIndicator:remove']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
<el-button icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['bzrkh:assessmentIndicator:remove']"> 删除 </el-button>
|
||||
</el-col>
|
||||
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
@@ -66,12 +52,7 @@
|
||||
</template>
|
||||
|
||||
<div class="table-container">
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
border
|
||||
:data="assessmentIndicatorList"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table v-loading="loading" border :data="assessmentIndicatorList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="指标编码" align="center" prop="indicatorCode" width="140" show-overflow-tooltip />
|
||||
<el-table-column label="指标名称" align="center" prop="indicatorName" min-width="180" show-overflow-tooltip />
|
||||
@@ -91,10 +72,22 @@
|
||||
<el-table-column label="操作" align="center" width="120" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="修改" placement="top">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['bzrkh:assessmentIndicator:edit']"></el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
icon="Edit"
|
||||
@click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['bzrkh:assessmentIndicator:edit']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['bzrkh:assessmentIndicator:remove']"></el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
icon="Delete"
|
||||
@click="handleDelete(scope.row)"
|
||||
v-hasPermi="['bzrkh:assessmentIndicator:remove']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -170,8 +163,21 @@
|
||||
</template>
|
||||
|
||||
<script setup name="AssessmentIndicator" lang="ts">
|
||||
import { listBmzrAssessmentIndicator, getBmzrAssessmentIndicator, delBmzrAssessmentIndicator, addBmzrAssessmentIndicator, updateBmzrAssessmentIndicator, getIndicatorTypeOptions, getParentIndicatorOptions } from '@/api/bzrkh/assessmentIndicator';
|
||||
import { AssessmentIndicatorVO, AssessmentIndicatorQuery, AssessmentIndicatorForm, AssessmentIndicatorTypeOption } from '@/api/bzrkh/assessmentIndicator/types';
|
||||
import {
|
||||
listBmzrAssessmentIndicator,
|
||||
getBmzrAssessmentIndicator,
|
||||
delBmzrAssessmentIndicator,
|
||||
addBmzrAssessmentIndicator,
|
||||
updateBmzrAssessmentIndicator,
|
||||
getIndicatorTypeOptions,
|
||||
getParentIndicatorOptions
|
||||
} from '@/api/bzrkh/assessmentIndicator';
|
||||
import {
|
||||
AssessmentIndicatorVO,
|
||||
AssessmentIndicatorQuery,
|
||||
AssessmentIndicatorForm,
|
||||
AssessmentIndicatorTypeOption
|
||||
} from '@/api/bzrkh/assessmentIndicator/types';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { sys_item_enable, sys_yes_no } = toRefs<any>(proxy?.useDict('sys_item_enable', 'sys_yes_no'));
|
||||
@@ -234,7 +240,7 @@ const { queryParams, form, rules } = toRefs(data);
|
||||
|
||||
/** 获取指标类型名称 */
|
||||
const getIndicatorTypeName = (code: string) => {
|
||||
const option = indicatorTypeOptions.value.find(item => item.indicatorTypeCode === code);
|
||||
const option = indicatorTypeOptions.value.find((item) => item.indicatorTypeCode === code);
|
||||
return option ? option.indicatorTypeName : code;
|
||||
};
|
||||
|
||||
@@ -359,8 +365,6 @@ const handleDelete = async (row?: AssessmentIndicatorVO) => {
|
||||
await getList();
|
||||
};
|
||||
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
getList();
|
||||
getIndicatorTypeOptionsList();
|
||||
@@ -400,4 +404,4 @@ onMounted(() => {
|
||||
background-color: #fff;
|
||||
margin-top: 0px !important;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
@@ -44,7 +44,9 @@
|
||||
<el-button icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['bzrkh:assessmentIndicatorType:edit']">修改</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['bzrkh:assessmentIndicatorType:remove']">删除</el-button>
|
||||
<el-button icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['bzrkh:assessmentIndicatorType:remove']"
|
||||
>删除</el-button
|
||||
>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
@@ -69,10 +71,22 @@
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="修改" placement="top">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['bzrkh:assessmentIndicatorType:edit']"></el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
icon="Edit"
|
||||
@click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['bzrkh:assessmentIndicatorType:edit']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['bzrkh:assessmentIndicatorType:remove']"></el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
icon="Delete"
|
||||
@click="handleDelete(scope.row)"
|
||||
v-hasPermi="['bzrkh:assessmentIndicatorType:remove']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -112,7 +126,13 @@
|
||||
</template>
|
||||
|
||||
<script setup name="AssessmentIndicatorType" lang="ts">
|
||||
import { listAssessmentIndicatorType, getAssessmentIndicatorType, delAssessmentIndicatorType, addAssessmentIndicatorType, updateAssessmentIndicatorType } from '@/api/bzrkh/assessmentIndicatorType';
|
||||
import {
|
||||
listAssessmentIndicatorType,
|
||||
getAssessmentIndicatorType,
|
||||
delAssessmentIndicatorType,
|
||||
addAssessmentIndicatorType,
|
||||
updateAssessmentIndicatorType
|
||||
} from '@/api/bzrkh/assessmentIndicatorType';
|
||||
import { AssessmentIndicatorTypeVO, AssessmentIndicatorTypeQuery, AssessmentIndicatorTypeForm } from '@/api/bzrkh/assessmentIndicatorType/types';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
@@ -300,4 +320,4 @@ onMounted(() => {
|
||||
background-color: #fff;
|
||||
margin-top: 0px !important;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
@@ -91,7 +91,7 @@
|
||||
<el-table-column label="计划编码" align="center" prop="planCode" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="考核方案" align="center" :show-overflow-tooltip="true">
|
||||
<template #default="scope">
|
||||
<span>{{ schemeOptions.find(item => item.id === scope.row.schemeId)?.schemeName || '' }}</span>
|
||||
<span>{{ schemeOptions.find((item) => item.id === scope.row.schemeId)?.schemeName || '' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="学年学期" align="center" width="120">
|
||||
@@ -165,7 +165,12 @@
|
||||
<el-col :span="24">
|
||||
<el-form-item label="考核方案" prop="schemeId">
|
||||
<el-select v-model="form.schemeId" placeholder="请选择考核方案" style="width: 100%">
|
||||
<el-option v-for="item in schemeOptions" :key="item.id" :label="`${item.schemeName}(${item.academicYear}${item.semester === '1' ? '上' : '下'}学期)`" :value="item.id" />
|
||||
<el-option
|
||||
v-for="item in schemeOptions"
|
||||
:key="item.id"
|
||||
:label="`${item.schemeName}(${item.academicYear}${item.semester === '1' ? '上' : '下'}学期)`"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@@ -277,11 +282,7 @@ import {
|
||||
changeAssessmentPlanStatus
|
||||
} from '@/api/bzrkh/assessmentPlan';
|
||||
import { listBmzrAssessmentScheme } from '@/api/bzrkh/assessmentScheme';
|
||||
import {
|
||||
AssessmentPlanVO,
|
||||
AssessmentPlanQuery,
|
||||
AssessmentPlanForm
|
||||
} from '@/api/bzrkh/assessmentPlan/types';
|
||||
import { AssessmentPlanVO, AssessmentPlanQuery, AssessmentPlanForm } from '@/api/bzrkh/assessmentPlan/types';
|
||||
import { BmzrAssessmentSchemeVO } from '@/api/bzrkh/assessmentScheme/types';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
@@ -371,7 +372,7 @@ const getList = async () => {
|
||||
|
||||
/** 获取考核方案选项 */
|
||||
const getSchemeOptions = async () => {
|
||||
const res = await listBmzrAssessmentScheme({
|
||||
const res = await listBmzrAssessmentScheme({
|
||||
status: '0', // 只获取正常状态的方案
|
||||
pageNum: 1,
|
||||
pageSize: 1000 // 设置较大的pageSize以获取所有可用方案
|
||||
@@ -479,18 +480,19 @@ const handleDelete = async (row?: AssessmentPlanVO) => {
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** 状态修改 */
|
||||
const handleStatusChange = async (row: AssessmentPlanVO) => {
|
||||
const text = row.planStatus === '1' ? '启用' : '禁用';
|
||||
await proxy?.$modal.confirm('确认要"' + text + '""' + row.planName + '"考核计划吗?').then(async () => {
|
||||
await changeAssessmentPlanStatus(row.id!, row.planStatus!).then(() => {
|
||||
proxy?.$modal.msgSuccess(text + '成功');
|
||||
await proxy?.$modal
|
||||
.confirm('确认要"' + text + '""' + row.planName + '"考核计划吗?')
|
||||
.then(async () => {
|
||||
await changeAssessmentPlanStatus(row.id!, row.planStatus!).then(() => {
|
||||
proxy?.$modal.msgSuccess(text + '成功');
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
row.planStatus = row.planStatus === '1' ? '0' : '1';
|
||||
});
|
||||
}).catch(() => {
|
||||
row.planStatus = row.planStatus === '1' ? '0' : '1';
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
@@ -532,4 +534,4 @@ onMounted(() => {
|
||||
background-color: #fff;
|
||||
margin-top: 0px !important;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
@@ -1,12 +1,5 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
title="指标体系管理"
|
||||
v-model="dialogVisible"
|
||||
width="1200px"
|
||||
append-to-body
|
||||
:close-on-click-modal="false"
|
||||
@close="handleClose"
|
||||
>
|
||||
<el-dialog title="指标体系管理" v-model="dialogVisible" width="1200px" append-to-body :close-on-click-modal="false" @close="handleClose">
|
||||
<div class="indicator-management-container">
|
||||
<!-- 检索区域 -->
|
||||
<div class="search-container">
|
||||
@@ -15,12 +8,7 @@
|
||||
<el-row :gutter="20">
|
||||
<el-col :lg="8" :md="12" :sm="24">
|
||||
<el-form-item label="指标名称" prop="indicatorName">
|
||||
<el-input
|
||||
v-model="searchParams.indicatorName"
|
||||
placeholder="请输入指标名称"
|
||||
clearable
|
||||
@keyup.enter="handleSearchIndicators"
|
||||
/>
|
||||
<el-input v-model="searchParams.indicatorName" placeholder="请输入指标名称" clearable @keyup.enter="handleSearchIndicators" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :lg="16" :md="12" :sm="24">
|
||||
@@ -31,15 +19,10 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
|
||||
<!-- 搜索结果提示 -->
|
||||
<div v-if="searchResultInfo" class="search-result-info">
|
||||
<el-alert
|
||||
:title="searchResultInfo"
|
||||
type="info"
|
||||
:closable="false"
|
||||
show-icon
|
||||
/>
|
||||
<el-alert :title="searchResultInfo" type="info" :closable="false" show-icon />
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
@@ -54,8 +37,8 @@
|
||||
|
||||
<!-- 没有搜索结果时显示提示 -->
|
||||
<div v-else-if="filteredIndicatorList.length === 0" class="empty-state">
|
||||
<div style="text-align: center; color: #909399;">
|
||||
<el-icon size="48" style="margin-bottom: 16px;"><Search /></el-icon>
|
||||
<div style="text-align: center; color: #909399">
|
||||
<el-icon size="48" style="margin-bottom: 16px"><Search /></el-icon>
|
||||
<p>未找到匹配"{{ searchParams.indicatorName }}"的指标</p>
|
||||
<el-button type="text" @click="resetSearch">清除搜索条件</el-button>
|
||||
</div>
|
||||
@@ -71,19 +54,14 @@
|
||||
</div>
|
||||
<div class="row-item">
|
||||
<span class="label">指标名称:</span>
|
||||
<el-select
|
||||
v-model="item.indicatorId"
|
||||
placeholder="请选择指标名称"
|
||||
<el-select
|
||||
v-model="item.indicatorId"
|
||||
placeholder="请选择指标名称"
|
||||
style="width: 250px"
|
||||
@change="handleIndicatorChange(item, getOriginalIndex(item))"
|
||||
filterable
|
||||
>
|
||||
<el-option
|
||||
v-for="indicator in availableIndicators"
|
||||
:key="indicator.id"
|
||||
:label="indicator.indicatorName"
|
||||
:value="indicator.id"
|
||||
/>
|
||||
<el-option v-for="indicator in availableIndicators" :key="indicator.id" :label="indicator.indicatorName" :value="indicator.id" />
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="row-item">
|
||||
@@ -153,13 +131,13 @@ const filteredIndicatorList = computed(() => {
|
||||
// 没有搜索条件时,显示所有指标
|
||||
return indicatorList.value;
|
||||
}
|
||||
|
||||
|
||||
// 有搜索条件时,只显示匹配的指标
|
||||
return indicatorList.value.filter(item => {
|
||||
return indicatorList.value.filter((item) => {
|
||||
// 根据indicatorId查找对应的指标名称
|
||||
const indicator = availableIndicators.value.find(ind => ind.id == item.indicatorId);
|
||||
const indicator = availableIndicators.value.find((ind) => ind.id == item.indicatorId);
|
||||
const indicatorName = indicator?.indicatorName || '';
|
||||
|
||||
|
||||
// 检查指标名称是否包含搜索关键词(不区分大小写)
|
||||
return indicatorName.toLowerCase().includes(keyword.toLowerCase());
|
||||
});
|
||||
@@ -167,10 +145,11 @@ const filteredIndicatorList = computed(() => {
|
||||
|
||||
// 获取指标在原始列表中的索引
|
||||
const getOriginalIndex = (item: BmzrAssessmentSchemeIndicatorForm) => {
|
||||
return indicatorList.value.findIndex(originalItem =>
|
||||
originalItem === item ||
|
||||
(originalItem.id && originalItem.id === item.id) ||
|
||||
(originalItem.indicatorId && originalItem.indicatorId === item.indicatorId && originalItem.sortOrder === item.sortOrder)
|
||||
return indicatorList.value.findIndex(
|
||||
(originalItem) =>
|
||||
originalItem === item ||
|
||||
(originalItem.id && originalItem.id === item.id) ||
|
||||
(originalItem.indicatorId && originalItem.indicatorId === item.indicatorId && originalItem.sortOrder === item.sortOrder)
|
||||
);
|
||||
};
|
||||
|
||||
@@ -178,10 +157,10 @@ const getOriginalIndex = (item: BmzrAssessmentSchemeIndicatorForm) => {
|
||||
const open = async (id: string | number) => {
|
||||
schemeId.value = id;
|
||||
dialogVisible.value = true;
|
||||
|
||||
|
||||
// 加载可选指标
|
||||
await loadAvailableIndicators();
|
||||
|
||||
|
||||
// 加载当前方案的指标
|
||||
await loadSchemeIndicators();
|
||||
};
|
||||
@@ -194,24 +173,24 @@ const loadAvailableIndicators = async () => {
|
||||
pageNum: 1,
|
||||
pageSize: 1000 // 加载所有指标
|
||||
};
|
||||
|
||||
|
||||
// 如果有搜索条件,添加到查询参数中
|
||||
const hasSearchCondition = !!(searchParams.value.indicatorName && searchParams.value.indicatorName.trim());
|
||||
if (hasSearchCondition) {
|
||||
queryParams.indicatorName = searchParams.value.indicatorName.trim();
|
||||
}
|
||||
|
||||
|
||||
const res = await listBmzrAssessmentIndicator(queryParams);
|
||||
availableIndicators.value = res.rows || [];
|
||||
|
||||
|
||||
// 调试日志
|
||||
console.log('loadAvailableIndicators完成:', {
|
||||
queryParams,
|
||||
hasSearchCondition,
|
||||
resultCount: availableIndicators.value.length,
|
||||
indicatorNames: availableIndicators.value.map(i => i.indicatorName)
|
||||
indicatorNames: availableIndicators.value.map((i) => i.indicatorName)
|
||||
});
|
||||
|
||||
|
||||
// 更新搜索结果信息
|
||||
updateSearchResultInfo(hasSearchCondition, availableIndicators.value.length);
|
||||
} catch (error) {
|
||||
@@ -225,15 +204,16 @@ const loadAvailableIndicators = async () => {
|
||||
const loadSchemeIndicators = async () => {
|
||||
try {
|
||||
const res = await listBySchemeId(schemeId.value);
|
||||
indicatorList.value = res.data?.map((item: any) => ({
|
||||
id: item.id,
|
||||
schemeId: item.schemeId,
|
||||
indicatorId: item.indicatorId,
|
||||
// 优先使用score字段,如果为空则使用customMaxScore字段(兜底方案)
|
||||
score: item.score || item.customMaxScore,
|
||||
remark: item.remark || getIndicatorRemark(item.indicatorId),
|
||||
sortOrder: item.sortOrder
|
||||
})) || [];
|
||||
indicatorList.value =
|
||||
res.data?.map((item: any) => ({
|
||||
id: item.id,
|
||||
schemeId: item.schemeId,
|
||||
indicatorId: item.indicatorId,
|
||||
// 优先使用score字段,如果为空则使用customMaxScore字段(兜底方案)
|
||||
score: item.score || item.customMaxScore,
|
||||
remark: item.remark || getIndicatorRemark(item.indicatorId),
|
||||
sortOrder: item.sortOrder
|
||||
})) || [];
|
||||
} catch (error) {
|
||||
console.error('加载方案指标失败:', error);
|
||||
proxy?.$modal.msgError('加载方案指标失败');
|
||||
@@ -242,7 +222,7 @@ const loadSchemeIndicators = async () => {
|
||||
|
||||
// 根据指标ID获取指标描述
|
||||
const getIndicatorRemark = (indicatorId: string | number) => {
|
||||
const indicator = availableIndicators.value.find(item => item.id == indicatorId);
|
||||
const indicator = availableIndicators.value.find((item) => item.id == indicatorId);
|
||||
return indicator?.remark || '';
|
||||
};
|
||||
|
||||
@@ -261,23 +241,23 @@ const updateSearchResultInfo = (hasSearchCondition: boolean, resultCount: number
|
||||
const getAvailableIndicators = (currentIndex: number) => {
|
||||
// 搜索功能:直接返回后端搜索结果,允许重复选择同一指标
|
||||
// 这样用户可以在不同位置选择相同的指标
|
||||
|
||||
|
||||
// 调试日志
|
||||
console.log('getAvailableIndicators调用:', {
|
||||
currentIndex,
|
||||
totalAvailable: availableIndicators.value.length,
|
||||
searchKeyword: searchParams.value.indicatorName,
|
||||
availableIndicatorIds: availableIndicators.value.map(i => i.id),
|
||||
availableIndicatorNames: availableIndicators.value.map(i => i.indicatorName),
|
||||
availableIndicatorIds: availableIndicators.value.map((i) => i.id),
|
||||
availableIndicatorNames: availableIndicators.value.map((i) => i.indicatorName),
|
||||
currentSelectedIndicatorId: indicatorList.value[currentIndex]?.indicatorId
|
||||
});
|
||||
|
||||
|
||||
return availableIndicators.value;
|
||||
};
|
||||
|
||||
// 指标选择变化
|
||||
const handleIndicatorChange = (item: BmzrAssessmentSchemeIndicatorForm, index: number) => {
|
||||
const selectedIndicator = availableIndicators.value.find(indicator => indicator.id == item.indicatorId);
|
||||
const selectedIndicator = availableIndicators.value.find((indicator) => indicator.id == item.indicatorId);
|
||||
if (selectedIndicator) {
|
||||
item.score = selectedIndicator.score;
|
||||
item.remark = selectedIndicator.remark;
|
||||
@@ -311,7 +291,7 @@ const handleSearchIndicators = async () => {
|
||||
proxy?.$modal.msgWarning('请输入搜索关键词');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
await loadAvailableIndicators();
|
||||
proxy?.$modal.msgSuccess('搜索完成');
|
||||
@@ -337,7 +317,7 @@ const resetSearch = async () => {
|
||||
// 保存
|
||||
const handleSave = async () => {
|
||||
// 验证必填项
|
||||
const invalidItems = indicatorList.value.filter(item => !item.indicatorId);
|
||||
const invalidItems = indicatorList.value.filter((item) => !item.indicatorId);
|
||||
if (invalidItems.length > 0) {
|
||||
proxy?.$modal.msgError('请选择所有指标名称');
|
||||
return;
|
||||
@@ -469,10 +449,10 @@ defineExpose({
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
|
||||
.row-item {
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
@@ -112,14 +112,26 @@
|
||||
<el-tooltip content="修改" placement="top">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['bzrkh:assessmentScheme:edit']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="指标体系管理" placement="top">
|
||||
<el-button link type="success" icon="Management" @click="handleIndicatorManagement(scope.row)" v-hasPermi="['bzrkh:assessmentSchemeIndicator:list']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['bzrkh:assessmentScheme:remove']"></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-tooltip content="指标体系管理" placement="top">
|
||||
<el-button
|
||||
link
|
||||
type="success"
|
||||
icon="Management"
|
||||
@click="handleIndicatorManagement(scope.row)"
|
||||
v-hasPermi="['bzrkh:assessmentSchemeIndicator:list']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
icon="Delete"
|
||||
@click="handleDelete(scope.row)"
|
||||
v-hasPermi="['bzrkh:assessmentScheme:remove']"
|
||||
></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<div class="pagination-container">
|
||||
@@ -222,9 +234,7 @@ import { BmzrAssessmentSchemeVO, BmzrAssessmentSchemeQuery, BmzrAssessmentScheme
|
||||
import IndicatorManagement from './IndicatorManagement.vue';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { sys_normal_disable, sys_yesno } = toRefs<any>(
|
||||
proxy?.useDict('sys_normal_disable', 'sys_yesno')
|
||||
);
|
||||
const { sys_normal_disable, sys_yesno } = toRefs<any>(proxy?.useDict('sys_normal_disable', 'sys_yesno'));
|
||||
|
||||
const schemeList = ref<BmzrAssessmentSchemeVO[]>([]);
|
||||
const buttonLoading = ref(false);
|
||||
@@ -385,8 +395,6 @@ const handleDelete = async (row?: BmzrAssessmentSchemeVO) => {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** 指标体系管理按钮操作 */
|
||||
const handleIndicatorManagement = (row: BmzrAssessmentSchemeVO) => {
|
||||
indicatorManagementRef.value?.open(row.id);
|
||||
@@ -485,10 +493,6 @@ onMounted(() => {
|
||||
border-bottom: none !important;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* 表单样式 */
|
||||
.scheme-form {
|
||||
padding: 20px;
|
||||
@@ -513,4 +517,4 @@ onMounted(() => {
|
||||
justify-content: flex-end;
|
||||
gap: 12px;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
@@ -720,12 +720,14 @@ function handleAdd() {
|
||||
|
||||
/** 修改按钮操作 */
|
||||
async function handleUpdate(row?: ScholarshipBatchVO) {
|
||||
reset();
|
||||
const _id = row?.id || ids.value[0];
|
||||
const res = await getScholarshipBatch(_id);
|
||||
|
||||
// 先赋值基本数据
|
||||
Object.assign(form.value, res.data);
|
||||
// 关键修复:不调用reset(),直接赋值数据,确保ID不会被重置为undefined
|
||||
form.value = {
|
||||
...res.data,
|
||||
id: String(_id) // 🔧 修复:确保ID为字符串格式,避免精度丢失
|
||||
};
|
||||
|
||||
// 确保scholarshipTypeIds正确赋值
|
||||
if (res.data.scholarshipTypeIds && Array.isArray(res.data.scholarshipTypeIds)) {
|
||||
@@ -734,6 +736,13 @@ async function handleUpdate(row?: ScholarshipBatchVO) {
|
||||
form.value.scholarshipTypeIds = [];
|
||||
}
|
||||
|
||||
// 清除表单验证状态,但不重置数据
|
||||
batchFormRef.value?.clearValidate();
|
||||
|
||||
// 调试日志 - 验证ID是否正确
|
||||
console.log('修改操作 - ID:', form.value.id, '类型:', typeof form.value.id);
|
||||
console.log('修改操作 - 完整数据:', JSON.stringify(form.value, null, 2));
|
||||
|
||||
dialog.visible = true;
|
||||
dialog.title = '修改奖学金评定批次';
|
||||
}
|
||||
@@ -753,6 +762,10 @@ function submitForm() {
|
||||
buttonLoading.value = true;
|
||||
|
||||
try {
|
||||
// 调试日志 - 提交前检查数据
|
||||
console.log('提交前表单数据:', form.value);
|
||||
console.log('提交前ID值:', form.value.id, '类型:', typeof form.value.id);
|
||||
|
||||
// 检查时间冲突
|
||||
const conflictCheck = await checkTimeConflict(form.value);
|
||||
if (conflictCheck.data) {
|
||||
@@ -760,10 +773,22 @@ function submitForm() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (form.value.id) {
|
||||
await updateScholarshipBatch(form.value);
|
||||
// 关键修复:更严格的ID检查逻辑
|
||||
const isUpdate = form.value.id && form.value.id !== undefined && form.value.id !== '' && form.value.id !== null;
|
||||
|
||||
if (isUpdate) {
|
||||
console.log('执行修改操作,ID:', form.value.id, '类型:', typeof form.value.id);
|
||||
// 🔧 修复:保持ID为字符串格式,避免精度丢失
|
||||
// 不再使用parseInt(),直接使用字符串格式的ID
|
||||
const updateData = {
|
||||
...form.value,
|
||||
id: String(form.value.id) // 确保ID始终为字符串格式
|
||||
};
|
||||
console.log('发送到后端的修改数据:', JSON.stringify(updateData, null, 2));
|
||||
await updateScholarshipBatch(updateData);
|
||||
proxy?.$modal.msgSuccess('修改成功');
|
||||
} else {
|
||||
console.log('执行新增操作,ID为:', form.value.id);
|
||||
await addScholarshipBatch(form.value);
|
||||
proxy?.$modal.msgSuccess('新增成功');
|
||||
}
|
||||
|
||||
@@ -16,10 +16,7 @@
|
||||
<span class="preview-label">岗位名称:</span>
|
||||
<el-tag type="primary" size="large">{{ position?.positionName }}</el-tag>
|
||||
</div>
|
||||
<div class="preview-item">
|
||||
<span class="preview-label">工作地点:</span>
|
||||
<span class="preview-value">{{ position?.workLocation }}</span>
|
||||
</div>
|
||||
|
||||
<div class="preview-item">
|
||||
<span class="preview-label">工作时间:</span>
|
||||
<span class="preview-value">{{ position?.workTime }}</span>
|
||||
@@ -196,7 +193,6 @@ const formData = reactive<WorkStudyApplicationForm>({
|
||||
positionId: 0,
|
||||
positionName: '',
|
||||
positionType: '',
|
||||
workLocation: '',
|
||||
familyFinancialStatus: '',
|
||||
applicationReason: '',
|
||||
personalAbility: '',
|
||||
|
||||
@@ -353,13 +353,7 @@
|
||||
</template>
|
||||
|
||||
<script setup name="OverseasApproval">
|
||||
import {
|
||||
listOverseasApproval,
|
||||
getOverseasApproval,
|
||||
delOverseasApproval,
|
||||
addOverseasApproval,
|
||||
updateOverseasApproval
|
||||
} from '@/api/rs/overseas';
|
||||
import { listOverseasApproval, getOverseasApproval, delOverseasApproval, addOverseasApproval, updateOverseasApproval } from '@/api/rs/overseas';
|
||||
import { ref, reactive, toRefs, onMounted, getCurrentInstance, nextTick } from 'vue';
|
||||
import { parseTime } from '@/utils/ruoyi';
|
||||
|
||||
|
||||
@@ -749,6 +749,10 @@ const rules = reactive({
|
||||
{ pattern: /^1[3-9]\d{9}$/, message: '手机号码格式不正确', trigger: 'blur' }
|
||||
],
|
||||
email: [{ type: 'email', message: '邮箱格式不正确', trigger: 'blur' }],
|
||||
postalCode: [
|
||||
{ max: 10, message: '邮政编码长度不能超过10个字符', trigger: 'blur' },
|
||||
{ pattern: /^\d{6}$/, message: '邮政编码格式不正确,应为6位数字', trigger: 'blur' }
|
||||
],
|
||||
employmentStatus: [{ required: true, message: '就业状态不能为空', trigger: 'change' }]
|
||||
});
|
||||
|
||||
|
||||
@@ -297,13 +297,13 @@
|
||||
<el-tooltip content="查看详情" placement="top">
|
||||
<el-button link type="info" icon="View" @click="handleDetail(scope.row)" v-hasPermi="['zsjy:graduate:query']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="编辑" placement="top" v-if="scope.row.auditStatus !== '1'">
|
||||
<el-tooltip content="编辑" placement="top">
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['zsjy:graduate:edit']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="审核" placement="top" v-if="scope.row.auditStatus === '0'">
|
||||
<el-button link type="success" icon="Check" @click="handleAudit(scope.row)" v-hasPermi="['zsjy:graduate:audit']"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="删除" placement="top" v-if="scope.row.auditStatus !== '1'">
|
||||
<el-tooltip content="删除" placement="top">
|
||||
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['zsjy:graduate:remove']"></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
|
||||
@@ -168,23 +168,6 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="签退时间" min-width="120" align="center">
|
||||
<template #default="scope">
|
||||
<div v-if="scope.row.checkOutTime">
|
||||
<div>{{ parseTime(scope.row.checkDate, '{y}-{m}-{d}') }}</div>
|
||||
<div class="time-detail">{{ scope.row.checkOutTime }}</div>
|
||||
</div>
|
||||
<span v-else class="text-muted">未签退</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="工作时长" min-width="100" align="center">
|
||||
<template #default="scope">
|
||||
<span v-if="scope.row.workDuration">{{ scope.row.workDuration }}小时</span>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="签到状态" min-width="100" align="center">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="attendanceStatusOptions" :value="scope.row.status" />
|
||||
@@ -193,8 +176,6 @@
|
||||
|
||||
<el-table-column label="签到地点" prop="checkInLocation" min-width="150" align="left" show-overflow-tooltip />
|
||||
|
||||
<el-table-column label="工作内容" prop="workContent" min-width="200" align="left" show-overflow-tooltip />
|
||||
|
||||
<el-table-column label="操作" width="120" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<div class="action-buttons">
|
||||
|
||||
@@ -29,9 +29,7 @@
|
||||
<el-tag v-if="detail.learningAbility" :type="getScoreTagType(detail.learningAbility)">
|
||||
学习能力: {{ detail.learningAbility }}分
|
||||
</el-tag>
|
||||
<el-tag v-if="detail.workAttitude" :type="getScoreTagType(detail.workAttitude)">
|
||||
工作态度: {{ detail.workAttitude }}分
|
||||
</el-tag>
|
||||
<el-tag v-if="detail.workAttitude" :type="getScoreTagType(detail.workAttitude)"> 工作态度: {{ detail.workAttitude }}分 </el-tag>
|
||||
</div>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="总字数">
|
||||
@@ -205,93 +203,81 @@
|
||||
<el-row class="mt-6">
|
||||
<el-col :span="24" class="text-center">
|
||||
<el-button @click="handleClose">关闭</el-button>
|
||||
<el-button
|
||||
v-if="detail.reviewStatus === 3"
|
||||
type="primary"
|
||||
@click="handleEdit"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="detail.reviewStatus === 3"
|
||||
type="success"
|
||||
@click="handleSubmit"
|
||||
>
|
||||
提交审核
|
||||
</el-button>
|
||||
<el-button v-if="detail.reviewStatus === 3" type="primary" @click="handleEdit"> 编辑 </el-button>
|
||||
<el-button v-if="detail.reviewStatus === 3" type="success" @click="handleSubmit"> 提交审核 </el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getInternshipMonthly, submitInternshipMonthly } from '@/api/zsjy/internshipMonthly'
|
||||
import { getInternshipMonthly, submitInternshipMonthly } from '@/api/zsjy/internshipMonthly';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance
|
||||
const { internship_monthly_status } = toRefs<any>(proxy?.useDict('internship_monthly_status'))
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { internship_monthly_status } = toRefs<any>(proxy?.useDict('internship_monthly_status'));
|
||||
|
||||
const emit = defineEmits(['edit', 'close'])
|
||||
const emit = defineEmits(['edit', 'close']);
|
||||
|
||||
// 详情数据
|
||||
const detail = ref<any>({})
|
||||
const loading = ref(false)
|
||||
const detail = ref<any>({});
|
||||
const loading = ref(false);
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = async (monthlyId: string | number) => {
|
||||
loading.value = true
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await getInternshipMonthly(monthlyId)
|
||||
detail.value = res.data || {}
|
||||
const res = await getInternshipMonthly(monthlyId);
|
||||
detail.value = res.data || {};
|
||||
} catch (error) {
|
||||
console.error('获取月度总结详情失败:', error)
|
||||
proxy?.$modal.msgError('获取详情失败')
|
||||
console.error('获取月度总结详情失败:', error);
|
||||
proxy?.$modal.msgError('获取详情失败');
|
||||
} finally {
|
||||
loading.value = false
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** 根据分数获取标签类型 */
|
||||
const getScoreTagType = (score: number) => {
|
||||
if (!score) return 'info'
|
||||
if (score >= 9) return 'success'
|
||||
if (score >= 7) return 'warning'
|
||||
if (score >= 5) return ''
|
||||
return 'danger'
|
||||
}
|
||||
if (!score) return 'info';
|
||||
if (score >= 9) return 'success';
|
||||
if (score >= 7) return 'warning';
|
||||
if (score >= 5) return '';
|
||||
return 'danger';
|
||||
};
|
||||
|
||||
/** 获取字数标签类型 */
|
||||
const getWordCountTagType = (count: number) => {
|
||||
if (!count) return 'info'
|
||||
if (count < 100) return 'info'
|
||||
if (count < 500) return 'warning'
|
||||
if (count < 1000) return 'success'
|
||||
return 'danger'
|
||||
}
|
||||
if (!count) return 'info';
|
||||
if (count < 100) return 'info';
|
||||
if (count < 500) return 'warning';
|
||||
if (count < 1000) return 'success';
|
||||
return 'danger';
|
||||
};
|
||||
|
||||
/** 编辑操作 */
|
||||
const handleEdit = () => {
|
||||
emit('edit', detail.value.monthlyId)
|
||||
}
|
||||
emit('edit', detail.value.monthlyId);
|
||||
};
|
||||
|
||||
/** 提交审核操作 */
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
await proxy?.$modal.confirm('确认提交该月度总结?提交后将无法再次编辑')
|
||||
await submitInternshipMonthly(detail.value.monthlyId)
|
||||
proxy?.$modal.msgSuccess('提交成功')
|
||||
await proxy?.$modal.confirm('确认提交该月度总结?提交后将无法再次编辑');
|
||||
await submitInternshipMonthly(detail.value.monthlyId);
|
||||
proxy?.$modal.msgSuccess('提交成功');
|
||||
// 重新加载详情
|
||||
await resetForm(detail.value.monthlyId)
|
||||
await resetForm(detail.value.monthlyId);
|
||||
} catch {}
|
||||
}
|
||||
};
|
||||
|
||||
/** 关闭操作 */
|
||||
const handleClose = () => {
|
||||
emit('close')
|
||||
}
|
||||
emit('close');
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
resetForm
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -316,4 +302,3 @@ defineExpose({
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -7,13 +7,7 @@
|
||||
<template #header>
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="font-medium">基本信息</span>
|
||||
<el-button
|
||||
v-if="form.monthlyId && form.reviewStatus === 3"
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="handleAutoSave"
|
||||
:loading="autoSaving"
|
||||
>
|
||||
<el-button v-if="form.monthlyId && form.reviewStatus === 3" type="primary" size="small" @click="handleAutoSave" :loading="autoSaving">
|
||||
{{ autoSaving ? '保存中...' : '保存草稿' }}
|
||||
</el-button>
|
||||
</div>
|
||||
@@ -262,12 +256,7 @@
|
||||
<span class="font-medium">月份检查</span>
|
||||
</template>
|
||||
|
||||
<el-alert
|
||||
:type="monthDuplicateCheck.type"
|
||||
:title="monthDuplicateCheck.message"
|
||||
show-icon
|
||||
:closable="false"
|
||||
/>
|
||||
<el-alert :type="monthDuplicateCheck.type" :title="monthDuplicateCheck.message" show-icon :closable="false" />
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -305,16 +294,16 @@ import {
|
||||
checkSummaryMonth,
|
||||
getMonthlyTemplate,
|
||||
getCurrentInternshipInfo
|
||||
} from '@/api/zsjy/internshipMonthly'
|
||||
} from '@/api/zsjy/internshipMonthly';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance
|
||||
const { internship_monthly_status } = toRefs<any>(proxy?.useDict('internship_monthly_status'))
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { internship_monthly_status } = toRefs<any>(proxy?.useDict('internship_monthly_status'));
|
||||
|
||||
const emit = defineEmits(['success'])
|
||||
const emit = defineEmits(['success']);
|
||||
|
||||
const monthlyFormRef = ref<ElFormInstance>()
|
||||
const submitting = ref(false)
|
||||
const autoSaving = ref(false)
|
||||
const monthlyFormRef = ref<ElFormInstance>();
|
||||
const submitting = ref(false);
|
||||
const autoSaving = ref(false);
|
||||
|
||||
// 表单数据
|
||||
const initFormData = {
|
||||
@@ -337,15 +326,13 @@ const initFormData = {
|
||||
reviewStatus: undefined,
|
||||
submitTime: undefined,
|
||||
createTime: undefined
|
||||
}
|
||||
};
|
||||
|
||||
const form = ref({ ...initFormData })
|
||||
const form = ref({ ...initFormData });
|
||||
|
||||
// 表单验证规则
|
||||
const rules = ref({
|
||||
summaryMonth: [
|
||||
{ required: true, message: '总结月份不能为空', trigger: 'change' }
|
||||
],
|
||||
summaryMonth: [{ required: true, message: '总结月份不能为空', trigger: 'change' }],
|
||||
workContent: [
|
||||
{ required: true, message: '工作内容不能为空', trigger: 'blur' },
|
||||
{ min: 10, message: '工作内容至少需要10个字符', trigger: 'blur' },
|
||||
@@ -380,107 +367,103 @@ const rules = ref({
|
||||
{ required: true, message: '自我评价不能为空', trigger: 'blur' },
|
||||
{ max: 300, message: '自我评价不能超过300个字符', trigger: 'blur' }
|
||||
],
|
||||
nextMonthPlan: [
|
||||
{ max: 300, message: '下月计划不能超过300个字符', trigger: 'blur' }
|
||||
],
|
||||
remark: [
|
||||
{ max: 200, message: '备注说明不能超过200个字符', trigger: 'blur' }
|
||||
]
|
||||
})
|
||||
nextMonthPlan: [{ max: 300, message: '下月计划不能超过300个字符', trigger: 'blur' }],
|
||||
remark: [{ max: 200, message: '备注说明不能超过200个字符', trigger: 'blur' }]
|
||||
});
|
||||
|
||||
// 月份重复检查
|
||||
const monthDuplicateCheck = ref({
|
||||
show: false,
|
||||
type: 'info',
|
||||
message: ''
|
||||
})
|
||||
});
|
||||
|
||||
// 自动保存定时器
|
||||
let autoSaveTimer: NodeJS.Timeout | null = null
|
||||
let autoSaveTimer: NodeJS.Timeout | null = null;
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = async (monthlyId?: string | number) => {
|
||||
form.value = { ...initFormData }
|
||||
monthDuplicateCheck.value.show = false
|
||||
form.value = { ...initFormData };
|
||||
monthDuplicateCheck.value.show = false;
|
||||
|
||||
if (monthlyId) {
|
||||
try {
|
||||
const res = await getInternshipMonthly(monthlyId)
|
||||
form.value = { ...res.data }
|
||||
updateWordCount()
|
||||
const res = await getInternshipMonthly(monthlyId);
|
||||
form.value = { ...res.data };
|
||||
updateWordCount();
|
||||
} catch (error) {
|
||||
console.error('获取月度总结详情失败:', error)
|
||||
console.error('获取月度总结详情失败:', error);
|
||||
}
|
||||
} else {
|
||||
// 新增时获取当前月份模板和学生实习信息
|
||||
const currentMonth = new Date().toISOString().substring(0, 7)
|
||||
form.value.summaryMonth = currentMonth
|
||||
await loadMonthlyTemplate(currentMonth)
|
||||
|
||||
const currentMonth = new Date().toISOString().substring(0, 7);
|
||||
form.value.summaryMonth = currentMonth;
|
||||
await loadMonthlyTemplate(currentMonth);
|
||||
|
||||
// 获取学生当前实习信息
|
||||
try {
|
||||
const internshipRes = await getCurrentInternshipInfo()
|
||||
const internshipRes = await getCurrentInternshipInfo();
|
||||
if (internshipRes.code === 200 && internshipRes.data) {
|
||||
form.value.internshipId = internshipRes.data.internshipId
|
||||
form.value.internshipId = internshipRes.data.internshipId;
|
||||
} else {
|
||||
// 如果没有进行中的实习记录,提示用户
|
||||
proxy?.$modal.msgError('您当前没有进行中的实习记录,无法创建月度总结')
|
||||
emit('success') // 关闭对话框
|
||||
return
|
||||
proxy?.$modal.msgError('您当前没有进行中的实习记录,无法创建月度总结');
|
||||
emit('success'); // 关闭对话框
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取学生实习信息失败:', error)
|
||||
proxy?.$modal.msgError('获取实习信息失败,请稍后重试')
|
||||
emit('success') // 关闭对话框
|
||||
return
|
||||
console.error('获取学生实习信息失败:', error);
|
||||
proxy?.$modal.msgError('获取实习信息失败,请稍后重试');
|
||||
emit('success'); // 关闭对话框
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
nextTick(() => {
|
||||
monthlyFormRef.value?.clearValidate()
|
||||
})
|
||||
}
|
||||
monthlyFormRef.value?.clearValidate();
|
||||
});
|
||||
};
|
||||
|
||||
/** 加载月份模板 */
|
||||
const loadMonthlyTemplate = async (month: string) => {
|
||||
try {
|
||||
const res = await getMonthlyTemplate(month)
|
||||
const res = await getMonthlyTemplate(month);
|
||||
if (res.data) {
|
||||
// 如果有模板数据,可以预填充一些内容
|
||||
// form.value.nextMonthPlan = res.data.suggestedPlan || ''
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('获取月份模板失败,将使用空模板')
|
||||
console.log('获取月份模板失败,将使用空模板');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** 检查月份重复 */
|
||||
const checkMonthDuplicate = async () => {
|
||||
if (!form.value.summaryMonth) return
|
||||
if (!form.value.summaryMonth) return;
|
||||
|
||||
try {
|
||||
const res = await checkSummaryMonth(form.value.summaryMonth)
|
||||
const res = await checkSummaryMonth(form.value.summaryMonth);
|
||||
if (res.data.exists && (!form.value.monthlyId || res.data.monthlyId !== form.value.monthlyId)) {
|
||||
monthDuplicateCheck.value = {
|
||||
show: true,
|
||||
type: 'warning',
|
||||
message: '该月份已存在总结记录,请选择其他月份'
|
||||
}
|
||||
};
|
||||
} else {
|
||||
monthDuplicateCheck.value = {
|
||||
show: true,
|
||||
type: 'success',
|
||||
message: '该月份可以创建总结'
|
||||
}
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
monthDuplicateCheck.value = {
|
||||
show: true,
|
||||
type: 'error',
|
||||
message: '检查月份失败,请重试'
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** 更新字数统计 */
|
||||
const updateWordCount = () => {
|
||||
@@ -492,91 +475,91 @@ const updateWordCount = () => {
|
||||
form.value.selfEvaluation,
|
||||
form.value.nextMonthPlan,
|
||||
form.value.remark
|
||||
].join('')
|
||||
].join('');
|
||||
|
||||
form.value.wordCount = content.length
|
||||
form.value.wordCount = content.length;
|
||||
|
||||
// 设置自动保存定时器
|
||||
if (form.value.monthlyId && form.value.reviewStatus === 3) {
|
||||
if (autoSaveTimer) {
|
||||
clearTimeout(autoSaveTimer)
|
||||
clearTimeout(autoSaveTimer);
|
||||
}
|
||||
autoSaveTimer = setTimeout(() => {
|
||||
handleAutoSave()
|
||||
}, 5000) // 5秒后自动保存
|
||||
handleAutoSave();
|
||||
}, 5000); // 5秒后自动保存
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** 获取字数标签类型 */
|
||||
const getWordCountTagType = (count: number) => {
|
||||
if (count < 100) return 'info'
|
||||
if (count < 500) return 'warning'
|
||||
if (count < 1000) return 'success'
|
||||
return 'danger'
|
||||
}
|
||||
if (count < 100) return 'info';
|
||||
if (count < 500) return 'warning';
|
||||
if (count < 1000) return 'success';
|
||||
return 'danger';
|
||||
};
|
||||
|
||||
/** 自动保存草稿 */
|
||||
const handleAutoSave = async () => {
|
||||
if (!form.value.monthlyId || form.value.reviewStatus !== 3) return
|
||||
if (!form.value.monthlyId || form.value.reviewStatus !== 3) return;
|
||||
|
||||
autoSaving.value = true
|
||||
autoSaving.value = true;
|
||||
try {
|
||||
await autosaveInternshipMonthly(form.value)
|
||||
proxy?.$message.success('草稿已自动保存')
|
||||
await autosaveInternshipMonthly(form.value);
|
||||
proxy?.$message.success('草稿已自动保存');
|
||||
} catch (error) {
|
||||
console.error('自动保存失败:', error)
|
||||
console.error('自动保存失败:', error);
|
||||
} finally {
|
||||
autoSaving.value = false
|
||||
autoSaving.value = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** 提交表单 */
|
||||
const handleSubmit = async (type: 'draft' | 'submit') => {
|
||||
if (!monthlyFormRef.value) return
|
||||
if (!monthlyFormRef.value) return;
|
||||
|
||||
const valid = await monthlyFormRef.value.validate().catch(() => false)
|
||||
if (!valid) return
|
||||
const valid = await monthlyFormRef.value.validate().catch(() => false);
|
||||
if (!valid) return;
|
||||
|
||||
submitting.value = true
|
||||
submitting.value = true;
|
||||
try {
|
||||
updateWordCount()
|
||||
updateWordCount();
|
||||
|
||||
const formData = {
|
||||
...form.value,
|
||||
reviewStatus: type === 'draft' ? 3 : 0, // 草稿=3,待审核=0
|
||||
submitTime: type === 'submit' ? new Date() : undefined
|
||||
}
|
||||
};
|
||||
|
||||
if (form.value.monthlyId) {
|
||||
await updateInternshipMonthly(formData)
|
||||
await updateInternshipMonthly(formData);
|
||||
} else {
|
||||
await addInternshipMonthly(formData)
|
||||
await addInternshipMonthly(formData);
|
||||
}
|
||||
|
||||
proxy?.$modal.msgSuccess(type === 'draft' ? '保存成功' : '提交成功')
|
||||
emit('success')
|
||||
proxy?.$modal.msgSuccess(type === 'draft' ? '保存成功' : '提交成功');
|
||||
emit('success');
|
||||
} catch (error) {
|
||||
console.error('提交失败:', error)
|
||||
console.error('提交失败:', error);
|
||||
} finally {
|
||||
submitting.value = false
|
||||
submitting.value = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** 取消操作 */
|
||||
const handleCancel = () => {
|
||||
emit('success')
|
||||
}
|
||||
emit('success');
|
||||
};
|
||||
|
||||
// 清理定时器
|
||||
onUnmounted(() => {
|
||||
if (autoSaveTimer) {
|
||||
clearTimeout(autoSaveTimer)
|
||||
clearTimeout(autoSaveTimer);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
resetForm
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -31,13 +31,7 @@
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="专业技能" prop="professionalSkills">
|
||||
<el-input-number
|
||||
v-model="queryParams.professionalSkills"
|
||||
:min="1"
|
||||
:max="10"
|
||||
placeholder="请输入专业技能评分"
|
||||
style="width: 100%"
|
||||
/>
|
||||
<el-input-number v-model="queryParams.professionalSkills" :min="1" :max="10" placeholder="请输入专业技能评分" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
@@ -60,10 +54,14 @@
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['zsjy:internshipmonthly:add']">新增</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['zsjy:internshipmonthly:edit']">修改</el-button>
|
||||
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['zsjy:internshipmonthly:edit']"
|
||||
>修改</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['zsjy:internshipmonthly:remove']">删除</el-button>
|
||||
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['zsjy:internshipmonthly:remove']"
|
||||
>删除</el-button
|
||||
>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['zsjy:internshipmonthly:export']">导出</el-button>
|
||||
@@ -80,20 +78,16 @@
|
||||
<el-table-column label="综合评分" align="center" width="120">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="专业/沟通/团队/学习/态度" placement="top">
|
||||
<el-tag :type="getScoreTagType(getAverageScore(scope.row))">
|
||||
{{ getAverageScore(scope.row).toFixed(1) }}分
|
||||
</el-tag>
|
||||
<el-tag :type="getScoreTagType(getAverageScore(scope.row))"> {{ getAverageScore(scope.row).toFixed(1) }}分 </el-tag>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="字数统计" align="center" width="100">
|
||||
<template #default="scope">
|
||||
{{ scope.row.wordCount || 0 }}字
|
||||
</template>
|
||||
<template #default="scope"> {{ scope.row.wordCount || 0 }}字 </template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审核状态" align="center" prop="reviewStatus" width="100">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="internship_monthly_status" :value="scope.row.reviewStatus" />
|
||||
<dict-tag :options="internship_monthly_status" :value="scope.row.reviewStatus" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="是否逾期" align="center" prop="isOverdue" width="100">
|
||||
@@ -116,59 +110,29 @@
|
||||
<template #default="scope">
|
||||
<!-- 查看详情 -->
|
||||
<el-tooltip content="查看详情" placement="top">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
icon="View"
|
||||
@click="handleDetail(scope.row)"
|
||||
v-hasPermi="['zsjy:internshipmonthly:list']"
|
||||
></el-button>
|
||||
<el-button link type="primary" icon="View" @click="handleDetail(scope.row)" v-hasPermi="['zsjy:internshipmonthly:list']"></el-button>
|
||||
</el-tooltip>
|
||||
|
||||
<!-- 编辑(只有草稿状态才能编辑) -->
|
||||
<el-tooltip content="编辑" placement="top" v-if="scope.row.reviewStatus === 3">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
icon="Edit"
|
||||
@click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['zsjy:internshipmonthly:edit']"
|
||||
></el-button>
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['zsjy:internshipmonthly:edit']"></el-button>
|
||||
</el-tooltip>
|
||||
|
||||
<!-- 提交(只有草稿状态才能提交) -->
|
||||
<el-tooltip content="提交" placement="top" v-if="scope.row.reviewStatus === 3">
|
||||
<el-button
|
||||
link
|
||||
type="success"
|
||||
icon="Check"
|
||||
@click="handleSubmit(scope.row)"
|
||||
v-hasPermi="['zsjy:internshipmonthly:submit']"
|
||||
></el-button>
|
||||
<el-button link type="success" icon="Check" @click="handleSubmit(scope.row)" v-hasPermi="['zsjy:internshipmonthly:submit']"></el-button>
|
||||
</el-tooltip>
|
||||
|
||||
<!-- 删除(只有草稿状态才能删除) -->
|
||||
<el-tooltip content="删除" placement="top" v-if="scope.row.reviewStatus === 3">
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
icon="Delete"
|
||||
@click="handleDelete(scope.row)"
|
||||
v-hasPermi="['zsjy:internshipmonthly:remove']"
|
||||
></el-button>
|
||||
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['zsjy:internshipmonthly:remove']"></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页 -->
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||||
</el-card>
|
||||
|
||||
<!-- 添加或修改月度总结对话框 -->
|
||||
@@ -190,30 +154,30 @@ import {
|
||||
delInternshipMonthly,
|
||||
submitInternshipMonthly,
|
||||
exportInternshipMonthly
|
||||
} from '@/api/zsjy/internshipMonthly'
|
||||
import MonthlyForm from './form.vue'
|
||||
import MonthlyDetail from './detail.vue'
|
||||
} from '@/api/zsjy/internshipMonthly';
|
||||
import MonthlyForm from './form.vue';
|
||||
import MonthlyDetail from './detail.vue';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance
|
||||
const { internship_monthly_status } = toRefs<any>(proxy?.useDict('internship_monthly_status'))
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { internship_monthly_status } = toRefs<any>(proxy?.useDict('internship_monthly_status'));
|
||||
|
||||
const monthlyList = ref<any[]>([])
|
||||
const loading = ref(true)
|
||||
const showSearch = ref(true)
|
||||
const ids = ref<Array<string | number>>([])
|
||||
const single = ref(true)
|
||||
const multiple = ref(true)
|
||||
const total = ref(0)
|
||||
const monthlyList = ref<any[]>([]);
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref<Array<string | number>>([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
|
||||
const dialog = reactive<DialogOption>({
|
||||
visible: false,
|
||||
title: ''
|
||||
})
|
||||
});
|
||||
|
||||
const detailDialog = reactive<DialogOption>({
|
||||
visible: false,
|
||||
title: ''
|
||||
})
|
||||
});
|
||||
|
||||
const initFormData: any = {
|
||||
pageNum: 1,
|
||||
@@ -221,101 +185,105 @@ const initFormData: any = {
|
||||
summaryMonth: undefined,
|
||||
reviewStatus: undefined,
|
||||
professionalSkills: undefined
|
||||
}
|
||||
};
|
||||
const data = reactive<PageData<any, any>>({
|
||||
form: {},
|
||||
queryParams: { ...initFormData },
|
||||
rules: {}
|
||||
})
|
||||
});
|
||||
|
||||
const { queryParams, form, rules } = toRefs(data)
|
||||
const { queryParams, form, rules } = toRefs(data);
|
||||
|
||||
const queryFormRef = ref<ElFormInstance>()
|
||||
const monthlyFormRef = ref<ComponentRef<typeof MonthlyForm>>()
|
||||
const monthlyDetailRef = ref<ComponentRef<typeof MonthlyDetail>>()
|
||||
const queryFormRef = ref<ElFormInstance>();
|
||||
const monthlyFormRef = ref<ComponentRef<typeof MonthlyForm>>();
|
||||
const monthlyDetailRef = ref<ComponentRef<typeof MonthlyDetail>>();
|
||||
|
||||
/** 查询月度总结列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
const res = await listInternshipMonthly(queryParams.value)
|
||||
monthlyList.value = res.rows
|
||||
total.value = res.total
|
||||
loading.value = false
|
||||
}
|
||||
loading.value = true;
|
||||
const res = await listInternshipMonthly(queryParams.value);
|
||||
monthlyList.value = res.rows;
|
||||
total.value = res.total;
|
||||
loading.value = false;
|
||||
};
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.value.pageNum = 1
|
||||
getList()
|
||||
}
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value?.resetFields()
|
||||
handleQuery()
|
||||
}
|
||||
queryFormRef.value?.resetFields();
|
||||
handleQuery();
|
||||
};
|
||||
|
||||
/** 多选框选中数据 */
|
||||
const handleSelectionChange = (selection: any) => {
|
||||
ids.value = selection.map((item: any) => item.monthlyId)
|
||||
single.value = selection.length !== 1
|
||||
multiple.value = !selection.length
|
||||
}
|
||||
ids.value = selection.map((item: any) => item.monthlyId);
|
||||
single.value = selection.length !== 1;
|
||||
multiple.value = !selection.length;
|
||||
};
|
||||
|
||||
/** 新增按钮操作 */
|
||||
const handleAdd = () => {
|
||||
dialog.visible = true
|
||||
dialog.title = '添加月度总结'
|
||||
dialog.visible = true;
|
||||
dialog.title = '添加月度总结';
|
||||
nextTick(() => {
|
||||
monthlyFormRef.value?.resetForm()
|
||||
})
|
||||
}
|
||||
monthlyFormRef.value?.resetForm();
|
||||
});
|
||||
};
|
||||
|
||||
/** 修改按钮操作 */
|
||||
const handleUpdate = async (row?: any) => {
|
||||
dialog.visible = true
|
||||
dialog.title = '修改月度总结'
|
||||
dialog.visible = true;
|
||||
dialog.title = '修改月度总结';
|
||||
nextTick(() => {
|
||||
const monthlyId = row?.monthlyId || ids.value[0]
|
||||
monthlyFormRef.value?.resetForm(monthlyId)
|
||||
})
|
||||
}
|
||||
const monthlyId = row?.monthlyId || ids.value[0];
|
||||
monthlyFormRef.value?.resetForm(monthlyId);
|
||||
});
|
||||
};
|
||||
|
||||
/** 查看详情操作 */
|
||||
const handleDetail = async (row: any) => {
|
||||
detailDialog.visible = true
|
||||
detailDialog.visible = true;
|
||||
nextTick(() => {
|
||||
monthlyDetailRef.value?.resetForm(row.monthlyId)
|
||||
})
|
||||
}
|
||||
monthlyDetailRef.value?.resetForm(row.monthlyId);
|
||||
});
|
||||
};
|
||||
|
||||
/** 提交审批操作 */
|
||||
const handleSubmit = async (row: any) => {
|
||||
try {
|
||||
await proxy?.$modal.confirm('确认提交该月度总结?提交后将无法再次编辑')
|
||||
await submitInternshipMonthly(row.monthlyId)
|
||||
await getList()
|
||||
proxy?.$modal.msgSuccess('提交成功')
|
||||
await proxy?.$modal.confirm('确认提交该月度总结?提交后将无法再次编辑');
|
||||
await submitInternshipMonthly(row.monthlyId);
|
||||
await getList();
|
||||
proxy?.$modal.msgSuccess('提交成功');
|
||||
} catch {}
|
||||
}
|
||||
};
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (row?: any) => {
|
||||
const monthlyIds = row?.monthlyId || ids.value
|
||||
const monthlyIds = row?.monthlyId || ids.value;
|
||||
try {
|
||||
await proxy?.$modal.confirm('是否确认删除月度总结编号为"' + monthlyIds + '"的数据项?')
|
||||
await delInternshipMonthly(monthlyIds)
|
||||
await getList()
|
||||
proxy?.$modal.msgSuccess('删除成功')
|
||||
await proxy?.$modal.confirm('是否确认删除月度总结编号为"' + monthlyIds + '"的数据项?');
|
||||
await delInternshipMonthly(monthlyIds);
|
||||
await getList();
|
||||
proxy?.$modal.msgSuccess('删除成功');
|
||||
} catch {}
|
||||
}
|
||||
};
|
||||
|
||||
/** 导出按钮操作 */
|
||||
const handleExport = () => {
|
||||
proxy?.$download('zsjy/internship/monthly/export', {
|
||||
...queryParams.value
|
||||
}, `月度总结_${new Date().getTime()}.xlsx`)
|
||||
}
|
||||
proxy?.$download(
|
||||
'zsjy/internship/monthly/export',
|
||||
{
|
||||
...queryParams.value
|
||||
},
|
||||
`月度总结_${new Date().getTime()}.xlsx`
|
||||
);
|
||||
};
|
||||
|
||||
/** 计算五项技能的平均分 */
|
||||
const getAverageScore = (row: any) => {
|
||||
@@ -325,23 +293,23 @@ const getAverageScore = (row: any) => {
|
||||
row.teamworkAbility || 0,
|
||||
row.learningAbility || 0,
|
||||
row.workAttitude || 0
|
||||
].filter(score => score > 0)
|
||||
|
||||
if (scores.length === 0) return 0
|
||||
return scores.reduce((sum, score) => sum + score, 0) / scores.length
|
||||
}
|
||||
].filter((score) => score > 0);
|
||||
|
||||
if (scores.length === 0) return 0;
|
||||
return scores.reduce((sum, score) => sum + score, 0) / scores.length;
|
||||
};
|
||||
|
||||
/** 根据分数获取标签类型 */
|
||||
const getScoreTagType = (score: number) => {
|
||||
if (score >= 9) return 'success'
|
||||
if (score >= 7) return 'warning'
|
||||
if (score >= 5) return ''
|
||||
return 'danger'
|
||||
}
|
||||
if (score >= 9) return 'success';
|
||||
if (score >= 7) return 'warning';
|
||||
if (score >= 5) return '';
|
||||
return 'danger';
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -14,20 +14,10 @@
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="学生姓名" prop="studentName">
|
||||
<el-input
|
||||
v-model="queryParams.studentName"
|
||||
placeholder="请输入学生姓名"
|
||||
clearable
|
||||
style="width: 150px"
|
||||
/>
|
||||
<el-input v-model="queryParams.studentName" placeholder="请输入学生姓名" clearable style="width: 150px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="学号" prop="studentNumber">
|
||||
<el-input
|
||||
v-model="queryParams.studentNumber"
|
||||
placeholder="请输入学号"
|
||||
clearable
|
||||
style="width: 150px"
|
||||
/>
|
||||
<el-input v-model="queryParams.studentNumber" placeholder="请输入学号" clearable style="width: 150px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="审核状态" prop="reviewStatus">
|
||||
<el-select v-model="queryParams.reviewStatus" placeholder="请选择状态" clearable style="width: 150px">
|
||||
@@ -84,34 +74,14 @@
|
||||
<div class="table-header">
|
||||
<div class="table-title">月度总结审核</div>
|
||||
<div class="table-actions">
|
||||
<el-button
|
||||
type="success"
|
||||
icon="Check"
|
||||
:disabled="!selections.length"
|
||||
@click="handleBatchApprove"
|
||||
>
|
||||
批量通过
|
||||
</el-button>
|
||||
<el-button
|
||||
type="warning"
|
||||
icon="Close"
|
||||
:disabled="!selections.length"
|
||||
@click="handleBatchReject"
|
||||
>
|
||||
批量拒绝
|
||||
</el-button>
|
||||
<el-button type="success" icon="Check" :disabled="!selections.length" @click="handleBatchApprove"> 批量通过 </el-button>
|
||||
<el-button type="warning" icon="Close" :disabled="!selections.length" @click="handleBatchReject"> 批量拒绝 </el-button>
|
||||
<el-button type="info" icon="Download" @click="handleExport">导出</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 数据表格 -->
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="reviewList"
|
||||
@selection-change="handleSelectionChange"
|
||||
border
|
||||
stripe
|
||||
>
|
||||
<el-table v-loading="loading" :data="reviewList" @selection-change="handleSelectionChange" border stripe>
|
||||
<el-table-column type="selection" width="50" align="center" />
|
||||
<el-table-column label="学生信息" width="200" fixed="left">
|
||||
<template #default="scope">
|
||||
@@ -125,10 +95,7 @@
|
||||
<el-table-column prop="monthNumber" label="第几个月" width="100" align="center" />
|
||||
<el-table-column label="状态" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag
|
||||
:type="getStatusType(scope.row.reviewStatus)"
|
||||
size="small"
|
||||
>
|
||||
<el-tag :type="getStatusType(scope.row.reviewStatus)" size="small">
|
||||
{{ getStatusText(scope.row.reviewStatus) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
@@ -146,48 +113,19 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="200" align="center" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button type="primary" link size="small" @click="handleDetail(scope.row)">
|
||||
查看详情
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="scope.row.reviewStatus === 0"
|
||||
type="success"
|
||||
link
|
||||
size="small"
|
||||
@click="handleApprove(scope.row)"
|
||||
>
|
||||
审核
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="scope.row.reviewStatus !== 0"
|
||||
type="warning"
|
||||
link
|
||||
size="small"
|
||||
@click="handleReReview(scope.row)"
|
||||
>
|
||||
重新审核
|
||||
</el-button>
|
||||
<el-button type="primary" link size="small" @click="handleDetail(scope.row)"> 查看详情 </el-button>
|
||||
<el-button v-if="scope.row.reviewStatus === 0" type="success" link size="small" @click="handleApprove(scope.row)"> 审核 </el-button>
|
||||
<el-button v-if="scope.row.reviewStatus !== 0" type="warning" link size="small" @click="handleReReview(scope.row)"> 重新审核 </el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页 -->
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||||
</el-card>
|
||||
|
||||
<!-- 审核对话框 -->
|
||||
<el-dialog
|
||||
:title="reviewForm.monthlyId ? '审核月度总结' : '批量审核'"
|
||||
v-model="reviewVisible"
|
||||
width="600px"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<el-dialog :title="reviewForm.monthlyId ? '审核月度总结' : '批量审核'" v-model="reviewVisible" width="600px" :close-on-click-modal="false">
|
||||
<el-form :model="reviewForm" :rules="reviewRules" ref="reviewFormRef" label-width="100px">
|
||||
<el-form-item label="审核结果" prop="reviewStatus">
|
||||
<el-radio-group v-model="reviewForm.reviewStatus">
|
||||
@@ -202,25 +140,11 @@
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="评分" prop="reviewScore">
|
||||
<el-input-number
|
||||
v-model="reviewForm.reviewScore"
|
||||
:min="0"
|
||||
:max="100"
|
||||
:precision="1"
|
||||
placeholder="请输入评分(0-100)"
|
||||
style="width: 200px"
|
||||
/>
|
||||
<el-input-number v-model="reviewForm.reviewScore" :min="0" :max="100" :precision="1" placeholder="请输入评分(0-100)" style="width: 200px" />
|
||||
<span class="ml-2 text-gray-500">分</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="审核意见" prop="reviewComment">
|
||||
<el-input
|
||||
v-model="reviewForm.reviewComment"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
placeholder="请输入审核意见"
|
||||
maxlength="500"
|
||||
show-word-limit
|
||||
/>
|
||||
<el-input v-model="reviewForm.reviewComment" type="textarea" :rows="4" placeholder="请输入审核意见" maxlength="500" show-word-limit />
|
||||
</el-form-item>
|
||||
<el-form-item label="改进建议" prop="reviewSuggestions">
|
||||
<el-input
|
||||
@@ -240,19 +164,14 @@
|
||||
</el-dialog>
|
||||
|
||||
<!-- 详情对话框 -->
|
||||
<monthly-detail
|
||||
v-if="detailVisible"
|
||||
:monthly-id="currentMonthlyId"
|
||||
:visible="detailVisible"
|
||||
@close="detailVisible = false"
|
||||
/>
|
||||
<monthly-detail v-if="detailVisible" :monthly-id="currentMonthlyId" :visible="detailVisible" @close="detailVisible = false" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="MonthlyReview">
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { Check, Close, Search, Refresh, Download } from '@element-plus/icons-vue'
|
||||
import { ref, reactive, onMounted } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { Check, Close, Search, Refresh, Download } from '@element-plus/icons-vue';
|
||||
import {
|
||||
getPendingReviewList,
|
||||
getReviewList,
|
||||
@@ -264,18 +183,18 @@ import {
|
||||
type MonthlyReviewParams,
|
||||
type BatchReviewParams,
|
||||
type ReviewStatistics
|
||||
} from '@/api/zsjy/internshipMonthlyReview'
|
||||
import MonthlyDetail from './detail.vue'
|
||||
} from '@/api/zsjy/internshipMonthlyReview';
|
||||
import MonthlyDetail from './detail.vue';
|
||||
|
||||
// 响应式数据
|
||||
const loading = ref(false)
|
||||
const reviewLoading = ref(false)
|
||||
const reviewVisible = ref(false)
|
||||
const detailVisible = ref(false)
|
||||
const currentMonthlyId = ref<number | null>(null)
|
||||
const selections = ref<any[]>([])
|
||||
const total = ref(0)
|
||||
const reviewList = ref<any[]>([])
|
||||
const loading = ref(false);
|
||||
const reviewLoading = ref(false);
|
||||
const reviewVisible = ref(false);
|
||||
const detailVisible = ref(false);
|
||||
const currentMonthlyId = ref<number | null>(null);
|
||||
const selections = ref<any[]>([]);
|
||||
const total = ref(0);
|
||||
const reviewList = ref<any[]>([]);
|
||||
|
||||
// 查询参数
|
||||
const queryParams = reactive<MonthlyReviewQueryParams & { pageNum: number; pageSize: number }>({
|
||||
@@ -285,7 +204,7 @@ const queryParams = reactive<MonthlyReviewQueryParams & { pageNum: number; pageS
|
||||
studentName: '',
|
||||
studentNumber: '',
|
||||
reviewStatus: undefined
|
||||
})
|
||||
});
|
||||
|
||||
// 统计数据
|
||||
const statistics = ref<ReviewStatistics>({
|
||||
@@ -295,7 +214,7 @@ const statistics = ref<ReviewStatistics>({
|
||||
rejectedCount: 0,
|
||||
averageScore: 0,
|
||||
completionRate: 0
|
||||
})
|
||||
});
|
||||
|
||||
// 审核表单
|
||||
const reviewForm = reactive<MonthlyReviewParams & { isBatch?: boolean; monthlyIds?: number[] }>({
|
||||
@@ -306,143 +225,143 @@ const reviewForm = reactive<MonthlyReviewParams & { isBatch?: boolean; monthlyId
|
||||
reviewSuggestions: '',
|
||||
isBatch: false,
|
||||
monthlyIds: []
|
||||
})
|
||||
});
|
||||
|
||||
// 表单校验规则
|
||||
const reviewRules = {
|
||||
reviewStatus: [{ required: true, message: '请选择审核结果', trigger: 'change' }],
|
||||
reviewComment: [{ required: true, message: '请输入审核意见', trigger: 'blur' }]
|
||||
}
|
||||
};
|
||||
|
||||
// ref引用
|
||||
const queryFormRef = ref()
|
||||
const reviewFormRef = ref()
|
||||
const queryFormRef = ref();
|
||||
const reviewFormRef = ref();
|
||||
|
||||
// 获取列表数据
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
loading.value = true;
|
||||
try {
|
||||
const response = await getReviewList(queryParams)
|
||||
reviewList.value = response.rows
|
||||
total.value = response.total
|
||||
const response = await getReviewList(queryParams);
|
||||
reviewList.value = response.rows;
|
||||
total.value = response.total;
|
||||
} catch (error) {
|
||||
console.error('获取审核列表失败:', error)
|
||||
console.error('获取审核列表失败:', error);
|
||||
} finally {
|
||||
loading.value = false
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 获取统计数据
|
||||
const getStatistics = async () => {
|
||||
try {
|
||||
const response = await getReviewStatistics(queryParams)
|
||||
statistics.value = response.data
|
||||
const response = await getReviewStatistics(queryParams);
|
||||
statistics.value = response.data;
|
||||
} catch (error) {
|
||||
console.error('获取统计数据失败:', error)
|
||||
console.error('获取统计数据失败:', error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 搜索
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNum = 1
|
||||
getList()
|
||||
getStatistics()
|
||||
}
|
||||
queryParams.pageNum = 1;
|
||||
getList();
|
||||
getStatistics();
|
||||
};
|
||||
|
||||
// 重置搜索
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value?.resetFields()
|
||||
queryParams.pageNum = 1
|
||||
getList()
|
||||
getStatistics()
|
||||
}
|
||||
queryFormRef.value?.resetFields();
|
||||
queryParams.pageNum = 1;
|
||||
getList();
|
||||
getStatistics();
|
||||
};
|
||||
|
||||
// 选择变化
|
||||
const handleSelectionChange = (selection: any[]) => {
|
||||
selections.value = selection
|
||||
}
|
||||
selections.value = selection;
|
||||
};
|
||||
|
||||
// 查看详情
|
||||
const handleDetail = (row: any) => {
|
||||
currentMonthlyId.value = row.monthlyId
|
||||
detailVisible.value = true
|
||||
}
|
||||
currentMonthlyId.value = row.monthlyId;
|
||||
detailVisible.value = true;
|
||||
};
|
||||
|
||||
// 单个审核
|
||||
const handleApprove = (row: any) => {
|
||||
reviewForm.monthlyId = row.monthlyId
|
||||
reviewForm.isBatch = false
|
||||
reviewForm.reviewStatus = 1
|
||||
reviewForm.reviewScore = undefined
|
||||
reviewForm.reviewComment = ''
|
||||
reviewForm.reviewSuggestions = ''
|
||||
reviewVisible.value = true
|
||||
}
|
||||
reviewForm.monthlyId = row.monthlyId;
|
||||
reviewForm.isBatch = false;
|
||||
reviewForm.reviewStatus = 1;
|
||||
reviewForm.reviewScore = undefined;
|
||||
reviewForm.reviewComment = '';
|
||||
reviewForm.reviewSuggestions = '';
|
||||
reviewVisible.value = true;
|
||||
};
|
||||
|
||||
// 重新审核
|
||||
const handleReReview = (row: any) => {
|
||||
reviewForm.monthlyId = row.monthlyId
|
||||
reviewForm.isBatch = false
|
||||
reviewForm.reviewStatus = row.reviewStatus
|
||||
reviewForm.reviewScore = row.reviewScore
|
||||
reviewForm.reviewComment = row.reviewComment || ''
|
||||
reviewForm.reviewSuggestions = row.reviewSuggestions || ''
|
||||
reviewVisible.value = true
|
||||
}
|
||||
reviewForm.monthlyId = row.monthlyId;
|
||||
reviewForm.isBatch = false;
|
||||
reviewForm.reviewStatus = row.reviewStatus;
|
||||
reviewForm.reviewScore = row.reviewScore;
|
||||
reviewForm.reviewComment = row.reviewComment || '';
|
||||
reviewForm.reviewSuggestions = row.reviewSuggestions || '';
|
||||
reviewVisible.value = true;
|
||||
};
|
||||
|
||||
// 批量通过
|
||||
const handleBatchApprove = () => {
|
||||
const pendingItems = selections.value.filter(item => item.reviewStatus === 0)
|
||||
const pendingItems = selections.value.filter((item) => item.reviewStatus === 0);
|
||||
if (pendingItems.length === 0) {
|
||||
ElMessage.warning('请选择待审核的记录')
|
||||
return
|
||||
ElMessage.warning('请选择待审核的记录');
|
||||
return;
|
||||
}
|
||||
|
||||
reviewForm.isBatch = true
|
||||
reviewForm.reviewStatus = 1
|
||||
reviewForm.reviewScore = 85 // 默认分数
|
||||
reviewForm.reviewComment = ''
|
||||
reviewForm.reviewSuggestions = ''
|
||||
reviewForm.monthlyIds = pendingItems.map(item => item.monthlyId)
|
||||
reviewVisible.value = true
|
||||
}
|
||||
|
||||
reviewForm.isBatch = true;
|
||||
reviewForm.reviewStatus = 1;
|
||||
reviewForm.reviewScore = 85; // 默认分数
|
||||
reviewForm.reviewComment = '';
|
||||
reviewForm.reviewSuggestions = '';
|
||||
reviewForm.monthlyIds = pendingItems.map((item) => item.monthlyId);
|
||||
reviewVisible.value = true;
|
||||
};
|
||||
|
||||
// 批量拒绝
|
||||
const handleBatchReject = () => {
|
||||
const pendingItems = selections.value.filter(item => item.reviewStatus === 0)
|
||||
const pendingItems = selections.value.filter((item) => item.reviewStatus === 0);
|
||||
if (pendingItems.length === 0) {
|
||||
ElMessage.warning('请选择待审核的记录')
|
||||
return
|
||||
ElMessage.warning('请选择待审核的记录');
|
||||
return;
|
||||
}
|
||||
|
||||
reviewForm.isBatch = true
|
||||
reviewForm.reviewStatus = 2
|
||||
reviewForm.reviewScore = undefined
|
||||
reviewForm.reviewComment = ''
|
||||
reviewForm.reviewSuggestions = ''
|
||||
reviewForm.monthlyIds = pendingItems.map(item => item.monthlyId)
|
||||
reviewVisible.value = true
|
||||
}
|
||||
|
||||
reviewForm.isBatch = true;
|
||||
reviewForm.reviewStatus = 2;
|
||||
reviewForm.reviewScore = undefined;
|
||||
reviewForm.reviewComment = '';
|
||||
reviewForm.reviewSuggestions = '';
|
||||
reviewForm.monthlyIds = pendingItems.map((item) => item.monthlyId);
|
||||
reviewVisible.value = true;
|
||||
};
|
||||
|
||||
// 提交审核
|
||||
const submitReview = async () => {
|
||||
try {
|
||||
await reviewFormRef.value?.validate()
|
||||
reviewLoading.value = true
|
||||
|
||||
await reviewFormRef.value?.validate();
|
||||
reviewLoading.value = true;
|
||||
|
||||
if (reviewForm.isBatch) {
|
||||
// 批量审核
|
||||
const batchData: BatchReviewParams = {
|
||||
reviewItems: reviewForm.monthlyIds!.map(monthlyId => ({
|
||||
reviewItems: reviewForm.monthlyIds!.map((monthlyId) => ({
|
||||
monthlyId,
|
||||
reviewStatus: reviewForm.reviewStatus,
|
||||
reviewScore: reviewForm.reviewScore,
|
||||
reviewComment: reviewForm.reviewComment,
|
||||
reviewSuggestions: reviewForm.reviewSuggestions
|
||||
}))
|
||||
}
|
||||
await batchApproveMonthly(batchData)
|
||||
ElMessage.success('批量审核成功')
|
||||
};
|
||||
await batchApproveMonthly(batchData);
|
||||
ElMessage.success('批量审核成功');
|
||||
} else {
|
||||
// 单个审核
|
||||
await approveMonthly({
|
||||
@@ -451,29 +370,29 @@ const submitReview = async () => {
|
||||
reviewScore: reviewForm.reviewScore,
|
||||
reviewComment: reviewForm.reviewComment,
|
||||
reviewSuggestions: reviewForm.reviewSuggestions
|
||||
})
|
||||
ElMessage.success('审核成功')
|
||||
});
|
||||
ElMessage.success('审核成功');
|
||||
}
|
||||
|
||||
reviewVisible.value = false
|
||||
getList()
|
||||
getStatistics()
|
||||
|
||||
reviewVisible.value = false;
|
||||
getList();
|
||||
getStatistics();
|
||||
} catch (error) {
|
||||
console.error('审核失败:', error)
|
||||
console.error('审核失败:', error);
|
||||
} finally {
|
||||
reviewLoading.value = false
|
||||
reviewLoading.value = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 导出数据
|
||||
const handleExport = async () => {
|
||||
try {
|
||||
await exportReviewData(queryParams)
|
||||
ElMessage.success('导出成功')
|
||||
await exportReviewData(queryParams);
|
||||
ElMessage.success('导出成功');
|
||||
} catch (error) {
|
||||
console.error('导出失败:', error)
|
||||
console.error('导出失败:', error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 获取状态类型
|
||||
const getStatusType = (status: number) => {
|
||||
@@ -481,9 +400,9 @@ const getStatusType = (status: number) => {
|
||||
0: 'warning',
|
||||
1: 'success',
|
||||
2: 'danger'
|
||||
}
|
||||
return statusMap[status] || 'info'
|
||||
}
|
||||
};
|
||||
return statusMap[status] || 'info';
|
||||
};
|
||||
|
||||
// 获取状态文本
|
||||
const getStatusText = (status: number) => {
|
||||
@@ -491,15 +410,15 @@ const getStatusText = (status: number) => {
|
||||
0: '待审核',
|
||||
1: '已审核',
|
||||
2: '审核拒绝'
|
||||
}
|
||||
return statusMap[status] || '未知'
|
||||
}
|
||||
};
|
||||
return statusMap[status] || '未知';
|
||||
};
|
||||
|
||||
// 组件挂载
|
||||
onMounted(() => {
|
||||
getList()
|
||||
getStatistics()
|
||||
})
|
||||
getList();
|
||||
getStatistics();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@@ -527,20 +446,38 @@ onMounted(() => {
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
|
||||
|
||||
.table-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.text-primary { color: #409eff; }
|
||||
.text-success { color: #67c23a; }
|
||||
.text-warning { color: #e6a23c; }
|
||||
.text-danger { color: #f56c6c; }
|
||||
.text-gray-500 { color: #9ca3af; }
|
||||
.font-bold { font-weight: bold; }
|
||||
.text-sm { font-size: 12px; }
|
||||
.mb-4 { margin-bottom: 16px; }
|
||||
.ml-2 { margin-left: 8px; }
|
||||
</style>
|
||||
.text-primary {
|
||||
color: #409eff;
|
||||
}
|
||||
.text-success {
|
||||
color: #67c23a;
|
||||
}
|
||||
.text-warning {
|
||||
color: #e6a23c;
|
||||
}
|
||||
.text-danger {
|
||||
color: #f56c6c;
|
||||
}
|
||||
.text-gray-500 {
|
||||
color: #9ca3af;
|
||||
}
|
||||
.font-bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
.text-sm {
|
||||
font-size: 12px;
|
||||
}
|
||||
.mb-4 {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.ml-2 {
|
||||
margin-left: 8px;
|
||||
}
|
||||
</style>
|
||||
@@ -22,26 +22,13 @@
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="统一评分">
|
||||
<el-input-number
|
||||
v-model="batchForm.reviewScore"
|
||||
:min="0"
|
||||
:max="100"
|
||||
:step="5"
|
||||
style="width: 100%"
|
||||
/>
|
||||
<el-input-number v-model="batchForm.reviewScore" :min="0" :max="100" :step="5" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-form-item label="统一审核意见">
|
||||
<el-input
|
||||
v-model="batchForm.reviewComment"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder="输入统一的审核意见"
|
||||
maxlength="500"
|
||||
show-word-limit
|
||||
/>
|
||||
<el-input v-model="batchForm.reviewComment" type="textarea" :rows="3" placeholder="输入统一的审核意见" maxlength="500" show-word-limit />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="统一改进建议">
|
||||
@@ -100,13 +87,7 @@
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="审核评分" :prop="`reviewItems.${index}.reviewScore`">
|
||||
<el-input-number
|
||||
v-model="item.reviewScore"
|
||||
:min="0"
|
||||
:max="100"
|
||||
:step="5"
|
||||
style="width: 100%"
|
||||
/>
|
||||
<el-input-number v-model="item.reviewScore" :min="0" :max="100" :step="5" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
@@ -119,25 +100,11 @@
|
||||
</el-row>
|
||||
|
||||
<el-form-item label="审核意见" :prop="`reviewItems.${index}.reviewComment`" required>
|
||||
<el-input
|
||||
v-model="item.reviewComment"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
placeholder="请输入审核意见"
|
||||
maxlength="500"
|
||||
show-word-limit
|
||||
/>
|
||||
<el-input v-model="item.reviewComment" type="textarea" :rows="2" placeholder="请输入审核意见" maxlength="500" show-word-limit />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="改进建议" :prop="`reviewItems.${index}.reviewSuggestions`">
|
||||
<el-input
|
||||
v-model="item.reviewSuggestions"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
placeholder="请输入改进建议"
|
||||
maxlength="300"
|
||||
show-word-limit
|
||||
/>
|
||||
<el-input v-model="item.reviewSuggestions" type="textarea" :rows="2" placeholder="请输入改进建议" maxlength="300" show-word-limit />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 快速操作 -->
|
||||
@@ -167,25 +134,21 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, defineProps, defineEmits } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import {
|
||||
SuccessFilled,
|
||||
CircleCloseFilled,
|
||||
Check
|
||||
} from '@element-plus/icons-vue'
|
||||
import { batchReviewInternshipMonthly } from '@/api/zsjy/internshipMonthlyReview'
|
||||
import { ref, reactive, defineProps, defineEmits } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { SuccessFilled, CircleCloseFilled, Check } from '@element-plus/icons-vue';
|
||||
import { batchReviewInternshipMonthly } from '@/api/zsjy/internshipMonthlyReview';
|
||||
|
||||
interface Props {
|
||||
selectedRows: any[]
|
||||
selectedRows: any[];
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
const emit = defineEmits(['refresh'])
|
||||
const props = defineProps<Props>();
|
||||
const emit = defineEmits(['refresh']);
|
||||
|
||||
// 状态管理
|
||||
const submitting = ref(false)
|
||||
const showBatchSetting = ref(true)
|
||||
const submitting = ref(false);
|
||||
const showBatchSetting = ref(true);
|
||||
|
||||
// 批量设置表单
|
||||
const batchForm = reactive({
|
||||
@@ -193,36 +156,38 @@ const batchForm = reactive({
|
||||
reviewScore: 85,
|
||||
reviewComment: '',
|
||||
reviewSuggestions: ''
|
||||
})
|
||||
});
|
||||
|
||||
// 审核项目列表
|
||||
const reviewItems = ref(props.selectedRows.map(row => ({
|
||||
monthlyId: row.monthlyId,
|
||||
studentName: row.studentName,
|
||||
studentNumber: row.studentNumber,
|
||||
summaryMonth: row.summaryMonth,
|
||||
className: row.className,
|
||||
reviewStatus: 1,
|
||||
reviewScore: 85,
|
||||
reviewComment: '',
|
||||
reviewSuggestions: ''
|
||||
})))
|
||||
const reviewItems = ref(
|
||||
props.selectedRows.map((row) => ({
|
||||
monthlyId: row.monthlyId,
|
||||
studentName: row.studentName,
|
||||
studentNumber: row.studentNumber,
|
||||
summaryMonth: row.summaryMonth,
|
||||
className: row.className,
|
||||
reviewStatus: 1,
|
||||
reviewScore: 85,
|
||||
reviewComment: '',
|
||||
reviewSuggestions: ''
|
||||
}))
|
||||
);
|
||||
|
||||
/** 获取分数等级 */
|
||||
function getScoreLevel(score: number) {
|
||||
if (score >= 90) return '优秀'
|
||||
if (score >= 80) return '良好'
|
||||
if (score >= 70) return '中等'
|
||||
if (score >= 60) return '及格'
|
||||
return '不及格'
|
||||
if (score >= 90) return '优秀';
|
||||
if (score >= 80) return '良好';
|
||||
if (score >= 70) return '中等';
|
||||
if (score >= 60) return '及格';
|
||||
return '不及格';
|
||||
}
|
||||
|
||||
/** 获取分数标签类型 */
|
||||
function getScoreTagType(score: number) {
|
||||
if (score >= 90) return 'success'
|
||||
if (score >= 80) return 'warning'
|
||||
if (score >= 70) return 'primary'
|
||||
return 'danger'
|
||||
if (score >= 90) return 'success';
|
||||
if (score >= 80) return 'warning';
|
||||
if (score >= 70) return 'primary';
|
||||
return 'danger';
|
||||
}
|
||||
|
||||
/** 应用批量设置 */
|
||||
@@ -232,60 +197,60 @@ function applyBatchSettings() {
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
reviewItems.value.forEach(item => {
|
||||
reviewItems.value.forEach((item) => {
|
||||
if (batchForm.reviewStatus) {
|
||||
item.reviewStatus = batchForm.reviewStatus
|
||||
item.reviewStatus = batchForm.reviewStatus;
|
||||
}
|
||||
if (batchForm.reviewScore !== null) {
|
||||
item.reviewScore = batchForm.reviewScore
|
||||
item.reviewScore = batchForm.reviewScore;
|
||||
}
|
||||
if (batchForm.reviewComment) {
|
||||
item.reviewComment = batchForm.reviewComment
|
||||
item.reviewComment = batchForm.reviewComment;
|
||||
}
|
||||
if (batchForm.reviewSuggestions) {
|
||||
item.reviewSuggestions = batchForm.reviewSuggestions
|
||||
item.reviewSuggestions = batchForm.reviewSuggestions;
|
||||
}
|
||||
})
|
||||
ElMessage.success('批量设置已应用')
|
||||
})
|
||||
});
|
||||
ElMessage.success('批量设置已应用');
|
||||
});
|
||||
}
|
||||
|
||||
/** 清空批量设置 */
|
||||
function clearBatchSettings() {
|
||||
batchForm.reviewStatus = 1
|
||||
batchForm.reviewScore = 85
|
||||
batchForm.reviewComment = ''
|
||||
batchForm.reviewSuggestions = ''
|
||||
batchForm.reviewStatus = 1;
|
||||
batchForm.reviewScore = 85;
|
||||
batchForm.reviewComment = '';
|
||||
batchForm.reviewSuggestions = '';
|
||||
}
|
||||
|
||||
/** 快速设置审核结果 */
|
||||
function setQuickReview(index: number, status: number, score: number, comment: string) {
|
||||
const item = reviewItems.value[index]
|
||||
item.reviewStatus = status
|
||||
item.reviewScore = score
|
||||
item.reviewComment = comment
|
||||
const item = reviewItems.value[index];
|
||||
item.reviewStatus = status;
|
||||
item.reviewScore = score;
|
||||
item.reviewComment = comment;
|
||||
}
|
||||
|
||||
/** 验证表单 */
|
||||
function validateForm() {
|
||||
for (let i = 0; i < reviewItems.value.length; i++) {
|
||||
const item = reviewItems.value[i]
|
||||
const item = reviewItems.value[i];
|
||||
if (!item.reviewStatus) {
|
||||
ElMessage.error(`请为 ${item.studentName} 选择审核结果`)
|
||||
return false
|
||||
ElMessage.error(`请为 ${item.studentName} 选择审核结果`);
|
||||
return false;
|
||||
}
|
||||
if (!item.reviewComment || item.reviewComment.trim().length < 10) {
|
||||
ElMessage.error(`请为 ${item.studentName} 输入审核意见(至少10个字符)`)
|
||||
return false
|
||||
ElMessage.error(`请为 ${item.studentName} 输入审核意见(至少10个字符)`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
|
||||
/** 提交批量审核 */
|
||||
function submitBatchReview() {
|
||||
if (!validateForm()) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
ElMessageBox.confirm(`确认批量审核 ${reviewItems.value.length} 条记录?`, '确认批量审核', {
|
||||
@@ -293,32 +258,35 @@ function submitBatchReview() {
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
submitting.value = true
|
||||
submitting.value = true;
|
||||
|
||||
const reviewData = {
|
||||
reviewItems: reviewItems.value.map(item => ({
|
||||
reviewItems: reviewItems.value.map((item) => ({
|
||||
monthlyId: item.monthlyId,
|
||||
reviewStatus: item.reviewStatus,
|
||||
reviewScore: item.reviewScore,
|
||||
reviewComment: item.reviewComment,
|
||||
reviewSuggestions: item.reviewSuggestions
|
||||
}))
|
||||
}
|
||||
};
|
||||
|
||||
batchReviewInternshipMonthly(reviewData).then(() => {
|
||||
ElMessage.success('批量审核完成')
|
||||
emit('refresh')
|
||||
}).catch(() => {
|
||||
ElMessage.error('批量审核失败')
|
||||
}).finally(() => {
|
||||
submitting.value = false
|
||||
})
|
||||
})
|
||||
batchReviewInternshipMonthly(reviewData)
|
||||
.then(() => {
|
||||
ElMessage.success('批量审核完成');
|
||||
emit('refresh');
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessage.error('批量审核失败');
|
||||
})
|
||||
.finally(() => {
|
||||
submitting.value = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/** 取消操作 */
|
||||
function handleCancel() {
|
||||
emit('refresh')
|
||||
emit('refresh');
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -67,69 +67,42 @@
|
||||
<el-col :span="12">
|
||||
<div class="score-item">
|
||||
<span class="score-label">专业技能:</span>
|
||||
<el-rate
|
||||
:model-value="monthlyData.professionalSkills"
|
||||
disabled
|
||||
show-score
|
||||
text-color="#ff9900"
|
||||
/>
|
||||
<el-rate :model-value="monthlyData.professionalSkills" disabled show-score text-color="#ff9900" />
|
||||
<span class="score-value">{{ monthlyData.professionalSkills || 0 }}分</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="score-item">
|
||||
<span class="score-label">沟通能力:</span>
|
||||
<el-rate
|
||||
:model-value="monthlyData.communicationSkills"
|
||||
disabled
|
||||
show-score
|
||||
text-color="#ff9900"
|
||||
/>
|
||||
<el-rate :model-value="monthlyData.communicationSkills" disabled show-score text-color="#ff9900" />
|
||||
<span class="score-value">{{ monthlyData.communicationSkills || 0 }}分</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="score-item">
|
||||
<span class="score-label">团队协作:</span>
|
||||
<el-rate
|
||||
:model-value="monthlyData.teamworkAbility"
|
||||
disabled
|
||||
show-score
|
||||
text-color="#ff9900"
|
||||
/>
|
||||
<el-rate :model-value="monthlyData.teamworkAbility" disabled show-score text-color="#ff9900" />
|
||||
<span class="score-value">{{ monthlyData.teamworkAbility || 0 }}分</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="score-item">
|
||||
<span class="score-label">学习能力:</span>
|
||||
<el-rate
|
||||
:model-value="monthlyData.learningAbility"
|
||||
disabled
|
||||
show-score
|
||||
text-color="#ff9900"
|
||||
/>
|
||||
<el-rate :model-value="monthlyData.learningAbility" disabled show-score text-color="#ff9900" />
|
||||
<span class="score-value">{{ monthlyData.learningAbility || 0 }}分</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="score-item">
|
||||
<span class="score-label">工作态度:</span>
|
||||
<el-rate
|
||||
:model-value="monthlyData.workAttitude"
|
||||
disabled
|
||||
show-score
|
||||
text-color="#ff9900"
|
||||
/>
|
||||
<el-rate :model-value="monthlyData.workAttitude" disabled show-score text-color="#ff9900" />
|
||||
<span class="score-value">{{ monthlyData.workAttitude || 0 }}分</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="score-item">
|
||||
<span class="score-label">综合评分:</span>
|
||||
<el-tag :type="getScoreTagType(getAverageScore())" size="large">
|
||||
{{ getAverageScore().toFixed(1) }}分
|
||||
</el-tag>
|
||||
<el-tag :type="getScoreTagType(getAverageScore())" size="large"> {{ getAverageScore().toFixed(1) }}分 </el-tag>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -147,8 +120,10 @@
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="审核评分">
|
||||
<el-tag v-if="monthlyData.reviewScore !== null && monthlyData.reviewScore !== undefined"
|
||||
:type="getReviewScoreTagType(monthlyData.reviewScore)">
|
||||
<el-tag
|
||||
v-if="monthlyData.reviewScore !== null && monthlyData.reviewScore !== undefined"
|
||||
:type="getReviewScoreTagType(monthlyData.reviewScore)"
|
||||
>
|
||||
{{ monthlyData.reviewScore }}分
|
||||
</el-tag>
|
||||
<span v-else class="text-gray-400">未评分</span>
|
||||
@@ -178,14 +153,14 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineProps, defineEmits } from 'vue'
|
||||
import { defineProps, defineEmits } from 'vue';
|
||||
|
||||
interface Props {
|
||||
monthlyData: any
|
||||
monthlyData: any;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
const emit = defineEmits(['refresh', 'review'])
|
||||
const props = defineProps<Props>();
|
||||
const emit = defineEmits(['refresh', 'review']);
|
||||
|
||||
/** 获取平均分 */
|
||||
function getAverageScore() {
|
||||
@@ -195,50 +170,58 @@ function getAverageScore() {
|
||||
props.monthlyData.teamworkAbility || 0,
|
||||
props.monthlyData.learningAbility || 0,
|
||||
props.monthlyData.workAttitude || 0
|
||||
]
|
||||
const total = scores.reduce((sum, score) => sum + score, 0)
|
||||
return total / scores.length
|
||||
];
|
||||
const total = scores.reduce((sum, score) => sum + score, 0);
|
||||
return total / scores.length;
|
||||
}
|
||||
|
||||
/** 获取评分标签类型 */
|
||||
function getScoreTagType(score: number) {
|
||||
if (score >= 9) return 'success'
|
||||
if (score >= 8) return 'warning'
|
||||
if (score >= 7) return 'primary'
|
||||
return 'danger'
|
||||
if (score >= 9) return 'success';
|
||||
if (score >= 8) return 'warning';
|
||||
if (score >= 7) return 'primary';
|
||||
return 'danger';
|
||||
}
|
||||
|
||||
/** 获取审核状态文本 */
|
||||
function getReviewStatusText(status: number) {
|
||||
switch (status) {
|
||||
case 0: return '待审核'
|
||||
case 1: return '已通过'
|
||||
case 2: return '已拒绝'
|
||||
default: return '未知'
|
||||
case 0:
|
||||
return '待审核';
|
||||
case 1:
|
||||
return '已通过';
|
||||
case 2:
|
||||
return '已拒绝';
|
||||
default:
|
||||
return '未知';
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取审核状态标签类型 */
|
||||
function getReviewStatusTagType(status: number) {
|
||||
switch (status) {
|
||||
case 0: return 'warning'
|
||||
case 1: return 'success'
|
||||
case 2: return 'danger'
|
||||
default: return 'info'
|
||||
case 0:
|
||||
return 'warning';
|
||||
case 1:
|
||||
return 'success';
|
||||
case 2:
|
||||
return 'danger';
|
||||
default:
|
||||
return 'info';
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取审核评分标签类型 */
|
||||
function getReviewScoreTagType(score: number) {
|
||||
if (score >= 90) return 'success'
|
||||
if (score >= 80) return 'warning'
|
||||
if (score >= 70) return 'primary'
|
||||
return 'danger'
|
||||
if (score >= 90) return 'success';
|
||||
if (score >= 80) return 'warning';
|
||||
if (score >= 70) return 'primary';
|
||||
return 'danger';
|
||||
}
|
||||
|
||||
/** 审核按钮点击 */
|
||||
function handleReview() {
|
||||
emit('review', props.monthlyData)
|
||||
emit('review', props.monthlyData);
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -145,27 +145,20 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, defineProps, defineEmits } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import {
|
||||
SuccessFilled,
|
||||
CircleCloseFilled,
|
||||
InfoFilled,
|
||||
Check,
|
||||
Refresh,
|
||||
View
|
||||
} from '@element-plus/icons-vue'
|
||||
import { reviewInternshipMonthly } from '@/api/zsjy/internshipMonthlyReview'
|
||||
import { ref, reactive, defineProps, defineEmits } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { SuccessFilled, CircleCloseFilled, InfoFilled, Check, Refresh, View } from '@element-plus/icons-vue';
|
||||
import { reviewInternshipMonthly } from '@/api/zsjy/internshipMonthlyReview';
|
||||
|
||||
interface Props {
|
||||
monthlyData: any
|
||||
monthlyData: any;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
const emit = defineEmits(['refresh'])
|
||||
const props = defineProps<Props>();
|
||||
const emit = defineEmits(['refresh']);
|
||||
|
||||
// 表单引用
|
||||
const reviewFormRef = ref()
|
||||
const reviewFormRef = ref();
|
||||
|
||||
// 表单数据
|
||||
const reviewForm = reactive({
|
||||
@@ -174,13 +167,11 @@ const reviewForm = reactive({
|
||||
reviewScore: 85, // 默认85分
|
||||
reviewComment: '',
|
||||
reviewSuggestions: ''
|
||||
})
|
||||
});
|
||||
|
||||
// 表单验证规则
|
||||
const reviewRules = {
|
||||
reviewStatus: [
|
||||
{ required: true, message: '请选择审核结果', trigger: 'change' }
|
||||
],
|
||||
reviewStatus: [{ required: true, message: '请选择审核结果', trigger: 'change' }],
|
||||
reviewScore: [
|
||||
{ required: true, message: '请输入审核评分', trigger: 'blur' },
|
||||
{ type: 'number', min: 0, max: 100, message: '评分范围为0-100分', trigger: 'blur' }
|
||||
@@ -189,14 +180,12 @@ const reviewRules = {
|
||||
{ required: true, message: '请输入审核意见', trigger: 'blur' },
|
||||
{ min: 10, max: 500, message: '审核意见长度在10到500个字符', trigger: 'blur' }
|
||||
],
|
||||
reviewSuggestions: [
|
||||
{ max: 300, message: '改进建议长度不能超过300个字符', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
reviewSuggestions: [{ max: 300, message: '改进建议长度不能超过300个字符', trigger: 'blur' }]
|
||||
};
|
||||
|
||||
// 状态管理
|
||||
const submitting = ref(false)
|
||||
const previewOpen = ref(false)
|
||||
const submitting = ref(false);
|
||||
const previewOpen = ref(false);
|
||||
|
||||
// 评语模板
|
||||
const commentTemplates = [
|
||||
@@ -220,86 +209,89 @@ const commentTemplates = [
|
||||
label: '需改进',
|
||||
content: '总结内容过于简单,缺乏具体的工作描述和深入的思考。请重新整理总结内容,确保内容的完整性和深度。'
|
||||
}
|
||||
]
|
||||
];
|
||||
|
||||
/** 格式化滑块提示 */
|
||||
function formatTooltip(value: number) {
|
||||
return `${value}分`
|
||||
return `${value}分`;
|
||||
}
|
||||
|
||||
/** 获取分数等级 */
|
||||
function getScoreLevel(score: number) {
|
||||
if (score >= 90) return '优秀'
|
||||
if (score >= 80) return '良好'
|
||||
if (score >= 70) return '中等'
|
||||
if (score >= 60) return '及格'
|
||||
return '不及格'
|
||||
if (score >= 90) return '优秀';
|
||||
if (score >= 80) return '良好';
|
||||
if (score >= 70) return '中等';
|
||||
if (score >= 60) return '及格';
|
||||
return '不及格';
|
||||
}
|
||||
|
||||
/** 获取分数标签类型 */
|
||||
function getScoreTagType(score: number) {
|
||||
if (score >= 90) return 'success'
|
||||
if (score >= 80) return 'warning'
|
||||
if (score >= 70) return 'primary'
|
||||
return 'danger'
|
||||
if (score >= 90) return 'success';
|
||||
if (score >= 80) return 'warning';
|
||||
if (score >= 70) return 'primary';
|
||||
return 'danger';
|
||||
}
|
||||
|
||||
/** 插入评语模板 */
|
||||
function insertTemplate(template: string) {
|
||||
if (reviewForm.reviewComment) {
|
||||
reviewForm.reviewComment += '\n\n' + template
|
||||
reviewForm.reviewComment += '\n\n' + template;
|
||||
} else {
|
||||
reviewForm.reviewComment = template
|
||||
reviewForm.reviewComment = template;
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
function resetForm() {
|
||||
reviewFormRef.value?.resetFields()
|
||||
reviewForm.reviewStatus = 1
|
||||
reviewForm.reviewScore = 85
|
||||
reviewForm.reviewComment = ''
|
||||
reviewForm.reviewSuggestions = ''
|
||||
reviewFormRef.value?.resetFields();
|
||||
reviewForm.reviewStatus = 1;
|
||||
reviewForm.reviewScore = 85;
|
||||
reviewForm.reviewComment = '';
|
||||
reviewForm.reviewSuggestions = '';
|
||||
}
|
||||
|
||||
/** 预览审核 */
|
||||
function previewReview() {
|
||||
reviewFormRef.value?.validate((valid: boolean) => {
|
||||
if (valid) {
|
||||
previewOpen.value = true
|
||||
previewOpen.value = true;
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/** 提交审核 */
|
||||
function submitReview() {
|
||||
reviewFormRef.value?.validate((valid: boolean) => {
|
||||
if (valid) {
|
||||
const message = reviewForm.reviewStatus === 1 ? '确认通过此月度总结?' : '确认拒绝此月度总结?'
|
||||
const message = reviewForm.reviewStatus === 1 ? '确认通过此月度总结?' : '确认拒绝此月度总结?';
|
||||
ElMessageBox.confirm(message, '确认审核', {
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
confirmSubmit()
|
||||
})
|
||||
confirmSubmit();
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/** 确认提交 */
|
||||
function confirmSubmit() {
|
||||
submitting.value = true
|
||||
previewOpen.value = false
|
||||
submitting.value = true;
|
||||
previewOpen.value = false;
|
||||
|
||||
reviewInternshipMonthly(reviewForm).then(() => {
|
||||
ElMessage.success('审核完成')
|
||||
emit('refresh')
|
||||
}).catch(() => {
|
||||
ElMessage.error('审核失败')
|
||||
}).finally(() => {
|
||||
submitting.value = false
|
||||
})
|
||||
reviewInternshipMonthly(reviewForm)
|
||||
.then(() => {
|
||||
ElMessage.success('审核完成');
|
||||
emit('refresh');
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessage.error('审核失败');
|
||||
})
|
||||
.finally(() => {
|
||||
submitting.value = false;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -138,7 +138,8 @@
|
||||
:disabled="multiple"
|
||||
@click="handleBatchReview"
|
||||
v-hasPermi="['zsjy:internshipmonthly:review']"
|
||||
>批量审核</el-button>
|
||||
>批量审核</el-button
|
||||
>
|
||||
</el-col>
|
||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||
</el-row>
|
||||
@@ -165,16 +166,12 @@
|
||||
<el-table-column label="自我评分" align="center" width="120">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="专业/沟通/团队/学习/态度" placement="top">
|
||||
<el-tag :type="getScoreTagType(getAverageScore(scope.row))">
|
||||
{{ getAverageScore(scope.row).toFixed(1) }}分
|
||||
</el-tag>
|
||||
<el-tag :type="getScoreTagType(getAverageScore(scope.row))"> {{ getAverageScore(scope.row).toFixed(1) }}分 </el-tag>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="字数统计" align="center" width="100">
|
||||
<template #default="scope">
|
||||
{{ scope.row.wordCount || 0 }}字
|
||||
</template>
|
||||
<template #default="scope"> {{ scope.row.wordCount || 0 }}字 </template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审核状态" align="center" prop="reviewStatus" width="100">
|
||||
<template #default="scope">
|
||||
@@ -186,9 +183,7 @@
|
||||
<el-table-column label="审核评分" align="center" prop="reviewScore" width="100">
|
||||
<template #default="scope">
|
||||
<span v-if="scope.row.reviewScore !== null && scope.row.reviewScore !== undefined">
|
||||
<el-tag :type="getReviewScoreTagType(scope.row.reviewScore)">
|
||||
{{ scope.row.reviewScore }}分
|
||||
</el-tag>
|
||||
<el-tag :type="getReviewScoreTagType(scope.row.reviewScore)"> {{ scope.row.reviewScore }}分 </el-tag>
|
||||
</span>
|
||||
<span v-else class="text-gray-400">未评分</span>
|
||||
</template>
|
||||
@@ -205,19 +200,14 @@
|
||||
icon="Check"
|
||||
@click="handleReview(scope.row)"
|
||||
v-hasPermi="['zsjy:internshipmonthly:review']"
|
||||
>审核</el-button>
|
||||
>审核</el-button
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
||||
</el-card>
|
||||
|
||||
<!-- 审核详情对话框 -->
|
||||
@@ -238,25 +228,16 @@
|
||||
</template>
|
||||
|
||||
<script setup name="InternshipMonthlyReview" lang="ts">
|
||||
import { onMounted, reactive, ref, toRefs, getCurrentInstance } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import {
|
||||
DocumentChecked,
|
||||
Clock,
|
||||
SuccessFilled,
|
||||
CircleCloseFilled,
|
||||
Search,
|
||||
Refresh,
|
||||
Check,
|
||||
View
|
||||
} from '@element-plus/icons-vue'
|
||||
import { getReviewList, getReviewStatistics } from '@/api/zsjy/internshipMonthlyReview'
|
||||
import type { InternshipMonthlyVO, InternshipMonthlyReviewStatisticsVO } from '@/api/zsjy/types'
|
||||
import ReviewDetail from './components/ReviewDetail.vue'
|
||||
import ReviewForm from './components/ReviewForm.vue'
|
||||
import BatchReviewForm from './components/BatchReviewForm.vue'
|
||||
import { onMounted, reactive, ref, toRefs, getCurrentInstance } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { DocumentChecked, Clock, SuccessFilled, CircleCloseFilled, Search, Refresh, Check, View } from '@element-plus/icons-vue';
|
||||
import { getReviewList, getReviewStatistics } from '@/api/zsjy/internshipMonthlyReview';
|
||||
import type { InternshipMonthlyVO, InternshipMonthlyReviewStatisticsVO } from '@/api/zsjy/types';
|
||||
import ReviewDetail from './components/ReviewDetail.vue';
|
||||
import ReviewForm from './components/ReviewForm.vue';
|
||||
import BatchReviewForm from './components/BatchReviewForm.vue';
|
||||
|
||||
const { proxy } = getCurrentInstance() as any
|
||||
const { proxy } = getCurrentInstance() as any;
|
||||
|
||||
// 数据状态
|
||||
const data = reactive({
|
||||
@@ -289,132 +270,123 @@ const data = reactive({
|
||||
averageScore: 0,
|
||||
completionRate: 0
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
const {
|
||||
loading,
|
||||
showSearch,
|
||||
reviewList,
|
||||
multipleSelection,
|
||||
single,
|
||||
multiple,
|
||||
total,
|
||||
queryParams,
|
||||
daterangeCreateTime,
|
||||
statistics
|
||||
} = toRefs(data)
|
||||
const { loading, showSearch, reviewList, multipleSelection, single, multiple, total, queryParams, daterangeCreateTime, statistics } = toRefs(data);
|
||||
|
||||
// 对话框状态
|
||||
const detailOpen = ref(false)
|
||||
const reviewOpen = ref(false)
|
||||
const batchReviewOpen = ref(false)
|
||||
const detailTitle = ref('')
|
||||
const currentMonthlyData = ref({})
|
||||
const detailOpen = ref(false);
|
||||
const reviewOpen = ref(false);
|
||||
const batchReviewOpen = ref(false);
|
||||
const detailTitle = ref('');
|
||||
const currentMonthlyData = ref({});
|
||||
|
||||
// 表单引用
|
||||
const queryFormRef = ref()
|
||||
const reviewDetailRef = ref()
|
||||
const reviewFormRef = ref()
|
||||
const batchReviewFormRef = ref()
|
||||
const queryFormRef = ref();
|
||||
const reviewDetailRef = ref();
|
||||
const reviewFormRef = ref();
|
||||
const batchReviewFormRef = ref();
|
||||
|
||||
/** 查询列表 */
|
||||
function getList() {
|
||||
data.loading = true
|
||||
data.loading = true;
|
||||
|
||||
// 处理时间范围
|
||||
if (data.daterangeCreateTime && data.daterangeCreateTime.length === 2) {
|
||||
data.queryParams.startDate = data.daterangeCreateTime[0]
|
||||
data.queryParams.endDate = data.daterangeCreateTime[1]
|
||||
data.queryParams.startDate = data.daterangeCreateTime[0];
|
||||
data.queryParams.endDate = data.daterangeCreateTime[1];
|
||||
} else {
|
||||
data.queryParams.startDate = undefined
|
||||
data.queryParams.endDate = undefined
|
||||
data.queryParams.startDate = undefined;
|
||||
data.queryParams.endDate = undefined;
|
||||
}
|
||||
|
||||
getReviewList(data.queryParams).then((response: any) => {
|
||||
data.reviewList = response.rows
|
||||
data.total = response.total
|
||||
data.loading = false
|
||||
}).catch(() => {
|
||||
data.loading = false
|
||||
})
|
||||
getReviewList(data.queryParams)
|
||||
.then((response: any) => {
|
||||
data.reviewList = response.rows;
|
||||
data.total = response.total;
|
||||
data.loading = false;
|
||||
})
|
||||
.catch(() => {
|
||||
data.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
/** 获取统计数据 */
|
||||
function getStatistics() {
|
||||
getReviewStatistics().then((response: any) => {
|
||||
data.statistics = response.data
|
||||
})
|
||||
data.statistics = response.data;
|
||||
});
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
function handleQuery() {
|
||||
data.queryParams.pageNum = 1
|
||||
getList()
|
||||
data.queryParams.pageNum = 1;
|
||||
getList();
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
function resetQuery() {
|
||||
data.daterangeCreateTime = []
|
||||
queryFormRef.value?.resetFields()
|
||||
handleQuery()
|
||||
data.daterangeCreateTime = [];
|
||||
queryFormRef.value?.resetFields();
|
||||
handleQuery();
|
||||
}
|
||||
|
||||
/** 多选框选中数据 */
|
||||
function handleSelectionChange(selection: any[]) {
|
||||
data.multipleSelection = selection
|
||||
data.single = selection.length !== 1
|
||||
data.multiple = !selection.length
|
||||
data.multipleSelection = selection;
|
||||
data.single = selection.length !== 1;
|
||||
data.multiple = !selection.length;
|
||||
}
|
||||
|
||||
/** 查看详情 */
|
||||
function handleDetail(row: any) {
|
||||
currentMonthlyData.value = { ...row }
|
||||
detailTitle.value = `${row.studentName}的月度总结详情`
|
||||
detailOpen.value = true
|
||||
currentMonthlyData.value = { ...row };
|
||||
detailTitle.value = `${row.studentName}的月度总结详情`;
|
||||
detailOpen.value = true;
|
||||
}
|
||||
|
||||
/** 单个审核 */
|
||||
function handleReview(row: any) {
|
||||
currentMonthlyData.value = { ...row }
|
||||
reviewOpen.value = true
|
||||
currentMonthlyData.value = { ...row };
|
||||
reviewOpen.value = true;
|
||||
}
|
||||
|
||||
/** 批量审核 */
|
||||
function handleBatchReview() {
|
||||
if (data.multipleSelection.length === 0) {
|
||||
ElMessage.warning('请选择要审核的数据')
|
||||
return
|
||||
ElMessage.warning('请选择要审核的数据');
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否都是待审核状态
|
||||
const pendingRows = data.multipleSelection.filter(row => row.reviewStatus === 0)
|
||||
const pendingRows = data.multipleSelection.filter((row) => row.reviewStatus === 0);
|
||||
if (pendingRows.length === 0) {
|
||||
ElMessage.warning('选择的数据中没有待审核的记录')
|
||||
return
|
||||
ElMessage.warning('选择的数据中没有待审核的记录');
|
||||
return;
|
||||
}
|
||||
|
||||
if (pendingRows.length !== data.multipleSelection.length) {
|
||||
ElMessage.warning('只能审核状态为"待审核"的记录,请重新选择')
|
||||
return
|
||||
ElMessage.warning('只能审核状态为"待审核"的记录,请重新选择');
|
||||
return;
|
||||
}
|
||||
|
||||
batchReviewOpen.value = true
|
||||
batchReviewOpen.value = true;
|
||||
}
|
||||
|
||||
/** 审核成功回调 */
|
||||
function handleReviewSuccess() {
|
||||
reviewOpen.value = false
|
||||
getList()
|
||||
getStatistics()
|
||||
ElMessage.success('审核完成')
|
||||
reviewOpen.value = false;
|
||||
getList();
|
||||
getStatistics();
|
||||
ElMessage.success('审核完成');
|
||||
}
|
||||
|
||||
/** 批量审核成功回调 */
|
||||
function handleBatchReviewSuccess() {
|
||||
batchReviewOpen.value = false
|
||||
getList()
|
||||
getStatistics()
|
||||
ElMessage.success('批量审核完成')
|
||||
batchReviewOpen.value = false;
|
||||
getList();
|
||||
getStatistics();
|
||||
ElMessage.success('批量审核完成');
|
||||
}
|
||||
|
||||
/** 获取平均分 */
|
||||
@@ -425,51 +397,59 @@ function getAverageScore(row: any) {
|
||||
row.teamworkSkills || 0,
|
||||
row.learningAbility || 0,
|
||||
row.workAttitude || 0
|
||||
]
|
||||
const total = scores.reduce((sum, score) => sum + score, 0)
|
||||
return total / scores.length
|
||||
];
|
||||
const total = scores.reduce((sum, score) => sum + score, 0);
|
||||
return total / scores.length;
|
||||
}
|
||||
|
||||
/** 获取评分标签类型 */
|
||||
function getScoreTagType(score: number) {
|
||||
if (score >= 9) return 'success'
|
||||
if (score >= 8) return 'warning'
|
||||
if (score >= 7) return 'primary'
|
||||
return 'danger'
|
||||
if (score >= 9) return 'success';
|
||||
if (score >= 8) return 'warning';
|
||||
if (score >= 7) return 'primary';
|
||||
return 'danger';
|
||||
}
|
||||
|
||||
/** 获取审核状态文本 */
|
||||
function getReviewStatusText(status: number) {
|
||||
switch (status) {
|
||||
case 0: return '待审核'
|
||||
case 1: return '已通过'
|
||||
case 2: return '已拒绝'
|
||||
default: return '未知'
|
||||
case 0:
|
||||
return '待审核';
|
||||
case 1:
|
||||
return '已通过';
|
||||
case 2:
|
||||
return '已拒绝';
|
||||
default:
|
||||
return '未知';
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取审核状态标签类型 */
|
||||
function getReviewStatusTagType(status: number) {
|
||||
switch (status) {
|
||||
case 0: return 'warning'
|
||||
case 1: return 'success'
|
||||
case 2: return 'danger'
|
||||
default: return 'info'
|
||||
case 0:
|
||||
return 'warning';
|
||||
case 1:
|
||||
return 'success';
|
||||
case 2:
|
||||
return 'danger';
|
||||
default:
|
||||
return 'info';
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取审核评分标签类型 */
|
||||
function getReviewScoreTagType(score: number) {
|
||||
if (score >= 90) return 'success'
|
||||
if (score >= 80) return 'warning'
|
||||
if (score >= 70) return 'primary'
|
||||
return 'danger'
|
||||
if (score >= 90) return 'success';
|
||||
if (score >= 80) return 'warning';
|
||||
if (score >= 70) return 'primary';
|
||||
return 'danger';
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getList()
|
||||
getStatistics()
|
||||
})
|
||||
getList();
|
||||
getStatistics();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -68,12 +68,24 @@
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="实习单位" prop="companyName">
|
||||
<el-input v-model="queryParams.companyName" placeholder="请输入实习单位名称" clearable style="width: 100%" @keyup.enter="handleQuery" />
|
||||
<el-input
|
||||
v-model="queryParams.companyName"
|
||||
placeholder="请输入实习单位名称"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="走访人" prop="visitorName">
|
||||
<el-input v-model="queryParams.visitorName" placeholder="请输入走访人姓名" clearable style="width: 100%" @keyup.enter="handleQuery" />
|
||||
<el-input
|
||||
v-model="queryParams.visitorName"
|
||||
placeholder="请输入走访人姓名"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
@@ -103,12 +115,10 @@
|
||||
<el-col :span="12">
|
||||
<div class="search-btn-container">
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
|
||||
<el-button type="info" plain icon="Search" @click="showSearch = !showSearch">
|
||||
{{ showSearch ? '隐藏' : '显示' }}搜索
|
||||
</el-button>
|
||||
<el-button type="info" plain icon="Search" @click="showSearch = !showSearch"> {{ showSearch ? '隐藏' : '显示' }}搜索 </el-button>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-col>
|
||||
@@ -126,9 +136,7 @@
|
||||
<div class="button-group">
|
||||
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['zsjy:internshipVisit:add']">新增走访</el-button>
|
||||
<el-button type="success" plain icon="Download" @click="handleExport" v-hasPermi="['zsjy:internshipVisit:export']">导出</el-button>
|
||||
<el-button type="info" plain icon="Search" @click="showSearch = !showSearch">
|
||||
{{ showSearch ? '隐藏' : '显示' }}搜索
|
||||
</el-button>
|
||||
<el-button type="info" plain icon="Search" @click="showSearch = !showSearch"> {{ showSearch ? '隐藏' : '显示' }}搜索 </el-button>
|
||||
<el-tooltip content="刷新" placement="top">
|
||||
<el-button icon="Refresh" circle @click="getList" />
|
||||
</el-tooltip>
|
||||
@@ -171,8 +179,6 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
|
||||
|
||||
<el-table-column label="走访状态" prop="status" min-width="100" align="center">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="visitStatusOptions" :value="scope.row.status || ''" />
|
||||
@@ -282,17 +288,10 @@
|
||||
:value="student.studentId"
|
||||
/>
|
||||
<!-- 空数据提示 -->
|
||||
<el-option
|
||||
v-if="studentOptions.length === 0 && !studentLoading"
|
||||
disabled
|
||||
label="暂无学生数据"
|
||||
value=""
|
||||
/>
|
||||
<el-option v-if="studentOptions.length === 0 && !studentLoading" disabled label="暂无学生数据" value="" />
|
||||
</el-select>
|
||||
<!-- 提示信息 -->
|
||||
<div class="form-item-tip">
|
||||
系统将自动显示您所管理班级的学生,支持输入姓名或学号搜索
|
||||
</div>
|
||||
<div class="form-item-tip">系统将自动显示您所管理班级的学生,支持输入姓名或学号搜索</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
@@ -457,54 +456,56 @@ function getList() {
|
||||
}
|
||||
delete params.visitDateRange;
|
||||
|
||||
listInternshipVisit(params).then((response) => {
|
||||
console.log('API响应数据:', response); // 用于调试
|
||||
listInternshipVisit(params)
|
||||
.then((response) => {
|
||||
console.log('API响应数据:', response); // 用于调试
|
||||
|
||||
// 健壮的响应处理逻辑
|
||||
if (response && typeof response === 'object') {
|
||||
// 情况1:直接的TableDataInfo格式 {total: number, rows: array}
|
||||
if (response.rows !== undefined && response.total !== undefined) {
|
||||
visitList.value = response.rows || [];
|
||||
total.value = response.total || 0;
|
||||
}
|
||||
// 情况2:嵌套在data中的TableDataInfo格式 {data: {total: number, rows: array}}
|
||||
else if (response.data && response.data.rows !== undefined && response.data.total !== undefined) {
|
||||
visitList.value = response.data.rows || [];
|
||||
total.value = response.data.total || 0;
|
||||
}
|
||||
// 情况3:直接返回数组格式
|
||||
else if (Array.isArray(response)) {
|
||||
visitList.value = response;
|
||||
total.value = response.length;
|
||||
}
|
||||
// 情况4:嵌套的数组格式
|
||||
else if (response.data && Array.isArray(response.data)) {
|
||||
visitList.value = response.data;
|
||||
total.value = response.data.length;
|
||||
}
|
||||
// 其他未知格式
|
||||
else {
|
||||
console.warn('未识别的API响应格式:', response);
|
||||
// 健壮的响应处理逻辑
|
||||
if (response && typeof response === 'object') {
|
||||
// 情况1:直接的TableDataInfo格式 {total: number, rows: array}
|
||||
if (response.rows !== undefined && response.total !== undefined) {
|
||||
visitList.value = response.rows || [];
|
||||
total.value = response.total || 0;
|
||||
}
|
||||
// 情况2:嵌套在data中的TableDataInfo格式 {data: {total: number, rows: array}}
|
||||
else if (response.data && response.data.rows !== undefined && response.data.total !== undefined) {
|
||||
visitList.value = response.data.rows || [];
|
||||
total.value = response.data.total || 0;
|
||||
}
|
||||
// 情况3:直接返回数组格式
|
||||
else if (Array.isArray(response)) {
|
||||
visitList.value = response;
|
||||
total.value = response.length;
|
||||
}
|
||||
// 情况4:嵌套的数组格式
|
||||
else if (response.data && Array.isArray(response.data)) {
|
||||
visitList.value = response.data;
|
||||
total.value = response.data.length;
|
||||
}
|
||||
// 其他未知格式
|
||||
else {
|
||||
console.warn('未识别的API响应格式:', response);
|
||||
visitList.value = [];
|
||||
total.value = 0;
|
||||
}
|
||||
} else {
|
||||
console.warn('API响应不是对象类型:', response);
|
||||
visitList.value = [];
|
||||
total.value = 0;
|
||||
}
|
||||
} else {
|
||||
console.warn('API响应不是对象类型:', response);
|
||||
|
||||
loading.value = false;
|
||||
// 计算统计数据
|
||||
calculateStatistics();
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('获取走访记录列表失败:', error);
|
||||
visitList.value = [];
|
||||
total.value = 0;
|
||||
}
|
||||
|
||||
loading.value = false;
|
||||
// 计算统计数据
|
||||
calculateStatistics();
|
||||
}).catch((error) => {
|
||||
console.error('获取走访记录列表失败:', error);
|
||||
visitList.value = [];
|
||||
total.value = 0;
|
||||
loading.value = false;
|
||||
// 可以添加用户友好的错误提示
|
||||
proxy?.$modal.msgError('获取数据失败,请稍后重试');
|
||||
});
|
||||
loading.value = false;
|
||||
// 可以添加用户友好的错误提示
|
||||
proxy?.$modal.msgError('获取数据失败,请稍后重试');
|
||||
});
|
||||
}
|
||||
|
||||
/** 计算统计数据 */
|
||||
@@ -760,17 +761,13 @@ function reset() {
|
||||
function initDictData() {
|
||||
// 获取走访状态字典
|
||||
getDicts('zsjy_visit_status').then((response) => {
|
||||
visitStatusOptions.value = response.data.map(item => ({
|
||||
visitStatusOptions.value = response.data.map((item) => ({
|
||||
label: item.dictLabel,
|
||||
value: item.dictValue
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** 加载当前班主任班级的学生列表 */
|
||||
function loadCurrentTeacherStudents() {
|
||||
const teacherId = userStore.userId;
|
||||
|
||||
@@ -7,22 +7,17 @@
|
||||
<div class="qrcode-header">
|
||||
<h2 class="qrcode-title">新生报名二维码</h2>
|
||||
<p class="qrcode-description">请扫码进行新生报名信息填写</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 二维码区域 -->
|
||||
<div class="qrcode-wrapper">
|
||||
<div class="qrcode-container">
|
||||
<canvas ref="qrcodeCanvas" class="qrcode-canvas"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 二维码URL显示 -->
|
||||
<div class="qrcode-url">
|
||||
<el-input
|
||||
v-model="qrcodeUrl"
|
||||
placeholder="请输入二维码链接地址"
|
||||
clearable
|
||||
@change="generateQrcode"
|
||||
>
|
||||
<el-input v-model="qrcodeUrl" placeholder="请输入二维码链接地址" clearable @change="generateQrcode">
|
||||
<template #prepend>链接地址</template>
|
||||
</el-input>
|
||||
</div>
|
||||
@@ -30,31 +25,13 @@
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div class="qrcode-actions">
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="Refresh"
|
||||
@click="generateQrcode"
|
||||
:loading="generating"
|
||||
v-hasPermi="['zsjy:qrcode:view']"
|
||||
>
|
||||
<el-button type="primary" icon="Refresh" @click="generateQrcode" :loading="generating" v-hasPermi="['zsjy:qrcode:view']">
|
||||
重新生成
|
||||
</el-button>
|
||||
<el-button
|
||||
type="success"
|
||||
icon="Download"
|
||||
@click="downloadQrcode"
|
||||
:disabled="!qrcodeGenerated"
|
||||
v-hasPermi="['zsjy:qrcode:view']"
|
||||
>
|
||||
<el-button type="success" icon="Download" @click="downloadQrcode" :disabled="!qrcodeGenerated" v-hasPermi="['zsjy:qrcode:view']">
|
||||
下载二维码
|
||||
</el-button>
|
||||
<el-button
|
||||
type="info"
|
||||
icon="Printer"
|
||||
@click="printQrcode"
|
||||
:disabled="!qrcodeGenerated"
|
||||
v-hasPermi="['zsjy:qrcode:view']"
|
||||
>
|
||||
<el-button type="info" icon="Printer" @click="printQrcode" :disabled="!qrcodeGenerated" v-hasPermi="['zsjy:qrcode:view']">
|
||||
打印二维码
|
||||
</el-button>
|
||||
</div>
|
||||
@@ -64,37 +41,35 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="QrCode">
|
||||
import { ref, onMounted, nextTick, getCurrentInstance } from 'vue'
|
||||
import QRCode from 'qrcode'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { ref, onMounted, nextTick, getCurrentInstance } from 'vue';
|
||||
import QRCode from 'qrcode';
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
const { proxy } = getCurrentInstance() as any
|
||||
const { proxy } = getCurrentInstance() as any;
|
||||
|
||||
// 响应式数据
|
||||
const qrcodeCanvas = ref<HTMLCanvasElement>()
|
||||
const qrcodeUrl = ref<string>('') // 二维码链接地址,可手动配置
|
||||
const generating = ref<boolean>(false)
|
||||
const qrcodeGenerated = ref<boolean>(false)
|
||||
const qrcodeCanvas = ref<HTMLCanvasElement>();
|
||||
const qrcodeUrl = ref<string>(''); // 二维码链接地址,可手动配置
|
||||
const generating = ref<boolean>(false);
|
||||
const qrcodeGenerated = ref<boolean>(false);
|
||||
|
||||
// 默认二维码URL(后续可手动配置)
|
||||
const getDefaultUrl = () => {
|
||||
// 使用 window.location 来判断环境
|
||||
const isProduction = window.location.hostname !== 'localhost'
|
||||
return isProduction
|
||||
? 'https://domain.com/mobile/#/pages/enrollment/form'
|
||||
: 'http://localhost:8081/#/pages/enrollment/form'
|
||||
}
|
||||
const isProduction = window.location.hostname !== 'localhost';
|
||||
return isProduction ? 'https://domain.com/mobile/#/pages/enrollment/form' : 'http://localhost:8081/#/pages/enrollment/form';
|
||||
};
|
||||
|
||||
/** 生成二维码 */
|
||||
const generateQrcode = async () => {
|
||||
if (!qrcodeUrl.value.trim()) {
|
||||
ElMessage.warning('请输入有效的链接地址')
|
||||
return
|
||||
ElMessage.warning('请输入有效的链接地址');
|
||||
return;
|
||||
}
|
||||
|
||||
generating.value = true
|
||||
generating.value = true;
|
||||
try {
|
||||
await nextTick()
|
||||
await nextTick();
|
||||
|
||||
if (qrcodeCanvas.value) {
|
||||
// 生成二维码到canvas
|
||||
@@ -106,64 +81,64 @@ const generateQrcode = async () => {
|
||||
light: '#FFFFFF'
|
||||
},
|
||||
errorCorrectionLevel: 'M'
|
||||
})
|
||||
});
|
||||
|
||||
qrcodeGenerated.value = true
|
||||
ElMessage.success('二维码生成成功')
|
||||
qrcodeGenerated.value = true;
|
||||
ElMessage.success('二维码生成成功');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('生成二维码失败:', error)
|
||||
ElMessage.error('生成二维码失败,请检查链接地址是否正确')
|
||||
console.error('生成二维码失败:', error);
|
||||
ElMessage.error('生成二维码失败,请检查链接地址是否正确');
|
||||
} finally {
|
||||
generating.value = false
|
||||
generating.value = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** 下载二维码 */
|
||||
const downloadQrcode = () => {
|
||||
if (!qrcodeCanvas.value || !qrcodeGenerated.value) {
|
||||
ElMessage.warning('请先生成二维码')
|
||||
return
|
||||
ElMessage.warning('请先生成二维码');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 创建下载链接
|
||||
const link = document.createElement('a')
|
||||
link.download = `新生报到二维码_${new Date().getTime()}.png`
|
||||
link.href = qrcodeCanvas.value.toDataURL('image/png')
|
||||
const link = document.createElement('a');
|
||||
link.download = `新生报到二维码_${new Date().getTime()}.png`;
|
||||
link.href = qrcodeCanvas.value.toDataURL('image/png');
|
||||
|
||||
// 触发下载
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
|
||||
ElMessage.success('二维码下载成功')
|
||||
ElMessage.success('二维码下载成功');
|
||||
} catch (error) {
|
||||
console.error('下载二维码失败:', error)
|
||||
ElMessage.error('下载失败')
|
||||
console.error('下载二维码失败:', error);
|
||||
ElMessage.error('下载失败');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** 打印二维码 */
|
||||
const printQrcode = () => {
|
||||
if (!qrcodeCanvas.value || !qrcodeGenerated.value) {
|
||||
ElMessage.warning('请先生成二维码')
|
||||
return
|
||||
ElMessage.warning('请先生成二维码');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 创建打印窗口
|
||||
const printWindow = window.open('', '_blank')
|
||||
const printWindow = window.open('', '_blank');
|
||||
if (!printWindow) {
|
||||
ElMessage.error('无法打开打印窗口,请检查浏览器设置')
|
||||
return
|
||||
ElMessage.error('无法打开打印窗口,请检查浏览器设置');
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取二维码图片数据
|
||||
const qrcodeDataUrl = qrcodeCanvas.value.toDataURL('image/png')
|
||||
const qrcodeDataUrl = qrcodeCanvas.value.toDataURL('image/png');
|
||||
|
||||
// 生成打印页面HTML
|
||||
const scriptTag = 'script'
|
||||
const scriptTag = 'script';
|
||||
const printContent = [
|
||||
'<!DOCTYPE html>',
|
||||
'<html>',
|
||||
@@ -196,27 +171,26 @@ const printQrcode = () => {
|
||||
`</${scriptTag}>`,
|
||||
'</body>',
|
||||
'</html>'
|
||||
].join('')
|
||||
].join('');
|
||||
|
||||
// 写入打印内容
|
||||
printWindow.document.write(printContent)
|
||||
printWindow.document.close()
|
||||
|
||||
printWindow.document.write(printContent);
|
||||
printWindow.document.close();
|
||||
} catch (error) {
|
||||
console.error('打印二维码失败:', error)
|
||||
ElMessage.error('打印失败')
|
||||
console.error('打印二维码失败:', error);
|
||||
ElMessage.error('打印失败');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 组件挂载后初始化
|
||||
onMounted(() => {
|
||||
// 设置默认URL
|
||||
qrcodeUrl.value = getDefaultUrl()
|
||||
qrcodeUrl.value = getDefaultUrl();
|
||||
// 自动生成二维码
|
||||
nextTick(() => {
|
||||
generateQrcode()
|
||||
})
|
||||
})
|
||||
generateQrcode();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
Reference in new issue
Block a user