This commit is contained in:
zlwu committed 2026-01-09 18:04:36 +08:00
1 parent aec39f984b
commit e772cd1435
55 files changed
+4466 -652

No files matched your search

@@ -44,7 +44,7 @@ public class CwApprovalRecordController extends BaseController {
/**
* 查询审批记录列表
*/
//@SaCheckPermission("cw:approval:list")
// @SaCheckPermission("cw:approval:list")
@GetMapping("/list")
public TableDataInfo<CwApprovalRecordVo> list(CwApprovalRecordBo bo, PageQuery pageQuery) {
return approvalRecordService.queryPageList(bo, pageQuery);
@@ -53,7 +53,7 @@ public class CwApprovalRecordController extends BaseController {
/**
* 导出审批记录列表
*/
//@SaCheckPermission("cw:approval:export")
// @SaCheckPermission("cw:approval:export")
@Log(title = "审批记录", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(CwApprovalRecordBo bo, HttpServletResponse response) {
@@ -64,17 +64,16 @@ public class CwApprovalRecordController extends BaseController {
/**
* 获取审批记录详细信息
*/
//@SaCheckPermission("cw:approval:query")
// @SaCheckPermission("cw:approval:query")
@GetMapping("/{id}")
public R<CwApprovalRecordVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
public R<CwApprovalRecordVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) {
return R.ok(approvalRecordService.queryById(id));
}
/**
* 根据申报ID查询审批记录列表
*/
//@SaCheckPermission("cw:approval:query")
// @SaCheckPermission("cw:approval:query")
@GetMapping("/byApplication/{applicationId}")
public R<List<CwApprovalRecordVo>> getByApplicationId(@PathVariable Long applicationId) {
List<CwApprovalRecordVo> list = approvalRecordService.queryByApplicationId(applicationId);
@@ -84,7 +83,7 @@ public class CwApprovalRecordController extends BaseController {
/**
* 查询当前用户待审批记录列表
*/
//@SaCheckPermission("cw:approval:list")
// @SaCheckPermission("cw:approval:list")
@GetMapping("/pending")
public R<List<CwApprovalRecordVo>> getPendingList() {
List<CwApprovalRecordVo> list = approvalRecordService.queryPendingByCurrentUser();
@@ -94,7 +93,7 @@ public class CwApprovalRecordController extends BaseController {
/**
* 查询当前用户已审批记录列表
*/
//@SaCheckPermission("cw:approval:list")
// @SaCheckPermission("cw:approval:list")
@GetMapping("/my")
public R<List<CwApprovalRecordVo>> getMyApprovedList() {
List<CwApprovalRecordVo> list = approvalRecordService.queryApprovedByCurrentUser();
@@ -104,7 +103,7 @@ public class CwApprovalRecordController extends BaseController {
/**
* 查询部门待审批申报列表
*/
//@SaCheckPermission("cw:approval:dept")
// @SaCheckPermission("cw:approval:dept")
@GetMapping("/dept/pending")
public R<List<CwApprovalRecordVo>> getDeptPendingList() {
List<CwApprovalRecordVo> list = approvalRecordService.queryDeptPendingApprovals();
@@ -114,7 +113,7 @@ public class CwApprovalRecordController extends BaseController {
/**
* 查询财务待审批申报列表
*/
//@SaCheckPermission("cw:approval:finance")
// @SaCheckPermission("cw:approval:finance")
@GetMapping("/finance/pending")
public R<List<CwApprovalRecordVo>> getFinancePendingList() {
List<CwApprovalRecordVo> list = approvalRecordService.queryFinancePendingApprovals();
@@ -124,7 +123,7 @@ public class CwApprovalRecordController extends BaseController {
/**
* 查询财务已审批申报列表
*/
//@SaCheckPermission("cw:approval:finance")
// @SaCheckPermission("cw:approval:finance")
@GetMapping("/finance/approved")
public R<List<CwApprovalRecordVo>> getFinanceApprovedList() {
List<CwApprovalRecordVo> list = approvalRecordService.queryFinanceApprovedRecords();
@@ -134,9 +133,8 @@ public class CwApprovalRecordController extends BaseController {
/**
* 查询学院领导待审批申报列表
*/
//@SaCheckPermission("cw:approval:leader")
@GetMapping("/leader/pending")</xai:function_call name="search_replace">
<parameter name="file_path">backend/ruoyi-modules/ruoyi-cw/src/main/java/org/dromara/cw/controller/CwApprovalRecordController.java
// @SaCheckPermission("cw:approval:leader")
@GetMapping("/leader/pending")
public R<List<CwApprovalRecordVo>> getLeaderPendingList() {
List<CwApprovalRecordVo> list = approvalRecordService.queryLeaderPendingApprovals();
return R.ok(list);
@@ -145,7 +143,7 @@ public class CwApprovalRecordController extends BaseController {
/**
* 查询学院领导已审批申报列表
*/
//@SaCheckPermission("cw:approval:leader")
// @SaCheckPermission("cw:approval:leader")
@GetMapping("/leader/approved")
public R<List<CwApprovalRecordVo>> getLeaderApprovedList() {
List<CwApprovalRecordVo> list = approvalRecordService.queryLeaderApprovedApprovals();
@@ -155,7 +153,7 @@ public class CwApprovalRecordController extends BaseController {
/**
* 查询所有待审批申报列表(超级管理员专用)
*/
//@SaCheckPermission("cw:approval:list")
// @SaCheckPermission("cw:approval:list")
@GetMapping("/all/pending")
public R<List<CwApprovalRecordVo>> getAllPendingList() {
List<CwApprovalRecordVo> list = approvalRecordService.queryAllPendingApprovals();
@@ -165,7 +163,7 @@ public class CwApprovalRecordController extends BaseController {
/**
* 新增审批记录
*/
//@SaCheckPermission("cw:approval:add")
// @SaCheckPermission("cw:approval:add")
@Log(title = "审批记录", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
@@ -176,7 +174,7 @@ public class CwApprovalRecordController extends BaseController {
/**
* 修改审批记录
*/
//@SaCheckPermission("cw:approval:edit")
// @SaCheckPermission("cw:approval:edit")
@Log(title = "审批记录", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
@@ -187,18 +185,17 @@ public class CwApprovalRecordController extends BaseController {
/**
* 删除审批记录
*/
//@SaCheckPermission("cw:approval:remove")
// @SaCheckPermission("cw:approval:remove")
@Log(title = "审批记录", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] ids) {
public R<Void> remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) {
return toAjax(approvalRecordService.deleteWithValidByIds(List.of(ids), true));
}
/**
* 创建审批流程
*/
//@SaCheckPermission("cw:approval:create")
// @SaCheckPermission("cw:approval:create")
@Log(title = "创建审批流程", businessType = BusinessType.INSERT)
@PostMapping("/create/{applicationId}")
public R<Void> createProcess(@PathVariable Long applicationId, @RequestParam String applicationNo) {
@@ -216,34 +213,34 @@ public class CwApprovalRecordController extends BaseController {
/**
* 审批操作
*/
//@SaCheckPermission("cw:approval:process")
// @SaCheckPermission("cw:approval:process")
@Log(title = "审批操作", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PostMapping("/process")
public R<Void> processApproval(@RequestParam Long applicationId,
@RequestParam String nodeCode,
@RequestParam String approveAction,
@RequestParam(required = false) String approveOpinion) {
@RequestParam String nodeCode,
@RequestParam String approveAction,
@RequestParam(required = false) String approveOpinion) {
return toAjax(approvalRecordService.processApproval(applicationId, nodeCode, approveAction, approveOpinion));
}
/**
* 部门审批
*/
//@SaCheckPermission("cw:approval:dept")
// @SaCheckPermission("cw:approval:dept")
@Log(title = "部门审批", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PostMapping("/dept")
public R<Void> deptApprove(@RequestParam Long applicationId,
@RequestParam String approveAction,
@RequestParam(required = false) String approveOpinion) {
@RequestParam String approveAction,
@RequestParam(required = false) String approveOpinion) {
return toAjax(approvalRecordService.deptApprove(applicationId, approveAction, approveOpinion));
}
/**
* 查询当前审批节点
*/
//@SaCheckPermission("cw:approval:query")
// @SaCheckPermission("cw:approval:query")
@GetMapping("/current/{applicationId}")
public R<CwApprovalRecordVo> getCurrentNode(@PathVariable Long applicationId) {
CwApprovalRecordVo record = approvalRecordService.queryCurrentApprovalNode(applicationId);
@@ -253,7 +250,7 @@ public class CwApprovalRecordController extends BaseController {
/**
* 检查审批权限
*/
//@SaCheckPermission("cw:approval:check")
// @SaCheckPermission("cw:approval:check")
@GetMapping("/check/{applicationId}")
public R<Boolean> checkPermission(@PathVariable Long applicationId, @RequestParam String nodeCode) {
Boolean hasPermission = approvalRecordService.checkApprovalPermission(applicationId, nodeCode);
@@ -263,7 +260,7 @@ public class CwApprovalRecordController extends BaseController {
/**
* 检查部门审批权限
*/
//@SaCheckPermission("cw:approval:dept")
// @SaCheckPermission("cw:approval:dept")
@GetMapping("/check/dept/{applicationId}")
public R<Boolean> checkDeptPermission(@PathVariable Long applicationId) {
Boolean hasPermission = approvalRecordService.checkDeptApprovalPermission(applicationId);
@@ -273,7 +270,7 @@ public class CwApprovalRecordController extends BaseController {
/**
* 获取可选审批人列表(院领导)
*/
//@SaCheckPermission("cw:approval:finance")
// @SaCheckPermission("cw:approval:finance")
@GetMapping("/approvers/{applicationId}")
public R<List<Map<String, Object>>> getAvailableApprovers(@PathVariable Long applicationId) {
List<Map<String, Object>> approvers = approvalRecordService.getAvailableApprovers(applicationId);
@@ -283,11 +280,11 @@ public class CwApprovalRecordController extends BaseController {
/**
* 分管领导审批
*/
//@SaCheckPermission("cw:approval:leader")
// @SaCheckPermission("cw:approval:leader")
@PostMapping("/leader")
public R<Void> leaderApproval(@RequestParam Long applicationId,
@RequestParam String approveAction,
@RequestParam String approveOpinion) {
@RequestParam String approveAction,
@RequestParam String approveOpinion) {
log.info("🔍 分管领导审批:applicationId={}, approveAction={}, approveOpinion={}",
applicationId, approveAction, approveOpinion);
@@ -308,12 +305,12 @@ public class CwApprovalRecordController extends BaseController {
/**
* 财务审批
*/
//@SaCheckPermission("cw:approval:finance")
// @SaCheckPermission("cw:approval:finance")
@PostMapping("/finance")
public R<Void> financeApproval(@RequestParam Long applicationId,
@RequestParam String approveAction,
@RequestParam String approveOpinion,
@RequestParam(required = false) Long selectedApproverId) {
@RequestParam String approveAction,
@RequestParam String approveOpinion,
@RequestParam(required = false) Long selectedApproverId) {
log.info("🔍 财务审批:applicationId={}, approveAction={}, approveOpinion={}, selectedApproverId={}",
applicationId, approveAction, approveOpinion, selectedApproverId);
@@ -0,0 +1,265 @@
package org.dromara.rs.controller;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.ArrayList;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.http.MediaType;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.excel.core.ExcelResult;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.rs.domain.vo.RsStaffInfoVo;
import org.dromara.rs.domain.bo.RsStaffInfoBo;
import org.dromara.rs.domain.vo.RsStaffInfoImportVo;
import org.dromara.rs.listener.RsStaffInfoImportListener;
import org.dromara.rs.service.IRsStaffInfoService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.system.service.ISysDeptService;
import org.dromara.system.domain.vo.SysDeptVo;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
/**
* 人员信息
*
* @author zlwu
* @date 2025-01-09
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/staff/staffInfo")
@Slf4j
public class RsStaffInfoController extends BaseController {
private final IRsStaffInfoService staffInfoService;
private final ISysDeptService deptService;
/**
* 查询人员信息列表
*/
// @SaCheckPermission("rs:staffInfo:list")
@GetMapping("/list")
public TableDataInfo<RsStaffInfoVo> list(RsStaffInfoBo bo, PageQuery pageQuery) {
return staffInfoService.queryPageList(bo, pageQuery);
}
/**
* 根据部门ID查询人员信息列表
*/
// @SaCheckPermission("rs:staffInfo:list")
@PostMapping("/listByDeptId")
public TableDataInfo<RsStaffInfoVo> listByDeptId(@RequestBody Map<String, Object> requestBody,
PageQuery pageQuery) {
// 从请求体中解析查询条件和部门ID
RsStaffInfoBo bo = new RsStaffInfoBo();
// 设置基础查询条件
if (requestBody.containsKey("name")) {
bo.setName((String) requestBody.get("name"));
}
if (requestBody.containsKey("staffNo")) {
bo.setStaffNo((String) requestBody.get("staffNo"));
}
if (requestBody.containsKey("phone")) {
bo.setPhone((String) requestBody.get("phone"));
}
if (requestBody.containsKey("staffType")) {
bo.setStaffType((String) requestBody.get("staffType"));
}
if (requestBody.containsKey("status")) {
bo.setStatus((String) requestBody.get("status"));
}
// 设置时间范围查询条件
Map<String, Object> params = new HashMap<>();
if (requestBody.containsKey("beginTime")) {
params.put("beginTime", requestBody.get("beginTime"));
}
if (requestBody.containsKey("endTime")) {
params.put("endTime", requestBody.get("endTime"));
}
bo.setParams(params);
// 提取部门ID参数
Object deptIdValue = null;
if (requestBody.containsKey("deptId") && requestBody.get("deptId") != null) {
deptIdValue = requestBody.get("deptId");
} else if (requestBody.containsKey("dept_id") && requestBody.get("dept_id") != null) {
deptIdValue = requestBody.get("dept_id");
}
if (deptIdValue != null) {
bo.setDeptId(Long.valueOf(deptIdValue.toString()));
}
// 处理排序参数
if (requestBody.containsKey("orderBy")) {
String orderBy = (String) requestBody.get("orderBy");
// 解析 orderBy 字符串,格式如 "staffType ASC, createTime DESC"
if (StringUtils.isNotBlank(orderBy)) {
String[] orderParts = orderBy.split(",");
StringBuilder columns = new StringBuilder();
StringBuilder directions = new StringBuilder();
for (String part : orderParts) {
part = part.trim();
String[] columnAndDirection = part.split("\\s+");
if (columnAndDirection.length == 2) {
if (columns.length() > 0) {
columns.append(",");
directions.append(",");
}
columns.append(columnAndDirection[0].trim());
String direction = columnAndDirection[1].trim().toLowerCase();
directions.append("asc".equals(direction) ? "asc" : "desc");
}
}
if (columns.length() > 0) {
pageQuery.setOrderByColumn(columns.toString());
pageQuery.setIsAsc(directions.toString());
}
}
}
return staffInfoService.queryPageListByDeptId(bo, pageQuery);
}
/**
* 获取人员信息详细信息
*/
// @SaCheckPermission("rs:staffInfo:query")
@GetMapping("/{id}")
public R<RsStaffInfoVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) {
return R.ok(staffInfoService.queryById(id));
}
/**
* 新增人员信息
*/
// @SaCheckPermission("rs:staffInfo:add")
@Log(title = "人员信息", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody RsStaffInfoBo bo) {
return toAjax(staffInfoService.insertByBo(bo));
}
/**
* 修改人员信息
*/
// @SaCheckPermission("rs:staffInfo:edit")
@Log(title = "人员信息", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody RsStaffInfoBo bo) {
return toAjax(staffInfoService.updateByBo(bo));
}
/**
* 删除人员信息
*/
// @SaCheckPermission("rs:staffInfo:remove")
@Log(title = "人员信息", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) {
return toAjax(staffInfoService.deleteWithRelatedData(java.util.Arrays.asList(ids)));
}
/**
* 获取部门树形数据
*/
// @SaCheckPermission("rs:staffInfo:list")
@GetMapping("/deptTree")
public R<List<SysDeptVo>> deptTree() {
List<SysDeptVo> depts = deptService.selectDeptList(
new org.dromara.system.domain.bo.SysDeptBo());
return R.ok(depts);
}
/**
* 导出人员信息
*/
// @SaCheckPermission("rs:staffInfo:export")
@Log(title = "人员信息", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(HttpServletResponse response, RsStaffInfoBo query) {
try {
List<RsStaffInfoVo> list = staffInfoService.queryList(query);
ExcelUtil.exportExcel(list, "人员信息", RsStaffInfoVo.class, response);
} catch (Exception e) {
log.error("导出人员信息失败", e);
throw new org.dromara.common.core.exception.ServiceException("导出失败: " + e.getMessage());
}
}
/**
* 下载人员信息导入模板
*/
@PostMapping("/importTemplate")
public void importTemplate(HttpServletResponse response) {
try {
// 创建示例数据
List<RsStaffInfoImportVo> templateData = new ArrayList<>();
RsStaffInfoImportVo example = new RsStaffInfoImportVo();
// 设置示例数据
example.setName("张三");
example.setGender("0"); // Excel中会显示为"男"
example.setPosition("讲师");
example.setDeptName("计算机学院");
example.setPhone("13800138000");
example.setStaffType("0"); // Excel中会显示为"正式工"
templateData.add(example);
// 动态生成Excel模板(自动包含字典下拉选项)
ExcelUtil.exportExcel(templateData, "人员信息导入模板", RsStaffInfoImportVo.class, response);
} catch (Exception e) {
log.error("生成导入模板失败", e);
throw new org.dromara.common.core.exception.ServiceException("生成模板失败: " + e.getMessage());
}
}
/**
* 导入人员数据
*/
// @SaCheckPermission("rs:staffInfo:import")
@Log(title = "人员信息", businessType = BusinessType.IMPORT)
@PostMapping(value = "/importData", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public R<Void> importData(@RequestPart("file") MultipartFile file, String deptId, boolean updateSupport) throws Exception {
log.info("导入人员信息,部门ID: {}, 更新支持: {}", deptId, updateSupport);
log.info("导入文件名: {}, 文件大小: {} bytes", file.getOriginalFilename(), file.getSize());
try {
ExcelResult<RsStaffInfoImportVo> result = ExcelUtil.importExcel(
file.getInputStream(),
RsStaffInfoImportVo.class,
new RsStaffInfoImportListener(updateSupport, deptId)
);
log.info("Excel导入完成,结果: {}", result.getAnalysis());
return R.ok(result.getAnalysis());
} catch (Exception e) {
log.error("Excel导入失败", e);
throw e;
}
}
}
@@ -0,0 +1,93 @@
package org.dromara.rs.domain;
import org.dromara.common.tenant.core.TenantEntity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serial;
/**
* 人员信息对象 rs_staff_info
*
* @author zlwu
* @date 2025-01-09
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("rs_staff_info")
public class RsStaffInfo extends TenantEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID(雪花ID)
*/
@TableId(value = "id")
private Long id;
/**
* 删除标志(0代表存在 1代表删除)
*/
@TableLogic(value = "0", delval = "1")
private String delFlag;
/**
* 工号
*/
private String staffNo;
/**
* 姓名
*/
private String name;
/**
* 性别:0-男,1-女
*/
private String gender;
/**
* 职务
*/
private String position;
/**
* 部门ID
*/
private Long deptId;
/**
* 部门名称
*/
private String deptName;
/**
* 手机号
*/
private String phone;
/**
* 人员类型(0正式工 1临时工 2外聘工)
*/
private String staffType;
/**
* 关联的用户ID
*/
private Long userId;
/**
* 状态(0正常 1停用)
*/
private String status;
/**
* 备注
*/
private String remark;
}
@@ -0,0 +1,92 @@
package org.dromara.rs.domain.bo;
import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.rs.domain.RsStaffInfo;
/**
* 人员信息业务对象 rs_staff_info
*
* @author zlwu
* @date 2025-01-09
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = RsStaffInfo.class, reverseConvertGenerate = false)
public class RsStaffInfoBo extends BaseEntity {
/**
* 主键ID
*/
@NotNull(message = "主键ID不能为空", groups = { EditGroup.class })
private Long id;
/**
* 工号
*/
private String staffNo;
/**
* 姓名
*/
@NotBlank(message = "姓名不能为空", groups = { AddGroup.class, EditGroup.class })
private String name;
/**
* 性别
*/
@NotBlank(message = "性别不能为空", groups = { AddGroup.class, EditGroup.class })
@Pattern(regexp = "^[01]$", message = "性别只能是0(男)或1(女)", groups = { AddGroup.class, EditGroup.class })
private String gender;
/**
* 职务
*/
private String position;
/**
* 部门ID
*/
@NotNull(message = "部门ID不能为空", groups = { AddGroup.class, EditGroup.class })
private Long deptId;
/**
* 部门名称
*/
private String deptName;
/**
* 手机号
*/
@NotBlank(message = "手机号不能为空", groups = { AddGroup.class, EditGroup.class })
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确", groups = { AddGroup.class, EditGroup.class })
private String phone;
/**
* 人员类型
*/
@NotBlank(message = "人员类型不能为空", groups = { AddGroup.class, EditGroup.class })
@Pattern(regexp = "^[0-2]$", message = "人员类型只能是0(正式工)、1(临时工)或2(外聘工)", groups = { AddGroup.class, EditGroup.class })
private String staffType;
/**
* 关联的用户ID
*/
private Long userId;
/**
* 状态
*/
private String status;
/**
* 备注
*/
private String remark;
}
@@ -0,0 +1,76 @@
package org.dromara.rs.domain.vo;
import cn.idev.excel.annotation.ExcelProperty;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictPreloadConvert;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 人员信息导入VO rs_staff_info
*
* @author zlwu
* @date 2025-01-09
*/
@Data
@cn.idev.excel.annotation.ExcelIgnoreUnannotated
public class RsStaffInfoImportVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 工号
*/
@ExcelProperty("工号")
private String staffNo;
/**
* 姓名
*/
@ExcelProperty("姓名")
@NotBlank(message = "姓名不能为空")
private String name;
/**
* 性别
*/
@ExcelProperty(value = "性别", converter = ExcelDictPreloadConvert.class)
@org.dromara.common.excel.annotation.ExcelDictFormat(dictType = "sys_user_sex")
@Pattern(regexp = "^[01]$", message = "性别只能选择:0-男,1-女")
private String gender;
/**
* 职务
*/
@ExcelProperty("职务")
private String position;
/**
* 部门名称
*/
@ExcelProperty("部门名称")
@NotBlank(message = "部门名称不能为空")
private String deptName;
/**
* 手机号
*/
@ExcelProperty("手机号")
@NotBlank(message = "手机号不能为空")
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
private String phone;
/**
* 人员类型
*/
@ExcelProperty(value = "人员类型", converter = ExcelDictPreloadConvert.class)
@org.dromara.common.excel.annotation.ExcelDictFormat(readConverterExp = "0=正式工,1=临时工,2=外聘工")
@Pattern(regexp = "^[0-2]$", message = "人员类型只能选择:0-正式工,1-临时工,2-外聘工")
private String staffType;
}
@@ -0,0 +1,110 @@
package org.dromara.rs.domain.vo;
import org.dromara.rs.domain.RsStaffInfo;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictPreloadConvert;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 人员信息视图对象 rs_staff_info
*
* @author zlwu
* @date 2025-01-09
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = RsStaffInfo.class)
public class RsStaffInfoVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@ExcelProperty(value = "主键ID")
private Long id;
/**
* 工号
*/
@ExcelProperty(value = "工号")
private String staffNo;
/**
* 姓名
*/
@ExcelProperty(value = "姓名")
private String name;
/**
* 性别
*/
@ExcelProperty(value = "性别", converter = ExcelDictPreloadConvert.class)
@ExcelDictFormat(dictType = "sys_user_sex")
private String gender;
/**
* 职务
*/
@ExcelProperty(value = "职务")
private String position;
/**
* 部门ID
*/
private Long deptId;
/**
* 部门名称
*/
@ExcelProperty(value = "部门名称")
private String deptName;
/**
* 手机号
*/
@ExcelProperty(value = "手机号")
private String phone;
/**
* 人员类型
*/
@ExcelProperty(value = "人员类型", converter = ExcelDictPreloadConvert.class)
@ExcelDictFormat(readConverterExp = "0=正式工,1=临时工,2=外聘工")
private String staffType;
/**
* 关联的用户ID
*/
private Long userId;
/**
* 状态
*/
@ExcelProperty(value = "状态", converter = ExcelDictPreloadConvert.class)
@ExcelDictFormat(readConverterExp = "0=正常,1=停用")
private String status;
/**
* 创建时间
*/
@ExcelProperty(value = "创建时间")
private Date createTime;
/**
* 备注
*/
@ExcelProperty(value = "备注")
private String remark;
}
@@ -0,0 +1,214 @@
package org.dromara.rs.listener;
import cn.hutool.core.bean.BeanUtil;
import cn.idev.excel.context.AnalysisContext;
import cn.idev.excel.event.AnalysisEventListener;
import cn.idev.excel.util.ListUtils;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.core.utils.ValidatorUtils;
import org.dromara.common.excel.core.ExcelListener;
import org.dromara.common.excel.core.ExcelResult;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.rs.domain.bo.RsStaffInfoBo;
import org.dromara.rs.domain.vo.RsStaffInfoImportVo;
import org.dromara.rs.service.IRsStaffInfoService;
import org.dromara.system.service.ISysDeptService;
import org.dromara.system.service.ISysUserService;
import org.dromara.system.domain.vo.SysDeptVo;
import org.dromara.common.core.utils.StringUtils;
import cn.hutool.http.HtmlUtil;
import java.util.List;
/**
* 人员信息自定义导入
*
* @author zlwu
* @date 2025-01-09
*/
@Slf4j
public class RsStaffInfoImportListener extends AnalysisEventListener<RsStaffInfoImportVo>
implements ExcelListener<RsStaffInfoImportVo> {
private final IRsStaffInfoService staffInfoService;
private final ISysDeptService deptService;
private final ISysUserService userService;
private final Boolean isUpdateSupport;
private final Long operUserId;
private final String deptId;
private int successNum = 0;
private int failureNum = 0;
private final StringBuilder successMsg = new StringBuilder();
private final StringBuilder failureMsg = new StringBuilder();
public RsStaffInfoImportListener(Boolean isUpdateSupport, String deptId) {
this.staffInfoService = SpringUtils.getBean(IRsStaffInfoService.class);
this.deptService = SpringUtils.getBean(ISysDeptService.class);
this.userService = SpringUtils.getBean(ISysUserService.class);
this.isUpdateSupport = isUpdateSupport;
this.deptId = deptId;
this.operUserId = LoginHelper.getUserId();
}
@Override
public void invoke(RsStaffInfoImportVo staffVo, AnalysisContext context) {
try {
// 1. 验证数据完整性
validateImportData(staffVo);
// 2. 验证唯一性(工号、手机号)
if (!checkUniqueness(staffVo)) {
return;
}
// 3. 根据部门名称查找部门ID
Long deptId = findDeptByName(staffVo.getDeptName());
if (deptId == null) {
failureNum++;
failureMsg.append("<br/>").append(failureNum).append("、部门 ").append(staffVo.getDeptName()).append(" 不存在");
return;
}
// 4. 转换并保存数据
RsStaffInfoBo staffBo = convertToBo(staffVo, deptId);
staffInfoService.insertByBo(staffBo);
successNum++;
successMsg.append("<br/>").append(successNum).append("、工号 ").append(staffBo.getStaffNo()).append(" 导入成功");
} catch (Exception e) {
handleImportError(staffVo, e);
}
}
/**
* 验证导入数据
*/
private void validateImportData(RsStaffInfoImportVo staffVo) {
if (staffVo == null) {
throw new ServiceException("导入数据为空");
}
// 验证必填字段
if (StringUtils.isBlank(staffVo.getName())) {
throw new ServiceException("姓名不能为空");
}
if (StringUtils.isBlank(staffVo.getGender())) {
throw new ServiceException("性别不能为空");
}
if (StringUtils.isBlank(staffVo.getDeptName())) {
throw new ServiceException("部门名称不能为空");
}
if (StringUtils.isBlank(staffVo.getPhone())) {
throw new ServiceException("手机号不能为空");
}
if (StringUtils.isBlank(staffVo.getStaffType())) {
throw new ServiceException("人员类型不能为空");
}
}
/**
* 验证唯一性
*/
private boolean checkUniqueness(RsStaffInfoImportVo staffVo) {
// 验证工号唯一性(如果提供了工号)
if (StringUtils.isNotBlank(staffVo.getStaffNo())) {
// 这里可以添加工号唯一性验证逻辑
// 暂时跳过,因为工号是自动生成的
}
// 验证手机号唯一性
if (StringUtils.isNotBlank(staffVo.getPhone())) {
var existingUser = userService.selectUserByPhonenumber(staffVo.getPhone());
if (existingUser != null) {
failureNum++;
failureMsg.append("<br/>").append(failureNum).append("、手机号 ").append(staffVo.getPhone()).append(" 已存在");
return false;
}
}
return true;
}
/**
* 根据部门名称查找部门ID
*/
private Long findDeptByName(String deptName) {
if (StringUtils.isBlank(deptName)) {
return null;
}
// 查询所有部门
List<SysDeptVo> allDepts = deptService.selectDeptList(new org.dromara.system.domain.bo.SysDeptBo());
for (SysDeptVo dept : allDepts) {
if (deptName.equals(dept.getDeptName())) {
return dept.getDeptId();
}
}
return null;
}
/**
* 转换VO到BO
*/
private RsStaffInfoBo convertToBo(RsStaffInfoImportVo staffVo, Long deptId) {
RsStaffInfoBo staffBo = BeanUtil.toBean(staffVo, RsStaffInfoBo.class);
staffBo.setDeptId(deptId);
staffBo.setCreateBy(operUserId);
staffBo.setUpdateBy(operUserId);
return staffBo;
}
/**
* 处理导入错误
*/
private void handleImportError(RsStaffInfoImportVo staffVo, Exception e) {
failureNum++;
String msg = "<br/>" + failureNum + "、姓名 " + HtmlUtil.cleanHtmlTag(staffVo.getName()) + " 导入失败:";
String message = e.getMessage();
if (e instanceof ConstraintViolationException cvException) {
message = StreamUtils.join(cvException.getConstraintViolations(), ConstraintViolation::getMessage,
", ");
}
failureMsg.append(msg).append(message);
log.error(msg, e);
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 处理完成后的逻辑
}
@Override
public ExcelResult<RsStaffInfoImportVo> getExcelResult() {
return new ExcelResult<>() {
@Override
public String getAnalysis() {
if (failureNum > 0) {
failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:");
throw new ServiceException(failureMsg.toString());
} else {
successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:");
}
return successMsg.toString();
}
@Override
public List<RsStaffInfoImportVo> getList() {
return null;
}
@Override
public List<String> getErrorList() {
return null;
}
};
}
}
@@ -0,0 +1,35 @@
package org.dromara.rs.mapper;
import org.dromara.rs.domain.RsStaffInfo;
import org.dromara.rs.domain.vo.RsStaffInfoVo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 人员信息Mapper接口
*
* @author zlwu
* @date 2025-01-09
*/
public interface RsStaffInfoMapper extends BaseMapperPlus<RsStaffInfo, RsStaffInfoVo> {
/**
* 根据人员类型获取该类别最大的工号
*
* @param staffType 人员类型(0正式工 1临时工 2外聘工)
* @param prefix 工号前缀
* @return 该类别最大的工号
*/
String selectMaxStaffNoByType(@Param("staffType") String staffType, @Param("prefix") String prefix);
/**
* 根据部门ID列表查询人员信息
*
* @param deptIds 部门ID列表
* @return 人员信息列表
*/
List<RsStaffInfoVo> selectListByDeptIds(@Param("deptIds") List<Long> deptIds);
}
@@ -0,0 +1,60 @@
package org.dromara.rs.service;
import org.dromara.rs.domain.RsStaffInfo;
import org.dromara.rs.domain.bo.RsStaffInfoBo;
import org.dromara.rs.domain.vo.RsStaffInfoVo;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import java.util.Collection;
import java.util.List;
/**
* 人员信息Service接口
*
* @author zlwu
* @date 2025-01-09
*/
public interface IRsStaffInfoService {
/**
* 查询人员信息
*/
RsStaffInfoVo queryById(Long id);
/**
* 查询人员信息列表
*/
TableDataInfo<RsStaffInfoVo> queryPageList(RsStaffInfoBo bo, PageQuery pageQuery);
/**
* 根据部门ID查询人员信息列表
*/
TableDataInfo<RsStaffInfoVo> queryPageListByDeptId(RsStaffInfoBo bo, PageQuery pageQuery);
/**
* 查询人员信息列表
*/
List<RsStaffInfoVo> queryList(RsStaffInfoBo bo);
/**
* 新增人员信息
*/
Boolean insertByBo(RsStaffInfoBo bo);
/**
* 修改人员信息
*/
Boolean updateByBo(RsStaffInfoBo bo);
/**
* 批量删除人员信息
*/
Boolean deleteWithRelatedData(Collection<Long> ids);
/**
* 生成工号
*/
String generateStaffNo(String staffType);
}
@@ -0,0 +1,285 @@
package org.dromara.rs.service.impl;
import cn.hutool.crypto.digest.BCrypt;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.satoken.utils.LoginHelper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.dromara.rs.domain.bo.RsStaffInfoBo;
import org.dromara.rs.domain.vo.RsStaffInfoVo;
import org.dromara.rs.domain.RsStaffInfo;
import org.dromara.rs.mapper.RsStaffInfoMapper;
import org.dromara.rs.service.IRsStaffInfoService;
import org.dromara.system.service.ISysUserService;
import org.dromara.system.service.ISysDeptService;
import org.dromara.system.domain.bo.SysUserBo;
import org.dromara.system.domain.vo.SysDeptVo;
import java.util.List;
import java.util.ArrayList;
import java.util.Collection;
/**
* 人员信息Service业务层处理
*
* @author zlwu
* @date 2025-01-09
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class RsStaffInfoServiceImpl implements IRsStaffInfoService {
private final RsStaffInfoMapper baseMapper;
private final ISysUserService sysUserService;
private final ISysDeptService sysDeptService;
/**
* 查询人员信息
*
* @param id 主键
* @return 人员信息
*/
@Override
public RsStaffInfoVo queryById(Long id) {
return baseMapper.selectVoById(id);
}
/**
* 查询人员信息列表
*
* @param bo 查询条件
* @param pageQuery 分页参数
* @return 人员信息分页列表
*/
@Override
public TableDataInfo<RsStaffInfoVo> queryPageList(RsStaffInfoBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<RsStaffInfo> lqw = buildQueryWrapper(bo);
Page<RsStaffInfoVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 根据部门ID查询人员信息列表
*
* @param bo 查询条件
* @param pageQuery 分页参数
* @return 人员信息分页列表
*/
@Override
public TableDataInfo<RsStaffInfoVo> queryPageListByDeptId(RsStaffInfoBo bo, PageQuery pageQuery) {
LambdaQueryWrapper<RsStaffInfo> lqw = buildQueryWrapper(bo);
Page<RsStaffInfoVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
private LambdaQueryWrapper<RsStaffInfo> buildQueryWrapper(RsStaffInfoBo bo) {
LambdaQueryWrapper<RsStaffInfo> lqw = Wrappers.lambdaQuery();
lqw.eq(bo.getDeptId() != null, RsStaffInfo::getDeptId, bo.getDeptId());
lqw.like(StringUtils.isNotBlank(bo.getName()), RsStaffInfo::getName, bo.getName());
lqw.like(StringUtils.isNotBlank(bo.getStaffNo()), RsStaffInfo::getStaffNo, bo.getStaffNo());
lqw.like(StringUtils.isNotBlank(bo.getPhone()), RsStaffInfo::getPhone, bo.getPhone());
lqw.eq(StringUtils.isNotBlank(bo.getStaffType()), RsStaffInfo::getStaffType, bo.getStaffType());
lqw.eq(StringUtils.isNotBlank(bo.getStatus()), RsStaffInfo::getStatus, bo.getStatus());
return lqw;
}
/**
* 查询人员信息列表
*
* @param bo 查询条件
* @return 人员信息列表
*/
@Override
public List<RsStaffInfoVo> queryList(RsStaffInfoBo bo) {
LambdaQueryWrapper<RsStaffInfo> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
/**
* 新增人员信息
*
* @param bo 人员信息
* @return 是否新增成功
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean insertByBo(RsStaffInfoBo bo) {
// 1. 生成工号
String staffNo = generateStaffNo(bo.getStaffType());
bo.setStaffNo(staffNo);
// 2. 插入人员主表
RsStaffInfo entity = MapstructUtils.convert(bo, RsStaffInfo.class);
entity.setCreateBy(LoginHelper.getUserId());
entity.setCreateDept(LoginHelper.getDeptId());
entity.setStatus("0"); // 默认正常状态
boolean flag = baseMapper.insert(entity) > 0;
if (flag) {
try {
// 3. 获取部门名称
if (bo.getDeptId() != null) {
SysDeptVo dept = sysDeptService.selectDeptById(bo.getDeptId());
if (dept != null) {
entity.setDeptName(dept.getDeptName());
baseMapper.updateById(entity);
}
}
// 4. 创建sys_user记录
SysUserBo sysUserBo = new SysUserBo();
sysUserBo.setUserId(entity.getId());
sysUserBo.setUserName(bo.getPhone());
sysUserBo.setNickName(bo.getName());
sysUserBo.setUserType("sys_user");
sysUserBo.setEmail("");
sysUserBo.setPhonenumber(bo.getPhone());
sysUserBo.setSex(bo.getGender());
sysUserBo.setDeptId(bo.getDeptId());
sysUserBo.setPassword(BCrypt.hashpw("Hzdyjs123456!@#", BCrypt.gensalt()));
sysUserBo.setStatus("0");
sysUserBo.setAccountType("1"); // 账号类型:教职工
sysUserBo.setUserNo(bo.getStaffNo());
int userResult = sysUserService.insertUser(sysUserBo);
if (userResult > 0) {
// 5. 绑定默认角色(4和1938120323613728770)
Long[] roleIds = { 4L, 1938120323613728770L };
sysUserService.insertUserAuth(entity.getId(), roleIds);
// 6. 更新人员表的user_id关联
entity.setUserId(entity.getId());
baseMapper.updateById(entity);
}
} catch (Exception e) {
log.error("创建人员系统用户失败: {}", e.getMessage(), e);
throw new RuntimeException("创建人员系统用户失败: " + e.getMessage(), e);
}
}
return flag;
}
/**
* 修改人员信息
*
* @param bo 人员信息
* @return 是否修改成功
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean updateByBo(RsStaffInfoBo bo) {
RsStaffInfo update = MapstructUtils.convert(bo, RsStaffInfo.class);
update.setUpdateBy(LoginHelper.getUserId());
boolean flag = baseMapper.updateById(update) > 0;
if (flag) {
try {
// 查询现有的sys_user记录
var existingUser = sysUserService.selectUserById(bo.getId());
if (existingUser != null) {
// 更新sys_user表信息
SysUserBo sysUserBo = new SysUserBo();
sysUserBo.setUserId(bo.getId());
sysUserBo.setUserName(bo.getPhone());
sysUserBo.setNickName(bo.getName());
sysUserBo.setPhonenumber(bo.getPhone());
sysUserBo.setSex(bo.getGender());
sysUserBo.setDeptId(bo.getDeptId());
sysUserBo.setUserNo(bo.getStaffNo());
sysUserBo.setAccountType("1");
sysUserService.updateUser(sysUserBo);
log.info("成功更新人员 {} 的系统用户信息", bo.getName());
}
} catch (Exception e) {
log.error("更新人员系统用户信息失败: {}", e.getMessage(), e);
throw new RuntimeException("更新人员系统用户信息失败: " + e.getMessage(), e);
}
}
return flag;
}
/**
* 批量删除人员信息
*
* @param ids 人员ID集合
* @return 是否删除成功
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean deleteWithRelatedData(Collection<Long> ids) {
// 1. 删除人员主表记录(逻辑删除)
boolean staffDeleteResult = baseMapper.deleteByIds(ids) > 0;
if (staffDeleteResult) {
try {
// 2. 同步删除sys_user表中的对应数据(逻辑删除)
Long[] userIds = ids.toArray(new Long[0]);
int userDeleteResult = sysUserService.deleteUserByIds(userIds);
if (userDeleteResult > 0) {
log.info("成功删除 {} 个人员对应的系统用户", userDeleteResult);
} else {
log.warn("删除人员对应的系统用户失败,删除数量: {}", userDeleteResult);
}
} catch (Exception e) {
log.error("删除人员信息时同步删除系统用户失败: {}", e.getMessage(), e);
throw new RuntimeException("删除人员信息时同步删除系统用户失败: " + e.getMessage(), e);
}
}
return staffDeleteResult;
}
/**
* 生成工号
*
* @param staffType 人员类型(0正式工 1临时工 2外聘工)
* @return 新生成的工号
*/
@Override
public String generateStaffNo(String staffType) {
// 根据人员类型确定前缀
String prefix = switch (staffType) {
case "0" -> "0"; // 正式工
case "1" -> "L"; // 临时工
case "2" -> "W"; // 外聘工
default -> throw new ServiceException("无效的人员类型: " + staffType);
};
// 从rs_staff_info表中获取该类别最大工号
String maxStaffNo = baseMapper.selectMaxStaffNoByType(staffType, prefix);
int nextNumber = 1;
if (StringUtils.isNotBlank(maxStaffNo)) {
String numberPart = maxStaffNo.substring(1);
try {
nextNumber = Integer.parseInt(numberPart) + 1;
} catch (NumberFormatException e) {
log.warn("工号格式错误: {}, 使用默认值1", maxStaffNo);
nextNumber = 1;
}
}
// 生成新的工号,确保是5位数字
String numberStr = String.format("%05d", nextNumber);
// 检查是否超过最大值
if (nextNumber > 99999) {
throw new ServiceException("工号已达到最大值,无法生成新的工号");
}
return prefix + numberStr;
}
}
@@ -0,0 +1,70 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.rs.mapper.RsStaffInfoMapper">
<resultMap type="org.dromara.rs.domain.RsStaffInfo" id="RsStaffInfoResult">
<result property="id" column="id"/>
<result property="tenantId" column="tenant_id"/>
<result property="delFlag" column="del_flag"/>
<result property="staffNo" column="staff_no"/>
<result property="name" column="name"/>
<result property="gender" column="gender"/>
<result property="position" column="position"/>
<result property="deptId" column="dept_id"/>
<result property="deptName" column="dept_name"/>
<result property="phone" column="phone"/>
<result property="staffType" column="staff_type"/>
<result property="userId" column="user_id"/>
<result property="status" column="status"/>
<result property="createDept" column="create_dept"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
<result property="remark" column="remark"/>
</resultMap>
<!-- 根据人员类型获取该类别最大的工号 -->
<select id="selectMaxStaffNoByType" resultType="java.lang.String">
SELECT MAX(staff_no) as maxStaffNo
FROM rs_staff_info
WHERE staff_type = #{staffType}
AND del_flag = '0'
AND staff_no LIKE CONCAT(#{prefix}, '%')
</select>
<!-- 根据部门ID列表查询人员信息 -->
<select id="selectListByDeptIds" resultMap="RsStaffInfoResult">
SELECT
id,
tenant_id,
del_flag,
staff_no,
name,
gender,
position,
dept_id,
dept_name,
phone,
staff_type,
user_id,
status,
create_dept,
create_by,
create_time,
update_by,
update_time,
remark
FROM rs_staff_info
WHERE del_flag = '0'
<if test="deptIds != null and deptIds.size() > 0">
AND dept_id IN
<foreach collection="deptIds" item="deptId" open="(" close=")" separator=",">
#{deptId}
</foreach>
</if>
ORDER BY create_time DESC
</select>
</mapper>
@@ -37,6 +37,11 @@ public class SysUser extends TenantEntity {
*/
private String userName;
/**
* 用户编号
*/
private String userNo;
/**
* 用户昵称
*/
@@ -42,6 +42,12 @@ public class SysUserBo extends BaseEntity {
@Size(min = 0, max = 30, message = "用户账号长度不能超过{max}个字符")
private String userName;
/**
* 用户编号
*/
@Size(min = 0, max = 30, message = "用户编号长度不能超过{max}个字符")
private String userNo;
/**
* 用户昵称
*/
@@ -1,6 +1,7 @@
package org.dromara.xg.controller;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.math.BigDecimal;
@@ -9,6 +10,8 @@ import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
@@ -17,7 +20,12 @@ import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.core.domain.R;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.excel.core.ExcelResult;
import org.dromara.xg.listener.XgConductScoreImportListener;
import org.dromara.xg.domain.vo.XgConductScoreVo;
import org.dromara.xg.domain.vo.XgConductScoreExportVo;
import org.dromara.xg.domain.vo.XgConductScoreImportVo;
import org.dromara.xg.domain.vo.XgConductScoreSemesterAverageExportVo;
import org.dromara.xg.domain.bo.XgConductScoreBo;
import org.dromara.xg.domain.bo.XgConductScoreQueryBo;
import org.dromara.xg.domain.bo.XgConductScoreBatchBo;
@@ -282,7 +290,62 @@ public class XgConductScoreController extends BaseController {
}
}
ExcelUtil.exportExcel(list, "学生操行分", XgConductScoreVo.class, response);
// 如果需要友好格式的学年显示,使用导出专用VO类
if (requestBody.containsKey("academicYearFormat") &&
"friendly".equals(requestBody.get("academicYearFormat").toString())) {
// 创建导出专用的VO列表,将学年转换为友好格式
java.util.List<XgConductScoreExportVo> exportList = new java.util.ArrayList<>();
for (XgConductScoreVo vo : list) {
XgConductScoreExportVo exportVo = new XgConductScoreExportVo();
// 复制所有基础字段
exportVo.setId(vo.getId());
exportVo.setTenantId(vo.getTenantId());
exportVo.setStudentId(vo.getStudentId());
exportVo.setStudentName(vo.getStudentName());
exportVo.setStudentNo(vo.getStudentNo());
exportVo.setClassId(vo.getClassId());
exportVo.setClassName(vo.getClassName());
exportVo.setCollegeName(vo.getCollegeName());
exportVo.setMajorName(vo.getMajorName());
exportVo.setClassTeacherId(vo.getClassTeacherId());
exportVo.setClassTeacherName(vo.getClassTeacherName());
exportVo.setScore(vo.getScore());
exportVo.setRemark(vo.getRemark());
exportVo.setEvaluationResult(vo.getEvaluationResult());
exportVo.setCreateTime(vo.getCreateTime());
exportVo.setCreateBy(vo.getCreateBy());
exportVo.setCreateByName(vo.getCreateByName());
exportVo.setUpdateBy(vo.getUpdateBy());
exportVo.setUpdateTime(vo.getUpdateTime());
// 学年:转换为友好格式,如 2026 -> "2025-2026学年"
String academicYearStr = vo.getAcademicYear() != null
? (vo.getAcademicYear() - 1) + "-" + vo.getAcademicYear() + "学年"
: "";
exportVo.setAcademicYear(academicYearStr);
// 学期:转换为中文,如 1 -> "第一学期"
String semesterStr = vo.getSemester() != null
? (vo.getSemester() == 1 ? "第一学期" : "第二学期")
: "";
exportVo.setSemester(semesterStr);
// 月份:转换为带月字,如 1 -> "1月"
String monthStr = vo.getMonth() != null
? vo.getMonth() + "月"
: "";
exportVo.setMonth(monthStr);
exportList.add(exportVo);
}
// 使用导出专用VO类进行导出
ExcelUtil.exportExcel(exportList, "学生操行分", XgConductScoreExportVo.class, response);
} else {
// 正常导出,使用原始VO类
ExcelUtil.exportExcel(list, "学生操行分", XgConductScoreVo.class, response);
}
}
/**
@@ -518,16 +581,92 @@ public class XgConductScoreController extends BaseController {
List<XgConductScoreSemesterAverageVo> list = xgConductScoreService
.getSemesterAverageByDeptId(academicYear, semester, bo, deptParams, pageQuery).getRows();
// 为每条数据计算评判结果
for (XgConductScoreSemesterAverageVo vo : list) {
if (vo.getAvgScore() != null && vo.getRecordCount() != null && vo.getRecordCount() > 0) {
vo.setEvaluationResult(calculateEvaluationResult(vo.getAvgScore()));
} else {
vo.setEvaluationResult("");
}
}
// 如果需要友好格式的学年显示,使用导出专用VO类
if (requestBody.containsKey("academicYearFormat") &&
"friendly".equals(requestBody.get("academicYearFormat").toString())) {
// 创建导出专用的VO列表,将学年转换为友好格式
java.util.List<XgConductScoreSemesterAverageExportVo> exportList = new java.util.ArrayList<>();
for (XgConductScoreSemesterAverageVo vo : list) {
XgConductScoreSemesterAverageExportVo exportVo = new XgConductScoreSemesterAverageExportVo();
ExcelUtil.exportExcel(list, "学期平均分统计", XgConductScoreSemesterAverageVo.class, response);
// 复制所有基础字段
exportVo.setStudentId(vo.getStudentId());
exportVo.setStudentNo(vo.getStudentNo());
exportVo.setStudentName(vo.getStudentName());
exportVo.setGender(vo.getGender());
exportVo.setClassId(vo.getClassId());
exportVo.setCollegeName(vo.getCollegeName());
exportVo.setMajorName(vo.getMajorName());
exportVo.setClassName(vo.getClassName());
exportVo.setAvgScore(vo.getAvgScore());
exportVo.setRecordCount(vo.getRecordCount());
exportVo.setMaxScore(vo.getMaxScore());
exportVo.setMinScore(vo.getMinScore());
exportVo.setScoreRange(vo.getScoreRange());
exportVo.setLastUpdateTime(vo.getLastUpdateTime());
exportVo.setCreateBy(vo.getCreateBy());
exportVo.setCreateTime(vo.getCreateTime());
exportVo.setUpdateBy(vo.getUpdateBy());
exportVo.setUpdateTime(vo.getUpdateTime());
// 计算评判结果
if (vo.getAvgScore() != null && vo.getRecordCount() != null && vo.getRecordCount() > 0) {
exportVo.setEvaluationResult(calculateEvaluationResult(vo.getAvgScore()));
} else {
exportVo.setEvaluationResult("");
}
// 学年:转换为友好格式,如 2026 -> "2025-2026学年"
String academicYearStr = vo.getAcademicYear() != null
? (vo.getAcademicYear() - 1) + "-" + vo.getAcademicYear() + "学年"
: "";
exportVo.setAcademicYear(academicYearStr);
// 学期:转换为中文,如 1 -> "第一学期"
String semesterStr = vo.getSemester() != null
? (vo.getSemester() == 1 ? "第一学期" : "第二学期")
: "";
exportVo.setSemester(semesterStr);
exportList.add(exportVo);
}
// 按学院、专业、班级、学号排序
exportList.sort((a, b) -> {
int result = java.util.Comparator.nullsLast(String::compareTo)
.compare(a.getCollegeName(), b.getCollegeName());
if (result != 0)
return result;
result = java.util.Comparator.nullsLast(String::compareTo)
.compare(a.getMajorName(), b.getMajorName());
if (result != 0)
return result;
result = java.util.Comparator.nullsLast(String::compareTo)
.compare(a.getClassName(), b.getClassName());
if (result != 0)
return result;
return java.util.Comparator.nullsLast(String::compareTo)
.compare(a.getStudentNo(), b.getStudentNo());
});
// 使用导出专用VO类进行导出
ExcelUtil.exportExcel(exportList, "学期平均分统计", XgConductScoreSemesterAverageExportVo.class, response);
} else {
// 为每条数据计算评判结果
for (XgConductScoreSemesterAverageVo vo : list) {
if (vo.getAvgScore() != null && vo.getRecordCount() != null && vo.getRecordCount() > 0) {
vo.setEvaluationResult(calculateEvaluationResult(vo.getAvgScore()));
} else {
vo.setEvaluationResult("");
}
}
// 正常导出,使用原始VO类
ExcelUtil.exportExcel(list, "学期平均分统计", XgConductScoreSemesterAverageVo.class, response);
}
}
/**
@@ -584,4 +723,60 @@ public class XgConductScoreController extends BaseController {
return R.ok(xgConductScoreService.checkInputPermission());
}
/**
* 下载操行分导入模板
*/
// @SaCheckPermission("xg:conductScore:importTemplate")
@PostMapping("/importTemplate")
public void importTemplate(HttpServletResponse response) {
try {
// 创建示例数据
List<XgConductScoreImportVo> templateData = new ArrayList<>();
XgConductScoreImportVo example = new XgConductScoreImportVo();
// 设置示例数据
example.setStudentNo("2024001");
example.setStudentName("张三");
example.setScore(85);
example.setRemark("表现良好");
templateData.add(example);
// 动态生成Excel模板
ExcelUtil.exportExcel(templateData, "操行分导入模板", XgConductScoreImportVo.class, response);
} catch (Exception e) {
log.error("生成导入模板失败", e);
throw new org.dromara.common.core.exception.ServiceException("生成模板失败: " + e.getMessage());
}
}
/**
* 导入操行分数据
*/
// @SaCheckPermission("xg:conductScore:importData")
@Log(title = "学生操行分", businessType = BusinessType.IMPORT)
@PostMapping(value = "/importData", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public R<Void> importData(@RequestPart("file") MultipartFile file,
@NotNull(message = "学年不能为空") Integer academicYear,
@NotNull(message = "学期不能为空") Integer semester,
@NotNull(message = "月份不能为空") Integer month,
String classId) throws Exception {
log.info("导入操行分数据,学年: {}, 学期: {}, 月份: {}, 班级ID: {}", academicYear, semester, month, classId);
log.info("导入文件名: {}, 文件大小: {} bytes", file.getOriginalFilename(), file.getSize());
try {
ExcelResult<XgConductScoreImportVo> result = ExcelUtil.importExcel(
file.getInputStream(),
XgConductScoreImportVo.class,
new XgConductScoreImportListener(academicYear, semester, month, classId)
);
log.info("Excel导入完成,结果: {}", result.getAnalysis());
return R.ok(result.getAnalysis());
} catch (Exception e) {
log.error("Excel导入失败", e);
throw e;
}
}
}
@@ -5,6 +5,8 @@ import lombok.EqualsAndHashCode;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.xg.domain.XgConductScore;
import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.*;
import java.math.BigDecimal;
@@ -16,6 +18,7 @@ import java.math.BigDecimal;
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = XgConductScore.class, reverseConvertGenerate = false)
public class XgConductScoreBo extends BaseEntity {
private static final long serialVersionUID = 1L;
@@ -0,0 +1,153 @@
package org.dromara.xg.domain.vo;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
import java.io.Serial;
import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonFormat;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import org.dromara.xg.domain.XgConductScore;
import io.github.linpeilie.annotations.AutoMapper;
/**
* 学生操行分导出视图对象
*
* @author Lion Li
* @date 2025-01-13
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = XgConductScore.class)
public class XgConductScoreExportVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* ID
*/
@cn.idev.excel.annotation.ExcelProperty(value = "记录ID")
private Long id;
/**
* 租户ID
*/
private String tenantId;
/**
* 学年(友好格式,如:2025-2026学年)
*/
@cn.idev.excel.annotation.ExcelProperty(value = "学年")
private String academicYear;
/**
* 学期(中文格式,如:第一学期)
*/
@cn.idev.excel.annotation.ExcelProperty(value = "学期")
private String semester;
/**
* 月份(带月字,如:1月)
*/
@cn.idev.excel.annotation.ExcelProperty(value = "月份")
private String month;
/**
* 学生ID
*/
private Long studentId;
/**
* 学生姓名
*/
@cn.idev.excel.annotation.ExcelProperty(value = "学生姓名")
private String studentName;
/**
* 学号
*/
@cn.idev.excel.annotation.ExcelProperty(value = "学号")
private String studentNo;
/**
* 班级ID
*/
private Long classId;
/**
* 班级名称
*/
@cn.idev.excel.annotation.ExcelProperty(value = "班级名称")
private String className;
/**
* 学院名称
*/
@cn.idev.excel.annotation.ExcelProperty(value = "学院名称")
private String collegeName;
/**
* 专业名称
*/
@cn.idev.excel.annotation.ExcelProperty(value = "专业名称")
private String majorName;
/**
* 班主任ID
*/
private Long classTeacherId;
/**
* 班主任姓名
*/
private String classTeacherName;
/**
* 分数
*/
@cn.idev.excel.annotation.ExcelProperty(value = "分数")
private BigDecimal score;
/**
* 备注
*/
@cn.idev.excel.annotation.ExcelProperty(value = "备注")
private String remark;
/**
* 评判结果
*/
@cn.idev.excel.annotation.ExcelProperty(value = "评判结果")
private String evaluationResult;
/**
* 创建时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@cn.idev.excel.annotation.ExcelProperty(value = "录入时间")
private Date createTime;
/**
* 创建者
*/
private Long createBy;
/**
* 创建者姓名
*/
@cn.idev.excel.annotation.ExcelProperty(value = "录入人")
private String createByName;
/**
* 更新者
*/
private Long updateBy;
/**
* 更新时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
}
@@ -0,0 +1,52 @@
package org.dromara.xg.domain.vo;
import cn.idev.excel.annotation.ExcelProperty;
import jakarta.validation.constraints.*;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 学生操行分导入VO xg_conduct_score
*
* @author Lion Li
* @date 2025-01-13
*/
@Data
@cn.idev.excel.annotation.ExcelIgnoreUnannotated
public class XgConductScoreImportVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 学号
*/
@ExcelProperty("学号")
@NotBlank(message = "学号不能为空")
private String studentNo;
/**
* 学生姓名
*/
@ExcelProperty("学生姓名")
@NotBlank(message = "学生姓名不能为空")
private String studentName;
/**
* 分数
*/
@ExcelProperty("分数")
@NotNull(message = "分数不能为空")
@Min(value = 0, message = "分数不能小于0")
@Max(value = 100, message = "分数不能大于100")
private Integer score;
/**
* 备注
*/
@ExcelProperty("备注")
private String remark;
}
@@ -0,0 +1,142 @@
package org.dromara.xg.domain.vo;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import org.dromara.common.excel.annotation.ExcelDictFormat;
/**
* 操行分学期平均分导出视图对象
*
* @author Lion Li
* @date 2025-01-13
*/
@Data
@ExcelIgnoreUnannotated
public class XgConductScoreSemesterAverageExportVo {
private static final long serialVersionUID = 1L;
/**
* 学生ID
*/
private Long studentId;
/**
* 学号
*/
@ExcelProperty(value = "学号")
private String studentNo;
/**
* 学生姓名
*/
@ExcelProperty(value = "学生姓名")
private String studentName;
/**
* 性别
*/
@ExcelProperty(value = "性别", converter = org.dromara.common.excel.convert.ExcelDictConvert.class)
@ExcelDictFormat(readConverterExp = "0=男,1=女")
private String gender;
/**
* 班级ID
*/
private Long classId;
/**
* 学院名称
*/
@ExcelProperty(value = "学院")
private String collegeName;
/**
* 专业名称
*/
@ExcelProperty(value = "专业")
private String majorName;
/**
* 班级名称
*/
@ExcelProperty(value = "班级")
private String className;
/**
* 学年(友好格式,如:2025-2026学年)
*/
@ExcelProperty(value = "学年")
private String academicYear;
/**
* 学期(中文格式,如:第一学期)
*/
@ExcelProperty(value = "学期")
private String semester;
/**
* 学期平均分
*/
@ExcelProperty(value = "学期平均分")
private BigDecimal avgScore;
/**
* 评判结果
*/
@ExcelProperty(value = "评判结果")
private String evaluationResult;
/**
* 录入月份数
*/
@ExcelProperty(value = "录入月份")
private Integer recordCount;
/**
* 最高分
*/
@ExcelProperty(value = "最高分")
private BigDecimal maxScore;
/**
* 最低分
*/
@ExcelProperty(value = "最低分")
private BigDecimal minScore;
/**
* 分数范围
*/
private String scoreRange;
/**
* 最后更新时间
*/
private Date lastUpdateTime;
/**
* 创建者
*/
private Long createBy;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新者
*/
private Long updateBy;
/**
* 更新时间
*/
private Date updateTime;
}
@@ -0,0 +1,200 @@
package org.dromara.xg.listener;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.http.HtmlUtil;
import cn.idev.excel.context.AnalysisContext;
import cn.idev.excel.event.AnalysisEventListener;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.core.utils.ValidatorUtils;
import org.dromara.common.excel.core.ExcelListener;
import org.dromara.common.excel.core.ExcelResult;
import org.dromara.common.excel.core.ExcelResult;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.xg.domain.bo.XgConductScoreBo;
import org.dromara.xg.domain.bo.XgStudentMsgBo;
import org.dromara.xg.domain.vo.XgConductScoreImportVo;
import org.dromara.xg.domain.vo.XgConductScoreVo;
import org.dromara.xg.domain.vo.XgStudentMsgVo;
import org.dromara.xg.service.IXgConductScoreService;
import org.dromara.xg.service.IXgStudentMsgService;
import org.dromara.common.core.utils.StringUtils;
import java.util.List;
/**
* 学生操行分自定义导入
*
* @author Lion Li
* @date 2025-01-13
*/
@Slf4j
public class XgConductScoreImportListener extends AnalysisEventListener<XgConductScoreImportVo>
implements ExcelListener<XgConductScoreImportVo> {
private final IXgConductScoreService conductScoreService;
private final IXgStudentMsgService studentMsgService;
private final Integer academicYear;
private final Integer semester;
private final Integer month;
private final String classId; // 可选,指定班级ID
private final Long operUserId;
private int successNum = 0;
private int failureNum = 0;
private final StringBuilder successMsg = new StringBuilder();
private final StringBuilder failureMsg = new StringBuilder();
public XgConductScoreImportListener(Integer academicYear, Integer semester, Integer month, String classId) {
this.conductScoreService = SpringUtils.getBean(IXgConductScoreService.class);
this.studentMsgService = SpringUtils.getBean(IXgStudentMsgService.class);
this.academicYear = academicYear;
this.semester = semester;
this.month = month;
this.classId = classId;
this.operUserId = LoginHelper.getUserId();
}
@Override
public void invoke(XgConductScoreImportVo importVo, AnalysisContext context) {
try {
// 1. 验证数据完整性
validateImportData(importVo);
// 2. 根据学号查找学生信息
XgStudentMsgBo queryBo = new XgStudentMsgBo();
queryBo.setStudentNo(importVo.getStudentNo());
List<XgStudentMsgVo> students = studentMsgService.queryList(queryBo);
if (students == null || students.isEmpty()) {
failureNum++;
failureMsg.append("<br/>").append(failureNum).append("、学号 ").append(importVo.getStudentNo()).append(" 不存在");
return;
}
XgStudentMsgVo student = students.get(0);
// 3. 验证学生姓名匹配
if (!importVo.getStudentName().equals(student.getName())) {
failureNum++;
failureMsg.append("<br/>").append(failureNum).append("、学号 ").append(importVo.getStudentNo())
.append(" 姓名不匹配,Excel中:").append(importVo.getStudentName())
.append(",系统中:").append(student.getName());
return;
}
// 4. 验证班级权限(如果指定了班级)
if (StringUtils.isNotBlank(classId) && !classId.equals(student.getClassId())) {
failureNum++;
failureMsg.append("<br/>").append(failureNum).append("、学号 ").append(importVo.getStudentNo()).append(" 不属于指定班级");
return;
}
// 5. 检查是否已有记录
XgConductScoreVo existingRecord = conductScoreService.selectByStudentAndTime(
student.getId(), academicYear, semester, month);
if (existingRecord != null) {
// 更新现有记录
XgConductScoreBo updateBo = new XgConductScoreBo();
updateBo.setId(existingRecord.getId());
updateBo.setScore(java.math.BigDecimal.valueOf(importVo.getScore()));
updateBo.setRemark(importVo.getRemark());
updateBo.setUpdateBy(operUserId);
conductScoreService.updateByBo(updateBo);
successNum++;
successMsg.append("<br/>").append(successNum).append("、学号 ").append(importVo.getStudentNo())
.append(" 更新成功(覆盖原有分数 ").append(existingRecord.getScore()).append(" → ").append(importVo.getScore()).append(")");
} else {
// 插入新记录
XgConductScoreBo insertBo = new XgConductScoreBo();
insertBo.setAcademicYear(academicYear);
insertBo.setSemester(semester);
insertBo.setMonth(month);
insertBo.setStudentId(student.getId());
insertBo.setClassId(Long.valueOf(student.getClassId()));
insertBo.setClassTeacherId(student.getClassTeacherId());
insertBo.setScore(java.math.BigDecimal.valueOf(importVo.getScore()));
insertBo.setRemark(importVo.getRemark());
insertBo.setCreateBy(operUserId);
insertBo.setUpdateBy(operUserId);
conductScoreService.insertByBo(insertBo);
successNum++;
successMsg.append("<br/>").append(successNum).append("、学号 ").append(importVo.getStudentNo()).append(" 导入成功");
}
} catch (Exception e) {
handleImportError(importVo, e);
}
}
/**
* 验证导入数据
*/
private void validateImportData(XgConductScoreImportVo importVo) {
if (importVo == null) {
throw new ServiceException("导入数据为空");
}
// 验证必填字段
if (StringUtils.isBlank(importVo.getStudentNo())) {
throw new ServiceException("学号不能为空");
}
if (StringUtils.isBlank(importVo.getStudentName())) {
throw new ServiceException("学生姓名不能为空");
}
if (importVo.getScore() == null) {
throw new ServiceException("分数不能为空");
}
}
/**
* 处理导入错误
*/
private void handleImportError(XgConductScoreImportVo importVo, Exception e) {
failureNum++;
String msg = "<br/>" + failureNum + "、学号 " + HtmlUtil.cleanHtmlTag(importVo.getStudentNo()) + " 导入失败:";
String message = e.getMessage();
if (e instanceof ConstraintViolationException cvException) {
message = StreamUtils.join(cvException.getConstraintViolations(), ConstraintViolation::getMessage,
", ");
}
failureMsg.append(msg).append(message);
log.error(msg, e);
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 处理完成后的逻辑
}
@Override
public ExcelResult<XgConductScoreImportVo> getExcelResult() {
return new ExcelResult<>() {
@Override
public String getAnalysis() {
if (failureNum > 0) {
failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:");
throw new ServiceException(failureMsg.toString());
} else {
successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:");
}
return successMsg.toString();
}
@Override
public List<XgConductScoreImportVo> getList() {
return null;
}
@Override
public List<String> getErrorList() {
return null;
}
};
}
}
@@ -111,6 +111,22 @@ public interface IXgConductScoreMapper extends BaseMapperPlus<XgConductScore, Xg
@org.apache.ibatis.annotations.Param("month") Integer month,
@org.apache.ibatis.annotations.Param("tenantId") String tenantId);
/**
* 根据学生ID、学年、学期、月份查询单个记录
*
* @param studentId 学生ID
* @param academicYear 学年
* @param semester 学期
* @param month 月份
* @param tenantId 租户ID
* @return 记录信息
*/
XgConductScoreVo selectByStudentAndTime(@org.apache.ibatis.annotations.Param("studentId") Long studentId,
@org.apache.ibatis.annotations.Param("academicYear") Integer academicYear,
@org.apache.ibatis.annotations.Param("semester") Integer semester,
@org.apache.ibatis.annotations.Param("month") Integer month,
@org.apache.ibatis.annotations.Param("tenantId") String tenantId);
/**
* 批量插入操行分记录
*
@@ -159,6 +159,17 @@ public interface IXgConductScoreService {
boolean existsByStudentAndMonth(Long studentId, Integer academicYear, Integer semester, Integer month,
String tenantId);
/**
* 根据学生ID、学年、学期、月份查询单个记录
*
* @param studentId 学生ID
* @param academicYear 学年
* @param semester 学期
* @param month 月份
* @return 记录信息
*/
XgConductScoreVo selectByStudentAndTime(Long studentId, Integer academicYear, Integer semester, Integer month);
/**
* 获取班主任负责的班级ID列表
*
@@ -611,6 +611,12 @@ public class XgConductScoreServiceImpl implements IXgConductScoreService {
return baseMapper.existsByStudentAndMonth(studentId, academicYear, semester, month, tenantId);
}
@Override
public XgConductScoreVo selectByStudentAndTime(Long studentId, Integer academicYear, Integer semester, Integer month) {
String tenantId = LoginHelper.getTenantId();
return baseMapper.selectByStudentAndTime(studentId, academicYear, semester, month, tenantId);
}
/**
* 获取班主任负责的班级ID列表
*
@@ -491,6 +491,30 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
AND deleted = 0
</select>
<select id="selectByStudentAndTime" resultMap="XgConductScoreResult">
SELECT
id,
tenant_id,
academic_year,
semester,
month,
student_id,
score,
remark,
create_time,
update_time,
create_by,
update_by
FROM xg_conduct_score
WHERE student_id = #{studentId}
AND academic_year = #{academicYear}
AND semester = #{semester}
AND month = #{month}
AND tenant_id = #{tenantId}
AND deleted = 0
LIMIT 1
</select>
<insert id="insertBatch" parameterType="java.util.List">
INSERT INTO xg_conduct_score (
id, tenant_id, academic_year, semester, month,
-3
View File
@@ -49,6 +49,3 @@ export const listImportItems = (batchId: number, params?: any) => {
params: { batchId, ...params }
});
};
+10 -11
View File
@@ -1,6 +1,6 @@
import request from '@/utils/request'
import type { AxiosPromise } from 'axios'
import type { CwProjectPerformanceSelfEvaluationVO, CwProjectPerformanceSelfEvaluationQuery } from './performanceEvaluation'
import request from '@/utils/request';
import type { AxiosPromise } from 'axios';
import type { CwProjectPerformanceSelfEvaluationVO, CwProjectPerformanceSelfEvaluationQuery } from './performanceEvaluation';
/**
* 运行监控管理API
@@ -11,7 +11,7 @@ import type { CwProjectPerformanceSelfEvaluationVO, CwProjectPerformanceSelfEval
* 运行监控管理查询对象
*/
export interface MonitoringManagementQuery extends CwProjectPerformanceSelfEvaluationQuery {
isLocked?: number
isLocked?: number;
}
/**
@@ -22,7 +22,7 @@ export function listMonitoringManagement(query: MonitoringManagementQuery): Axio
url: '/cw/monitoringManagement/list',
method: 'get',
params: query
})
});
}
/**
@@ -32,7 +32,7 @@ export function getMonitoringManagement(id: number): AxiosPromise<CwProjectPerfo
return request({
url: `/cw/monitoringManagement/${id}`,
method: 'get'
})
});
}
/**
@@ -42,7 +42,7 @@ export function lockEvaluation(id: number): AxiosPromise<any> {
return request({
url: `/cw/monitoringManagement/lock/${id}`,
method: 'post'
})
});
}
/**
@@ -52,7 +52,7 @@ export function unlockEvaluation(id: number): AxiosPromise<any> {
return request({
url: `/cw/monitoringManagement/unlock/${id}`,
method: 'post'
})
});
}
/**
@@ -63,7 +63,7 @@ export function lockBatchEvaluation(ids: number[]): AxiosPromise<any> {
url: '/cw/monitoringManagement/lock/batch',
method: 'post',
data: ids
})
});
}
/**
@@ -74,6 +74,5 @@ export function unlockBatchEvaluation(ids: number[]): AxiosPromise<any> {
url: '/cw/monitoringManagement/unlock/batch',
method: 'post',
data: ids
})
});
}
+321 -322
View File
@@ -1,5 +1,5 @@
import request from '@/utils/request'
import type { AxiosPromise } from 'axios'
import request from '@/utils/request';
import type { AxiosPromise } from 'axios';
// ========== 绩效自评API ==========
@@ -7,300 +7,300 @@ import type { AxiosPromise } from 'axios'
* 项目绩效自评视图对象
*/
export interface CwProjectPerformanceSelfEvaluationVO {
id?: number
tenantId?: string
projectApplicationId: number
applicationNo: string
projectName: string
supervisoryDept?: string
implementUnit?: string
evaluationYear: number
initialBudget?: number
principal?: string
contactPhone?: string
startDate?: string
endDate?: string
totalInvestment?: number
annualBudget?: number
actualExpenditure?: number
budgetExecutionRate?: number
id?: number;
tenantId?: string;
projectApplicationId: number;
applicationNo: string;
projectName: string;
supervisoryDept?: string;
implementUnit?: string;
evaluationYear: number;
initialBudget?: number;
principal?: string;
contactPhone?: string;
startDate?: string;
endDate?: string;
totalInvestment?: number;
annualBudget?: number;
actualExpenditure?: number;
budgetExecutionRate?: number;
// 资金到位(年度资金总额)
fundValue?: number
fundExecutionRate?: number
fundScore?: number
fundValue?: number;
fundExecutionRate?: number;
fundScore?: number;
// 当年财政拨款
currentYearBudgetInitial?: number
currentYearBudgetAnnual?: number
currentYearBudgetActual?: number
currentYearBudgetValue?: number
currentYearBudgetExecutionRate?: number
currentYearBudgetScore?: number
currentYearBudgetInitial?: number;
currentYearBudgetAnnual?: number;
currentYearBudgetActual?: number;
currentYearBudgetValue?: number;
currentYearBudgetExecutionRate?: number;
currentYearBudgetScore?: number;
// 上年结转资金
lastYearCarryoverInitial?: number
lastYearCarryoverAnnual?: number
lastYearCarryoverActual?: number
lastYearCarryoverValue?: number
lastYearCarryoverExecutionRate?: number
lastYearCarryoverScore?: number
lastYearCarryoverInitial?: number;
lastYearCarryoverAnnual?: number;
lastYearCarryoverActual?: number;
lastYearCarryoverValue?: number;
lastYearCarryoverExecutionRate?: number;
lastYearCarryoverScore?: number;
// 其他资金
otherFundsInitial?: number
otherFundsAnnual?: number
otherFundsActual?: number
otherFundsValue?: number
otherFundsExecutionRate?: number
otherFundsScore?: number
otherFundsInitial?: number;
otherFundsAnnual?: number;
otherFundsActual?: number;
otherFundsValue?: number;
otherFundsExecutionRate?: number;
otherFundsScore?: number;
performanceGoal?: string
targetCompletion?: string
performanceGoal?: string;
targetCompletion?: string;
// 数量指标
quantityIndicator?: string
quantityTarget?: string
quantityActual?: string
quantityValue?: number
quantityScore?: number
quantityDeviation?: string
quantityIndicator?: string;
quantityTarget?: string;
quantityActual?: string;
quantityValue?: number;
quantityScore?: number;
quantityDeviation?: string;
// 质量指标
qualityIndicator?: string
qualityTarget?: string
qualityActual?: string
qualityValue?: number
qualityScore?: number
qualityDeviation?: string
qualityIndicator?: string;
qualityTarget?: string;
qualityActual?: string;
qualityValue?: number;
qualityScore?: number;
qualityDeviation?: string;
// 时效指标
timelinessIndicator?: string
timelinessTarget?: string
timelinessActual?: string
timelinessValue?: number
timelinessScore?: number
timelinessDeviation?: string
timelinessIndicator?: string;
timelinessTarget?: string;
timelinessActual?: string;
timelinessValue?: number;
timelinessScore?: number;
timelinessDeviation?: string;
// 成本指标
costIndicator?: string
costTarget?: string
costActual?: string
costValue?: number
costScore?: number
costDeviation?: string
costIndicator?: string;
costTarget?: string;
costActual?: string;
costValue?: number;
costScore?: number;
costDeviation?: string;
// 经济效益指标
economicIndicator?: string
economicTarget?: string
economicActual?: string
economicValue?: number
economicScore?: number
economicDeviation?: string
economicIndicator?: string;
economicTarget?: string;
economicActual?: string;
economicValue?: number;
economicScore?: number;
economicDeviation?: string;
// 社会效益指标
socialIndicator?: string
socialTarget?: string
socialActual?: string
socialValue?: number
socialScore?: number
socialDeviation?: string
socialIndicator?: string;
socialTarget?: string;
socialActual?: string;
socialValue?: number;
socialScore?: number;
socialDeviation?: string;
// 生态效益指标
ecologicalIndicator?: string
ecologicalTarget?: string
ecologicalActual?: string
ecologicalValue?: number
ecologicalScore?: number
ecologicalDeviation?: string
ecologicalIndicator?: string;
ecologicalTarget?: string;
ecologicalActual?: string;
ecologicalValue?: number;
ecologicalScore?: number;
ecologicalDeviation?: string;
// 满意度指标
satisfactionIndicator?: string
satisfactionTarget?: string
satisfactionActual?: string
satisfactionValue?: number
satisfactionScore?: number
satisfactionDeviation?: string
satisfactionIndicator?: string;
satisfactionTarget?: string;
satisfactionActual?: string;
satisfactionValue?: number;
satisfactionScore?: number;
satisfactionDeviation?: string;
decisionScore?: number
decisionContent?: string
processScore?: number
processContent?: string
outputScore?: number
outputContent?: string
benefitScore?: number
benefitContent?: string
totalScore?: number
selfEvaluationLevel?: string
existingProblems?: string
improvementMeasures?: string
evaluationStatus: string
allowEditAfterSubmit?: number
submitTime?: string
submitBy?: number
submitByName?: string
approveTime?: string
approveBy?: number
approveByName?: string
approveOpinion?: string
remark?: string
decisionScore?: number;
decisionContent?: string;
processScore?: number;
processContent?: string;
outputScore?: number;
outputContent?: string;
benefitScore?: number;
benefitContent?: string;
totalScore?: number;
selfEvaluationLevel?: string;
existingProblems?: string;
improvementMeasures?: string;
evaluationStatus: string;
allowEditAfterSubmit?: number;
submitTime?: string;
submitBy?: number;
submitByName?: string;
approveTime?: string;
approveBy?: number;
approveByName?: string;
approveOpinion?: string;
remark?: string;
// 锁定相关字段
isLocked?: number
lockTime?: string
lockBy?: number
lockByName?: string
createTime?: string
updateTime?: string
isLocked?: number;
lockTime?: string;
lockBy?: number;
lockByName?: string;
createTime?: string;
updateTime?: string;
// 前端使用的字段,用于标记是否有运行监控数据
hasMonitoringData?: boolean
hasMonitoringData?: boolean;
// 运行监控数据列表
monitoringList?: CwPerformanceMonitoringVO[]
monitoringList?: CwPerformanceMonitoringVO[];
}
/**
* 项目绩效自评表单对象
*/
export interface CwProjectPerformanceSelfEvaluationForm {
id?: number
projectApplicationId: number
applicationNo?: string
projectName: string
supervisoryDept?: string
implementUnit?: string
evaluationYear: number
initialBudget?: number
principal?: string
contactPhone?: string
startDate?: string
endDate?: string
totalInvestment?: number
annualBudget?: number
actualExpenditure?: number
budgetExecutionRate?: number
id?: number;
projectApplicationId: number;
applicationNo?: string;
projectName: string;
supervisoryDept?: string;
implementUnit?: string;
evaluationYear: number;
initialBudget?: number;
principal?: string;
contactPhone?: string;
startDate?: string;
endDate?: string;
totalInvestment?: number;
annualBudget?: number;
actualExpenditure?: number;
budgetExecutionRate?: number;
// 资金到位(年度资金总额)
fundValue?: number
fundExecutionRate?: number
fundScore?: number
fundValue?: number;
fundExecutionRate?: number;
fundScore?: number;
// 当年财政拨款
currentYearBudgetInitial?: number
currentYearBudgetAnnual?: number
currentYearBudgetActual?: number
currentYearBudgetValue?: number
currentYearBudgetExecutionRate?: number
currentYearBudgetScore?: number
currentYearBudgetInitial?: number;
currentYearBudgetAnnual?: number;
currentYearBudgetActual?: number;
currentYearBudgetValue?: number;
currentYearBudgetExecutionRate?: number;
currentYearBudgetScore?: number;
// 上年结转资金
lastYearCarryoverInitial?: number
lastYearCarryoverAnnual?: number
lastYearCarryoverActual?: number
lastYearCarryoverValue?: number
lastYearCarryoverExecutionRate?: number
lastYearCarryoverScore?: number
lastYearCarryoverInitial?: number;
lastYearCarryoverAnnual?: number;
lastYearCarryoverActual?: number;
lastYearCarryoverValue?: number;
lastYearCarryoverExecutionRate?: number;
lastYearCarryoverScore?: number;
// 其他资金
otherFundsInitial?: number
otherFundsAnnual?: number
otherFundsActual?: number
otherFundsValue?: number
otherFundsExecutionRate?: number
otherFundsScore?: number
otherFundsInitial?: number;
otherFundsAnnual?: number;
otherFundsActual?: number;
otherFundsValue?: number;
otherFundsExecutionRate?: number;
otherFundsScore?: number;
performanceGoal?: string
targetCompletion?: string
performanceGoal?: string;
targetCompletion?: string;
// 数量指标
quantityIndicator?: string
quantityTarget?: string
quantityActual?: string
quantityValue?: number
quantityScore?: number
quantityDeviation?: string
quantityIndicator?: string;
quantityTarget?: string;
quantityActual?: string;
quantityValue?: number;
quantityScore?: number;
quantityDeviation?: string;
// 质量指标
qualityIndicator?: string
qualityTarget?: string
qualityActual?: string
qualityValue?: number
qualityScore?: number
qualityDeviation?: string
qualityIndicator?: string;
qualityTarget?: string;
qualityActual?: string;
qualityValue?: number;
qualityScore?: number;
qualityDeviation?: string;
// 时效指标
timelinessIndicator?: string
timelinessTarget?: string
timelinessActual?: string
timelinessValue?: number
timelinessScore?: number
timelinessDeviation?: string
timelinessIndicator?: string;
timelinessTarget?: string;
timelinessActual?: string;
timelinessValue?: number;
timelinessScore?: number;
timelinessDeviation?: string;
// 成本指标
costIndicator?: string
costTarget?: string
costActual?: string
costValue?: number
costScore?: number
costDeviation?: string
costIndicator?: string;
costTarget?: string;
costActual?: string;
costValue?: number;
costScore?: number;
costDeviation?: string;
// 经济效益指标
economicIndicator?: string
economicTarget?: string
economicActual?: string
economicValue?: number
economicScore?: number
economicDeviation?: string
economicIndicator?: string;
economicTarget?: string;
economicActual?: string;
economicValue?: number;
economicScore?: number;
economicDeviation?: string;
// 社会效益指标
socialIndicator?: string
socialTarget?: string
socialActual?: string
socialValue?: number
socialScore?: number
socialDeviation?: string
socialIndicator?: string;
socialTarget?: string;
socialActual?: string;
socialValue?: number;
socialScore?: number;
socialDeviation?: string;
// 生态效益指标
ecologicalIndicator?: string
ecologicalTarget?: string
ecologicalActual?: string
ecologicalValue?: number
ecologicalScore?: number
ecologicalDeviation?: string
ecologicalIndicator?: string;
ecologicalTarget?: string;
ecologicalActual?: string;
ecologicalValue?: number;
ecologicalScore?: number;
ecologicalDeviation?: string;
// 满意度指标
satisfactionIndicator?: string
satisfactionTarget?: string
satisfactionActual?: string
satisfactionValue?: number
satisfactionScore?: number
satisfactionDeviation?: string
satisfactionIndicator?: string;
satisfactionTarget?: string;
satisfactionActual?: string;
satisfactionValue?: number;
satisfactionScore?: number;
satisfactionDeviation?: string;
decisionScore?: number
decisionContent?: string
processScore?: number
processContent?: string
outputScore?: number
outputContent?: string
benefitScore?: number
benefitContent?: string
totalScore?: number
selfEvaluationLevel?: string
existingProblems?: string
improvementMeasures?: string
evaluationStatus?: string
remark?: string
decisionScore?: number;
decisionContent?: string;
processScore?: number;
processContent?: string;
outputScore?: number;
outputContent?: string;
benefitScore?: number;
benefitContent?: string;
totalScore?: number;
selfEvaluationLevel?: string;
existingProblems?: string;
improvementMeasures?: string;
evaluationStatus?: string;
remark?: string;
}
/**
* 项目绩效自评查询对象
*/
export interface CwProjectPerformanceSelfEvaluationQuery {
pageNum?: number
pageSize?: number
projectApplicationId?: number
applicationNo?: string
projectName?: string
evaluationYear?: number
evaluationStatus?: string
selfEvaluationLevel?: string
pageNum?: number;
pageSize?: number;
projectApplicationId?: number;
applicationNo?: string;
projectName?: string;
evaluationYear?: number;
evaluationStatus?: string;
selfEvaluationLevel?: string;
}
/**
@@ -311,7 +311,7 @@ export function listEvaluation(query: CwProjectPerformanceSelfEvaluationQuery):
url: '/cw/evaluation/list',
method: 'get',
params: query
})
});
}
/**
@@ -321,7 +321,7 @@ export function getEvaluation(id: number): AxiosPromise<CwProjectPerformanceSelf
return request({
url: `/cw/evaluation/${id}`,
method: 'get'
})
});
}
/**
@@ -332,7 +332,7 @@ export function addEvaluation(data: CwProjectPerformanceSelfEvaluationForm): Axi
url: '/cw/evaluation',
method: 'post',
data
})
});
}
/**
@@ -343,7 +343,7 @@ export function updateEvaluation(data: CwProjectPerformanceSelfEvaluationForm):
url: '/cw/evaluation',
method: 'put',
data
})
});
}
/**
@@ -354,7 +354,7 @@ export function saveDraft(data: CwProjectPerformanceSelfEvaluationForm): AxiosPr
url: '/cw/evaluation/saveDraft',
method: 'post',
data
})
});
}
/**
@@ -364,7 +364,7 @@ export function submitEvaluation(id: number): AxiosPromise<any> {
return request({
url: `/cw/evaluation/submit/${id}`,
method: 'post'
})
});
}
/**
@@ -375,7 +375,7 @@ export function approveEvaluation(id: number, opinion: string, approved: boolean
url: `/cw/evaluation/approve/${id}`,
method: 'post',
params: { opinion, approved }
})
});
}
/**
@@ -385,7 +385,7 @@ export function delEvaluation(ids: number | number[]): AxiosPromise<any> {
return request({
url: `/cw/evaluation/${ids}`,
method: 'delete'
})
});
}
/**
@@ -395,7 +395,7 @@ export function getMyEvaluations(): AxiosPromise<CwProjectPerformanceSelfEvaluat
return request({
url: '/cw/evaluation/my',
method: 'get'
})
});
}
/**
@@ -405,7 +405,7 @@ export function getPendingApprovals(): AxiosPromise<CwProjectPerformanceSelfEval
return request({
url: '/cw/evaluation/pending',
method: 'get'
})
});
}
/**
@@ -417,7 +417,7 @@ export function exportEvaluation(query: CwProjectPerformanceSelfEvaluationQuery)
method: 'post',
data: query,
responseType: 'blob'
})
});
}
// ========== 月度运行监控API ==========
@@ -426,59 +426,59 @@ export function exportEvaluation(query: CwProjectPerformanceSelfEvaluationQuery)
* 项目月度运行监控视图对象
*/
export interface CwProjectMonthlyMonitoringVO {
id?: number
evaluationId: number
projectApplicationId: number
monitoringYear: number
monitoringMonth: number
plannedProgress?: number
actualProgress?: number
progressVariance?: number
monthlyBudget?: number
monthlyExpenditure?: number
cumulativeBudget?: number
cumulativeExpenditure?: number
fundExecutionRate?: number
keyMilestones?: string
milestoneCompletion?: string
problemsEncountered?: string
riskDescription?: string
countermeasures?: string
nextMonthPlan?: string
fillStatus: string
fillTime?: string
fillBy?: number
fillByName?: string
remark?: string
createTime?: string
updateTime?: string
id?: number;
evaluationId: number;
projectApplicationId: number;
monitoringYear: number;
monitoringMonth: number;
plannedProgress?: number;
actualProgress?: number;
progressVariance?: number;
monthlyBudget?: number;
monthlyExpenditure?: number;
cumulativeBudget?: number;
cumulativeExpenditure?: number;
fundExecutionRate?: number;
keyMilestones?: string;
milestoneCompletion?: string;
problemsEncountered?: string;
riskDescription?: string;
countermeasures?: string;
nextMonthPlan?: string;
fillStatus: string;
fillTime?: string;
fillBy?: number;
fillByName?: string;
remark?: string;
createTime?: string;
updateTime?: string;
}
/**
* 项目月度运行监控表单对象
*/
export interface CwProjectMonthlyMonitoringForm {
id?: number
evaluationId: number
projectApplicationId: number
monitoringYear: number
monitoringMonth: number
plannedProgress?: number
actualProgress?: number
progressVariance?: number
monthlyBudget?: number
monthlyExpenditure?: number
cumulativeBudget?: number
cumulativeExpenditure?: number
fundExecutionRate?: number
keyMilestones?: string
milestoneCompletion?: string
problemsEncountered?: string
riskDescription?: string
countermeasures?: string
nextMonthPlan?: string
fillStatus?: string
remark?: string
id?: number;
evaluationId: number;
projectApplicationId: number;
monitoringYear: number;
monitoringMonth: number;
plannedProgress?: number;
actualProgress?: number;
progressVariance?: number;
monthlyBudget?: number;
monthlyExpenditure?: number;
cumulativeBudget?: number;
cumulativeExpenditure?: number;
fundExecutionRate?: number;
keyMilestones?: string;
milestoneCompletion?: string;
problemsEncountered?: string;
riskDescription?: string;
countermeasures?: string;
nextMonthPlan?: string;
fillStatus?: string;
remark?: string;
}
/**
@@ -488,7 +488,7 @@ export function getYearlyMonitoring(evaluationId: number, year: number): AxiosPr
return request({
url: `/cw/monitoring/year/${evaluationId}/${year}`,
method: 'get'
})
});
}
/**
@@ -498,7 +498,7 @@ export function getMonitoringByEvaluationId(evaluationId: number): AxiosPromise<
return request({
url: `/cw/monitoring/evaluation/${evaluationId}`,
method: 'get'
})
});
}
/**
@@ -509,7 +509,7 @@ export function fillMonthlyData(data: CwProjectMonthlyMonitoringForm): AxiosProm
url: '/cw/monitoring/fill',
method: 'post',
data
})
});
}
/**
@@ -523,7 +523,7 @@ export function batchSaveMonitoring(evaluationId: number, list: CwProjectMonthly
evaluationId,
list
}
})
});
}
/**
@@ -534,7 +534,7 @@ export function updateMonitoring(data: CwProjectMonthlyMonitoringForm): AxiosPro
url: '/cw/monitoring',
method: 'put',
data
})
});
}
// ========== 绩效运行监控API ==========
@@ -543,36 +543,36 @@ export function updateMonitoring(data: CwProjectMonthlyMonitoringForm): AxiosPro
* 绩效运行监控视图对象
*/
export interface CwPerformanceMonitoringVO {
id?: number
evaluationId: number
monitoringPeriod: string
fundUsage: string
performanceTargetCompletion: string
indicators?: string // JSON字符串,存储指标数组
existingProblems?: string
indicatorVarianceAnalysis?: string
nextStepSuggestions?: string
fillBy?: number
fillByName?: string
createTime?: string
updateTime?: string
id?: number;
evaluationId: number;
monitoringPeriod: string;
fundUsage: string;
performanceTargetCompletion: string;
indicators?: string; // JSON字符串,存储指标数组
existingProblems?: string;
indicatorVarianceAnalysis?: string;
nextStepSuggestions?: string;
fillBy?: number;
fillByName?: string;
createTime?: string;
updateTime?: string;
}
/**
* 绩效运行监控表单数据结构
*/
export interface PerformanceMonitoringForm {
id?: number
evaluationId: number
monitoringPeriod: string
fundUsage: string
performanceTargetCompletion: string
indicators: string // JSON字符串,存储指标数组
existingProblems?: string
indicatorVarianceAnalysis?: string
nextStepSuggestions?: string
fillByName?: string
createTime?: string
id?: number;
evaluationId: number;
monitoringPeriod: string;
fundUsage: string;
performanceTargetCompletion: string;
indicators: string; // JSON字符串,存储指标数组
existingProblems?: string;
indicatorVarianceAnalysis?: string;
nextStepSuggestions?: string;
fillByName?: string;
createTime?: string;
}
/**
@@ -582,7 +582,7 @@ export function listPerformanceMonitoring(evaluationId: number): AxiosPromise<Pe
return request({
url: `/cw/performanceMonitoring/list/${evaluationId}`,
method: 'get'
})
});
}
/**
@@ -592,7 +592,7 @@ export function getPerformanceMonitoring(id: number): AxiosPromise<PerformanceMo
return request({
url: `/cw/performanceMonitoring/${id}`,
method: 'get'
})
});
}
/**
@@ -603,7 +603,7 @@ export function addPerformanceMonitoring(data: PerformanceMonitoringForm): Axios
url: '/cw/performanceMonitoring',
method: 'post',
data
})
});
}
/**
@@ -614,7 +614,7 @@ export function updatePerformanceMonitoring(data: PerformanceMonitoringForm): Ax
url: '/cw/performanceMonitoring',
method: 'put',
data
})
});
}
/**
@@ -624,6 +624,5 @@ export function delPerformanceMonitoring(id: number): AxiosPromise<any> {
return request({
url: `/cw/performanceMonitoring/${id}`,
method: 'delete'
})
});
}
+1 -1
View File
@@ -73,7 +73,7 @@ export interface BatchAddDetailRequest {
nickName: string;
deptId: string | number;
deptName: string;
phoneNumber: string; // 注意:这里使用驼峰命名,确保与数据库字段映射
phoneNumber: string; // 注意:这里使用驼峰命名,确保与数据库字段映射
email: string;
}>;
remark?: string;
+11 -11
View File
@@ -93,11 +93,11 @@ export interface OverseasApplicationVO {
administrativeLevelText?: string;
personnelCategoryText?: string;
// 工作流相关字段
approvalStatus?: string; // 工作流审批状态:draft/waiting/finish/back/cancel
approvalStatusText?: string; // 工作流审批状态文本
taskId?: string; // 当前待办任务ID(用于审批跳转)
approvalStatus?: string; // 工作流审批状态:draft/waiting/finish/back/cancel
approvalStatusText?: string; // 工作流审批状态文本
taskId?: string; // 当前待办任务ID(用于审批跳转)
// 附件相关字段
attachmentIds?: string; // 附件OSS ID列表(逗号分隔)
attachmentIds?: string; // 附件OSS ID列表(逗号分隔)
}
// 出国申请表单类型
@@ -152,9 +152,9 @@ export interface OverseasApplicationForm {
transportationPlan?: string;
applicantRemark?: string;
// 工作流相关字段
approvalStatus?: string; // 工作流审批状态
approvalStatus?: string; // 工作流审批状态
// 附件相关字段
attachmentIds?: string; // 附件OSS ID列表(逗号分隔)
attachmentIds?: string; // 附件OSS ID列表(逗号分隔)
}
// 审批请求类型(支持标准工作流模式)
@@ -353,13 +353,13 @@ export function listPendingOverseasApplicationPage(query: OverseasApplicationQue
// ========================================
// 🚨 标准化改造:删除自定义待审批查询API
//
//
// 原因:出国申请工作流应使用标准工作流模式
// 用户应通过工作流API pageByTaskWait() 获取待办任务
// 而不是调用自定义的业务API
//
//
// 删除的API:listPendingOverseasApplication()
//
//
// 标准工作流模式:
// 1. 导入工作流API:import { pageByTaskWait } from '@/api/workflow/task'
// 2. 调用标准API:pageByTaskWait(queryParams)
@@ -865,9 +865,9 @@ export interface ItineraryChangeVO {
// 行程变更表单类型
export interface ItineraryChangeForm {
changeId?: number | string;
recordId: number; // 关联出国记录ID(必填)
recordId: number; // 关联出国记录ID(必填)
applicationId: number;
applicantId: number; // 申请人ID(必填)
applicantId: number; // 申请人ID(必填)
applicantName?: string;
gender?: string;
birthDate?: string;
+36 -37
View File
@@ -1,7 +1,7 @@
import request from '@/utils/request'
import request from '@/utils/request';
// API基础路径
const baseUrl = '/rs/overseas/monitor'
const baseUrl = '/rs/overseas/monitor';
/**
* 查询审批中的申请列表
@@ -9,11 +9,11 @@ const baseUrl = '/rs/overseas/monitor'
* @returns {Promise}
*/
export function getPendingList(query) {
return request({
url: `${baseUrl}/pending/list`,
method: 'get',
params: query
})
return request({
url: `${baseUrl}/pending/list`,
method: 'get',
params: query
});
}
/**
@@ -22,11 +22,11 @@ export function getPendingList(query) {
* @returns {Promise}
*/
export function getCompletedList(query) {
return request({
url: `${baseUrl}/completed/list`,
method: 'get',
params: query
})
return request({
url: `${baseUrl}/completed/list`,
method: 'get',
params: query
});
}
/**
@@ -35,10 +35,10 @@ export function getCompletedList(query) {
* @returns {Promise}
*/
export function getApprovalHistory(applicationId) {
return request({
url: `${baseUrl}/history/${applicationId}`,
method: 'get'
})
return request({
url: `${baseUrl}/history/${applicationId}`,
method: 'get'
});
}
/**
@@ -47,10 +47,10 @@ export function getApprovalHistory(applicationId) {
* @returns {Promise}
*/
export function getDetail(applicationId) {
return request({
url: `${baseUrl}/detail/${applicationId}`,
method: 'get'
})
return request({
url: `${baseUrl}/detail/${applicationId}`,
method: 'get'
});
}
/**
@@ -59,12 +59,12 @@ export function getDetail(applicationId) {
* @returns {Promise}
*/
export function exportPending(query) {
return request({
url: `${baseUrl}/pending/export`,
method: 'post',
data: query,
responseType: 'blob'
})
return request({
url: `${baseUrl}/pending/export`,
method: 'post',
data: query,
responseType: 'blob'
});
}
/**
@@ -73,12 +73,12 @@ export function exportPending(query) {
* @returns {Promise}
*/
export function exportCompleted(query) {
return request({
url: `${baseUrl}/completed/export`,
method: 'post',
data: query,
responseType: 'blob'
})
return request({
url: `${baseUrl}/completed/export`,
method: 'post',
data: query,
responseType: 'blob'
});
}
/**
@@ -86,9 +86,8 @@ export function exportCompleted(query) {
* @returns {Promise}
*/
export function getStatistics() {
return request({
url: `${baseUrl}/statistics`,
method: 'get'
})
return request({
url: `${baseUrl}/statistics`,
method: 'get'
});
}
+131
View File
@@ -0,0 +1,131 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { StaffInfoQuery, StaffInfoVO, StaffInfoForm, DeptTreeVO, R, TableDataInfo } from './types';
/**
* 查询人员信息列表
* @param query 查询参数
*/
export function listStaffInfo(query?: StaffInfoQuery): AxiosPromise<R<TableDataInfo<StaffInfoVO[]>>> {
return request({
url: '/staff/staffInfo/list',
method: 'get',
params: query
});
}
/**
* 根据部门ID查询人员信息列表
* @param query 查询参数
*/
export function listStaffInfoByDeptId(query?: any): AxiosPromise<R<TableDataInfo<StaffInfoVO[]>>> {
return request({
url: '/staff/staffInfo/listByDeptId',
method: 'post',
data: query
});
}
/**
* 获取人员信息详细信息
* @param id 人员ID
*/
export function getStaffInfo(id: number | string): AxiosPromise<R<StaffInfoVO>> {
return request({
url: '/staff/staffInfo/' + id,
method: 'get'
});
}
/**
* 新增人员信息
* @param data 人员信息
*/
export function addStaffInfo(data: StaffInfoForm): AxiosPromise<R<void>> {
return request({
url: '/staff/staffInfo',
method: 'post',
data: data
});
}
/**
* 修改人员信息
* @param data 人员信息
*/
export function updateStaffInfo(data: StaffInfoForm): AxiosPromise<R<void>> {
return request({
url: '/staff/staffInfo',
method: 'put',
data: data
});
}
/**
* 删除人员信息
* @param ids 人员ID数组
*/
export function delStaffInfo(ids: (number | string)[]): AxiosPromise<R<void>> {
return request({
url: '/staff/staffInfo/' + ids.join(','),
method: 'delete'
});
}
/**
* 获取部门树形数据
*/
export function getDeptTree(): AxiosPromise<R<DeptTreeVO[]>> {
return request({
url: '/staff/staffInfo/deptTree',
method: 'get'
});
}
/**
* 导出人员信息
* @param query 查询参数
*/
export function exportStaffInfo(query?: StaffInfoQuery): AxiosPromise<any> {
return request({
url: '/staff/staffInfo/export',
method: 'post',
params: query,
responseType: 'blob'
});
}
/**
* 下载人员信息导入模板
*/
export function downloadImportTemplate(): AxiosPromise<any> {
return request({
url: '/staff/staffInfo/importTemplate',
method: 'post',
responseType: 'blob'
});
}
/**
* 导入人员数据
* @param file 上传的文件
* @param deptId 部门ID
* @param updateSupport 是否支持更新
*/
export function importStaffInfo(file: File, deptId?: string, updateSupport: boolean = false): AxiosPromise<R<string>> {
const formData = new FormData();
formData.append('file', file);
if (deptId) {
formData.append('deptId', deptId);
}
formData.append('updateSupport', updateSupport.toString());
return request({
url: '/staff/staffInfo/importData',
method: 'post',
data: formData,
headers: {
'Content-Type': 'multipart/form-data'
}
});
}
+92
View File
@@ -0,0 +1,92 @@
/**
* 人员信息查询参数
*/
export interface StaffInfoQuery {
pageNum: number;
pageSize: number;
name?: string;
staffNo?: string;
phone?: string;
staffType?: string;
status?: string;
deptId?: number;
}
/**
* 人员信息视图对象
*/
export interface StaffInfoVO {
id: number | string;
staffNo: string;
name: string;
gender: string;
position: string;
deptId: number;
deptName: string;
phone: string;
staffType: string;
userId?: number;
status: string;
createTime?: string;
updateTime?: string;
remark?: string;
}
/**
* 人员信息表单对象
*/
export interface StaffInfoForm {
id?: number | string;
staffNo?: string;
name: string;
gender: string;
position: string;
deptId: number;
deptName?: string;
phone: string;
staffType: string;
status?: string;
remark?: string;
}
/**
* 人员信息导入VO
*/
export interface StaffInfoImportVo {
staffNo?: string;
name: string;
gender: string;
position?: string;
deptName: string;
phone: string;
staffType: string;
}
/**
* 部门树结构
*/
export interface DeptTreeVO {
deptId: number;
parentId: number;
deptName: string;
children?: DeptTreeVO[];
}
/**
* 通用响应对象
*/
export interface R<T = any> {
code: number;
msg: string;
data: T;
}
/**
* 分页数据信息
*/
export interface TableDataInfo<T = any> {
total: number;
rows: T[];
code: number;
msg: string;
}
+29 -29
View File
@@ -70,15 +70,15 @@ export interface ApplicationVO {
userName: string;
department: string;
position?: string;
currentTitle?: string; // 现职称
currentPosition?: string; // 现岗位
workStartTime?: string; // 参加工作时间
phone?: string; // 联系电话
organizerUnit?: string; // 主办单位
trainingType?: string; // 培训类型
trainingFee?: number; // 培训费用
companions?: string; // 同行人员(JSON字符串)
trainingPurpose?: string; // 培训目的
currentTitle?: string; // 现职称
currentPosition?: string; // 现岗位
workStartTime?: string; // 参加工作时间
phone?: string; // 联系电话
organizerUnit?: string; // 主办单位
trainingType?: string; // 培训类型
trainingFee?: number; // 培训费用
companions?: string; // 同行人员(JSON字符串)
trainingPurpose?: string; // 培训目的
reason?: string;
status: string;
applyTime: string;
@@ -87,12 +87,12 @@ export interface ApplicationVO {
duration?: number;
startTime?: string;
endTime?: string;
rejectTime?: string; // 拒绝时间
attachmentIds?: string; // 附件ID列表
rejectTime?: string; // 拒绝时间
attachmentIds?: string; // 附件ID列表
createTime: string;
updateTime: string;
createBy: number; // 修正类型:string -> number
updateBy: number; // 修正类型:string -> number
createBy: number; // 修正类型:string -> number
updateBy: number; // 修正类型:string -> number
remark: string;
}
@@ -105,23 +105,23 @@ export interface ApplicationForm {
userName?: string;
department?: string;
position?: string;
currentTitle?: string; // 现职称
currentPosition?: string; // 现岗位
workStartTime?: string; // 参加工作时间
phone?: string; // 联系电话
organizerUnit?: string; // 主办单位
trainingType?: string; // 培训类型
trainingTypeOther?: string; // 其他培训类型描述
trainingFee?: number; // 培训费用
currentTitle?: string; // 现职称
currentPosition?: string; // 现岗位
workStartTime?: string; // 参加工作时间
phone?: string; // 联系电话
organizerUnit?: string; // 主办单位
trainingType?: string; // 培训类型
trainingTypeOther?: string; // 其他培训类型描述
trainingFee?: number; // 培训费用
companions?: (string | number)[]; // 同行人员数组
trainingPurpose?: string; // 培训目的
trainingPurpose?: string; // 培训目的
reason?: string;
status?: string;
location?: string;
duration?: number;
startTime?: string;
endTime?: string;
attachmentIds?: string[]; // 附件ID数组
attachmentIds?: string[]; // 附件ID数组
remark?: string;
}
@@ -164,11 +164,11 @@ export interface RecordVO {
updateBy: string;
remark: string;
// 关联字段 - 从申请表获取
organizerUnit?: string; // 主办单位
trainingType?: string; // 培训类型
companions?: string; // 同行人员
startTime?: string; // 开始时间
endTime?: string; // 结束时间
organizerUnit?: string; // 主办单位
trainingType?: string; // 培训类型
companions?: string; // 同行人员
startTime?: string; // 开始时间
endTime?: string; // 结束时间
}
// 培训记录表单类型
@@ -440,7 +440,7 @@ export function updateRecordAttachments(recordId: string | number, data: { attac
// 审批培训申请(7级审批制度)
export function approveTrainingApplication(data: {
applicationId: number | string;
approvalStatus: string; // 1-通过,2-拒绝
approvalStatus: string; // 1-通过,2-拒绝
approvalOpinion?: string;
approvalSignature?: string; // base64格式
}) {
-1
View File
@@ -128,4 +128,3 @@ export const delActivityAudit = (id: string | number | Array<string | number>) =
method: 'delete'
});
};
+25 -25
View File
@@ -1,6 +1,6 @@
import request from '@/utils/request'
import { AxiosPromise } from 'axios'
import { StglAuditorVO, StglAuditorForm, StglAuditorQuery } from './types'
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { StglAuditorVO, StglAuditorForm, StglAuditorQuery } from './types';
/**
* 查询社团管理审核人配置列表
@@ -13,8 +13,8 @@ export const listStglAuditor = (query?: StglAuditorQuery): AxiosPromise<StglAudi
url: '/stgl/auditor/list',
method: 'get',
params: query
})
}
});
};
/**
* 查询社团管理审核人配置详细
@@ -24,8 +24,8 @@ export const getStglAuditor = (id: string | number): AxiosPromise<StglAuditorVO>
return request({
url: '/stgl/auditor/' + id,
method: 'get'
})
}
});
};
/**
* 新增社团管理审核人配置
@@ -36,8 +36,8 @@ export const addStglAuditor = (data: StglAuditorForm) => {
url: '/stgl/auditor',
method: 'post',
data: data
})
}
});
};
/**
* 修改社团管理审核人配置
@@ -48,8 +48,8 @@ export const updateStglAuditor = (data: StglAuditorForm) => {
url: '/stgl/auditor',
method: 'put',
data: data
})
}
});
};
/**
* 删除社团管理审核人配置
@@ -59,8 +59,8 @@ export const delStglAuditor = (id: string | number | Array<string | number>) =>
return request({
url: '/stgl/auditor/' + id,
method: 'delete'
})
}
});
};
/**
* 导出社团管理审核人配置
@@ -73,8 +73,8 @@ export const exportStglAuditor = (query?: StglAuditorQuery) => {
method: 'post',
data: query,
responseType: 'blob'
})
}
});
};
/**
* 批量添加审核人
@@ -89,8 +89,8 @@ export const batchAddAuditors = (userIds: number[], remark?: string) => {
userIds,
remark
}
})
}
});
};
/**
* 查询我配置的审核人列表
@@ -101,8 +101,8 @@ export const listMyConfigAuditors = (query?: StglAuditorQuery): AxiosPromise<Stg
url: '/stgl/auditor/myConfig',
method: 'get',
params: query
})
}
});
};
/**
* 批量启用审核人
@@ -113,8 +113,8 @@ export const enableAuditors = (ids: number[]) => {
url: '/stgl/auditor/enable',
method: 'post',
data: ids
})
}
});
};
/**
* 批量停用审核人
@@ -125,8 +125,8 @@ export const disableAuditors = (ids: number[]) => {
url: '/stgl/auditor/disable',
method: 'post',
data: ids
})
}
});
};
/**
* 根据部门ID获取用户列表
@@ -136,5 +136,5 @@ export const listUserByDeptId = (deptId: string | number) => {
return request({
url: '/stgl/auditor/users/' + deptId,
method: 'get'
})
}
});
};
+7 -8
View File
@@ -1,4 +1,4 @@
import { PageQuery } from '@/api/types'
import { PageQuery } from '@/api/types';
export interface StglAuditorVO {
/**
@@ -145,7 +145,6 @@ export interface StglAuditorForm {
}
export interface StglAuditorQuery extends PageQuery {
/**
* 用户ID
*/
@@ -176,9 +175,9 @@ export interface StglAuditorQuery extends PageQuery {
*/
configBy?: number;
/**
* 日期范围参数
*/
'params[beginCreateTime]'?: string;
'params[endCreateTime]'?: string;
}
/**
* 日期范围参数
*/
'params[beginCreateTime]'?: string;
'params[endCreateTime]'?: string;
}
+5 -5
View File
@@ -176,10 +176,10 @@ export interface StglClubAdvisorHistoryQuery extends PageQuery {
*/
endTime?: string;
/**
* 日期范围参数
*/
params?: any;
/**
* 日期范围参数
*/
params?: any;
}
/**
@@ -273,4 +273,4 @@ export const delStglClubAdvisorHistory = (id: string | number | Array<string | n
url: '/stgl/club/advisor/history/' + id,
method: 'delete'
});
};
};
+3 -8
View File
@@ -124,12 +124,7 @@ export const withdrawStglClubAnnualReview = (id: string | number) => {
* 审核年审记录
* @param params
*/
export const auditStglClubAnnualReview = (params: {
id: string | number;
auditResult: string;
auditOpinion?: string;
auditType: string;
}) => {
export const auditStglClubAnnualReview = (params: { id: string | number; auditResult: string; auditOpinion?: string; auditType: string }) => {
return request({
url: '/stgl/club/annualReview/audit',
method: 'post',
@@ -205,7 +200,7 @@ export const getClubActivitiesByReviewYear = (clubId: string | number, reviewYea
isEncrypt: 'true'
}
});
};
};
/**
* 获取年审记录的活动数据(从stgl_club_annual_review_activity表中获取)
@@ -219,4 +214,4 @@ export const getAnnualReviewActivities = (annualReviewId: string | number) => {
isEncrypt: 'true'
}
});
};
};
+1 -1
View File
@@ -320,4 +320,4 @@ export const ANNUAL_REVIEW_STATUS_OPTIONS = [
export const YES_NO_OPTIONS = [
{ label: '是', value: 'Y' },
{ label: '否', value: 'N' }
];
];
@@ -91,10 +91,10 @@ export const delStglAnnualReviewAudit = (id: string | number | Array<string | nu
* @param nextAuditors 下一步审核人列表(可选)
*/
export const approveAnnualReview = (
auditId: string | number,
approveResult: string,
approveOpinion: string,
nextAuditors?: Array<{userId: string | number; userName: string; nickName: string}>
auditId: string | number,
approveResult: string,
approveOpinion: string,
nextAuditors?: Array<{ userId: string | number; userName: string; nickName: string }>
) => {
return request({
url: '/stgl/annualReviewAudit/approve',
@@ -106,4 +106,4 @@ export const approveAnnualReview = (
nextAuditors: nextAuditors || []
}
});
};
};
@@ -179,4 +179,4 @@ export const AUDIT_NODE_STATUS_OPTIONS = [
export const APPROVE_RESULT_OPTIONS = [
{ label: '通过', value: '通过' },
{ label: '不通过', value: '不通过' }
];
];
+19 -19
View File
@@ -1,22 +1,22 @@
import request from '@/utils/request'
import request from '@/utils/request';
// 社团活动权限检查结果类型定义
export interface ClubActivityPermissionResult {
canCreateActivity: boolean
status: 'NORMAL' | 'TEMPORARILY_LIMITED' | 'PERMANENTLY_LIMITED'
message: string
deadlineTime?: string
remainingHours?: number
remainingDays?: number
annualReviewStartTime?: string
canCreateActivity: boolean;
status: 'NORMAL' | 'TEMPORARILY_LIMITED' | 'PERMANENTLY_LIMITED';
message: string;
deadlineTime?: string;
remainingHours?: number;
remainingDays?: number;
annualReviewStartTime?: string;
}
// 权限详情类型定义
export interface ClubPermissionDetails {
permissionResult: ClubActivityPermissionResult
isInAnnualReviewWindow: boolean
daysUntilNextAnnualReview: number
hasSubmittedCurrentYearAnnualReview: boolean
permissionResult: ClubActivityPermissionResult;
isInAnnualReviewWindow: boolean;
daysUntilNextAnnualReview: number;
hasSubmittedCurrentYearAnnualReview: boolean;
}
/**
@@ -28,7 +28,7 @@ export function checkClubActivityPermission(clubId: number | string) {
return request<ClubActivityPermissionResult>({
url: `/stgl/club/permission/checkActivityPermission/${clubId}`,
method: 'get'
})
});
}
/**
@@ -41,7 +41,7 @@ export function batchCheckClubPermission(clubIds: number[]) {
url: '/stgl/club/permission/batchCheckPermission',
method: 'post',
data: clubIds
})
});
}
/**
@@ -53,7 +53,7 @@ export function isInAnnualReviewWindow(clubId: number) {
return request<boolean>({
url: `/stgl/club/permission/isInAnnualReviewWindow/${clubId}`,
method: 'get'
})
});
}
/**
@@ -65,7 +65,7 @@ export function getDaysUntilNextAnnualReview(clubId: number) {
return request<number>({
url: `/stgl/club/permission/getDaysUntilNextAnnualReview/${clubId}`,
method: 'get'
})
});
}
/**
@@ -77,7 +77,7 @@ export function hasSubmittedCurrentYearAnnualReview(clubId: number) {
return request<boolean>({
url: `/stgl/club/permission/hasSubmittedCurrentYearAnnualReview/${clubId}`,
method: 'get'
})
});
}
/**
@@ -89,5 +89,5 @@ export function getClubPermissionDetails(clubId: number) {
return request<ClubPermissionDetails>({
url: `/stgl/club/permission/getPermissionDetails/${clubId}`,
method: 'get'
})
}
});
}
+15 -15
View File
@@ -1,4 +1,4 @@
import request from '@/utils/request'
import request from '@/utils/request';
// 查询操行分列表
export function conductScoreList(query: any) {
@@ -6,7 +6,7 @@ export function conductScoreList(query: any) {
url: '/xg/conduct-score/list',
method: 'get',
params: query
})
});
}
// 根据部门ID查询操行分列表(带权限过滤)
@@ -15,7 +15,7 @@ export function conductScoreListByDeptId(query: any) {
url: '/xg/conduct-score/listByDeptId',
method: 'post',
data: query
})
});
}
// 查询操行分详细
@@ -23,7 +23,7 @@ export function conductScoreInfo(id: any) {
return request({
url: '/xg/conduct-score/' + id,
method: 'get'
})
});
}
// 新增操行分
@@ -32,7 +32,7 @@ export function conductScoreAdd(data: any) {
url: '/xg/conduct-score',
method: 'post',
data: data
})
});
}
// 修改操行分
@@ -41,7 +41,7 @@ export function conductScoreEdit(data: any) {
url: '/xg/conduct-score/' + data.id,
method: 'put',
data: data
})
});
}
// 删除操行分
@@ -49,7 +49,7 @@ export function conductScoreRemove(ids: any) {
return request({
url: '/xg/conduct-score/' + ids,
method: 'delete'
})
});
}
// 导出操行分
@@ -58,7 +58,7 @@ export function conductScoreExport(query: any) {
url: '/xg/conduct-score/export',
method: 'post',
params: query
})
});
}
// 批量录入操行分
@@ -67,7 +67,7 @@ export function conductScoreBatchAdd(data: any) {
url: '/xg/conduct-score/batch',
method: 'post',
data: data
})
});
}
// 获取班主任负责的班级学生列表
@@ -76,7 +76,7 @@ export function conductScoreClassStudents(query: any) {
url: '/xg/conduct-score/class-students',
method: 'get',
params: query
})
});
}
// 查询学期平均分统计
@@ -85,7 +85,7 @@ export function conductScoreSemesterAverage(query: any) {
url: '/xg/conduct-score/semester-average',
method: 'get',
params: query
})
});
}
// 根据部门ID查询学期平均分统计(带权限过滤)
@@ -94,7 +94,7 @@ export function conductScoreSemesterAverageByDeptId(query: any) {
url: '/xg/conduct-score/semester-average-by-dept-id',
method: 'post',
data: query
})
});
}
// 导出学期统计数据
@@ -103,7 +103,7 @@ export function conductScoreExportSemester(query: any) {
url: '/xg/conduct-score/export-semester',
method: 'get',
params: query
})
});
}
// 验证录入权限
@@ -112,7 +112,7 @@ export function conductScoreCheckPermission(query: any) {
url: '/xg/conduct-score/check-permission',
method: 'get',
params: query
})
});
}
// 获取学生详细记录(按月份)
@@ -121,5 +121,5 @@ export function conductScoreGetStudentRecords(studentId: any, academicYear: any,
url: `/xg/conduct-score/student-records/${studentId}`,
method: 'get',
params: { academicYear, semester, tenantId }
})
});
}
-1
View File
@@ -114,4 +114,3 @@ export function exportCollegeLeader(query: any) {
responseType: 'blob'
});
}
-1
View File
@@ -266,4 +266,3 @@ export function getStudentLeave(id: string | number): AxiosPromise<StudentLeaveV
* 获取学生请假详细信息(别名)
*/
export const getStudentLeaveInfo = getStudentLeave;
+33 -33
View File
@@ -1,45 +1,45 @@
import request from '@/utils/request'
import request from '@/utils/request';
export interface StudentStatisticsBo {
startTime: string
endTime: string
collegeId?: number
majorId?: number
classId?: number
startTime: string;
endTime: string;
collegeId?: number;
majorId?: number;
classId?: number;
}
export interface StatisticsChartData {
name: string
dormDeductionCount: number
dormViolationCount: number
dormViolationDeduction: number
teachingViolationCount: number
teachingViolationDeduction: number
name: string;
dormDeductionCount: number;
dormViolationCount: number;
dormViolationDeduction: number;
teachingViolationCount: number;
teachingViolationDeduction: number;
}
export interface StatisticsDetailData {
id: number
name: string
parentName: string
dormDeductionCount: number
dormViolationCount: number
dormViolationDeduction: number
teachingViolationCount: number
teachingViolationDeduction: number
totalDeduction: number
id: number;
name: string;
parentName: string;
dormDeductionCount: number;
dormViolationCount: number;
dormViolationDeduction: number;
teachingViolationCount: number;
teachingViolationDeduction: number;
totalDeduction: number;
}
export interface StudentStatisticsVo {
dimension: string
chartData: StatisticsChartData[]
detailData: StatisticsDetailData[]
dimension: string;
chartData: StatisticsChartData[];
detailData: StatisticsDetailData[];
}
export interface CascadeOptionVo {
id: number
label: string
value: string
sort: number
id: number;
label: string;
value: string;
sort: number;
}
// 获取统计数据
@@ -48,7 +48,7 @@ export function getStatistics(data: StudentStatisticsBo) {
url: '/xg/studentStatistics/statistics',
method: 'post',
data: data
})
});
}
// 导出统计数据
@@ -58,7 +58,7 @@ export function exportStatistics(data: StudentStatisticsBo) {
method: 'post',
data: data,
responseType: 'blob'
})
});
}
// 获取二级学院列表
@@ -66,7 +66,7 @@ export function getColleges() {
return request({
url: '/xg/studentStatistics/colleges',
method: 'get'
})
});
}
// 根据学院ID获取专业列表
@@ -74,7 +74,7 @@ export function getMajorsByCollegeId(collegeId: number) {
return request({
url: `/xg/studentStatistics/majors/${collegeId}`,
method: 'get'
})
});
}
// 根据专业ID获取班级列表
@@ -82,5 +82,5 @@ export function getClassesByMajorId(majorId: number) {
return request({
url: `/xg/studentStatistics/classes/${majorId}`,
method: 'get'
})
});
}
@@ -1,6 +1,12 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { HolidayStayVO, HolidayStayForm, HolidayStayQuery, StudentOptionVO, StudentOptionQuery } from '@/api/xg/xgss/holidayStay/holidayStay/types-sg';
import {
HolidayStayVO,
HolidayStayForm,
HolidayStayQuery,
StudentOptionVO,
StudentOptionQuery
} from '@/api/xg/xgss/holidayStay/holidayStay/types-sg';
/**
* 查询假期留宿管理列表
+2 -2
View File
@@ -484,8 +484,8 @@ export interface DormApplicationQuery extends PageQuery {
applyTime?: string;
/**
* 日期范围参数
*/
* 日期范围参数
*/
params?: any;
}
+22 -22
View File
@@ -239,8 +239,8 @@ export const listXljkConsulter = (query?: XljkConsulterQuery): AxiosPromise<Xljk
url: '/xljk/consulter/list',
method: 'get',
params: query
})
}
});
};
/**
* 查询心理咨询师配置详细
@@ -250,8 +250,8 @@ export const getXljkConsulter = (id: string | number): AxiosPromise<XljkConsulte
return request({
url: '/xljk/consulter/' + id,
method: 'get'
})
}
});
};
/**
* 新增心理咨询师配置
@@ -262,8 +262,8 @@ export const addXljkConsulter = (data: XljkConsulterForm) => {
url: '/xljk/consulter',
method: 'post',
data: data
})
}
});
};
/**
* 修改心理咨询师配置
@@ -274,8 +274,8 @@ export const updateXljkConsulter = (data: XljkConsulterForm) => {
url: '/xljk/consulter',
method: 'put',
data: data
})
}
});
};
/**
* 删除心理咨询师配置
@@ -285,8 +285,8 @@ export const delXljkConsulter = (id: string | number | Array<string | number>) =
return request({
url: '/xljk/consulter/' + id,
method: 'delete'
})
}
});
};
/**
* 导出心理咨询师配置
@@ -299,8 +299,8 @@ export const exportXljkConsulter = (query?: XljkConsulterQuery) => {
method: 'post',
data: query,
responseType: 'blob'
})
}
});
};
/**
* 批量添加咨询师
@@ -315,8 +315,8 @@ export const batchAddConsulters = (userIds: number[], remark?: string) => {
userIds,
remark
}
})
}
});
};
/**
* 查询我配置的咨询师列表
@@ -327,8 +327,8 @@ export const listMyConfigConsulters = (query?: XljkConsulterQuery): AxiosPromise
url: '/xljk/consulter/myConfig',
method: 'get',
params: query
})
}
});
};
/**
* 批量启用咨询师
@@ -339,8 +339,8 @@ export const enableConsulters = (ids: number[]) => {
url: '/xljk/consulter/enable',
method: 'post',
data: ids
})
}
});
};
/**
* 批量停用咨询师
@@ -351,8 +351,8 @@ export const disableConsulters = (ids: number[]) => {
url: '/xljk/consulter/disable',
method: 'post',
data: ids
})
}
});
};
/**
* 根据部门ID获取用户列表
@@ -362,5 +362,5 @@ export const listUserByDeptId = (deptId: string | number) => {
return request({
url: '/xljk/consulter/users/' + deptId,
method: 'get'
})
}
});
};
+6 -7
View File
@@ -1,4 +1,4 @@
import { PageQuery } from '@/api/types'
import { PageQuery } from '@/api/types';
export interface XljkConsultationScheduleVO {
/**
@@ -941,7 +941,6 @@ export interface XljkConsulterForm {
}
export interface XljkConsulterQuery extends PageQuery {
/**
* 用户ID
*/
@@ -977,9 +976,9 @@ export interface XljkConsulterQuery extends PageQuery {
*/
office?: string;
/**
* 日期范围参数
*/
'params[beginCreateTime]'?: string;
'params[endCreateTime]'?: string;
/**
* 日期范围参数
*/
'params[beginCreateTime]'?: string;
'params[endCreateTime]'?: string;
}
+178
View File
@@ -0,0 +1,178 @@
# 人员录入功能说明
## 📋 功能概述
人员录入功能支持录入正式工、临时工、外聘工三种类型的人员信息,包含完整的增删改查、导入导出功能。
## 🔧 功能特性
### 核心功能
- ✅ 人员信息录入(姓名、性别、职务、部门、手机号、工号)
- ✅ 工号自动生成(正式工0XXXXX、临时工LXXXXX、外聘工WXXXXX)
- ✅ 增删改查操作
- ✅ 数据同步(自动同步sys_user和sys_user_role表)
- ✅ 导入导出Excel功能
- ✅ 部门树形选择
### 工号规则
| 人员类型 | 工号格式 | 示例 |
|---------|---------|------|
| 正式工 | 0XXXXX | 000001 |
| 临时工 | LXXXXX | L00001 |
| 外聘工 | WXXXXX | W00001 |
## 📁 文件结构
```
backend/ruoyi-modules/ruoyi-rs/
├── domain/
│ ├── RsStaffInfo.java # 实体类
│ ├── vo/
│ │ ├── RsStaffInfoVo.java # 视图对象
│ │ └── RsStaffInfoImportVo.java # 导入VO
│ └── bo/
│ └── RsStaffInfoBo.java # 业务对象
├── mapper/
│ ├── RsStaffInfoMapper.java # 数据访问层接口
│ └── RsStaffInfoMapper.xml # MyBatis映射文件
├── service/
│ ├── IRsStaffInfoService.java # 服务接口
│ └── impl/
│ └── RsStaffInfoServiceImpl.java # 服务实现
├── listener/
│ └── RsStaffInfoImportListener.java # 导入监听器
└── controller/
└── RsStaffInfoController.java # 控制器
web/src/views/rs/staff-info/
├── index.vue # 主页面
└── README.md # 说明文档
web/src/api/rs/staffInfo/
├── index.ts # API接口
└── types.ts # 类型定义
```
## 🔌 API接口
### 主要接口
- `GET /staff/staffInfo/list` - 查询人员列表
- `POST /staff/staffInfo/listByDeptId` - 根据部门查询人员列表
- `GET /staff/staffInfo/{id}` - 获取人员详情
- `POST /staff/staffInfo` - 新增人员
- `PUT /staff/staffInfo` - 修改人员
- `DELETE /staff/staffInfo/{ids}` - 删除人员
- `GET /staff/staffInfo/deptTree` - 获取部门树
- `POST /staff/staffInfo/importData` - 导入人员数据
- `POST /staff/staffInfo/importTemplate` - 下载导入模板
- `POST /staff/staffInfo/export` - 导出人员数据
## 📊 数据同步逻辑
### 新增人员时同步操作
1. 在`rs_staff_info`表插入人员记录
2. 自动生成工号(根据人员类型)
3. 在`sys_user`表创建用户记录
4. 在`sys_user_role`表添加角色(4和1938120323613728770)
### 修改人员时同步操作
1. 更新`rs_staff_info`表记录
2. 同步更新`sys_user`表的对应字段
### 删除人员时同步操作
1. 逻辑删除`rs_staff_info`表记录
2. 逻辑删除`sys_user`表对应记录
3. 级联删除`sys_user_role`表记录
## 📥 导入导出功能
### 导入模板格式
```excel
| 姓名 | 性别 | 职务 | 部门名称 | 手机号 | 人员类型 |
|------|------|------|----------|--------|----------|
| 张三 | 男 | 讲师 | 计算机学院 | 13800138000 | 正式工 |
```
### 模板特性
- **性别列**:下拉选择(男/女)
- **人员类型列**:下拉选择(正式工/临时工/外聘工)
- **数据验证**:自动校验必填字段和格式
### 导入验证规则
- 姓名:必填
- 性别:必填,必须是"男"或"女"
- 部门名称:必填,必须存在于系统中
- 手机号:必填,11位数字格式,且系统中唯一
- 人员类型:必填,必须是"正式工"、"临时工"或"外聘工"
## 🎨 前端页面布局
### 页面结构
```
├── 左侧部门树
│ ├── 部门搜索框
│ └── 部门树形结构
└── 右侧内容区域
├── 搜索表单
│ ├── 姓名、工号、手机号等查询条件
│ └── 时间范围选择
├── 操作按钮栏
│ ├── 新增、修改、删除按钮
│ └── 导入、导出按钮
└── 数据表格
├── 工号、姓名、性别等列
└── 操作列(修改、删除、详情)
```
### 交互逻辑
1. **部门选择**:点击左侧部门树节点,右侧显示该部门人员
2. **搜索过滤**:支持多条件组合查询
3. **批量操作**:支持批量删除
4. **导入导出**:完整的Excel导入导出流程
## 🔧 使用说明
### 1. 访问页面
在系统菜单中配置人员录入功能入口,路径为:`/rs/staff-info`
### 2. 新增人员
1. 选择左侧部门节点
2. 点击"新增"按钮
3. 填写人员信息
4. 系统自动生成工号
5. 点击"确定"保存
### 3. 导入人员
1. 选择左侧部门节点
2. 点击"更多" → "导入数据"
3. 下载导入模板
4. 按照模板格式填写数据
5. 上传Excel文件
6. 查看导入结果
### 4. 导出人员
1. 设置查询条件(可选)
2. 点击"更多" → "导出数据"
3. 系统自动下载Excel文件
## ⚠️ 注意事项
1. **部门选择**:导入和新增操作前必须先选择部门节点
2. **数据唯一性**:手机号在整个系统中必须唯一
3. **工号生成**:工号自动生成,无需手动填写
4. **角色分配**:新增人员自动分配默认角色
5. **权限控制**:不同操作需要相应权限
## 🔄 数据流转
```
用户操作 → 前端页面 → API接口 → 控制器 → 服务层 → 数据访问层 → 数据库
↑ ↓
导入文件 ← Excel处理 ← 导入监听器 ← 业务验证 ← 数据转换 ← 自动生成工号
↑ ↓
导出文件 ← Excel生成 ← 模板下载 ← 查询结果 ← 条件筛选 ← 用户查询
```
## 📞 技术支持
如有问题,请联系开发团队或查看系统日志获取详细错误信息。
File diff suppressed because it is too large. Load diff
@@ -73,12 +73,7 @@
collapse-tags-tooltip
@change="handleQuery"
>
<el-option
v-for="status in student_status"
:key="status.value"
:label="status.label"
:value="status.value"
/>
<el-option v-for="status in student_status" :key="status.value" :label="status.label" :value="status.value" />
</el-select>
</el-form-item>
</el-col>
@@ -108,6 +103,20 @@
<el-col :span="1.5">
<el-button icon="Refresh" @click="handleRefresh">刷新</el-button>
</el-col>
<el-col :span="1.5">
<el-dropdown class="mt-[1px]">
<el-button>
更多
<el-icon class="el-icon--right"><ArrowDown /></el-icon>
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item icon="Download" @click="handleImportTemplate">下载模板</el-dropdown-item>
<el-dropdown-item icon="Top" @click="handleImportData">导入数据</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</el-col>
<RightToolbar v-model:show-search="showSearch" :columns="columns" :search="true" @query-table="getList"></RightToolbar>
</el-row>
</template>
@@ -120,8 +129,8 @@
v-model="batchModalForm.score"
:min="0"
:max="100"
:precision="2"
:step="0.5"
:precision="0"
:step="1"
size="small"
placeholder="输入分数"
style="width: 100%"
@@ -146,6 +155,70 @@
</template>
</el-dialog>
<!-- 导入数据弹窗 -->
<el-dialog
v-model="importModalVisible"
title="导入操行分数据"
width="500px"
append-to-body
:close-on-click-modal="false"
destroy-on-close
>
<div class="mb-4">
<div class="text-sm text-gray-600 mb-2">
<strong>当前录入条件:</strong>
{{ queryParams.academicYear - 1 }}-{{ queryParams.academicYear }}学年
{{ queryParams.semester === 1 ? '第一学期' : '第二学期' }}
{{ queryParams.month }}月
{{ activeTab ? `(${classTabs.find((tab) => tab.classId.toString() === activeTab)?.className})` : '' }}
</div>
<div class="text-sm text-red-600 mb-3">
<el-icon class="mr-1"><InfoFilled /></el-icon>
如果学生已有该月份的分数记录,将被Excel中的数据覆盖
</div>
</div>
<el-upload
ref="importUploadRef"
:limit="1"
accept=".xlsx, .xls"
:headers="importUpload.headers"
:action="getImportUrl()"
:disabled="importUpload.isUploading"
:on-progress="handleImportFileUploadProgress"
:on-success="handleImportFileSuccess"
:on-error="handleImportFileError"
:auto-upload="false"
drag
>
<el-icon class="el-icon--upload">
<i-ep-upload-filled />
</el-icon>
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
<template #tip>
<div class="text-center el-upload__tip">
<div class="el-upload__tip" style="color: #409eff; margin-bottom: 5px">模板包含4列:学号、学生姓名、分数、备注</div>
<div class="el-upload__tip" style="color: red">仅处理班主任负责班级的学生数据,学号不存在或姓名不匹配的数据将被跳过</div>
<span>仅允许导入xls、xlsx格式文件。</span>
<el-link
type="primary"
:underline="false"
style="font-size: 12px; vertical-align: baseline; margin-left: 10px"
@click="handleImportTemplate"
>
下载模板
</el-link>
</div>
</template>
</el-upload>
<template #footer>
<div class="dialog-footer">
<el-button @click="importModalVisible = false">取消</el-button>
<el-button type="primary" @click="submitImportFile" :loading="importUpload.isUploading">确定导入</el-button>
</div>
</template>
</el-dialog>
<el-table
v-if="currentClassStudents && currentClassStudents.length > 0"
v-loading="tabLoading"
@@ -189,7 +262,7 @@
</el-table-column>
<el-table-column v-if="columns[3].visible" key="score" label="分数" align="center" width="150">
<template #default="scope">
<el-input-number v-model="scope.row.score" :min="0" :max="100" :precision="2" :step="0.5" size="small" placeholder="输入分数" />
<el-input-number v-model="scope.row.score" :min="0" :max="100" :precision="0" :step="1" size="small" placeholder="输入分数" />
</template>
</el-table-column>
<el-table-column v-if="columns[4].visible" key="evaluation" label="评判结果" align="center" width="100">
@@ -250,9 +323,11 @@
</template>
<script setup lang="ts" name="ConductScoreInput">
import { ref, reactive, onMounted, computed, getCurrentInstance, toRefs } from 'vue';
import { ref, reactive, onMounted, computed, getCurrentInstance, toRefs, nextTick } from 'vue';
import ElMessage from 'element-plus/es/components/message/index';
import ElMessageBox from 'element-plus/es/components/message-box/index';
import { ArrowDown, InfoFilled } from '@element-plus/icons-vue';
import { globalHeaders } from '@/utils/request';
// API接口
import { conductScoreClassStudents, conductScoreBatchAdd } from '@/api/xg/conductScore';
@@ -265,7 +340,20 @@ const { student_status } = toRefs<any>(proxy?.useDict('student_status'));
// 响应式数据
const queryParams = reactive({
academicYear: new Date().getFullYear(),
academicYear: (() => {
const now = new Date();
const currentYear = now.getFullYear();
const currentMonth = now.getMonth() + 1; // 1-12
const currentDate = now.getDate();
// 判断是否在9月1日之后(包括9月1日)
// 如果是9月之后,或者是9月1日及之后,则属于下一学年
if (currentMonth > 9 || (currentMonth === 9 && currentDate >= 1)) {
return currentYear + 1; // 下一学年,如2026年9月1日后,选中2026-2027学年(值为2027)
} else {
return currentYear; // 当前学年,如2026年9月1日前,选中2025-2026学年(值为2026)
}
})(),
semester: 1,
month: new Date().getMonth() + 1,
studentNo: '',
@@ -355,10 +443,14 @@ const queryFormRef = ref();
// 选项数据
const yearOptions = computed(() => {
const currentYear = new Date().getFullYear();
return Array.from({ length: 8 }, (_, i) => ({
label: `${currentYear - 7 + i}年`,
value: currentYear - 7 + i
}));
// 生成9个学年选项(往前7个 + 当前1个 + 往后多生成1个)
return Array.from({ length: 9 }, (_, i) => {
const yearValue = currentYear - 7 + i;
return {
label: `${yearValue - 1}-${yearValue}学年`,
value: yearValue
};
});
});
const semesterOptions = [
@@ -601,6 +693,15 @@ const batchModalForm = reactive({
remark: ''
});
// 导入数据相关
const importModalVisible = ref(false);
const importUploadRef = ref<any>();
const importUpload = reactive({
isUploading: false,
headers: globalHeaders(),
url: ''
});
const confirmBatchModalSave = () => {
// 批量录入弹窗填写的分数/备注将应用到所有已勾选的学生(包含已录入的,视为覆盖)
const targets = selectedStudents.value || [];
@@ -662,6 +763,90 @@ const handleSelectionChange = (selection) => {
selectedStudents.value = selection;
};
// 导入相关方法
const handleImportData = () => {
if (!activeTab.value) {
ElMessage.warning('请先选择要导入数据的班级');
return;
}
importModalVisible.value = true;
};
const handleImportTemplate = () => {
proxy?.download('xg/conduct-score/importTemplate', {}, `操行分导入模板_${new Date().getTime()}.xlsx`);
};
const getImportUrl = () => {
const baseUrl = import.meta.env.VITE_APP_BASE_API + '/xg/conduct-score/importData';
const params = new URLSearchParams();
params.append('academicYear', queryParams.academicYear.toString());
params.append('semester', queryParams.semester.toString());
params.append('month', queryParams.month.toString());
// 如果选择了特定班级,则只导入该班级数据
if (activeTab.value) {
params.append('classId', activeTab.value);
}
return `${baseUrl}?${params.toString()}`;
};
const handleImportFileUploadProgress = () => {
importUpload.isUploading = true;
};
const handleImportFileSuccess = (response) => {
importModalVisible.value = false;
importUpload.isUploading = false;
importUploadRef.value[0]?.clearFiles();
ElMessage.closeAll();
ElMessageBox.alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + '</div>', '导入结果', {
dangerouslyUseHTMLString: true
});
// 刷新数据
getList();
};
const handleImportFileError = (error) => {
importUpload.isUploading = false;
importUploadRef.value[0]?.clearFiles();
ElMessage.closeAll();
console.error('文件上传失败:', error);
ElMessage.error('导入失败,请检查文件格式或网络连接');
};
const submitImportFile = () => {
if (!importUploadRef.value || !importUploadRef.value[0]) {
ElMessage.error('上传组件未初始化');
return;
}
ElMessage({
message: '正在导入数据,请耐心等待...',
type: 'info',
duration: 0,
zIndex: 4000
});
importUpload.isUploading = true;
// 使用 nextTick 确保组件已完全挂载
nextTick(() => {
try {
importUploadRef.value[0]?.submit();
} catch (error) {
console.error('Submit error:', error);
ElMessage.error('上传失败:' + error.message);
importUpload.isUploading = false;
}
});
};
// 生命周期
onMounted(() => {
getList();
@@ -184,7 +184,11 @@
width="100"
:show-overflow-tooltip="true"
/>
<el-table-column v-if="columns[5].visible" key="academicYear" label="学年" align="center" prop="academicYear" width="80" />
<el-table-column v-if="columns[5].visible" key="academicYear" label="学年" align="center" width="120">
<template #default="scope">
{{ scope.row.academicYear ? `${scope.row.academicYear - 1}-${scope.row.academicYear}学年` : '-' }}
</template>
</el-table-column>
<el-table-column v-if="columns[6].visible" key="semester" label="学期" align="center" prop="semester" width="80">
<template #default="scope">
{{ scope.row.semester === 1 ? '第一学期' : '第二学期' }}
@@ -345,7 +349,20 @@ const { xg_class_status } = toRefs<any>(proxy?.useDict('xg_class_status'));
const queryParams = reactive({
pageNum: 1,
pageSize: 10,
academicYear: new Date().getFullYear(),
academicYear: (() => {
const now = new Date();
const currentYear = now.getFullYear();
const currentMonth = now.getMonth() + 1; // 1-12
const currentDate = now.getDate();
// 判断是否在9月1日之后(包括9月1日)
// 如果是9月之后,或者是9月1日及之后,则属于下一学年
if (currentMonth > 9 || (currentMonth === 9 && currentDate >= 1)) {
return currentYear + 1; // 下一学年,如2026年9月1日后,选中2026-2027学年(值为2027)
} else {
return currentYear; // 当前学年,如2026年9月1日前,选中2025-2026学年(值为2026)
}
})(),
semester: 1,
month: new Date().getMonth() + 1, // 默认当前月份
studentName: '',
@@ -428,11 +445,14 @@ const modalMode = ref<'single' | 'batch'>('single');
// 选项数据
const yearOptions = computed(() => {
const currentYear = new Date().getFullYear();
// 历史8年:从 currentYear - 7 到 currentYear
return Array.from({ length: 8 }, (_, i) => ({
label: `${currentYear - 7 + i}年`,
value: currentYear - 7 + i
}));
// 生成9个学年选项(往前7个 + 当前1个 + 往后多生成1个)
return Array.from({ length: 9 }, (_, i) => {
const yearValue = currentYear - 7 + i;
return {
label: `${yearValue - 1}-${yearValue}学年`,
value: yearValue
};
});
});
const semesterOptions = [
@@ -916,7 +936,8 @@ const handleExport = () => {
{
...queryParams,
...proxy?.addDateRange(queryParams, dateRange.value),
includeEvaluation: true // 添加标识,表示需要包含评判结果
includeEvaluation: true, // 添加标识,表示需要包含评判结果
academicYearFormat: 'friendly' // 添加标识,表示需要将学年格式化为友好形式(如2025-2026学年)
},
`conduct_score_${new Date().getTime()}.xlsx`
)
@@ -44,7 +44,7 @@
<transition enter-active-class="animate__animated animate__fadeIn" leave-active-class="animate__animated animate__fadeOut">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams">
<el-form ref="queryFormRef" :model="queryParams" label-width="80px">
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="学年" prop="academicYear">
@@ -357,7 +357,20 @@ const { xg_class_status } = toRefs<any>(proxy?.useDict('xg_class_status'));
const queryParams = reactive({
pageNum: 1,
pageSize: 10,
academicYear: new Date().getFullYear(),
academicYear: (() => {
const now = new Date();
const currentYear = now.getFullYear();
const currentMonth = now.getMonth() + 1; // 1-12
const currentDate = now.getDate();
// 判断是否在9月1日之后(包括9月1日)
// 如果是9月之后,或者是9月1日及之后,则属于下一学年
if (currentMonth > 9 || (currentMonth === 9 && currentDate >= 1)) {
return currentYear + 1; // 下一学年,如2026年9月1日后,选中2026-2027学年(值为2027)
} else {
return currentYear; // 当前学年,如2026年9月1日前,选中2025-2026学年(值为2026)
}
})(),
semester: 1,
className: '',
studentName: '',
@@ -431,11 +444,14 @@ const monthlyRecords = ref([]);
// 选项数据
const yearOptions = computed(() => {
const currentYear = new Date().getFullYear();
// 历史8年:从 currentYear - 7 到 currentYear
return Array.from({ length: 8 }, (_, i) => ({
label: `${currentYear - 7 + i}年`,
value: currentYear - 7 + i
}));
// 生成9个学年选项(往前7个 + 当前1个 + 往后多生成1个)
return Array.from({ length: 9 }, (_, i) => {
const yearValue = currentYear - 7 + i;
return {
label: `${yearValue - 1}-${yearValue}学年`,
value: yearValue
};
});
});
const semesterOptions = [
@@ -898,7 +914,8 @@ const handleExport = () => {
'xg/conduct-score/export-semester',
{
...queryParams,
...proxy?.addDateRange(queryParams, dateRange.value)
...proxy?.addDateRange(queryParams, dateRange.value),
academicYearFormat: 'friendly' // 添加标识,表示需要将学年格式化为友好形式(如2025-2026学年)
},
`semester_statistics_${new Date().getTime()}.xlsx`
)
@@ -188,10 +188,10 @@
<el-table-column label="操作" fixed="right" width="240" 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)"></el-button>
<el-button v-hasPermi="['stu:studentMsg:update']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)"></el-button>
<el-button v-hasPermi="['stu:studentMsg:delete']" link type="danger" icon="Delete" @click="handleDelete(scope.row)"></el-button>
</el-tooltip>
<!--
<el-tooltip content="重置密码" placement="top">