新增教研室和教员的时候,同步创建部门和教员账号
This commit is contained in:
+86
@@ -71,6 +71,92 @@ public class DownloadController {
|
|||||||
downloadTemplate(response, "教研室模板.xls");
|
downloadTemplate(response, "教研室模板.xls");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 教员导入模板(动态生成:数据 sheet + 填写说明 sheet)。
|
||||||
|
* <p>列序与 /jys/teacher/import 解析一致:
|
||||||
|
* 教员编号、教员姓名、教研室代号、职称、专业技术职务等级、教员类别、
|
||||||
|
* 性别、手机号、身份证号、备注、序号、虚实类型。</p>
|
||||||
|
*/
|
||||||
|
@GetMapping("/jy")
|
||||||
|
public void downloadJy(HttpServletResponse response) throws IOException {
|
||||||
|
org.apache.poi.ss.usermodel.Workbook workbook = new org.apache.poi.xssf.usermodel.XSSFWorkbook();
|
||||||
|
try {
|
||||||
|
org.apache.poi.ss.usermodel.CellStyle headerStyle = workbook.createCellStyle();
|
||||||
|
org.apache.poi.ss.usermodel.Font headerFont = workbook.createFont();
|
||||||
|
headerFont.setBold(true);
|
||||||
|
headerStyle.setFont(headerFont);
|
||||||
|
org.apache.poi.ss.usermodel.CellStyle requiredStyle = workbook.createCellStyle();
|
||||||
|
org.apache.poi.ss.usermodel.Font requiredFont = workbook.createFont();
|
||||||
|
requiredFont.setBold(true);
|
||||||
|
requiredFont.setColor(org.apache.poi.ss.usermodel.IndexedColors.RED.getIndex());
|
||||||
|
requiredStyle.setFont(requiredFont);
|
||||||
|
|
||||||
|
String[] headers = {"教员编号", "教员姓名*", "教研室代号*", "职称", "专业技术职务等级",
|
||||||
|
"教员类别", "性别", "手机号", "身份证号", "备注", "序号", "虚实类型"};
|
||||||
|
org.apache.poi.ss.usermodel.Sheet dataSheet = workbook.createSheet("教员");
|
||||||
|
org.apache.poi.ss.usermodel.Row headerRow = dataSheet.createRow(0);
|
||||||
|
for (int i = 0; i < headers.length; i++) {
|
||||||
|
org.apache.poi.ss.usermodel.Cell cell = headerRow.createCell(i);
|
||||||
|
cell.setCellValue(headers[i]);
|
||||||
|
cell.setCellStyle(headers[i].endsWith("*") ? requiredStyle : headerStyle);
|
||||||
|
dataSheet.setColumnWidth(i, 16 * 256);
|
||||||
|
}
|
||||||
|
|
||||||
|
String[][] instructions = {
|
||||||
|
{"教员编号", "选填", "留空则系统自动生成(JY+流水号);手填须唯一"},
|
||||||
|
{"教员姓名", "必填", "文本"},
|
||||||
|
{"教研室代号", "必填", "教研室表中的教研室代号(非名称),必须已存在"},
|
||||||
|
{"职称", "选填", "文本,如:讲师、副教授"},
|
||||||
|
{"专业技术职务等级", "选填", "文本,如:初级、中级、高级"},
|
||||||
|
{"教员类别", "选填", "文本,如:专业技术军官"},
|
||||||
|
{"性别", "选填", "数字:1=男,0=女"},
|
||||||
|
{"手机号", "选填", "文本"},
|
||||||
|
{"身份证号", "选填", "文本"},
|
||||||
|
{"备注", "选填", "文本"},
|
||||||
|
{"序号", "选填", "数字"},
|
||||||
|
{"虚实类型", "选填", "数字:0=实员"}
|
||||||
|
};
|
||||||
|
org.apache.poi.ss.usermodel.Sheet helpSheet = workbook.createSheet("填写说明");
|
||||||
|
org.apache.poi.ss.usermodel.Row titleRow = helpSheet.createRow(0);
|
||||||
|
org.apache.poi.ss.usermodel.Cell titleCell = titleRow.createCell(0);
|
||||||
|
titleCell.setCellValue("教员导入填写说明(红色表头列为必填;导入只写教员档案不建账号,账号用列表【对齐到用户表】批量开通)");
|
||||||
|
titleCell.setCellStyle(headerStyle);
|
||||||
|
String[] helpHeaders = {"列名", "是否必填", "填写说明"};
|
||||||
|
org.apache.poi.ss.usermodel.Row helpHeaderRow = helpSheet.createRow(1);
|
||||||
|
for (int i = 0; i < helpHeaders.length; i++) {
|
||||||
|
org.apache.poi.ss.usermodel.Cell cell = helpHeaderRow.createCell(i);
|
||||||
|
cell.setCellValue(helpHeaders[i]);
|
||||||
|
cell.setCellStyle(headerStyle);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < instructions.length; i++) {
|
||||||
|
org.apache.poi.ss.usermodel.Row row = helpSheet.createRow(i + 2);
|
||||||
|
for (int j = 0; j < 3; j++) {
|
||||||
|
row.createCell(j).setCellValue(instructions[i][j]);
|
||||||
|
}
|
||||||
|
if ("必填".equals(instructions[i][1])) {
|
||||||
|
row.getCell(1).setCellStyle(requiredStyle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
helpSheet.setColumnWidth(0, 18 * 256);
|
||||||
|
helpSheet.setColumnWidth(1, 10 * 256);
|
||||||
|
helpSheet.setColumnWidth(2, 60 * 256);
|
||||||
|
|
||||||
|
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||||||
|
FileUtils.setAttachmentResponseHeader(response, "教员导入模板.xlsx");
|
||||||
|
workbook.write(response.getOutputStream());
|
||||||
|
response.getOutputStream().flush();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ServiceException("生成教员导入模板失败:" + e.getMessage());
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
workbook.close();
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 联教连训管理模板
|
* 联教连训管理模板
|
||||||
*/
|
*/
|
||||||
|
|||||||
+3
-3
@@ -38,7 +38,7 @@ public class FacultyController {
|
|||||||
*/
|
*/
|
||||||
@PostMapping("/office/add")
|
@PostMapping("/office/add")
|
||||||
public Result<Void> addOffice(@RequestBody JYSB entity) {
|
public Result<Void> addOffice(@RequestBody JYSB entity) {
|
||||||
facultyService.addJYSB(entity);
|
facultyService.addJYSB(entity, null);
|
||||||
return Result.success();
|
return Result.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ public class FacultyController {
|
|||||||
*/
|
*/
|
||||||
@PostMapping("/office/disable")
|
@PostMapping("/office/disable")
|
||||||
public Result<Void> disableOffice(@RequestParam("jysdh") String jysdh) {
|
public Result<Void> disableOffice(@RequestParam("jysdh") String jysdh) {
|
||||||
facultyService.disableJYSB(jysdh);
|
facultyService.disableJYSB(jysdh, null);
|
||||||
return Result.success();
|
return Result.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ public class FacultyController {
|
|||||||
*/
|
*/
|
||||||
@PostMapping("/office/update")
|
@PostMapping("/office/update")
|
||||||
public Result<Void> updateOffice(@RequestBody JYSB entity) {
|
public Result<Void> updateOffice(@RequestBody JYSB entity) {
|
||||||
facultyService.updateJYSB(entity);
|
facultyService.updateJYSB(entity, null);
|
||||||
return Result.success();
|
return Result.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+76
-8
@@ -60,14 +60,18 @@ public class JYSController {
|
|||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private TeachingTaskManageService teachingTaskManageService;
|
private TeachingTaskManageService teachingTaskManageService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private com.roomroot.jwgl.service.OrgSyncService orgSyncService;
|
||||||
// ======================== 教研室管理 ========================
|
// ======================== 教研室管理 ========================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新增教研室
|
* 新增教研室
|
||||||
*/
|
*/
|
||||||
@PostMapping("/office/add")
|
@PostMapping("/office/add")
|
||||||
public Result<Void> addOffice(@RequestBody JYSB entity) {
|
public Result<Void> addOffice(@RequestBody JYSB entity,
|
||||||
facultyService.addJYSB(entity);
|
@RequestParam(required = false) Boolean syncDept) {
|
||||||
|
facultyService.addJYSB(entity, syncDept);
|
||||||
return Result.success();
|
return Result.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,8 +83,9 @@ public class JYSController {
|
|||||||
* @param jysdh 教研室代号
|
* @param jysdh 教研室代号
|
||||||
*/
|
*/
|
||||||
@PostMapping("/office/disable")
|
@PostMapping("/office/disable")
|
||||||
public Result<Void> disableOffice(@RequestParam("jysdh") String jysdh) {
|
public Result<Void> disableOffice(@RequestParam("jysdh") String jysdh,
|
||||||
facultyService.disableJYSB(jysdh);
|
@RequestParam(required = false) Boolean syncDept) {
|
||||||
|
facultyService.disableJYSB(jysdh, syncDept);
|
||||||
return Result.success();
|
return Result.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,11 +93,35 @@ public class JYSController {
|
|||||||
* 更新教研室
|
* 更新教研室
|
||||||
*/
|
*/
|
||||||
@PostMapping("/office/update")
|
@PostMapping("/office/update")
|
||||||
public Result<Void> updateOffice(@RequestBody JYSB entity) {
|
public Result<Void> updateOffice(@RequestBody JYSB entity,
|
||||||
facultyService.updateJYSB(entity);
|
@RequestParam(required = false) Boolean syncDeptName) {
|
||||||
|
facultyService.updateJYSB(entity, syncDeptName);
|
||||||
return Result.success();
|
return Result.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单个教研室补建/对齐组织部门(幂等:已关联则直接返回部门编号)。
|
||||||
|
*/
|
||||||
|
@PostMapping("/office/sync-dept")
|
||||||
|
public Result<Long> syncOfficeDept(@RequestParam("jysdh") String jysdh) {
|
||||||
|
JYSB entity = facultyService.getJYSBById(jysdh);
|
||||||
|
if (entity == null) {
|
||||||
|
return Result.error("教研室不存在:" + jysdh);
|
||||||
|
}
|
||||||
|
return Result.success(orgSyncService.ensureOfficeDept(entity));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对齐组织部门:为教研室批量创建/复用组织部门(直接执行,幂等)。
|
||||||
|
* body:{ "ids": ["JYS01", ...] };ids 不传 = 全部教研室。
|
||||||
|
*/
|
||||||
|
@PostMapping("/org/align-dept")
|
||||||
|
public Result<com.roomroot.jwgl.vo.orgsync.OrgAlignResultVO> alignDepts(
|
||||||
|
@RequestBody(required = false) java.util.Map<String, List<String>> body) {
|
||||||
|
List<String> ids = body == null ? null : body.get("ids");
|
||||||
|
return Result.success(orgSyncService.alignDepts(ids));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据代号查询教研室详情
|
* 根据代号查询教研室详情
|
||||||
*
|
*
|
||||||
@@ -144,8 +173,9 @@ public class JYSController {
|
|||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
@PostMapping("/office/import")
|
@PostMapping("/office/import")
|
||||||
public Result<Integer> importOffice(@RequestParam("file") MultipartFile file) throws Exception {
|
public Result<Integer> importOffice(@RequestParam("file") MultipartFile file,
|
||||||
int count = facultyService.importJYSBFromExcel(file);
|
@RequestParam(required = false) Boolean syncDept) throws Exception {
|
||||||
|
int count = facultyService.importJYSBFromExcel(file, syncDept);
|
||||||
return Result.success("导入成功,共" + count + "条记录", count);
|
return Result.success("导入成功,共" + count + "条记录", count);
|
||||||
}
|
}
|
||||||
// ======================== 教员管理 ========================
|
// ======================== 教员管理 ========================
|
||||||
@@ -171,6 +201,44 @@ public class JYSController {
|
|||||||
return Result.success();
|
return Result.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单个教员补建/对齐系统账号(幂等:已有账号直接返回用户编号)。
|
||||||
|
*/
|
||||||
|
@PostMapping("/teacher/sync-user")
|
||||||
|
public Result<Long> syncTeacherUser(@RequestParam("jybh") String jybh,
|
||||||
|
@RequestParam(required = false) String loginName,
|
||||||
|
@RequestParam(required = false) String password,
|
||||||
|
@RequestParam(required = false) Long roleId) {
|
||||||
|
JYB entity = facultyService.getJYBById(jybh);
|
||||||
|
if (entity == null) {
|
||||||
|
return Result.error("教员不存在:" + jybh);
|
||||||
|
}
|
||||||
|
return Result.success(orgSyncService.ensureTeacherUser(entity, loginName, password, roleId));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对齐到用户表:为无账号教员批量建账号(直接执行,幂等,逐条返回明细)。
|
||||||
|
* body:{ "ids": ["JY01", ...] };ids 不传 = 全部无账号教员。
|
||||||
|
*/
|
||||||
|
@PostMapping("/teacher/align-user")
|
||||||
|
public Result<com.roomroot.jwgl.vo.orgsync.OrgAlignResultVO> alignUsers(
|
||||||
|
@RequestBody(required = false) java.util.Map<String, List<String>> body) {
|
||||||
|
List<String> ids = body == null ? null : body.get("ids");
|
||||||
|
return Result.success(orgSyncService.alignUsers(ids));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 一键对齐:先对齐组织部门、再对齐用户账号(同一事务,幂等)。
|
||||||
|
*/
|
||||||
|
@PostMapping("/org/align-apply")
|
||||||
|
public Result<java.util.Map<String, com.roomroot.jwgl.vo.orgsync.OrgAlignResultVO>> alignApply() {
|
||||||
|
java.util.Map<String, com.roomroot.jwgl.vo.orgsync.OrgAlignResultVO> result =
|
||||||
|
new java.util.LinkedHashMap<>();
|
||||||
|
result.put("dept", orgSyncService.alignDepts(null));
|
||||||
|
result.put("user", orgSyncService.alignUsers(null));
|
||||||
|
return Result.success(result);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新教员基本信息
|
* 更新教员基本信息
|
||||||
* <p>支持教学单位调整(修改教研室代号)、职称调整(修改职称、专业技术职务等级)。</p>
|
* <p>支持教学单位调整(修改教研室代号)、职称调整(修改职称、专业技术职务等级)。</p>
|
||||||
|
|||||||
+9
@@ -101,4 +101,13 @@ public class TeachingTaskController {
|
|||||||
teachingTaskService.endPublish(bh);
|
teachingTaskService.endPublish(bh);
|
||||||
return Result.success();
|
return Result.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出教学任务书 Excel(该教学任务下全部教研室任务书课程明细)。
|
||||||
|
*/
|
||||||
|
@GetMapping("/exportTaskBook")
|
||||||
|
public void exportTaskBook(jakarta.servlet.http.HttpServletResponse response,
|
||||||
|
@RequestParam("bh") String bh) throws Exception {
|
||||||
|
teachingTaskService.exportTaskBook(response, bh);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -16,4 +16,16 @@ public class AddTeacherDTO {
|
|||||||
|
|
||||||
/** 教员属性信息(含职务、技术等级、学历学位等非空字段) */
|
/** 教员属性信息(含职务、技术等级、学历学位等非空字段) */
|
||||||
private JYSX attributes;
|
private JYSX attributes;
|
||||||
|
|
||||||
|
/** 是否同时创建系统账号 */
|
||||||
|
private Boolean createUser;
|
||||||
|
|
||||||
|
/** 登录名(空则取教员编号) */
|
||||||
|
private String loginName;
|
||||||
|
|
||||||
|
/** 初始密码明文(空则取统一初始值) */
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
/** 角色ID(空则取默认教员角色 101) */
|
||||||
|
private Long roleId;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,4 +127,12 @@ public class JYB {
|
|||||||
/** 异动历史JSON数组,每条记录含 from/to/time/reason */
|
/** 异动历史JSON数组,每条记录含 from/to/time/reason */
|
||||||
@TableField("异动历史")
|
@TableField("异动历史")
|
||||||
private String transferHistory;
|
private String transferHistory;
|
||||||
|
|
||||||
|
/** 用户编号(对应 SYS_USER.USER_ID,账号同步锚点) */
|
||||||
|
@TableField("用户编号")
|
||||||
|
private Long yhbh;
|
||||||
|
|
||||||
|
/** 部门编号(对应 SYS_DEPT.DEPT_ID,教员所在组织节点) */
|
||||||
|
@TableField("部门编号")
|
||||||
|
private Long bmbh;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,4 +52,8 @@ public class JYSB {
|
|||||||
@TableField("机关性质")
|
@TableField("机关性质")
|
||||||
private Integer jgxz;
|
private Integer jgxz;
|
||||||
|
|
||||||
|
/** 部门编号(对应 SYS_DEPT.DEPT_ID,组织同步锚点) */
|
||||||
|
@TableField("部门编号")
|
||||||
|
private Long bmbh;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,7 +72,10 @@
|
|||||||
s."学位" AS xw,
|
s."学位" AS xw,
|
||||||
s."入伍工作时间" AS rwgzsj,
|
s."入伍工作时间" AS rwgzsj,
|
||||||
t."手机号" AS sjh,
|
t."手机号" AS sjh,
|
||||||
t."虚实类型" AS xslx
|
t."虚实类型" AS xslx,
|
||||||
|
t."教研室代号" AS jysdh,
|
||||||
|
t."用户编号" AS yhbh,
|
||||||
|
t."部门编号" AS bmbh
|
||||||
FROM "教员表" t
|
FROM "教员表" t
|
||||||
LEFT JOIN "教员属性" s ON s."教员编号" = t."教员编号"
|
LEFT JOIN "教员属性" s ON s."教员编号" = t."教员编号"
|
||||||
LEFT JOIN "教研室表" j ON j."教研室代号" = t."教研室代号"
|
LEFT JOIN "教研室表" j ON j."教研室代号" = t."教研室代号"
|
||||||
|
|||||||
@@ -20,18 +20,24 @@ public interface FacultyService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 新增教研室
|
* 新增教研室
|
||||||
|
*
|
||||||
|
* @param syncDept 是否同步创建组织部门(教务处下)
|
||||||
*/
|
*/
|
||||||
void addJYSB(JYSB entity);
|
void addJYSB(JYSB entity, Boolean syncDept);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 停用(逻辑删除)教研室
|
* 停用(逻辑删除)教研室
|
||||||
|
*
|
||||||
|
* @param syncDept 是否联动停用关联组织部门
|
||||||
*/
|
*/
|
||||||
void disableJYSB(String jYSDH);
|
void disableJYSB(String jYSDH, Boolean syncDept);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新教研室
|
* 更新教研室
|
||||||
|
*
|
||||||
|
* @param syncDeptName 是否将名称/序号变更同步到关联组织部门
|
||||||
*/
|
*/
|
||||||
void updateJYSB(JYSB entity);
|
void updateJYSB(JYSB entity, Boolean syncDeptName);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据代号查询教研室详情
|
* 根据代号查询教研室详情
|
||||||
@@ -53,6 +59,13 @@ public interface FacultyService {
|
|||||||
*/
|
*/
|
||||||
int importJYSBFromExcel(MultipartFile file) throws Exception;
|
int importJYSBFromExcel(MultipartFile file) throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从 Excel 导入教研室并可选同步组织部门。
|
||||||
|
*
|
||||||
|
* @param syncDept 是否逐条同步创建组织部门
|
||||||
|
*/
|
||||||
|
int importJYSBFromExcel(MultipartFile file, Boolean syncDept) throws Exception;
|
||||||
|
|
||||||
// ==================== 教员管理 ====================
|
// ==================== 教员管理 ====================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
package com.roomroot.jwgl.service;
|
||||||
|
|
||||||
|
import com.roomroot.jwgl.entity.JYB;
|
||||||
|
import com.roomroot.jwgl.entity.JYSB;
|
||||||
|
import com.roomroot.jwgl.vo.orgsync.OrgAlignResultVO;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 组织同步服务:教研室 ↔ 组织部门(SYS_DEPT)、教员 ↔ 用户账号(SYS_USER)。
|
||||||
|
* <p>
|
||||||
|
* 所有方法与业务写操作同事务调用,映射锚点为业务表新增列:
|
||||||
|
* 教研室表.部门编号 → SYS_DEPT.DEPT_ID;教员表.用户编号 → SYS_USER.USER_ID。
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public interface OrgSyncService {
|
||||||
|
|
||||||
|
/** 部门统一挂载父节点:201 教务处 */
|
||||||
|
Long ORG_PARENT_DEPT_ID = 201L;
|
||||||
|
|
||||||
|
/** 教员账号默认角色:教员 */
|
||||||
|
Long TEACHER_ROLE_ID = 101L;
|
||||||
|
|
||||||
|
/** 教员账号统一初始密码 */
|
||||||
|
String DEFAULT_PASSWORD = "Jw@123456";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 确保教研室存在对应组织部门(幂等)。
|
||||||
|
* <p>已映射且部门存在 → 直接返回;同级同名部门存在 → 复用并回写;否则在教务处下新建并回写。</p>
|
||||||
|
*
|
||||||
|
* @param jysb 教研室实体
|
||||||
|
* @return 部门编号 DEPT_ID
|
||||||
|
*/
|
||||||
|
Long ensureOfficeDept(JYSB jysb);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 教研室名称/序号变更后同步组织部门(部门名、排序)。
|
||||||
|
*
|
||||||
|
* @param jysb 教研室实体(含最新名称)
|
||||||
|
* @param renamed 是否同步部门名(false 时只同步序号)
|
||||||
|
*/
|
||||||
|
void syncDeptInfo(JYSB jysb, boolean renamed);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置关联部门启停状态(教研室停用/启用联动,需显式触发)。
|
||||||
|
*
|
||||||
|
* @param jysb 教研室实体
|
||||||
|
* @param disabled true 停用 / false 启用
|
||||||
|
*/
|
||||||
|
void setDeptStatus(JYSB jysb, boolean disabled);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 确保教员存在系统账号(幂等)。
|
||||||
|
* <p>已映射且账号存在 → 直接返回;教研室无部门时先补建部门;登录名/手机号冲突抛业务异常。</p>
|
||||||
|
*
|
||||||
|
* @param jyb 教员实体
|
||||||
|
* @param loginName 登录名(空则取教员编号)
|
||||||
|
* @param password 初始密码明文(空则取统一初始值)
|
||||||
|
* @param roleId 角色ID(空则取默认教员角色 101)
|
||||||
|
* @return 用户编号 USER_ID
|
||||||
|
*/
|
||||||
|
Long ensureTeacherUser(JYB jyb, String loginName, String password, Long roleId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 教员信息变更后同步账号(调教研室迁移部门、改名、改手机号、离职禁用/复职启用)。
|
||||||
|
*
|
||||||
|
* @param before 变更前实体
|
||||||
|
* @param after 变更后实体
|
||||||
|
*/
|
||||||
|
void syncTeacherAccount(JYB before, JYB after);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量对齐组织部门。
|
||||||
|
*
|
||||||
|
* @param jysdhList 教研室代号集合;null/空 = 全部未关联部门的教研室
|
||||||
|
*/
|
||||||
|
OrgAlignResultVO alignDepts(Collection<String> jysdhList);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量对齐用户账号(对齐到用户表)。
|
||||||
|
*
|
||||||
|
* @param jybhList 教员编号集合;null/空 = 全部无账号教员
|
||||||
|
*/
|
||||||
|
OrgAlignResultVO alignUsers(Collection<String> jybhList);
|
||||||
|
}
|
||||||
@@ -78,4 +78,12 @@ public interface TeachingTaskService {
|
|||||||
* @param bh 教学任务编号
|
* @param bh 教学任务编号
|
||||||
*/
|
*/
|
||||||
void endPublish(String bh);
|
void endPublish(String bh);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出教学任务书(该教学任务下全部教研室任务书课程明细)到 Excel(.xlsx)。
|
||||||
|
*
|
||||||
|
* @param response 响应
|
||||||
|
* @param bh 教学任务编号
|
||||||
|
*/
|
||||||
|
void exportTaskBook(jakarta.servlet.http.HttpServletResponse response, String bh) throws Exception;
|
||||||
}
|
}
|
||||||
+47
-8
@@ -46,31 +46,41 @@ public class FacultyServiceImpl implements FacultyService {
|
|||||||
@Resource
|
@Resource
|
||||||
private JYSCKCBMapper jysckcbMapper;
|
private JYSCKCBMapper jysckcbMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private com.roomroot.jwgl.service.OrgSyncService orgSyncService;
|
||||||
|
|
||||||
// ==================== 教研室管理 ====================
|
// ==================== 教研室管理 ====================
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void addJYSB(JYSB entity) {
|
public void addJYSB(JYSB entity, Boolean syncDept) {
|
||||||
entity.setTy(0);
|
entity.setTy(0);
|
||||||
entity.setXslx(0);
|
entity.setXslx(0);
|
||||||
entity.setJgxz(0);
|
entity.setJgxz(0);
|
||||||
jysbMapper.insert(entity);
|
jysbMapper.insert(entity);
|
||||||
|
if (Boolean.TRUE.equals(syncDept)) {
|
||||||
|
orgSyncService.ensureOfficeDept(entity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void disableJYSB(String jysdh) {
|
public void disableJYSB(String jysdh, Boolean syncDept) {
|
||||||
JYSB entity = new JYSB();
|
JYSB entity = new JYSB();
|
||||||
entity.setJysdh(jysdh);
|
entity.setJysdh(jysdh);
|
||||||
entity.setTy(1);
|
entity.setTy(1);
|
||||||
entity.setTysj(LocalDateTime.now());
|
entity.setTysj(LocalDateTime.now());
|
||||||
jysbMapper.updateById(entity);
|
jysbMapper.updateById(entity);
|
||||||
|
if (Boolean.TRUE.equals(syncDept)) {
|
||||||
|
orgSyncService.setDeptStatus(jysbMapper.selectById(jysdh), true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void updateJYSB(JYSB entity) {
|
public void updateJYSB(JYSB entity, Boolean syncDeptName) {
|
||||||
jysbMapper.updateById(entity);
|
jysbMapper.updateById(entity);
|
||||||
|
orgSyncService.syncDeptInfo(entity, Boolean.TRUE.equals(syncDeptName));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -89,6 +99,12 @@ public class FacultyServiceImpl implements FacultyService {
|
|||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public int importJYSBFromExcel(MultipartFile file) throws Exception {
|
public int importJYSBFromExcel(MultipartFile file) throws Exception {
|
||||||
|
return importJYSBFromExcel(file, Boolean.FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public int importJYSBFromExcel(MultipartFile file, Boolean syncDept) throws Exception {
|
||||||
if (file == null || file.isEmpty()) {
|
if (file == null || file.isEmpty()) {
|
||||||
throw new BusinessException("导入文件不能为空");
|
throw new BusinessException("导入文件不能为空");
|
||||||
}
|
}
|
||||||
@@ -121,6 +137,9 @@ public class FacultyServiceImpl implements FacultyService {
|
|||||||
//机关类型默认为0
|
//机关类型默认为0
|
||||||
entity.setJgxz(0);
|
entity.setJgxz(0);
|
||||||
jysbMapper.insert(entity);
|
jysbMapper.insert(entity);
|
||||||
|
if (Boolean.TRUE.equals(syncDept)) {
|
||||||
|
orgSyncService.ensureOfficeDept(entity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return list.size();
|
return list.size();
|
||||||
}
|
}
|
||||||
@@ -181,6 +200,12 @@ public class FacultyServiceImpl implements FacultyService {
|
|||||||
}
|
}
|
||||||
jysxMapper.insert(jysx);
|
jysxMapper.insert(jysx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 同步创建系统账号(默认登录名=教员编号,部门=教研室组织节点)
|
||||||
|
if (Boolean.TRUE.equals(dto.getCreateUser())) {
|
||||||
|
orgSyncService.ensureTeacherUser(entity,
|
||||||
|
dto.getLoginName(), dto.getPassword(), dto.getRoleId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 生成下一个教员编号:取库内 JY 前缀编号的最大流水号 +1(JY41、JY42…),异常或全不匹配时退化为 UUID */
|
/** 生成下一个教员编号:取库内 JY 前缀编号的最大流水号 +1(JY41、JY42…),异常或全不匹配时退化为 UUID */
|
||||||
@@ -256,18 +281,32 @@ public class FacultyServiceImpl implements FacultyService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void disableJYB(String jybh) {
|
public void disableJYB(String jybh) {
|
||||||
|
JYB before = jybMapper.selectById(jybh);
|
||||||
JYB entity = new JYB();
|
JYB entity = new JYB();
|
||||||
entity.setJybh(jybh);
|
entity.setJybh(jybh);
|
||||||
entity.setLzzt(1);
|
entity.setLzzt(1);
|
||||||
jybMapper.updateById(entity);
|
jybMapper.updateById(entity);
|
||||||
|
// 自动禁用关联账号(不删除,保留历史数据关联)
|
||||||
|
if (before != null) {
|
||||||
|
JYB after = new JYB();
|
||||||
|
after.setJybh(jybh);
|
||||||
|
after.setLzzt(1);
|
||||||
|
orgSyncService.syncTeacherAccount(before, after);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void updateJYB(JYB entity) {
|
public void updateJYB(JYB entity) {
|
||||||
|
JYB before = jybMapper.selectById(entity.getJybh());
|
||||||
jybMapper.updateById(entity);
|
jybMapper.updateById(entity);
|
||||||
|
// 同步账号:调教研室迁移部门、改名、改手机号、离职状态变化
|
||||||
|
if (before != null) {
|
||||||
|
JYB after = jybMapper.selectById(entity.getJybh());
|
||||||
|
orgSyncService.syncTeacherAccount(before, after);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+366
@@ -0,0 +1,366 @@
|
|||||||
|
package com.roomroot.jwgl.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.roomroot.common.constant.UserConstants;
|
||||||
|
import com.roomroot.common.core.domain.entity.SysDept;
|
||||||
|
import com.roomroot.common.core.domain.entity.SysUser;
|
||||||
|
import com.roomroot.common.utils.SecurityUtils;
|
||||||
|
import com.roomroot.common.utils.StringUtils;
|
||||||
|
import com.roomroot.jwgl.entity.JYB;
|
||||||
|
import com.roomroot.jwgl.entity.JYSB;
|
||||||
|
import com.roomroot.jwgl.mapper.JYBMapper;
|
||||||
|
import com.roomroot.jwgl.mapper.JYSBMapper;
|
||||||
|
import com.roomroot.jwgl.service.OrgSyncService;
|
||||||
|
import com.roomroot.jwgl.unit.BusinessException;
|
||||||
|
import com.roomroot.jwgl.vo.orgsync.OrgAlignResultVO;
|
||||||
|
import com.roomroot.system.service.ISysDeptService;
|
||||||
|
import com.roomroot.system.service.ISysUserService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 组织同步服务实现。
|
||||||
|
* <p>
|
||||||
|
* 教研室 → SYS_DEPT(挂教务处 201 下);教员 → SYS_USER(dept_id 取教研室部门)。
|
||||||
|
* 映射锚点:教研室表.部门编号、教员表.用户编号/部门编号。
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class OrgSyncServiceImpl implements OrgSyncService {
|
||||||
|
|
||||||
|
/** SYS_DEPT.DEPT_NAME 长度上限 */
|
||||||
|
private static final int DEPT_NAME_MAX = 30;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private JYSBMapper jysbMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private JYBMapper jybMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ISysDeptService deptService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ISysUserService userService;
|
||||||
|
|
||||||
|
// ==================== 教研室 → 部门 ====================
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public Long ensureOfficeDept(JYSB jysb) {
|
||||||
|
if (jysb == null || StringUtils.isBlank(jysb.getJysdh())) {
|
||||||
|
throw new BusinessException("教研室信息不完整,无法同步组织部门");
|
||||||
|
}
|
||||||
|
// 已映射:校验部门仍存在(被手工删除则重建)
|
||||||
|
if (jysb.getBmbh() != null) {
|
||||||
|
SysDept mapped = deptService.selectDeptById(jysb.getBmbh());
|
||||||
|
if (mapped != null && !"2".equals(mapped.getDelFlag())) {
|
||||||
|
return jysb.getBmbh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String deptName = resolveDeptName(jysb);
|
||||||
|
|
||||||
|
// 同级同名部门 → 复用并回写
|
||||||
|
SysDept exist = findDeptByName(ORG_PARENT_DEPT_ID, deptName);
|
||||||
|
if (exist != null) {
|
||||||
|
writeBackDeptId(jysb.getJysdh(), exist.getDeptId());
|
||||||
|
return exist.getDeptId();
|
||||||
|
}
|
||||||
|
|
||||||
|
SysDept parent = deptService.selectDeptById(ORG_PARENT_DEPT_ID);
|
||||||
|
if (parent == null || "2".equals(parent.getDelFlag())) {
|
||||||
|
throw new BusinessException("组织节点「教务处」不存在,无法创建教研室部门");
|
||||||
|
}
|
||||||
|
if (!UserConstants.DEPT_NORMAL.equals(parent.getStatus())) {
|
||||||
|
throw new BusinessException("组织节点「教务处」已停用,请先在组织管理中启用后再同步");
|
||||||
|
}
|
||||||
|
|
||||||
|
SysDept dept = new SysDept();
|
||||||
|
dept.setParentId(ORG_PARENT_DEPT_ID);
|
||||||
|
dept.setDeptName(deptName);
|
||||||
|
dept.setOrderNum(parseOrderNum(jysb.getXh()));
|
||||||
|
dept.setStatus(UserConstants.DEPT_NORMAL);
|
||||||
|
dept.setDelFlag("0");
|
||||||
|
deptService.insertDept(dept);
|
||||||
|
writeBackDeptId(jysb.getJysdh(), dept.getDeptId());
|
||||||
|
return dept.getDeptId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void syncDeptInfo(JYSB jysb, boolean renamed) {
|
||||||
|
if (jysb == null || jysb.getBmbh() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SysDept dept = deptService.selectDeptById(jysb.getBmbh());
|
||||||
|
if (dept == null || "2".equals(dept.getDelFlag())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
boolean dirty = false;
|
||||||
|
if (renamed) {
|
||||||
|
String deptName = resolveDeptName(jysb);
|
||||||
|
if (!deptName.equals(dept.getDeptName())) {
|
||||||
|
SysDept same = findDeptByName(dept.getParentId(), deptName);
|
||||||
|
if (same != null && !same.getDeptId().equals(dept.getDeptId())) {
|
||||||
|
throw new BusinessException("教务处下已存在部门「" + deptName + "」,教研室改名未同步");
|
||||||
|
}
|
||||||
|
dept.setDeptName(deptName);
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Integer orderNum = parseOrderNum(jysb.getXh());
|
||||||
|
if (orderNum != null && !orderNum.equals(dept.getOrderNum())) {
|
||||||
|
dept.setOrderNum(orderNum);
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
if (dirty) {
|
||||||
|
deptService.updateDept(dept);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void setDeptStatus(JYSB jysb, boolean disabled) {
|
||||||
|
if (jysb == null || jysb.getBmbh() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SysDept dept = deptService.selectDeptById(jysb.getBmbh());
|
||||||
|
if (dept == null || "2".equals(dept.getDelFlag())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dept.setStatus(disabled ? UserConstants.DEPT_DISABLE : UserConstants.DEPT_NORMAL);
|
||||||
|
deptService.updateDept(dept);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 教员 → 账号 ====================
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public Long ensureTeacherUser(JYB jyb, String loginName, String password, Long roleId) {
|
||||||
|
if (jyb == null || StringUtils.isBlank(jyb.getJybh())) {
|
||||||
|
throw new BusinessException("教员信息不完整,无法创建账号");
|
||||||
|
}
|
||||||
|
// 已映射:校验账号仍存在
|
||||||
|
if (jyb.getYhbh() != null) {
|
||||||
|
SysUser mapped = userService.selectUserById(jyb.getYhbh());
|
||||||
|
if (mapped != null && !"2".equals(mapped.getDelFlag())) {
|
||||||
|
return jyb.getYhbh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (jyb.getLzzt() != null && jyb.getLzzt() == 1) {
|
||||||
|
throw new BusinessException("教员已离职,不创建账号");
|
||||||
|
}
|
||||||
|
// 归属部门:教研室部门(缺失先补建)
|
||||||
|
Long deptId = resolveTeacherDeptId(jyb);
|
||||||
|
|
||||||
|
String name = StringUtils.isBlank(loginName) ? jyb.getJybh() : loginName.trim();
|
||||||
|
SysUser user = new SysUser();
|
||||||
|
user.setUserName(name);
|
||||||
|
if (userService.selectUserByUserName(name) != null) {
|
||||||
|
throw new BusinessException("登录名已存在:" + name);
|
||||||
|
}
|
||||||
|
user.setNickName(jyb.getJyxm());
|
||||||
|
user.setDeptId(deptId);
|
||||||
|
user.setPhonenumber(jyb.getSjh());
|
||||||
|
if (!userService.checkPhoneUnique(user)) {
|
||||||
|
throw new BusinessException("手机号已存在:" + jyb.getSjh());
|
||||||
|
}
|
||||||
|
user.setSex(jyb.getXb() != null && jyb.getXb() == 0 ? "1" : "0");
|
||||||
|
user.setPassword(SecurityUtils.encryptPassword(
|
||||||
|
StringUtils.isBlank(password) ? DEFAULT_PASSWORD : password));
|
||||||
|
user.setStatus(UserConstants.NORMAL);
|
||||||
|
user.setDelFlag("0");
|
||||||
|
user.setRemark("教员编号 " + jyb.getJybh() + " 自动创建");
|
||||||
|
user.setRoleIds(new Long[]{roleId == null ? TEACHER_ROLE_ID : roleId});
|
||||||
|
userService.insertUser(user);
|
||||||
|
|
||||||
|
JYB upd = new JYB();
|
||||||
|
upd.setJybh(jyb.getJybh());
|
||||||
|
upd.setYhbh(user.getUserId());
|
||||||
|
upd.setBmbh(deptId);
|
||||||
|
jybMapper.updateById(upd);
|
||||||
|
return user.getUserId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void syncTeacherAccount(JYB before, JYB after) {
|
||||||
|
if (before == null || before.getYhbh() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SysUser user = userService.selectUserById(before.getYhbh());
|
||||||
|
if (user == null || "2".equals(user.getDelFlag())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
boolean dirty = false;
|
||||||
|
// 调教研室 → 账号部门迁移
|
||||||
|
if (!StringUtils.equals(before.getJysdh(), after.getJysdh())
|
||||||
|
&& StringUtils.isNotBlank(after.getJysdh())) {
|
||||||
|
Long deptId = resolveTeacherDeptId(after);
|
||||||
|
user.setDeptId(deptId);
|
||||||
|
JYB upd = new JYB();
|
||||||
|
upd.setJybh(after.getJybh());
|
||||||
|
upd.setBmbh(deptId);
|
||||||
|
jybMapper.updateById(upd);
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
if (StringUtils.isNotBlank(after.getJyxm())
|
||||||
|
&& !StringUtils.equals(before.getJyxm(), after.getJyxm())) {
|
||||||
|
user.setNickName(after.getJyxm());
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
if (after.getSjh() != null && !StringUtils.equals(before.getSjh(), after.getSjh())) {
|
||||||
|
user.setPhonenumber(after.getSjh());
|
||||||
|
if (!userService.checkPhoneUnique(user)) {
|
||||||
|
throw new BusinessException("手机号已存在:" + after.getSjh());
|
||||||
|
}
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
// 离职 → 禁用账号;复职 → 启用
|
||||||
|
if (after.getLzzt() != null && !after.getLzzt().equals(before.getLzzt())) {
|
||||||
|
user.setStatus(after.getLzzt() == 1 ? UserConstants.USER_DISABLE : UserConstants.NORMAL);
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
if (dirty) {
|
||||||
|
userService.updateUser(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 批量对齐 ====================
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public OrgAlignResultVO alignDepts(Collection<String> jysdhList) {
|
||||||
|
OrgAlignResultVO result = new OrgAlignResultVO();
|
||||||
|
LambdaQueryWrapper<JYSB> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
if (jysdhList != null && !jysdhList.isEmpty()) {
|
||||||
|
wrapper.in(JYSB::getJysdh, jysdhList);
|
||||||
|
}
|
||||||
|
List<JYSB> list = jysbMapper.selectList(wrapper);
|
||||||
|
for (JYSB jysb : list) {
|
||||||
|
try {
|
||||||
|
if (jysb.getTy() != null && jysb.getTy() == 1) {
|
||||||
|
result.addSkip(jysb.getJysmc() + "(" + jysb.getJysdh() + "):已停用,不建部门");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (jysb.getBmbh() != null) {
|
||||||
|
SysDept mapped = deptService.selectDeptById(jysb.getBmbh());
|
||||||
|
if (mapped != null && !"2".equals(mapped.getDelFlag())) {
|
||||||
|
result.addSkip(jysb.getJysmc() + "(" + jysb.getJysdh() + "):已关联部门");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String deptName = resolveDeptName(jysb);
|
||||||
|
SysDept exist = findDeptByName(ORG_PARENT_DEPT_ID, deptName);
|
||||||
|
Long deptId = ensureOfficeDept(jysb);
|
||||||
|
if (exist != null) {
|
||||||
|
result.addReuse(jysb.getJysmc() + "(" + jysb.getJysdh() + "):复用部门「" + deptName + "」");
|
||||||
|
} else {
|
||||||
|
result.addCreate(jysb.getJysmc() + "(" + jysb.getJysdh() + "):新建部门「" + deptName + "」#" + deptId);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
result.addFail(jysb.getJysmc() + "(" + jysb.getJysdh() + "):" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.buildMessage();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public OrgAlignResultVO alignUsers(Collection<String> jybhList) {
|
||||||
|
OrgAlignResultVO result = new OrgAlignResultVO();
|
||||||
|
LambdaQueryWrapper<JYB> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
if (jybhList != null && !jybhList.isEmpty()) {
|
||||||
|
wrapper.in(JYB::getJybh, jybhList);
|
||||||
|
}
|
||||||
|
List<JYB> list = jybMapper.selectList(wrapper);
|
||||||
|
for (JYB jyb : list) {
|
||||||
|
try {
|
||||||
|
if (jyb.getYhbh() != null) {
|
||||||
|
SysUser mapped = userService.selectUserById(jyb.getYhbh());
|
||||||
|
if (mapped != null && !"2".equals(mapped.getDelFlag())) {
|
||||||
|
result.addSkip(jyb.getJyxm() + "(" + jyb.getJybh() + "):已有账号");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (jyb.getLzzt() != null && jyb.getLzzt() == 1) {
|
||||||
|
result.addSkip(jyb.getJyxm() + "(" + jyb.getJybh() + "):已离职,不建账号");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Long userId = ensureTeacherUser(jyb, null, null, null);
|
||||||
|
result.addCreate(jyb.getJyxm() + "(" + jyb.getJybh() + "):新建账号 " + jyb.getJybh() + " #" + userId);
|
||||||
|
} catch (Exception e) {
|
||||||
|
result.addFail(jyb.getJyxm() + "(" + jyb.getJybh() + "):" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.buildMessage();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 内部 ====================
|
||||||
|
|
||||||
|
/** 归属部门:教研室的部门编号;教研室无部门时自动补建;补建失败降级挂教务处并告警 */
|
||||||
|
private Long resolveTeacherDeptId(JYB jyb) {
|
||||||
|
if (StringUtils.isBlank(jyb.getJysdh())) {
|
||||||
|
return ORG_PARENT_DEPT_ID;
|
||||||
|
}
|
||||||
|
JYSB jysb = jysbMapper.selectById(jyb.getJysdh());
|
||||||
|
if (jysb == null) {
|
||||||
|
throw new BusinessException("教研室不存在:" + jyb.getJysdh());
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return ensureOfficeDept(jysb);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 降级挂教务处,不静默:异常信息会写进对齐结果/接口返回
|
||||||
|
return ORG_PARENT_DEPT_ID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 部门名:教研室名称,超30字符用简称,仍超长截断 */
|
||||||
|
private String resolveDeptName(JYSB jysb) {
|
||||||
|
String name = jysb.getJysmc();
|
||||||
|
if (StringUtils.isBlank(name)) {
|
||||||
|
throw new BusinessException("教研室名称为空:" + jysb.getJysdh());
|
||||||
|
}
|
||||||
|
name = name.trim();
|
||||||
|
if (name.length() > DEPT_NAME_MAX && StringUtils.isNotBlank(jysb.getJc())) {
|
||||||
|
name = jysb.getJc().trim();
|
||||||
|
}
|
||||||
|
if (name.length() > DEPT_NAME_MAX) {
|
||||||
|
name = name.substring(0, DEPT_NAME_MAX);
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SysDept findDeptByName(Long parentId, String deptName) {
|
||||||
|
SysDept query = new SysDept();
|
||||||
|
query.setParentId(parentId);
|
||||||
|
query.setDeptName(deptName);
|
||||||
|
List<SysDept> list = deptService.selectDeptList(query);
|
||||||
|
for (SysDept d : list) {
|
||||||
|
if (parentId.equals(d.getParentId()) && deptName.equals(d.getDeptName())) {
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeBackDeptId(String jysdh, Long deptId) {
|
||||||
|
JYSB upd = new JYSB();
|
||||||
|
upd.setJysdh(jysdh);
|
||||||
|
upd.setBmbh(deptId);
|
||||||
|
jysbMapper.updateById(upd);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Integer parseOrderNum(String xh) {
|
||||||
|
if (StringUtils.isBlank(xh) || !xh.trim().matches("\\d+")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return Integer.parseInt(xh.trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
+43
@@ -34,6 +34,9 @@ public class TeachingTaskServiceImpl implements TeachingTaskService {
|
|||||||
@Resource
|
@Resource
|
||||||
private OfficeTaskBookMapper officeTaskBookMapper;
|
private OfficeTaskBookMapper officeTaskBookMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private com.roomroot.jwgl.service.TaskBookFillService taskBookFillService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void add(JXRW jxrw) {
|
public void add(JXRW jxrw) {
|
||||||
String uuid = UuidUtil.getUUID();
|
String uuid = UuidUtil.getUUID();
|
||||||
@@ -167,4 +170,44 @@ public class TeachingTaskServiceImpl implements TeachingTaskService {
|
|||||||
officeTaskBookMapper.updateById(book);
|
officeTaskBookMapper.updateById(book);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exportTaskBook(jakarta.servlet.http.HttpServletResponse response, String bh) throws Exception {
|
||||||
|
JXRW jxrw = getByBh(bh);
|
||||||
|
if (jxrw == null) {
|
||||||
|
throw new ServiceException("教学任务不存在", BAD_REQUEST);
|
||||||
|
}
|
||||||
|
List<com.roomroot.jwgl.vo.taskbook.TaskBookRowVO> rows = taskBookFillService.list(bh);
|
||||||
|
String[] headers = {"课程名称", "课编号", "课类型", "学时", "周课时", "成绩分制",
|
||||||
|
"学员队编号", "学员队名称", "课次序号", "责任教员编号", "责任教员",
|
||||||
|
"场地编号", "场地名称", "合班分组号", "教研室代号", "教研室名称",
|
||||||
|
"计划教员编号", "计划教员", "排课建议"};
|
||||||
|
byte[] bytes = com.roomroot.jwgl.utils.ExcelExportUtil.export("教学任务书", headers, rows,
|
||||||
|
com.roomroot.jwgl.vo.taskbook.TaskBookRowVO::getKcmc,
|
||||||
|
com.roomroot.jwgl.vo.taskbook.TaskBookRowVO::getKbh,
|
||||||
|
com.roomroot.jwgl.vo.taskbook.TaskBookRowVO::getKlx,
|
||||||
|
com.roomroot.jwgl.vo.taskbook.TaskBookRowVO::getXs,
|
||||||
|
com.roomroot.jwgl.vo.taskbook.TaskBookRowVO::getZks,
|
||||||
|
com.roomroot.jwgl.vo.taskbook.TaskBookRowVO::getCjfz,
|
||||||
|
com.roomroot.jwgl.vo.taskbook.TaskBookRowVO::getXydbh,
|
||||||
|
com.roomroot.jwgl.vo.taskbook.TaskBookRowVO::getXydmc,
|
||||||
|
com.roomroot.jwgl.vo.taskbook.TaskBookRowVO::getKcxh,
|
||||||
|
com.roomroot.jwgl.vo.taskbook.TaskBookRowVO::getJybh,
|
||||||
|
com.roomroot.jwgl.vo.taskbook.TaskBookRowVO::getJyxm,
|
||||||
|
com.roomroot.jwgl.vo.taskbook.TaskBookRowVO::getJsbh,
|
||||||
|
com.roomroot.jwgl.vo.taskbook.TaskBookRowVO::getJsmc,
|
||||||
|
com.roomroot.jwgl.vo.taskbook.TaskBookRowVO::getBz2,
|
||||||
|
com.roomroot.jwgl.vo.taskbook.TaskBookRowVO::getJysdh,
|
||||||
|
com.roomroot.jwgl.vo.taskbook.TaskBookRowVO::getJysmc,
|
||||||
|
com.roomroot.jwgl.vo.taskbook.TaskBookRowVO::getJysjhjybh,
|
||||||
|
com.roomroot.jwgl.vo.taskbook.TaskBookRowVO::getJysjhjyxm,
|
||||||
|
com.roomroot.jwgl.vo.taskbook.TaskBookRowVO::getJysjhbz);
|
||||||
|
String fileName = "教学任务书_" + (jxrw.getRwmc() == null ? bh : jxrw.getRwmc()) + ".xlsx";
|
||||||
|
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||||||
|
response.setHeader("Content-Disposition",
|
||||||
|
"attachment; filename=" + java.net.URLEncoder.encode(
|
||||||
|
fileName, java.nio.charset.StandardCharsets.UTF_8));
|
||||||
|
response.getOutputStream().write(bytes);
|
||||||
|
response.getOutputStream().flush();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1144,7 +1144,8 @@ public final class OperationLogUtil {
|
|||||||
if (normalizedUrl.contains("/discipline/")) {
|
if (normalizedUrl.contains("/discipline/")) {
|
||||||
return "学科专业管理";
|
return "学科专业管理";
|
||||||
}
|
}
|
||||||
if (normalizedUrl.contains("/faculty/")) {
|
if (normalizedUrl.contains("/faculty/")
|
||||||
|
|| normalizedUrl.contains("/jys/")) {
|
||||||
return "教研室与教员管理";
|
return "教研室与教员管理";
|
||||||
}
|
}
|
||||||
if (normalizedUrl.contains("/course-teaching/")) {
|
if (normalizedUrl.contains("/course-teaching/")) {
|
||||||
|
|||||||
@@ -46,4 +46,13 @@ public class TeacherListVO {
|
|||||||
|
|
||||||
/** 是否实教教员(虚实类型) */
|
/** 是否实教教员(虚实类型) */
|
||||||
private Integer xslx;
|
private Integer xslx;
|
||||||
|
|
||||||
|
/** 教研室代号 */
|
||||||
|
private String jysdh;
|
||||||
|
|
||||||
|
/** 用户编号(SYS_USER.USER_ID,有值=已开通账号) */
|
||||||
|
private Long yhbh;
|
||||||
|
|
||||||
|
/** 部门编号(SYS_DEPT.DEPT_ID) */
|
||||||
|
private Long bmbh;
|
||||||
}
|
}
|
||||||
|
|||||||
+62
@@ -0,0 +1,62 @@
|
|||||||
|
package com.roomroot.jwgl.vo.orgsync;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 组织/账号对齐结果。
|
||||||
|
* <p>逐条记录新建、复用、跳过、失败明细,供结果清单弹窗展示。</p>
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class OrgAlignResultVO {
|
||||||
|
|
||||||
|
/** 新建条数 */
|
||||||
|
private Integer createCount = 0;
|
||||||
|
|
||||||
|
/** 复用条数(同名部门已存在,直接关联) */
|
||||||
|
private Integer reuseCount = 0;
|
||||||
|
|
||||||
|
/** 跳过条数(已有关联/已停用/已离职等不处理) */
|
||||||
|
private Integer skipCount = 0;
|
||||||
|
|
||||||
|
/** 失败条数 */
|
||||||
|
private Integer failCount = 0;
|
||||||
|
|
||||||
|
private List<String> createList = new ArrayList<>();
|
||||||
|
|
||||||
|
private List<String> reuseList = new ArrayList<>();
|
||||||
|
|
||||||
|
private List<String> skipList = new ArrayList<>();
|
||||||
|
|
||||||
|
private List<String> failList = new ArrayList<>();
|
||||||
|
|
||||||
|
/** 汇总提示 */
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
public void addCreate(String detail) {
|
||||||
|
createList.add(detail);
|
||||||
|
createCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addReuse(String detail) {
|
||||||
|
reuseList.add(detail);
|
||||||
|
reuseCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addSkip(String detail) {
|
||||||
|
skipList.add(detail);
|
||||||
|
skipCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addFail(String detail) {
|
||||||
|
failList.add(detail);
|
||||||
|
failCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void buildMessage() {
|
||||||
|
this.message = String.format("新建 %d / 复用 %d / 跳过 %d / 失败 %d",
|
||||||
|
createCount, reuseCount, skipCount, failCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -87,7 +87,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
where dept_name=#{deptName} and parent_id = #{parentId} and del_flag = '0' limit 1
|
where dept_name=#{deptName} and parent_id = #{parentId} and del_flag = '0' limit 1
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<insert id="insertDept" parameterType="SysDept">
|
<insert id="insertDept" parameterType="SysDept" useGeneratedKeys="true" keyProperty="deptId">
|
||||||
insert into sys_dept(
|
insert into sys_dept(
|
||||||
<if test="deptId != null and deptId != 0">dept_id,</if>
|
<if test="deptId != null and deptId != 0">dept_id,</if>
|
||||||
<if test="parentId != null and parentId != 0">parent_id,</if>
|
<if test="parentId != null and parentId != 0">parent_id,</if>
|
||||||
|
|||||||
@@ -100,3 +100,16 @@ export function endPublishTeachingTask(bh) {
|
|||||||
params: { bh }
|
params: { bh }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出教学任务书 Excel(该任务下全部教研室任务书课程明细)
|
||||||
|
* GET /teachingTask/exportTaskBook?bh=
|
||||||
|
*/
|
||||||
|
export function exportTaskBook(bh) {
|
||||||
|
return request({
|
||||||
|
url: '/teachingTask/exportTaskBook',
|
||||||
|
method: 'get',
|
||||||
|
params: { bh },
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,30 +9,32 @@ export function listOffice(query) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增教研室
|
// 新增教研室(syncDept=true 时同步创建组织部门)
|
||||||
export function addOffice(data) {
|
export function addOffice(data, syncDept) {
|
||||||
return request({
|
return request({
|
||||||
url: '/jys/office/add',
|
url: '/jys/office/add',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
|
params: syncDept === undefined ? {} : { syncDept },
|
||||||
data: data
|
data: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新教研室
|
// 更新教研室(syncDeptName=true 时将名称/序号同步到关联部门)
|
||||||
export function updateOffice(data) {
|
export function updateOffice(data, syncDeptName) {
|
||||||
return request({
|
return request({
|
||||||
url: '/jys/office/update',
|
url: '/jys/office/update',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
|
params: syncDeptName === undefined ? {} : { syncDeptName },
|
||||||
data: data
|
data: data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 停用教研室(jysdh 必传,query 参数)
|
// 停用教研室(jysdh 必传,query 参数;syncDept=true 时联动停用部门)
|
||||||
export function disableOffice(jysdh) {
|
export function disableOffice(jysdh, syncDept) {
|
||||||
return request({
|
return request({
|
||||||
url: '/jys/office/disable',
|
url: '/jys/office/disable',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
params: { jysdh: jysdh }
|
params: syncDept === undefined ? { jysdh } : { jysdh, syncDept }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,3 +46,31 @@ export function downloadOfficeTemplate() {
|
|||||||
responseType: 'blob'
|
responseType: 'blob'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 单个教研室补建/对齐组织部门
|
||||||
|
export function syncOfficeDept(jysdh) {
|
||||||
|
return request({
|
||||||
|
url: '/jys/office/sync-dept',
|
||||||
|
method: 'post',
|
||||||
|
params: { jysdh }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对齐组织部门:批量创建/复用(ids 不传 = 全部教研室)
|
||||||
|
export function alignDepts(ids) {
|
||||||
|
return request({
|
||||||
|
url: '/jys/org/align-dept',
|
||||||
|
method: 'post',
|
||||||
|
data: ids ? { ids } : {},
|
||||||
|
timeout: 120000
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 一键对齐:先部门后账号(幂等,返回 { dept, user } 两环节结果)
|
||||||
|
export function alignApply() {
|
||||||
|
return request({
|
||||||
|
url: '/jys/org/align-apply',
|
||||||
|
method: 'post',
|
||||||
|
timeout: 180000
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -73,3 +73,57 @@ export function updateTeacherAttribute(data) {
|
|||||||
data
|
data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 下载教员导入模板 GET /download/jy */
|
||||||
|
export function downloadTeacherTemplate() {
|
||||||
|
return request({
|
||||||
|
url: '/download/jy',
|
||||||
|
method: 'get',
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 导出教员 Excel GET /jys/teacher/export */
|
||||||
|
export function exportTeacher(params) {
|
||||||
|
return request({
|
||||||
|
url: '/jys/teacher/export',
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 导入教员 Excel POST /jys/teacher/import */
|
||||||
|
export function importTeacher(file) {
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('file', file)
|
||||||
|
return request({
|
||||||
|
url: '/jys/teacher/import',
|
||||||
|
method: 'post',
|
||||||
|
data: formData,
|
||||||
|
headers: { 'Content-Type': 'multipart/form-data' },
|
||||||
|
timeout: 60000
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 单个教员补建/对齐系统账号 POST /jys/teacher/sync-user */
|
||||||
|
export function syncTeacherUser(jybh, params) {
|
||||||
|
return request({
|
||||||
|
url: '/jys/teacher/sync-user',
|
||||||
|
method: 'post',
|
||||||
|
params: Object.assign({ jybh }, params || {})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对齐到用户表:为无账号教员批量建账号
|
||||||
|
* POST /jys/teacher/align-user body: { ids }(不传 = 全部无账号教员)
|
||||||
|
*/
|
||||||
|
export function alignTeacherUsers(ids) {
|
||||||
|
return request({
|
||||||
|
url: '/jys/teacher/align-user',
|
||||||
|
method: 'post',
|
||||||
|
data: ids && ids.length ? { ids } : {},
|
||||||
|
timeout: 180000
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,112 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
:title="title"
|
||||||
|
:visible.sync="visible"
|
||||||
|
width="640px"
|
||||||
|
append-to-body
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
>
|
||||||
|
<div class="align-summary">
|
||||||
|
<span>新建 <b class="ok">{{ result.createCount || 0 }}</b></span>
|
||||||
|
<span>复用 <b>{{ result.reuseCount || 0 }}</b></span>
|
||||||
|
<span>跳过 <b class="warn">{{ result.skipCount || 0 }}</b></span>
|
||||||
|
<span>失败 <b class="err">{{ result.failCount || 0 }}</b></span>
|
||||||
|
</div>
|
||||||
|
<div class="align-detail">
|
||||||
|
<template v-for="sec in sections">
|
||||||
|
<div v-if="sec.list && sec.list.length" :key="sec.key" class="align-sec">
|
||||||
|
<div class="sec-title" :class="sec.cls">{{ sec.label }}({{ sec.list.length }})</div>
|
||||||
|
<div class="sec-body">
|
||||||
|
<div v-for="(item, idx) in sec.list" :key="sec.key + idx">{{ item }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-empty v-if="isEmpty" description="无需处理" :image-size="60" />
|
||||||
|
</div>
|
||||||
|
<div slot="footer">
|
||||||
|
<el-button type="primary" @click="visible = false">关 闭</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
/**
|
||||||
|
* 组织/账号对齐结果清单弹窗。
|
||||||
|
* result: { createCount, reuseCount, skipCount, failCount, createList, reuseList, skipList, failList, message }
|
||||||
|
*/
|
||||||
|
export default {
|
||||||
|
name: 'OrgSyncResultDialog',
|
||||||
|
props: {
|
||||||
|
title: { type: String, default: '对齐结果' }
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
visible: false,
|
||||||
|
result: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
sections() {
|
||||||
|
const r = this.result || {}
|
||||||
|
return [
|
||||||
|
{ key: 'create', label: '新建', cls: 'ok', list: r.createList },
|
||||||
|
{ key: 'reuse', label: '复用', cls: '', list: r.reuseList },
|
||||||
|
{ key: 'skip', label: '跳过', cls: 'warn', list: r.skipList },
|
||||||
|
{ key: 'fail', label: '失败', cls: 'err', list: r.failList }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
isEmpty() {
|
||||||
|
const r = this.result || {}
|
||||||
|
return !(r.createCount || r.reuseCount || r.skipCount || r.failCount)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
open(result) {
|
||||||
|
this.result = result || {}
|
||||||
|
this.visible = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.align-summary {
|
||||||
|
display: flex;
|
||||||
|
gap: 24px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
b { color: #409eff; }
|
||||||
|
b.ok { color: #67c23a; }
|
||||||
|
b.warn { color: #e6a23c; }
|
||||||
|
b.err { color: #f56c6c; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.align-detail {
|
||||||
|
max-height: 50vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
.align-sec {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
.sec-title {
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
color: #303133;
|
||||||
|
|
||||||
|
&.ok { color: #67c23a; }
|
||||||
|
&.warn { color: #e6a23c; }
|
||||||
|
&.err { color: #f56c6c; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.sec-body {
|
||||||
|
padding: 8px 10px;
|
||||||
|
background: #f5f7fa;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #606266;
|
||||||
|
line-height: 1.8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -75,9 +75,16 @@
|
|||||||
<el-table-column prop="jssj" label="结束时间" width="200" align="center" :formatter="fmtDateTime" />
|
<el-table-column prop="jssj" label="结束时间" width="200" align="center" :formatter="fmtDateTime" />
|
||||||
<el-table-column prop="cjsj" label="创建时间" width="200" align="center" :formatter="fmtDateTime" />
|
<el-table-column prop="cjsj" label="创建时间" width="200" align="center" :formatter="fmtDateTime" />
|
||||||
<el-table-column prop="jcxqscsj" label="教材需求生成时间" width="200" align="center" :formatter="fmtDateTime" />
|
<el-table-column prop="jcxqscsj" label="教材需求生成时间" width="200" align="center" :formatter="fmtDateTime" />
|
||||||
<el-table-column label="操作" align="center" fixed="right">
|
<el-table-column label="操作" width="160" align="center" fixed="right">
|
||||||
<template slot-scope="{ row }">
|
<template slot-scope="{ row }">
|
||||||
<el-button type="text" size="small" @click="handleDetail(row)">详情</el-button>
|
<el-button type="text" size="small" @click="handleDetail(row)">详情</el-button>
|
||||||
|
<el-button
|
||||||
|
type="text"
|
||||||
|
size="small"
|
||||||
|
icon="el-icon-download"
|
||||||
|
:loading="exportingBh === row.bh"
|
||||||
|
@click="handleExport(row)"
|
||||||
|
>导出</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
@@ -127,8 +134,9 @@
|
|||||||
* 仅提供 分页查询 list + 详情 get?bh=,不做增删改发(避免与教学任务计划管理页重复操作同一实体)
|
* 仅提供 分页查询 list + 详情 get?bh=,不做增删改发(避免与教学任务计划管理页重复操作同一实体)
|
||||||
* 增删改发统一在 src/views/teachOffice/taskPlan/index.vue(教学任务计划管理)完成
|
* 增删改发统一在 src/views/teachOffice/taskPlan/index.vue(教学任务计划管理)完成
|
||||||
*/
|
*/
|
||||||
import { listTeachingTask, getTeachingTask } from '@/api/teachBusiness/teachingTask'
|
import { listTeachingTask, getTeachingTask, exportTaskBook } from '@/api/teachBusiness/teachingTask'
|
||||||
import { listAllSemester } from '@/api/teachBusiness/semester'
|
import { listAllSemester } from '@/api/teachBusiness/semester'
|
||||||
|
import { saveAs } from 'file-saver'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'TeachingTask',
|
name: 'TeachingTask',
|
||||||
@@ -156,7 +164,10 @@ export default {
|
|||||||
// ==================== 3. 详情 ====================
|
// ==================== 3. 详情 ====================
|
||||||
detailVisible: false,
|
detailVisible: false,
|
||||||
detailLoading: false,
|
detailLoading: false,
|
||||||
detailData: {}
|
detailData: {},
|
||||||
|
|
||||||
|
// ==================== 4. 导出 ====================
|
||||||
|
exportingBh: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
@@ -242,6 +253,18 @@ export default {
|
|||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
this.detailLoading = false
|
this.detailLoading = false
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/* ---------- 导出教学任务书 ---------- */
|
||||||
|
handleExport(row) {
|
||||||
|
if (this.exportingBh) return
|
||||||
|
this.exportingBh = row.bh
|
||||||
|
exportTaskBook(row.bh).then(blob => {
|
||||||
|
saveAs(blob, `教学任务书_${row.rwmc || row.bh}.xlsx`)
|
||||||
|
this.$message.success('导出成功')
|
||||||
|
}).catch(() => {}).finally(() => {
|
||||||
|
this.exportingBh = ''
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,6 +52,9 @@
|
|||||||
<el-col :span="1.5">
|
<el-col :span="1.5">
|
||||||
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleDownloadTemplate">模板下载</el-button>
|
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleDownloadTemplate">模板下载</el-button>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
<el-col :span="1.5">
|
||||||
|
<el-button type="success" plain icon="el-icon-refresh" size="mini" :loading="alignLoading" @click="handleAlignDepts">对齐组织部门</el-button>
|
||||||
|
</el-col>
|
||||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
@@ -117,6 +120,22 @@
|
|||||||
<el-form-item label="备注" prop="bz">
|
<el-form-item label="备注" prop="bz">
|
||||||
<el-input v-model="form.bz" type="textarea" placeholder="请输入备注" :rows="3" maxlength="200" show-word-limit />
|
<el-input v-model="form.bz" type="textarea" placeholder="请输入备注" :rows="3" maxlength="200" show-word-limit />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-divider content-position="left">组织同步</el-divider>
|
||||||
|
<template v-if="isAdd">
|
||||||
|
<el-form-item label="同步创建部门">
|
||||||
|
<el-checkbox v-model="form.syncDept">在「学员综合管理 › 教务处」下创建同名组织部门</el-checkbox>
|
||||||
|
<div class="sync-tip">部门名称跟随教研室名称(超30字用简称);创建后可在组织管理中调整负责人/排序</div>
|
||||||
|
</el-form-item>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<el-form-item label="组织部门">
|
||||||
|
<el-tag v-if="form.bmbh" type="success" size="small">已关联(教务处下)</el-tag>
|
||||||
|
<span v-else class="sync-tip">未关联,保存后可用「对齐组织部门」补建</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="form.bmbh" label="重命名同步">
|
||||||
|
<el-checkbox v-model="form.syncDeptName">保存时将教研室名称/序号同步到组织部门</el-checkbox>
|
||||||
|
</el-form-item>
|
||||||
|
</template>
|
||||||
</el-form>
|
</el-form>
|
||||||
<div slot="footer" class="dialog-footer">
|
<div slot="footer" class="dialog-footer">
|
||||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||||
@@ -143,21 +162,27 @@
|
|||||||
<span>仅允许导入 xls、xlsx 格式文件。</span>
|
<span>仅允许导入 xls、xlsx 格式文件。</span>
|
||||||
</div>
|
</div>
|
||||||
</el-upload>
|
</el-upload>
|
||||||
|
<el-checkbox v-model="importSyncDept" class="import-sync">同时创建组织部门(挂在教务处下)</el-checkbox>
|
||||||
<div slot="footer" class="dialog-footer">
|
<div slot="footer" class="dialog-footer">
|
||||||
<el-button type="primary" @click="submitImport">确 定</el-button>
|
<el-button type="primary" @click="submitImport">确 定</el-button>
|
||||||
<el-button @click="importOpen = false">取 消</el-button>
|
<el-button @click="importOpen = false">取 消</el-button>
|
||||||
</div>
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 对齐结果清单弹窗 -->
|
||||||
|
<org-sync-result-dialog ref="alignResult" title="对齐组织部门结果" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { listOffice, addOffice, updateOffice, disableOffice, downloadOfficeTemplate } from "@/api/teachOffice/office"
|
import { listOffice, addOffice, updateOffice, disableOffice, downloadOfficeTemplate, alignDepts } from "@/api/teachOffice/office"
|
||||||
import { getToken } from '@/utils/auth'
|
import { getToken } from '@/utils/auth'
|
||||||
import { saveAs } from 'file-saver'
|
import { saveAs } from 'file-saver'
|
||||||
|
import OrgSyncResultDialog from '@/components/OrgSyncResultDialog'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "Office",
|
name: "Office",
|
||||||
|
components: { OrgSyncResultDialog },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
// 遮罩层
|
// 遮罩层
|
||||||
@@ -178,6 +203,10 @@ export default {
|
|||||||
isAdd: true,
|
isAdd: true,
|
||||||
// 是否显示导入弹出层
|
// 是否显示导入弹出层
|
||||||
importOpen: false,
|
importOpen: false,
|
||||||
|
// 导入时是否同步创建组织部门
|
||||||
|
importSyncDept: true,
|
||||||
|
// 对齐执行中
|
||||||
|
alignLoading: false,
|
||||||
// 查询参数
|
// 查询参数
|
||||||
queryParams: {
|
queryParams: {
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
@@ -202,7 +231,8 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
importUrl() {
|
importUrl() {
|
||||||
return process.env.VUE_APP_BASE_API + '/jys/office/import'
|
const base = process.env.VUE_APP_BASE_API + '/jys/office/import'
|
||||||
|
return this.importSyncDept ? base + '?syncDept=true' : base
|
||||||
},
|
},
|
||||||
uploadHeaders() {
|
uploadHeaders() {
|
||||||
return { Authorization: 'Bearer ' + getToken() }
|
return { Authorization: 'Bearer ' + getToken() }
|
||||||
@@ -255,33 +285,56 @@ export default {
|
|||||||
jc: row.jc,
|
jc: row.jc,
|
||||||
bx: row.bx,
|
bx: row.bx,
|
||||||
xh: row.xh,
|
xh: row.xh,
|
||||||
bz: row.bz
|
bz: row.bz,
|
||||||
|
bmbh: row.bmbh,
|
||||||
|
syncDeptName: true
|
||||||
}
|
}
|
||||||
this.open = true
|
this.open = true
|
||||||
this.title = "编辑教研室"
|
this.title = "编辑教研室"
|
||||||
},
|
},
|
||||||
/** 停用按钮 */
|
/** 停用按钮:已关联部门时询问是否联动停用 */
|
||||||
handleDisable(row) {
|
handleDisable(row) {
|
||||||
const jysdh = row.jysdh
|
const jysdh = row.jysdh
|
||||||
this.$modal.confirm('确认停用教研室【' + row.jysmc + '】吗?').then(() => {
|
this.$modal.confirm('确认停用教研室【' + row.jysmc + '】吗?').then(() => {
|
||||||
return disableOffice(jysdh)
|
if (!row.bmbh) return false
|
||||||
}).then(() => {
|
return this.$modal.confirm('该教研室已关联组织部门,是否一并停用?')
|
||||||
|
.then(() => true)
|
||||||
|
.catch(() => false)
|
||||||
|
}).then(syncDept => {
|
||||||
|
if (syncDept === undefined) return
|
||||||
|
return disableOffice(jysdh, syncDept === true)
|
||||||
|
}).then(res => {
|
||||||
|
if (res === undefined) return
|
||||||
this.getList()
|
this.getList()
|
||||||
this.$modal.msgSuccess("停用成功")
|
this.$modal.msgSuccess("停用成功")
|
||||||
}).catch(() => {})
|
}).catch(() => {})
|
||||||
},
|
},
|
||||||
|
/** 批量对齐组织部门(直接执行,幂等) */
|
||||||
|
handleAlignDepts() {
|
||||||
|
this.$modal.confirm('将为全部教研室创建/复用「教务处」下的同名组织部门,已关联的自动跳过。是否继续?').then(() => {
|
||||||
|
this.alignLoading = true
|
||||||
|
return alignDepts()
|
||||||
|
}).then(response => {
|
||||||
|
this.alignLoading = false
|
||||||
|
if (response === undefined) return
|
||||||
|
this.$refs.alignResult.open(response.data)
|
||||||
|
this.getList()
|
||||||
|
}).catch(() => {
|
||||||
|
this.alignLoading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
/** 提交表单 */
|
/** 提交表单 */
|
||||||
submitForm() {
|
submitForm() {
|
||||||
this.$refs["form"].validate(valid => {
|
this.$refs["form"].validate(valid => {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
if (this.isAdd) {
|
if (this.isAdd) {
|
||||||
addOffice(this.form).then(response => {
|
addOffice(this.form, this.form.syncDept).then(response => {
|
||||||
this.$modal.msgSuccess("新增成功")
|
this.$modal.msgSuccess("新增成功")
|
||||||
this.open = false
|
this.open = false
|
||||||
this.getList()
|
this.getList()
|
||||||
}).catch(() => {})
|
}).catch(() => {})
|
||||||
} else {
|
} else {
|
||||||
updateOffice(this.form).then(response => {
|
updateOffice(this.form, this.form.bmbh ? this.form.syncDeptName : undefined).then(response => {
|
||||||
this.$modal.msgSuccess("修改成功")
|
this.$modal.msgSuccess("修改成功")
|
||||||
this.open = false
|
this.open = false
|
||||||
this.getList()
|
this.getList()
|
||||||
@@ -298,7 +351,9 @@ export default {
|
|||||||
jc: undefined,
|
jc: undefined,
|
||||||
bx: undefined,
|
bx: undefined,
|
||||||
xh: undefined,
|
xh: undefined,
|
||||||
bz: undefined
|
bz: undefined,
|
||||||
|
syncDept: true,
|
||||||
|
syncDeptName: true
|
||||||
}
|
}
|
||||||
this.resetForm("form")
|
this.resetForm("form")
|
||||||
},
|
},
|
||||||
@@ -360,5 +415,15 @@ export default {
|
|||||||
.mb8 {
|
.mb8 {
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sync-tip {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #909399;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-sync {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -39,6 +39,12 @@
|
|||||||
<el-button type="primary" icon="el-icon-plus" @click="handleNew">新建</el-button>
|
<el-button type="primary" icon="el-icon-plus" @click="handleNew">新建</el-button>
|
||||||
<el-button type="danger" plain icon="el-icon-delete" @click="handleDeleteSelected">删除所选</el-button>
|
<el-button type="danger" plain icon="el-icon-delete" @click="handleDeleteSelected">删除所选</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="right-group">
|
||||||
|
<el-button icon="el-icon-upload2" @click="openImportDialog">导入</el-button>
|
||||||
|
<el-button icon="el-icon-download" @click="handleExport">导出</el-button>
|
||||||
|
<el-button icon="el-icon-document" @click="handleDownloadTemplate">下载模板</el-button>
|
||||||
|
<el-button type="success" plain icon="el-icon-refresh" :loading="alignLoading" @click="handleAlignUsers">对齐到用户表</el-button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-card shadow="never" class="table-card">
|
<el-card shadow="never" class="table-card">
|
||||||
@@ -65,7 +71,14 @@
|
|||||||
<el-table-column label="虚实类型" width="80" align="center">
|
<el-table-column label="虚实类型" width="80" align="center">
|
||||||
<template slot-scope="{ row }">{{ fmtXslx(row.xslx) }}</template>
|
<template slot-scope="{ row }">{{ fmtXslx(row.xslx) }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="操作" align="center" fixed="right">
|
<el-table-column label="账号" width="130" align="center">
|
||||||
|
<template slot-scope="{ row }">
|
||||||
|
<el-tag v-if="row.yhbh" type="success" size="mini">{{ row.jybh }}</el-tag>
|
||||||
|
<el-button v-else type="text" size="small" icon="el-icon-link"
|
||||||
|
@click="handleCreateAccount(row)">创建账号</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" align="center" fixed="right" width="200">
|
||||||
<template slot-scope="{ row }">
|
<template slot-scope="{ row }">
|
||||||
<el-button type="text" size="small" icon="el-icon-view" @click="handleViewAttribute(row)">查看属性</el-button>
|
<el-button type="text" size="small" icon="el-icon-view" @click="handleViewAttribute(row)">查看属性</el-button>
|
||||||
<el-button type="text" size="small" icon="el-icon-edit" @click="handleEdit(row)">编辑</el-button>
|
<el-button type="text" size="small" icon="el-icon-edit" @click="handleEdit(row)">编辑</el-button>
|
||||||
@@ -347,6 +360,42 @@
|
|||||||
<el-form-item label="发表论文"><el-input v-model="addForm.attributes.fblw" placeholder="选填" /></el-form-item>
|
<el-form-item label="发表论文"><el-input v-model="addForm.attributes.fblw" placeholder="选填" /></el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
<el-divider content-position="left">登录账号</el-divider>
|
||||||
|
<el-row :gutter="16">
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-form-item label="同时创建账号">
|
||||||
|
<el-checkbox v-model="addForm.createUser">创建系统账号(登录名默认=教员工号,角色=教员)</el-checkbox>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<template v-if="addForm.createUser">
|
||||||
|
<el-row :gutter="16">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="登录名">
|
||||||
|
<el-input v-model="addForm.loginName" placeholder="留空则取教员工号" maxlength="30" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="初始密码">
|
||||||
|
<el-input v-model="addForm.password" placeholder="默认 Jw@123456" show-password maxlength="30" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="16">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="角色">
|
||||||
|
<el-select v-model="addForm.roleId" style="width:100%" :loading="roleLoading">
|
||||||
|
<el-option v-for="r in roleOptions" :key="r.roleId" :label="r.roleName" :value="r.roleId" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="归属组织">
|
||||||
|
<span class="org-path">{{ orgPathText }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</template>
|
||||||
</el-form>
|
</el-form>
|
||||||
<div slot="footer">
|
<div slot="footer">
|
||||||
<el-button @click="addDialogVisible = false">取消</el-button>
|
<el-button @click="addDialogVisible = false">取消</el-button>
|
||||||
@@ -621,6 +670,36 @@
|
|||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 教员数据导入弹窗 -->
|
||||||
|
<el-dialog title="教员数据导入" :visible.sync="importDialog.visible" width="560px" append-to-body
|
||||||
|
:close-on-click-modal="false">
|
||||||
|
<el-alert type="info" :closable="false" show-icon class="import-tip"
|
||||||
|
title="导入只写教员档案,不创建登录账号;需要账号时回列表点【对齐到用户表】。" />
|
||||||
|
<el-form label-width="100px">
|
||||||
|
<el-form-item label="导入模板">
|
||||||
|
<el-button icon="el-icon-download" :loading="importDialog.downloading" @click="handleDownloadTemplate">
|
||||||
|
下载导入模板
|
||||||
|
</el-button>
|
||||||
|
<span class="tip-text">红色表头列为必填,详见模板「填写说明」sheet</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="数据文件">
|
||||||
|
<input ref="importFileInput" type="file" accept=".xls,.xlsx" style="display: none"
|
||||||
|
@change="handleImportFileChange" />
|
||||||
|
<el-button icon="el-icon-folder-opened" @click="handleChooseFile">选择文件</el-button>
|
||||||
|
<span class="file-name">{{ importDialog.fileName || '未选择任何文件' }}</span>
|
||||||
|
<el-button v-if="importDialog.file" type="text" class="danger-text" @click="clearImportFile">清除</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<div slot="footer">
|
||||||
|
<el-button @click="importDialog.visible = false">关 闭</el-button>
|
||||||
|
<el-button type="primary" :loading="importDialog.importing" :disabled="!importDialog.file"
|
||||||
|
@click="handleImport">开始导入</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<!-- 对齐结果清单弹窗 -->
|
||||||
|
<org-sync-result-dialog ref="alignResult" title="对齐结果" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -632,13 +711,22 @@ import {
|
|||||||
updateTeacher,
|
updateTeacher,
|
||||||
disableTeacher,
|
disableTeacher,
|
||||||
getTeacherAttribute,
|
getTeacherAttribute,
|
||||||
updateTeacherAttribute
|
updateTeacherAttribute,
|
||||||
|
downloadTeacherTemplate,
|
||||||
|
exportTeacher,
|
||||||
|
importTeacher,
|
||||||
|
syncTeacherUser,
|
||||||
|
alignTeacherUsers
|
||||||
} from '@/api/teachOffice/teacher'
|
} from '@/api/teachOffice/teacher'
|
||||||
import { listOffice } from '@/api/teachOffice/office'
|
import { listOffice } from '@/api/teachOffice/office'
|
||||||
import { getDicts } from '@/api/system/dict/data'
|
import { getDicts } from '@/api/system/dict/data'
|
||||||
|
import { listRole } from '@/api/system/role'
|
||||||
|
import { saveAs } from 'file-saver'
|
||||||
|
import OrgSyncResultDialog from '@/components/OrgSyncResultDialog'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Teacher',
|
name: 'Teacher',
|
||||||
|
components: { OrgSyncResultDialog },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
// 查询条件(仅后端 JYBMapper.xml 支持的字段:jyxm 模糊 / jysdh 等值 / zc 模糊 / jylb 等值)
|
// 查询条件(仅后端 JYBMapper.xml 支持的字段:jyxm 模糊 / jysdh 等值 / zc 模糊 / jylb 等值)
|
||||||
@@ -711,13 +799,36 @@ export default {
|
|||||||
dslbOptions: [],
|
dslbOptions: [],
|
||||||
// 教研室下拉(新增教员时选择所属教研室)
|
// 教研室下拉(新增教员时选择所属教研室)
|
||||||
officeOptions: [],
|
officeOptions: [],
|
||||||
officeLoading: false
|
officeLoading: false,
|
||||||
|
// 角色下拉(建账号时可选角色,默认教员 101)
|
||||||
|
roleOptions: [],
|
||||||
|
roleLoading: false,
|
||||||
|
// 导入弹窗
|
||||||
|
importDialog: {
|
||||||
|
visible: false,
|
||||||
|
downloading: false,
|
||||||
|
importing: false,
|
||||||
|
file: null,
|
||||||
|
fileName: ''
|
||||||
|
},
|
||||||
|
// 对齐执行中
|
||||||
|
alignLoading: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
/** 新增弹窗里所选教研室对应的组织路径展示 */
|
||||||
|
orgPathText() {
|
||||||
|
const office = this.officeOptions.find(o => o.jysdh === this.addForm.teacher.jysdh)
|
||||||
|
if (!office) return '请选择教研室后自动显示'
|
||||||
|
const suffix = office.bmbh ? '' : '(尚未建部门,保存时自动创建)'
|
||||||
|
return '学员综合管理 › 教务处 › ' + office.jysmc + suffix
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.fetchList()
|
this.fetchList()
|
||||||
this.loadDicts()
|
this.loadDicts()
|
||||||
this.loadOfficeOptions()
|
this.loadOfficeOptions()
|
||||||
|
this.loadRoleOptions()
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
/** 统一格式化后端 LocalDateTime(去除 T、截断到秒) */
|
/** 统一格式化后端 LocalDateTime(去除 T、截断到秒) */
|
||||||
@@ -772,7 +883,7 @@ export default {
|
|||||||
// 已停用(ty=1)的教研室不再参与新建教员
|
// 已停用(ty=1)的教研室不再参与新建教员
|
||||||
if (!jysdh || seen[jysdh] || Number(row.ty) === 1) return
|
if (!jysdh || seen[jysdh] || Number(row.ty) === 1) return
|
||||||
seen[jysdh] = true
|
seen[jysdh] = true
|
||||||
options.push({ jysdh, jysmc: row.jysmc || jysdh })
|
options.push({ jysdh, jysmc: row.jysmc || jysdh, bmbh: row.bmbh })
|
||||||
})
|
})
|
||||||
this.officeOptions = options
|
this.officeOptions = options
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
@@ -806,7 +917,11 @@ export default {
|
|||||||
xl: '', xw: '', jl: 0, jljsrq: '', zr: 0, zw: '', zwsj: '', jszw: '',
|
xl: '', xw: '', jl: 0, jljsrq: '', zr: 0, zw: '', zwsj: '', jszw: '',
|
||||||
jszwsj: '', jsdj: '', jsdjsj: '', jxwz: '', jxwzsj: '', cssj: '',
|
jszwsj: '', jsdj: '', jsdjsj: '', jxwz: '', jxwzsj: '', cssj: '',
|
||||||
rwgzsj: '', rdzjl: '', fblw: ''
|
rwgzsj: '', rdzjl: '', fblw: ''
|
||||||
}
|
},
|
||||||
|
createUser: true,
|
||||||
|
loginName: '',
|
||||||
|
password: '',
|
||||||
|
roleId: 101
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -872,6 +987,11 @@ export default {
|
|||||||
this.$message.info('后端暂未提供该接口')
|
this.$message.info('后端暂未提供该接口')
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// ==================== 导入弹窗 ====================
|
||||||
|
openImportDialog() {
|
||||||
|
this.importDialog.visible = true
|
||||||
|
},
|
||||||
|
|
||||||
// ==================== 新增 ====================
|
// ==================== 新增 ====================
|
||||||
handleNew() {
|
handleNew() {
|
||||||
this.addForm = this.createEmptyAddForm()
|
this.addForm = this.createEmptyAddForm()
|
||||||
@@ -886,7 +1006,11 @@ export default {
|
|||||||
if (!valid) return
|
if (!valid) return
|
||||||
const dto = {
|
const dto = {
|
||||||
teacher: this.buildTeacherPayload(this.addForm.teacher),
|
teacher: this.buildTeacherPayload(this.addForm.teacher),
|
||||||
attributes: this.buildAttributePayload(this.addForm.attributes)
|
attributes: this.buildAttributePayload(this.addForm.attributes),
|
||||||
|
createUser: this.addForm.createUser,
|
||||||
|
loginName: this.addForm.loginName,
|
||||||
|
password: this.addForm.password,
|
||||||
|
roleId: this.addForm.roleId
|
||||||
}
|
}
|
||||||
this.addSaving = true
|
this.addSaving = true
|
||||||
addTeacher(dto).then(() => {
|
addTeacher(dto).then(() => {
|
||||||
@@ -1062,6 +1186,135 @@ export default {
|
|||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
this.attributeSaving = false
|
this.attributeSaving = false
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// ==================== 建账号 / 对齐到用户表 ====================
|
||||||
|
handleCreateAccount(row) {
|
||||||
|
const h = this.$createElement
|
||||||
|
this.$msgbox({
|
||||||
|
title: '为教员建账号',
|
||||||
|
message: h('div', null, [
|
||||||
|
h('p', null, '教员:' + row.jyxm + '(' + row.jybh + ')'),
|
||||||
|
h('p', null, '用户名 = 教员工号,手机号 = 联系方式,角色 = 教员(101)'),
|
||||||
|
h('p', { style: 'color:#E6A23C' }, '初始密码将生成随机密码,请到用户管理重置后发本人')
|
||||||
|
]),
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonText: '创建',
|
||||||
|
cancelButtonText: '取消'
|
||||||
|
}).then(() => {
|
||||||
|
syncTeacherUser(row.jybh).then(res => {
|
||||||
|
if (res.code === 200) {
|
||||||
|
this.$message.success('账号创建成功(用户编号:' + res.data + ')')
|
||||||
|
this.fetchList()
|
||||||
|
} else {
|
||||||
|
this.$message.error(res.msg || '创建失败')
|
||||||
|
}
|
||||||
|
}).catch(() => {})
|
||||||
|
}).catch(() => {})
|
||||||
|
},
|
||||||
|
|
||||||
|
handleAlignUsers() {
|
||||||
|
this.$confirm('扫描在职且未挂账号的教员,自动创建登录账号并回写用户编号?(已建账号的教员跳过,不重复建号)', '对齐到用户表', {
|
||||||
|
type: 'warning',
|
||||||
|
confirmButtonText: '开始对齐',
|
||||||
|
cancelButtonText: '取消'
|
||||||
|
}).then(() => {
|
||||||
|
this.alignLoading = true
|
||||||
|
alignTeacherUsers().then(res => {
|
||||||
|
if (res.code === 200 && res.data) {
|
||||||
|
this.$refs.alignResult.open(res.data)
|
||||||
|
this.fetchList()
|
||||||
|
} else {
|
||||||
|
this.$message.error(res.msg || '对齐失败')
|
||||||
|
}
|
||||||
|
}).catch(e => {
|
||||||
|
console.error('对齐失败', e)
|
||||||
|
this.$message.error(e.message || '对齐失败')
|
||||||
|
}).finally(() => {
|
||||||
|
this.alignLoading = false
|
||||||
|
})
|
||||||
|
}).catch(() => {})
|
||||||
|
},
|
||||||
|
|
||||||
|
// ==================== 导入 / 导出 / 模板 ====================
|
||||||
|
handleDownloadTemplate() {
|
||||||
|
this.importDialog.downloading = true
|
||||||
|
downloadTeacherTemplate().then(res => {
|
||||||
|
const blob = res instanceof Blob ? res : new Blob([res])
|
||||||
|
saveAs(blob, '教员导入模板.xlsx')
|
||||||
|
}).catch(e => {
|
||||||
|
console.error('模板下载失败', e)
|
||||||
|
this.$message.error(e.message || '模板下载失败')
|
||||||
|
}).finally(() => {
|
||||||
|
this.importDialog.downloading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
handleExport() {
|
||||||
|
exportTeacher({ ...this.searchForm }).then(res => {
|
||||||
|
const blob = res instanceof Blob ? res : new Blob([res])
|
||||||
|
saveAs(blob, '教员档案.xlsx')
|
||||||
|
}).catch(e => {
|
||||||
|
console.error('导出失败', e)
|
||||||
|
this.$message.error(e.message || '导出失败')
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
handleChooseFile() {
|
||||||
|
this.$refs.importFileInput.click()
|
||||||
|
},
|
||||||
|
|
||||||
|
handleImportFileChange(e) {
|
||||||
|
const file = e.target.files && e.target.files[0]
|
||||||
|
if (!file) return
|
||||||
|
if (!/\.(xls|xlsx)$/i.test(file.name)) {
|
||||||
|
this.$message.error('请选择 xls/xlsx 文件')
|
||||||
|
e.target.value = ''
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.importDialog.file = file
|
||||||
|
this.importDialog.fileName = file.name
|
||||||
|
e.target.value = ''
|
||||||
|
},
|
||||||
|
|
||||||
|
clearImportFile() {
|
||||||
|
this.importDialog.file = null
|
||||||
|
this.importDialog.fileName = ''
|
||||||
|
},
|
||||||
|
|
||||||
|
handleImport() {
|
||||||
|
if (!this.importDialog.file) {
|
||||||
|
this.$message.warning('请选择数据文件')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.importDialog.importing = true
|
||||||
|
importTeacher(this.importDialog.file).then(res => {
|
||||||
|
if (res.code === 200) {
|
||||||
|
this.$message.success(res.msg || '导入完成')
|
||||||
|
this.importDialog.visible = false
|
||||||
|
this.clearImportFile()
|
||||||
|
this.fetchList()
|
||||||
|
} else {
|
||||||
|
this.$message.error(res.msg || '导入失败')
|
||||||
|
}
|
||||||
|
}).catch(e => {
|
||||||
|
console.error('导入失败', e)
|
||||||
|
this.$message.error(e.message || '导入失败')
|
||||||
|
}).finally(() => {
|
||||||
|
this.importDialog.importing = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// ==================== 角色下拉 ====================
|
||||||
|
loadRoleOptions() {
|
||||||
|
this.roleLoading = true
|
||||||
|
listRole({ pageNum: 1, pageSize: 200, status: '0' }).then(res => {
|
||||||
|
this.roleOptions = res.rows || res.data || []
|
||||||
|
}).catch(e => {
|
||||||
|
console.error('加载角色列表失败', e)
|
||||||
|
}).finally(() => {
|
||||||
|
this.roleLoading = false
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1128,6 +1381,42 @@ export default {
|
|||||||
.danger-text {
|
.danger-text {
|
||||||
color: #f56c6c;
|
color: #f56c6c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.account-section {
|
||||||
|
.section-title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.account-tip {
|
||||||
|
color: #909399;
|
||||||
|
font-size: 12px;
|
||||||
|
margin: -6px 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.org-path {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #606266;
|
||||||
|
line-height: 32px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.import-tip {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-name {
|
||||||
|
margin-left: 8px;
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tip-text {
|
||||||
|
margin-left: 8px;
|
||||||
|
color: #909399;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user