From 375a2c8df7ae124fb69d1fc5476bbcc1657ad6db Mon Sep 17 00:00:00 2001 From: zlwu <704541015@qq.com> Date: Fri, 28 Nov 2025 20:01:32 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/dromara/rs/controller/.gitkeep | 2 - .../RsOverseasApplicationController.java | 68 +- .../RsOverseasItineraryChangeController.java | 16 +- .../RsOverseasRegistrationController.java | 66 +- .../rs/domain/RsOverseasApplication.java | 4 +- .../rs/domain/RsOverseasRegistration.java | 2 +- .../rs/domain/bo/RsOverseasApplicationBo.java | 26 + .../rs/domain/vo/RsOverseasApplicationVo.java | 4 +- .../vo/RsOverseasApprovalMonitorVo.java | 5 + .../mapper/RsOverseasApplicationMapper.java | 19 + .../IRsOverseasApplicationService.java | 10 +- .../RsOverseasApplicationServiceImpl.java | 214 +- .../RsOverseasApprovalMonitorServiceImpl.java | 1 + .../RsOverseasItineraryChangeServiceImpl.java | 208 +- .../mapper/rs/RsOverseasApplicationMapper.xml | 37 + .../mapper/rs/RsOverseasRecordMapper.xml | 13 +- .../main/resources/mapper/rs/recruitment.sql | 142 -- .../mapper/rs/remove-submit-count-field.sql | 11 - .../rs/training-data-consistency-check.sql | 62 - .../mapper/rs/training-demo-data.sql | 38 - .../rs/training-fix-approval-column.sql | 43 - ...ining-update-approval-history-complete.sql | 58 - .../rs/training-update-approval-history.sql | 19 - .../resources/mapper/rs/training-update.sql | 17 - .../src/main/resources/mapper/rs/training.sql | 122 -- .../mapper/rs/update-custom-course-name.sql | 5 - .../mapper/rs/update-rs-application-table.sql | 7 - .../update_party_retention_file_oss_id.sql | 15 + docs/开发文档/API/出国申请管理API.md | 23 +- docs/开发文档/API/出国行程变更管理API.md | 4 + web/package.json | 6 +- web/public/baoliudangji.docx | Bin 0 -> 11686 bytes web/public/chuguodengji.docx | Bin 0 -> 16040 bytes web/public/chuguojingshenpibiao.docx | Bin 0 -> 18968 bytes web/public/chuguoqingkuanghuizhi.docx | Bin 0 -> 16514 bytes web/public/zunshoudangjichengnuoshu.docx | Bin 0 -> 14822 bytes web/src/api/rs/overseas-registration.ts | 14 +- web/src/api/rs/overseas.ts | 17 +- web/src/permission.ts | 2 +- web/src/utils/request.ts | 10 +- web/src/views/login.vue | 28 +- .../rs/overseas-registration/index-all.vue | 1138 +++++++++++ .../views/rs/overseas-registration/index.vue | 1207 +++++------- web/src/views/rs/overseas/approval.vue | 1107 +++++++---- web/src/views/rs/overseas/index.vue | 1715 +++++++++++------ .../rs/overseas/itinerary-change-approval.vue | 93 +- .../views/rs/overseas/itinerary-change.vue | 421 ++-- web/src/views/rs/overseas/monitor/index.vue | 237 ++- .../rs/overseas/monitor/修改总结-快速参考.md | 88 - .../monitor/出国申请进度查询功能优化说明.md | 365 ---- .../monitor/出国申请进度查询功能修改说明.md | 394 ---- .../monitor/审批环节命名标准分析报告.md | 411 ---- .../rs/overseas/monitor/审批级别修复说明.md | 286 --- .../views/rs/overseas/monitor/权限修改说明.md | 183 -- 54 files changed, 4631 insertions(+), 4352 deletions(-) delete mode 100644 backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/.gitkeep delete mode 100644 backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/recruitment.sql delete mode 100644 backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/remove-submit-count-field.sql delete mode 100644 backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training-data-consistency-check.sql delete mode 100644 backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training-demo-data.sql delete mode 100644 backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training-fix-approval-column.sql delete mode 100644 backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training-update-approval-history-complete.sql delete mode 100644 backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training-update-approval-history.sql delete mode 100644 backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training-update.sql delete mode 100644 backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training.sql delete mode 100644 backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/update-custom-course-name.sql delete mode 100644 backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/update-rs-application-table.sql create mode 100644 backend/ruoyi-modules/ruoyi-rs/src/main/resources/sql/update_party_retention_file_oss_id.sql create mode 100644 web/public/baoliudangji.docx create mode 100644 web/public/chuguodengji.docx create mode 100644 web/public/chuguojingshenpibiao.docx create mode 100644 web/public/chuguoqingkuanghuizhi.docx create mode 100644 web/public/zunshoudangjichengnuoshu.docx create mode 100644 web/src/views/rs/overseas-registration/index-all.vue delete mode 100644 web/src/views/rs/overseas/monitor/修改总结-快速参考.md delete mode 100644 web/src/views/rs/overseas/monitor/出国申请进度查询功能优化说明.md delete mode 100644 web/src/views/rs/overseas/monitor/出国申请进度查询功能修改说明.md delete mode 100644 web/src/views/rs/overseas/monitor/审批环节命名标准分析报告.md delete mode 100644 web/src/views/rs/overseas/monitor/审批级别修复说明.md delete mode 100644 web/src/views/rs/overseas/monitor/权限修改说明.md diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/.gitkeep b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/.gitkeep deleted file mode 100644 index acf886df..00000000 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/.gitkeep +++ /dev/null @@ -1,2 +0,0 @@ -# 人事管理模块 - 控制器层 -# 此目录用于存放REST API控制器类 diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsOverseasApplicationController.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsOverseasApplicationController.java index a02dff6d..50c7fbbc 100644 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsOverseasApplicationController.java +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsOverseasApplicationController.java @@ -24,6 +24,7 @@ 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.excel.utils.ExcelUtil; +import org.dromara.common.core.utils.StringUtils; import org.dromara.common.satoken.utils.LoginHelper; import cn.dev33.satoken.annotation.SaCheckPermission; import cn.dev33.satoken.annotation.SaMode; @@ -97,6 +98,8 @@ public class RsOverseasApplicationController extends BaseController { /** * 查询当前用户的待审批申请列表(五级审批模式) + * + * @deprecated 已废弃,请使用 pendingPageList 方法(支持分页和搜索) */ @SaCheckPermission("rs:overseasapproval:list") @GetMapping("/pending/list") @@ -105,6 +108,30 @@ public class RsOverseasApplicationController extends BaseController { return R.ok(list); } + /** + * 查询当前用户的待审批申请分页列表(根据角色过滤,支持分页和搜索) + * + * 🔴 核心逻辑: + * 1. 根据用户角色确定审批级别列表 + * 2. 部门负责人(rs_bmfzr)只能看到本部门的申请 + * 3. 其他角色可以看到全校的申请 + * 4. 支持分页和搜索(前往国家、出境事由) + * 5. 只返回status='0'(待审核)的申请 + * 6. 按创建时间倒序排列 + * + * 角色映射: + * - rs_bmfzr → approval_level='1' + 部门过滤 + * - hr_dept_role → approval_level='2' + * - discipline_committee_role → approval_level='3' + * - college_leader_role → approval_level='4' + * - party_secretary_role → approval_level='5' + */ + @SaCheckPermission("rs:overseasapproval:list") + @GetMapping("/pending/page") + public TableDataInfo pendingPageList(RsOverseasApplicationQuery query) { + return applicationService.queryPendingPageListByRole(query); + } + // ======================================== // 五级审批模式说明 // @@ -321,7 +348,7 @@ public class RsOverseasApplicationController extends BaseController { @PostMapping("/approve") public R approve(@RequestBody ApprovalRequest request) { Long approverId = LoginHelper.getUserId(); - String approverName = LoginHelper.getUsername(); + String approverName = StringUtils.defaultIfBlank(LoginHelper.getNickname(), LoginHelper.getUsername()); // 🔴 标准工作流模式:支持任务ID参数 // 如果传入了taskId,将通过工作流引擎进行审批 @@ -488,12 +515,6 @@ public class RsOverseasApplicationController extends BaseController { public R> uploadStampedFile(@RequestParam("file") MultipartFile file, @RequestParam("applicationId") Long applicationId) { try { - // 验证文件格式 - String contentType = file.getContentType(); - if (!isValidFileType(contentType)) { - return R.fail("不支持的文件格式,请上传PDF、JPG或PNG文件"); - } - // 验证文件大小(10MB) long maxSize = 10 * 1024 * 1024; // 10MB if (file.getSize() > maxSize) { @@ -561,12 +582,6 @@ public class RsOverseasApplicationController extends BaseController { public R> uploadPartyRetentionFile(@RequestParam("file") MultipartFile file, @RequestParam("applicationId") Long applicationId) { try { - // 验证文件格式 - String contentType = file.getContentType(); - if (!isValidFileType(contentType)) { - return R.fail("不支持的文件格式,请上传PDF、JPG或PNG文件"); - } - // 验证文件大小(10MB) long maxSize = 10 * 1024 * 1024; // 10MB if (file.getSize() > maxSize) { @@ -583,7 +598,7 @@ public class RsOverseasApplicationController extends BaseController { ossResult.getOriginalName(), // 原始文件名 new Date(), // 上传时间 "1", // 上传状态:已上传 - ossResult.getOssId() // OSS文件ID + String.valueOf(ossResult.getOssId()) // OSS文件ID - 转换为字符串 ); // 返回结果 @@ -616,25 +631,24 @@ public class RsOverseasApplicationController extends BaseController { return; } + // 处理多个OSS ID的情况(逗号分隔),取第一个ID进行下载 + String ossIdStr = application.getPartyRetentionFileOssId(); + Long ossId; + if (ossIdStr.contains(",")) { + // 如果包含逗号,取第一个ID + ossId = Long.parseLong(ossIdStr.split(",")[0].trim()); + } else { + // 单个ID直接解析 + ossId = Long.parseLong(ossIdStr); + } + // 使用系统OSS服务进行安全下载 - ossService.secureDownload(application.getPartyRetentionFileOssId(), response); + ossService.secureDownload(ossId, response); } catch (Exception e) { e.printStackTrace(); } } - /** - * 验证文件类型 - */ - private boolean isValidFileType(String contentType) { - if (contentType == null) - return false; - return contentType.equals("application/pdf") || - contentType.equals("image/jpeg") || - contentType.equals("image/jpg") || - contentType.equals("image/png"); - } - /** * 获取文件扩展名 */ diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsOverseasItineraryChangeController.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsOverseasItineraryChangeController.java index dbcd2194..1b0c7b97 100644 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsOverseasItineraryChangeController.java +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsOverseasItineraryChangeController.java @@ -62,17 +62,13 @@ public class RsOverseasItineraryChangeController extends BaseController { } /** - * 🔴 标准化改造:删除自定义待审批查询接口 - * - * 原因:出国行程变更工作流应使用标准工作流模式 - * 用户应通过工作流API pageByTaskWait() 获取待办任务 - * 而不是调用自定义的业务API + * 查询待审批的行程变更申请列表(status=0) */ @SaCheckPermission("rs:itineraryChange:approve") @GetMapping("/pending/list") - public R pendingList(RsOverseasItineraryChangeQuery query) { - // 提示用户使用标准工作流API - return R.fail("请使用标准工作流待办页面查看审批任务。访问:工作流 > 我的任务 > 待办任务"); + public TableDataInfo pendingList(RsOverseasItineraryChangeQuery query) { + query.setTenantId(LoginHelper.getTenantId()); + return changeService.queryPendingList(query); } /** @@ -167,7 +163,7 @@ public class RsOverseasItineraryChangeController extends BaseController { /** * 导出行程变更申请列表 */ - @SaCheckPermission("rs:itineraryChange:export") + // @SaCheckPermission("rs:itineraryChange:export") @Log(title = "行程变更申请", businessType = BusinessType.EXPORT) @PostMapping("/export") public void export(RsOverseasItineraryChangeQuery query, HttpServletResponse response) { @@ -179,7 +175,7 @@ public class RsOverseasItineraryChangeController extends BaseController { /** * 查询某个出国记录的所有变更历史 */ - @SaCheckPermission("rs:itineraryChange:query") + // @SaCheckPermission("rs:itineraryChange:query") @GetMapping("/record/{recordId}/history") public R> getChangeHistory(@PathVariable Long recordId) { return R.ok(changeService.queryByRecordId(recordId)); diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsOverseasRegistrationController.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsOverseasRegistrationController.java index d73f0f0f..0b7cb56b 100644 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsOverseasRegistrationController.java +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/controller/RsOverseasRegistrationController.java @@ -32,7 +32,7 @@ import org.dromara.rs.service.IRsOverseasRegistrationService; /** * 出国登记Controller - * + * * @author weizzzz * @date 2025-09-30 */ @@ -47,7 +47,7 @@ public class RsOverseasRegistrationController extends BaseController { /** * 查询出国登记列表 */ - @SaCheckPermission("rs:overseas:registration:list") + // @SaCheckPermission("rs:overseasregistration:list") @GetMapping("/list") public TableDataInfo list(RsOverseasRegistrationQuery query) { return registrationService.queryPageList(query); @@ -56,7 +56,7 @@ public class RsOverseasRegistrationController extends BaseController { /** * 查询我的出国登记列表 */ - @SaCheckPermission("rs:overseas:registration:list") + // @SaCheckPermission("rs:overseasregistration:list") @GetMapping("/my/list") public TableDataInfo myList(RsOverseasRegistrationQuery query) { return registrationService.queryMyRegistrationList(query); @@ -65,7 +65,7 @@ public class RsOverseasRegistrationController extends BaseController { /** * 导出出国登记列表 */ - @SaCheckPermission("rs:overseas:registration:export") + // @SaCheckPermission("rs:overseasregistration:export") @Log(title = "出国登记", businessType = BusinessType.EXPORT) @PostMapping("/export") public void export(RsOverseasRegistrationQuery query, HttpServletResponse response) { @@ -76,27 +76,26 @@ public class RsOverseasRegistrationController extends BaseController { /** * 获取出国登记详细信息 */ - @SaCheckPermission("rs:overseas:registration:query") + // @SaCheckPermission("rs:overseasregistration:query") @GetMapping("/{registrationId}") - public R getInfo(@NotNull(message = "主键不能为空") - @PathVariable Long registrationId) { + public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long registrationId) { return R.ok(registrationService.queryById(registrationId)); } /** * 根据申请ID获取出国登记信息 */ - @SaCheckPermission("rs:overseas:registration:query") + // @SaCheckPermission("rs:overseasregistration:query") @GetMapping("/application/{applicationId}") - public R getByApplicationId(@NotNull(message = "申请ID不能为空") - @PathVariable Long applicationId) { + public R getByApplicationId( + @NotNull(message = "申请ID不能为空") @PathVariable Long applicationId) { return R.ok(registrationService.queryByApplicationId(applicationId)); } /** * 新增出国登记 */ - @SaCheckPermission("rs:overseas:registration:add") + // @SaCheckPermission("rs:overseasregistration:add") @Log(title = "出国登记", businessType = BusinessType.INSERT) @PostMapping() public R add(@Validated(AddGroup.class) @RequestBody RsOverseasRegistrationBo bo) { @@ -106,7 +105,7 @@ public class RsOverseasRegistrationController extends BaseController { /** * 新增并提交出国登记 */ - @SaCheckPermission("rs:overseas:registration:add") + // @SaCheckPermission("rs:overseasregistration:add") @Log(title = "出国登记", businessType = BusinessType.INSERT) @PostMapping("/addAndSubmit") public R addAndSubmit(@Validated(AddGroup.class) @RequestBody RsOverseasRegistrationBo bo) { @@ -116,7 +115,7 @@ public class RsOverseasRegistrationController extends BaseController { /** * 修改出国登记 */ - @SaCheckPermission("rs:overseas:registration:edit") + // @SaCheckPermission("rs:overseasregistration:edit") @Log(title = "出国登记", businessType = BusinessType.UPDATE) @PutMapping() public R edit(@Validated(EditGroup.class) @RequestBody RsOverseasRegistrationBo bo) { @@ -126,67 +125,61 @@ public class RsOverseasRegistrationController extends BaseController { /** * 删除出国登记 */ - @SaCheckPermission("rs:overseas:registration:remove") + // @SaCheckPermission("rs:overseasregistration:remove") @Log(title = "出国登记", businessType = BusinessType.DELETE) @DeleteMapping("/{registrationIds}") - public R remove(@NotEmpty(message = "主键不能为空") - @PathVariable Long[] registrationIds) { + public R remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] registrationIds) { return toAjax(registrationService.deleteWithValidByIds(List.of(registrationIds), true)); } /** * 提交出国登记 */ - @SaCheckPermission("rs:overseas:registration:submit") + // @SaCheckPermission("rs:overseasregistration:submit") @Log(title = "出国登记", businessType = BusinessType.UPDATE) @PutMapping("/submit/{registrationId}") - public R submit(@NotNull(message = "主键不能为空") - @PathVariable Long registrationId) { + public R submit(@NotNull(message = "主键不能为空") @PathVariable Long registrationId) { return toAjax(registrationService.submitRegistration(registrationId)); } /** * 确认出国登记 */ - @SaCheckPermission("rs:overseas:registration:confirm") + // @SaCheckPermission("rs:overseasregistration:confirm") @Log(title = "出国登记", businessType = BusinessType.UPDATE) @PutMapping("/confirm/{registrationId}") - public R confirm(@NotNull(message = "主键不能为空") - @PathVariable Long registrationId) { + public R confirm(@NotNull(message = "主键不能为空") @PathVariable Long registrationId) { return toAjax(registrationService.confirmRegistration(registrationId)); } /** * 撤回出国登记 */ - @SaCheckPermission("rs:overseas:registration:withdraw") + // @SaCheckPermission("rs:overseasregistration:withdraw") @Log(title = "出国登记", businessType = BusinessType.UPDATE) @PutMapping("/withdraw/{registrationId}") - public R withdraw(@NotNull(message = "主键不能为空") - @PathVariable Long registrationId) { + public R withdraw(@NotNull(message = "主键不能为空") @PathVariable Long registrationId) { return toAjax(registrationService.withdrawRegistration(registrationId)); } /** * 基于出国申请创建登记记录 */ - @SaCheckPermission("rs:overseas:registration:create") + // @SaCheckPermission("rs:overseasregistration:create") @Log(title = "出国登记", businessType = BusinessType.INSERT) @PostMapping("/create/{applicationId}") - public R createFromApplication(@NotNull(message = "申请ID不能为空") - @PathVariable Long applicationId) { + public R createFromApplication(@NotNull(message = "申请ID不能为空") @PathVariable Long applicationId) { return toAjax(registrationService.createFromApplication(applicationId)); } /** * 批准出国登记 */ - @SaCheckPermission("rs:registration:approve") + // @SaCheckPermission("rs:registration:approve") @Log(title = "出国登记", businessType = BusinessType.UPDATE) @PutMapping("/approve/{registrationId}") - public R approve(@NotNull(message = "主键不能为空") - @PathVariable Long registrationId, - @RequestBody(required = false) RsOverseasRegistrationBo bo) { + public R approve(@NotNull(message = "主键不能为空") @PathVariable Long registrationId, + @RequestBody(required = false) RsOverseasRegistrationBo bo) { String approveNotes = (bo != null && bo.getRemarks() != null) ? bo.getRemarks() : "同意"; return toAjax(registrationService.approveRegistration(registrationId, approveNotes)); } @@ -194,13 +187,12 @@ public class RsOverseasRegistrationController extends BaseController { /** * 拒绝出国登记 */ - @SaCheckPermission("rs:registration:reject") + // @SaCheckPermission("rs:registration:reject") @Log(title = "出国登记", businessType = BusinessType.UPDATE) @PutMapping("/reject/{registrationId}") - public R reject(@NotNull(message = "主键不能为空") - @PathVariable Long registrationId, - @RequestBody(required = false) RsOverseasRegistrationBo bo) { + public R reject(@NotNull(message = "主键不能为空") @PathVariable Long registrationId, + @RequestBody(required = false) RsOverseasRegistrationBo bo) { String rejectReason = (bo != null && bo.getRemarks() != null) ? bo.getRemarks() : "不同意"; return toAjax(registrationService.rejectRegistration(registrationId, rejectReason)); } -} \ No newline at end of file +} diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/RsOverseasApplication.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/RsOverseasApplication.java index 55d544e1..1c587106 100644 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/RsOverseasApplication.java +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/RsOverseasApplication.java @@ -384,10 +384,10 @@ public class RsOverseasApplication extends BaseEntity { private String partyRetentionUploadStatus; /** - * 党籍保留附件OSS ID(用于安全下载) + * 党籍保留附件OSS ID(用于安全下载,支持多个ID用逗号分隔) */ @ExcelIgnore - private Long partyRetentionFileOssId; + private String partyRetentionFileOssId; /** * 租户编号 diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/RsOverseasRegistration.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/RsOverseasRegistration.java index ee06d702..755e76a1 100644 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/RsOverseasRegistration.java +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/RsOverseasRegistration.java @@ -29,7 +29,7 @@ public class RsOverseasRegistration extends BaseEntity { /** * 登记ID */ - @TableId(value = "registration_id", type = IdType.AUTO) + @TableId(value = "registration_id", type = IdType.ASSIGN_ID) @ExcelProperty(value = "登记ID") private Long registrationId; diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/bo/RsOverseasApplicationBo.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/bo/RsOverseasApplicationBo.java index 6f97b7d7..903f7dca 100644 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/bo/RsOverseasApplicationBo.java +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/bo/RsOverseasApplicationBo.java @@ -317,6 +317,32 @@ public class RsOverseasApplicationBo extends BaseEntity { */ private String stampedFileUploadStatus; + /** + * 党籍保留附件文件路径 + */ + private String partyRetentionFilePath; + + /** + * 党籍保留附件文件名 + */ + private String partyRetentionFileName; + + /** + * 党籍保留附件上传时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date partyRetentionUploadTime; + + /** + * 党籍保留附件上传状态:0-未上传,1-已上传 + */ + private String partyRetentionUploadStatus; + + /** + * 党籍保留附件OSS ID(支持多个ID用逗号分隔) + */ + private String partyRetentionFileOssId; + /** * 租户ID */ diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/vo/RsOverseasApplicationVo.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/vo/RsOverseasApplicationVo.java index 8e722a56..1a4ef80b 100644 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/vo/RsOverseasApplicationVo.java +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/vo/RsOverseasApplicationVo.java @@ -365,9 +365,9 @@ public class RsOverseasApplicationVo implements Serializable { private String partyRetentionUploadStatus; /** - * 党籍保留附件OSS ID(用于安全下载) + * 党籍保留附件OSS ID(用于安全下载,支持多个ID用逗号分隔) */ - private Long partyRetentionFileOssId; + private String partyRetentionFileOssId; /** * 租户编号 diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/vo/RsOverseasApprovalMonitorVo.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/vo/RsOverseasApprovalMonitorVo.java index 9a3e3219..b43402d0 100644 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/vo/RsOverseasApprovalMonitorVo.java +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/vo/RsOverseasApprovalMonitorVo.java @@ -196,6 +196,11 @@ public class RsOverseasApprovalMonitorVo implements Serializable { @ExcelProperty(value = "应急联系人及电话") private String emergencyContact; + /** + * 申请人手写签名(base64) + */ + private String signature; + // ==================== 审批状态信息 ==================== /** diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/mapper/RsOverseasApplicationMapper.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/mapper/RsOverseasApplicationMapper.java index d371ea95..6065b2d1 100644 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/mapper/RsOverseasApplicationMapper.java +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/mapper/RsOverseasApplicationMapper.java @@ -144,4 +144,23 @@ public interface RsOverseasApplicationMapper extends BaseMapperPlus selectPendingApplications(List approvalLevels, Long deptId); + + /** + * 🔴 新增:根据角色查询待审批申请列表(支持分页和搜索) + * + * 查询逻辑: + * 1. 根据用户角色确定审批级别列表 + * 2. 关联 sys_user 表获取申请人的部门信息 + * 3. 按审批级别过滤(approvalLevel IN (...)) + * 4. 按状态过滤(status = '0' 待审核) + * 5. 按部门过滤(部门负责人:u.dept_id = 当前用户部门ID) + * 6. 按搜索条件过滤(前往国家、出境事由) + * 7. 按创建时间倒序排列 + * + * @param query 查询条件(包含分页、搜索条件) + * @param approvalLevels 审批级别列表,例如:["1", "2"] + * @param deptId 部门ID(部门负责人需要),为null则不过滤部门 + * @return 待审批申请分页列表 + */ + List selectPendingApplicationsByRole(RsOverseasApplicationQuery query, List approvalLevels, Long deptId); } \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/IRsOverseasApplicationService.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/IRsOverseasApplicationService.java index cbdc7557..0b0b5dff 100644 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/IRsOverseasApplicationService.java +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/IRsOverseasApplicationService.java @@ -48,6 +48,14 @@ public interface IRsOverseasApplicationService { */ List queryPendingByCurrentUser(); + /** + * 查询当前用户的待审批申请分页列表(根据角色过滤) + * + * @param query 查询条件(包含分页、搜索条件) + * @return 待审批申请分页集合 + */ + TableDataInfo queryPendingPageListByRole(RsOverseasApplicationQuery query); + /** * 查询出国申请详细信息 * @@ -207,7 +215,7 @@ public interface IRsOverseasApplicationService { * @return 是否成功 */ Boolean updatePartyRetentionFileInfo(Long applicationId, String filePath, String fileName, - java.util.Date uploadTime, String uploadStatus, Long ossId); + java.util.Date uploadTime, String uploadStatus, String ossId); /** * 校验出国登记前置条件 diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/impl/RsOverseasApplicationServiceImpl.java b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/impl/RsOverseasApplicationServiceImpl.java index f9fa4656..1ca828af 100644 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/impl/RsOverseasApplicationServiceImpl.java +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/impl/RsOverseasApplicationServiceImpl.java @@ -51,7 +51,7 @@ import org.dromara.common.mybatis.core.page.PageQuery; /** * 出国申请Service业务层处理 - * + * * @author weizzzz * @date 2025-07-28 */ @@ -232,14 +232,14 @@ public class RsOverseasApplicationServiceImpl extends ServiceImpl queryPendingPageListByRole(RsOverseasApplicationQuery query) { + Long currentUserId = LoginHelper.getUserId(); + String currentUserName = LoginHelper.getUsername(); + String tenantId = LoginHelper.getTenantId(); + + log.info("🔍 查询用户待审批申请分页列表 - 用户ID: {}, 用户名: {}", currentUserId, currentUserName); + + // 1. 获取当前用户的角色列表 + List userRoles = roleService.selectRolesByUserId(currentUserId); + if (userRoles == null || userRoles.isEmpty()) { + log.warn("❌ 用户 {} 没有任何角色,返回空列表", currentUserName); + return TableDataInfo.build(); + } + + // 2. 根据角色编码确定审批级别列表 + List deptHeadLevels = new ArrayList<>(); // 部门负责人审批级别 + List otherLevels = new ArrayList<>(); // 其他审批级别 + Long deptIdForFilter = null; + boolean hasDeptHeadRole = false; + boolean hasOtherRole = false; + + for (SysRoleVo role : userRoles) { + String roleKey = role.getRoleKey(); + if (roleKey == null) { + continue; + } + + // 角色编码映射到审批级别 + if ("rs_bmfzr".equals(roleKey)) { + deptHeadLevels.add("1"); + hasDeptHeadRole = true; + } else if ("hr_dept_role".equals(roleKey)) { + otherLevels.add("2"); + hasOtherRole = true; + } else if ("discipline_committee_role".equals(roleKey)) { + otherLevels.add("3"); + hasOtherRole = true; + } else if ("college_leader_role".equals(roleKey)) { + otherLevels.add("4"); + hasOtherRole = true; + } else if ("party_secretary_role".equals(roleKey)) { + otherLevels.add("5"); + hasOtherRole = true; + } + } + + if (!hasDeptHeadRole && !hasOtherRole) { + log.warn("❌ 用户 {} 没有任何出国审批角色,返回空列表", currentUserName); + return TableDataInfo.build(); + } + + log.info("✅ 用户 {} 拥有审批级别 - 部门负责人: {}, 其他角色: {}", + currentUserName, deptHeadLevels, otherLevels); + + // 3. 如果是部门负责人,获取部门ID进行过滤 + if (hasDeptHeadRole) { + SysUserVo currentUser = userService.selectUserById(currentUserId); + if (currentUser != null && currentUser.getDeptId() != null) { + deptIdForFilter = currentUser.getDeptId(); + log.info("🔒 [部门权限过滤] 部门负责人,用户: {}, 部门ID: {}, 只查看本部门待审批", + currentUserName, deptIdForFilter); + } else { + log.warn("⚠️ 用户 {} 是部门负责人但没有部门信息,无法过滤", currentUserName); + } + } + + // 4. 设置租户ID + if (StringUtils.isNotBlank(tenantId)) { + query.setTenantId(tenantId); + } + + // 5. 🔴 修复:分别查询不同角色的数据,然后合并去重 + List allList = new ArrayList<>(); + Map applicationMap = new HashMap<>(); // 用于去重,key=applicationId + + // 5.1 查询部门负责人的数据(本部门,审批级别='1') + if (hasDeptHeadRole && !deptHeadLevels.isEmpty() && deptIdForFilter != null) { + List deptList = baseMapper.selectPendingApplicationsByRole( + query, deptHeadLevels, deptIdForFilter); + log.info("📋 [部门负责人] 查询到 {} 条本部门待审批申请", deptList.size()); + for (RsOverseasApplicationVo vo : deptList) { + applicationMap.put(vo.getApplicationId(), vo); + } + } + + // 5.2 查询其他角色的数据(全校,审批级别为其他级别) + if (hasOtherRole && !otherLevels.isEmpty()) { + List otherList = baseMapper.selectPendingApplicationsByRole( + query, otherLevels, null); // deptIdForFilter=null,不限制部门 + log.info("📋 [其他角色] 查询到 {} 条全校待审批申请", otherList.size()); + for (RsOverseasApplicationVo vo : otherList) { + applicationMap.put(vo.getApplicationId(), vo); + } + } + + // 5.3 合并去重后的结果,按创建时间倒序排列 + allList = new ArrayList<>(applicationMap.values()); + allList.sort((a, b) -> { + if (a.getCreateTime() != null && b.getCreateTime() != null) { + return b.getCreateTime().compareTo(a.getCreateTime()); + } + return 0; + }); + + log.info("✅ [合并结果] 去重后共 {} 条待审批申请", allList.size()); + + // 6. 手动分页处理 + int pageNum = query.getPageNum(); + int pageSize = query.getPageSize(); + int total = allList.size(); + int start = (pageNum - 1) * pageSize; + int end = Math.min(start + pageSize, total); + + List pageList; + if (start >= total) { + pageList = new ArrayList<>(); + } else { + pageList = allList.subList(start, end); + } + + // 7. 构建分页对象 + Page page = new Page<>(pageNum, pageSize); + page.setRecords(pageList); + page.setTotal(total); + + log.info("📋 用户 {} 查询到 {} 条待审批申请,分页显示第 {} 页,共 {} 条", + currentUserName, total, pageNum, pageList.size()); + + return TableDataInfo.build(page); + } + /** * 转换实体为VO对象 * 🔴 修复:使用Mapper查询完整的VO对象,包含字典翻译等扩展信息 @@ -514,7 +664,7 @@ public class RsOverseasApplicationServiceImpl extends ServiceImpl() .eq(RsOverseasApplication::getApplicationId, applicationId) @@ -1087,7 +1263,7 @@ public class RsOverseasApplicationServiceImpl extends ServiceImpl queryPageList(RsOverseasItineraryChangeQuery query) { + // 查询所有符合条件的数据(不分页) List list = changeMapper.selectChangeList(query); - return TableDataInfo.build(list); + + // 手动分页处理 + int pageNum = query.getPageNum(); + int pageSize = query.getPageSize(); + int total = list.size(); + int start = (pageNum - 1) * pageSize; + int end = Math.min(start + pageSize, total); + + List pageList; + if (start >= total) { + pageList = new ArrayList<>(); + } else { + pageList = list.subList(start, end); + } + + // 构建分页对象 + Page page = new Page<>(pageNum, pageSize); + page.setRecords(pageList); + page.setTotal(total); + + return TableDataInfo.build(page); } /** @@ -89,11 +123,12 @@ public class RsOverseasItineraryChangeServiceImpl implements IRsOverseasItinerar @Transactional(rollbackFor = Exception.class) public Boolean insertByBo(RsOverseasItineraryChangeBo bo) { // 查询出国申请信息 - org.dromara.rs.domain.vo.RsOverseasApplicationVo application = applicationService.queryById(bo.getApplicationId()); + org.dromara.rs.domain.vo.RsOverseasApplicationVo application = applicationService + .queryById(bo.getApplicationId()); if (application == null) { throw new ServiceException("出国申请不存在"); } - + // 检查申请状态是否为已通过 if (!"1".equals(application.getStatus())) { throw new ServiceException("只能为已通过审批的出国申请提交变更申请"); @@ -102,8 +137,8 @@ public class RsOverseasItineraryChangeServiceImpl implements IRsOverseasItinerar // 检查是否有未完成的变更申请 LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); wrapper.eq(RsOverseasItineraryChange::getApplicationId, bo.getApplicationId()) - .in(RsOverseasItineraryChange::getStatus, Arrays.asList("0")) // 待审核 - .eq(RsOverseasItineraryChange::getDeleted, 0); + .in(RsOverseasItineraryChange::getStatus, Arrays.asList("0")) // 待审核 + .eq(RsOverseasItineraryChange::getDeleted, 0); Long count = changeMapper.selectCount(wrapper); if (count > 0) { throw new ServiceException("该出国申请已有待审核的变更申请,请先处理"); @@ -117,11 +152,12 @@ public class RsOverseasItineraryChangeServiceImpl implements IRsOverseasItinerar entity.setApplicantId(userId); entity.setApplicantName(LoginHelper.getUsername()); entity.setApplicationId(bo.getApplicationId()); - + // 从出国申请中复制原始信息 entity.setOriginalDestination(application.getDestination()); entity.setOriginalDestinationCity(application.getDestinationCity()); - entity.setOriginalExitReason(application.getExitReason() != null ? application.getExitReason() : application.getPurposeDetail()); + entity.setOriginalExitReason( + application.getExitReason() != null ? application.getExitReason() : application.getPurposeDetail()); entity.setOriginalStartDate(application.getStartDate()); entity.setOriginalEndDate(application.getEndDate()); entity.setOriginalDuration(application.getDuration()); @@ -148,9 +184,10 @@ public class RsOverseasItineraryChangeServiceImpl implements IRsOverseasItinerar throw new ServiceException("变更申请不存在"); } - // 只有待审核或已拒绝的申请可以修改 - if (!"0".equals(original.getStatus()) && !"2".equals(original.getStatus())) { - throw new ServiceException("只有待审核或已拒绝的变更申请可以修改"); + // 只有待审核、已拒绝或已撤回的申请可以修改 + if (!"0".equals(original.getStatus()) && !"2".equals(original.getStatus()) + && !"3".equals(original.getStatus())) { + throw new ServiceException("只有待审核、已拒绝或已撤回的变更申请可以修改"); } // 检查权限:只能修改自己的申请 @@ -161,7 +198,7 @@ public class RsOverseasItineraryChangeServiceImpl implements IRsOverseasItinerar // 转换BO到Entity RsOverseasItineraryChange entity = BeanUtil.toBean(bo, RsOverseasItineraryChange.class); - + // 重新提交时,状态改为待审核 entity.setStatus("0"); entity.setApprovalOpinion(null); @@ -179,24 +216,24 @@ public class RsOverseasItineraryChangeServiceImpl implements IRsOverseasItinerar @Transactional(rollbackFor = Exception.class) public Boolean deleteByIds(List changeIds) { Long userId = LoginHelper.getUserId(); - + for (Long changeId : changeIds) { RsOverseasItineraryChange change = changeMapper.selectById(changeId); if (change == null) { continue; } - + // 检查权限:只能删除自己的申请 if (!userId.equals(change.getApplicantId())) { throw new ServiceException("只能删除自己的变更申请"); } - + // 只有待审核或已拒绝的申请可以删除 if (!"0".equals(change.getStatus()) && !"2".equals(change.getStatus())) { throw new ServiceException("只有待审核或已拒绝的变更申请可以删除"); } } - + return changeMapper.deleteBatchIds(changeIds) > 0; } @@ -229,6 +266,12 @@ public class RsOverseasItineraryChangeServiceImpl implements IRsOverseasItinerar /** * 审批变更申请 + * + * 🔴 审批通过后,需要更新原申请单和记录的数据: + * 1. 前往国家 → 更新到 rs_overseas_application 和 rs_overseas_record + * 2. 实际出行开始时间、结束时间 → 更新到 rs_overseas_application 和 rs_overseas_record + * 3. 行程安排 → 更新到 rs_overseas_application 和 rs_overseas_record + * 4. 出境事由 → 追加到 rs_overseas_application 的备注字段 */ @Override @Transactional(rollbackFor = Exception.class) @@ -249,9 +292,139 @@ public class RsOverseasItineraryChangeServiceImpl implements IRsOverseasItinerar change.setApprovalOpinion(opinion); change.setApprovalTime(new Date()); change.setApproverId(userId); - change.setApproverName(LoginHelper.getUsername()); + String displayName = StringUtils.defaultIfBlank(LoginHelper.getNickname(), LoginHelper.getUsername()); + change.setApproverName(displayName); - return changeMapper.updateById(change) > 0; + // 更新变更申请状态 + boolean updateChangeResult = changeMapper.updateById(change) > 0; + if (!updateChangeResult) { + throw new ServiceException("更新变更申请状态失败"); + } + + // 🔴 如果审批通过,需要更新原申请单和记录的数据 + if (approved && change.getApplicationId() != null) { + log.info("🔧 [审批通过] 开始更新原申请单和记录数据,变更ID: {}, 申请ID: {}", changeId, change.getApplicationId()); + + try { + // 1. 更新 rs_overseas_application 表 + RsOverseasApplication application = applicationMapper.selectById(change.getApplicationId()); + if (application != null) { + LambdaUpdateWrapper appUpdateWrapper = new LambdaUpdateWrapper<>(); + appUpdateWrapper.eq(RsOverseasApplication::getApplicationId, change.getApplicationId()); + + // 更新前往国家(destination 和 destinationCountry 同步更新) + if (change.getNewDestination() != null && !change.getNewDestination().trim().isEmpty()) { + appUpdateWrapper.set(RsOverseasApplication::getDestination, change.getNewDestination()); + appUpdateWrapper.set(RsOverseasApplication::getDestinationCountry, change.getNewDestination()); + log.info("✅ [更新申请单] 前往国家: {} (同步更新 destination 和 destinationCountry)", + change.getNewDestination()); + } + + // 更新出发日期和返回日期 + if (change.getNewStartDate() != null) { + appUpdateWrapper.set(RsOverseasApplication::getStartDate, change.getNewStartDate()); + log.info("✅ [更新申请单] 出发日期: {}", change.getNewStartDate()); + } + if (change.getNewEndDate() != null) { + appUpdateWrapper.set(RsOverseasApplication::getEndDate, change.getNewEndDate()); + log.info("✅ [更新申请单] 返回日期: {}", change.getNewEndDate()); + + // 重新计算出国天数 + if (change.getNewStartDate() != null && change.getNewEndDate() != null) { + long diffInMillies = change.getNewEndDate().getTime() - change.getNewStartDate().getTime(); + int days = (int) (diffInMillies / (1000 * 60 * 60 * 24)) + 1; + appUpdateWrapper.set(RsOverseasApplication::getDuration, days); + log.info("✅ [更新申请单] 出国天数: {} 天", days); + } + } + + // 更新行程安排 + if (change.getNewItinerary() != null && !change.getNewItinerary().trim().isEmpty()) { + appUpdateWrapper.set(RsOverseasApplication::getItinerary, change.getNewItinerary()); + log.info("✅ [更新申请单] 行程安排已更新"); + } + + // 追加出境事由到备注字段 + if (change.getNewExitReason() != null && !change.getNewExitReason().trim().isEmpty()) { + String currentRemarks = application.getRemarks() != null ? application.getRemarks() : ""; + String newRemarks = currentRemarks.isEmpty() + ? "行程变更-出境事由: " + change.getNewExitReason() + : currentRemarks + "\n行程变更-出境事由: " + change.getNewExitReason(); + appUpdateWrapper.set(RsOverseasApplication::getRemarks, newRemarks); + log.info("✅ [更新申请单] 备注已追加出境事由: {}", change.getNewExitReason()); + } + + boolean appUpdateResult = applicationMapper.update(null, appUpdateWrapper) > 0; + if (!appUpdateResult) { + log.warn("⚠️ [更新申请单] 更新失败,申请ID: {}", change.getApplicationId()); + } else { + log.info("✅ [更新申请单] 更新成功,申请ID: {}", change.getApplicationId()); + } + } else { + log.warn("⚠️ [更新申请单] 申请单不存在,申请ID: {}", change.getApplicationId()); + } + + // 2. 更新 rs_overseas_record 表 + RsOverseasRecordVo recordVo = recordMapper + .selectOverseasRecordByApplicationId(change.getApplicationId()); + if (recordVo != null) { + RsOverseasRecord record = recordMapper.selectById(recordVo.getRecordId()); + if (record != null) { + LambdaUpdateWrapper recordUpdateWrapper = new LambdaUpdateWrapper<>(); + recordUpdateWrapper.eq(RsOverseasRecord::getRecordId, record.getRecordId()); + + // 更新前往国家 + if (change.getNewDestination() != null && !change.getNewDestination().trim().isEmpty()) { + recordUpdateWrapper.set(RsOverseasRecord::getDestination, change.getNewDestination()); + log.info("✅ [更新记录] 前往国家: {}", change.getNewDestination()); + } + + // 更新实际开始日期和结束日期 + if (change.getNewStartDate() != null) { + recordUpdateWrapper.set(RsOverseasRecord::getActualStartDate, change.getNewStartDate()); + log.info("✅ [更新记录] 实际开始日期: {}", change.getNewStartDate()); + } + if (change.getNewEndDate() != null) { + recordUpdateWrapper.set(RsOverseasRecord::getActualEndDate, change.getNewEndDate()); + log.info("✅ [更新记录] 实际结束日期: {}", change.getNewEndDate()); + + // 重新计算实际出国天数 + if (change.getNewStartDate() != null && change.getNewEndDate() != null) { + long diffInMillies = change.getNewEndDate().getTime() + - change.getNewStartDate().getTime(); + int days = (int) (diffInMillies / (1000 * 60 * 60 * 24)) + 1; + recordUpdateWrapper.set(RsOverseasRecord::getActualDuration, days); + log.info("✅ [更新记录] 实际出国天数: {} 天", days); + } + } + + // 更新行程安排 + if (change.getNewItinerary() != null && !change.getNewItinerary().trim().isEmpty()) { + recordUpdateWrapper.set(RsOverseasRecord::getItinerary, change.getNewItinerary()); + log.info("✅ [更新记录] 行程安排已更新"); + } + + boolean recordUpdateResult = recordMapper.update(null, recordUpdateWrapper) > 0; + if (!recordUpdateResult) { + log.warn("⚠️ [更新记录] 更新失败,记录ID: {}", record.getRecordId()); + } else { + log.info("✅ [更新记录] 更新成功,记录ID: {}", record.getRecordId()); + } + } else { + log.warn("⚠️ [更新记录] 记录不存在,记录ID: {}", recordVo.getRecordId()); + } + } else { + log.warn("⚠️ [更新记录] 未找到关联记录,申请ID: {}", change.getApplicationId()); + } + + log.info("✅ [审批通过] 数据更新完成,变更ID: {}", changeId); + } catch (Exception e) { + log.error("❌ [审批通过] 更新原申请单和记录数据失败,变更ID: {}, 错误: {}", changeId, e.getMessage(), e); + throw new ServiceException("审批通过后更新原申请单和记录数据失败: " + e.getMessage()); + } + } + + return true; } /** @@ -262,4 +435,3 @@ public class RsOverseasItineraryChangeServiceImpl implements IRsOverseasItinerar return changeMapper.selectChangeListByRecordId(recordId); } } - diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/RsOverseasApplicationMapper.xml b/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/RsOverseasApplicationMapper.xml index 9563709c..f017d21a 100644 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/RsOverseasApplicationMapper.xml +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/RsOverseasApplicationMapper.xml @@ -617,5 +617,42 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" ORDER BY a.apply_time DESC, a.create_time DESC + + + \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/RsOverseasRecordMapper.xml b/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/RsOverseasRecordMapper.xml index 27d09569..7c57722c 100644 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/RsOverseasRecordMapper.xml +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/RsOverseasRecordMapper.xml @@ -74,8 +74,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" select ors.record_id, ors.application_id, ors.applicant_id, ors.applicant_name, ors.department, - ors.position, - COALESCE(sp.post_name, ors.position) as position_text, + oa.position, + oa.position as position_text, ors.contact_phone, ors.contact_email, ors.purpose, ors.purpose_detail, ors.destination, ors.destination_city, ors.actual_start_date, ors.actual_end_date, ors.actual_duration, ors.actual_budget, ors.invitation, ors.itinerary, ors.accommodation_plan, ors.transportation_plan, @@ -134,7 +134,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 'record' as data_source from rs_overseas_record ors left join rs_overseas_application oa on ors.application_id = oa.application_id and oa.deleted = '0' - left join sys_post sp on (ors.position = CAST(sp.post_id AS CHAR) or ors.position = sp.post_code) and sp.status = '0' @@ -142,8 +141,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" -- 查询已创建的出国记录(关联申请表获取扩展字段) select ors.record_id, ors.application_id, ors.applicant_id, ors.applicant_name, ors.department, - ors.position, - COALESCE(sp1.post_name, ors.position) as position_text, + oa.position, + oa.position as position_text, ors.contact_phone, ors.contact_email, ors.purpose, ors.purpose_detail, ors.destination, ors.destination_city, ors.actual_start_date as actual_start_date, ors.actual_end_date as actual_end_date, ors.actual_duration as actual_duration, ors.actual_budget as actual_budget, @@ -212,7 +211,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 'record' as data_source from rs_overseas_record ors left join rs_overseas_application oa on ors.application_id = oa.application_id and oa.deleted = '0' - left join sys_post sp1 on (ors.position = CAST(sp1.post_id AS CHAR) or ors.position = sp1.post_code) and sp1.status = '0' where ors.deleted = '0' UNION ALL @@ -222,7 +220,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" NULL as record_id, oa.application_id, oa.applicant_id, oa.applicant_name, oa.department, oa.position, - COALESCE(sp2.post_name, oa.position) as position_text, + oa.position as position_text, oa.contact_phone, oa.contact_email, oa.purpose, oa.purpose_detail, oa.destination, oa.destination_city, oa.start_date as actual_start_date, oa.end_date as actual_end_date, oa.duration as actual_duration, NULL as actual_budget, @@ -284,7 +282,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" '进行中' as completion_status_text, 'approved_application' as data_source from rs_overseas_application oa - left join sys_post sp2 on (oa.position = CAST(sp2.post_id AS CHAR) or oa.position = sp2.post_code) and sp2.status = '0' where oa.deleted = '0' and oa.status = '1' and not exists ( diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/recruitment.sql b/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/recruitment.sql deleted file mode 100644 index 48cd1b6a..00000000 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/recruitment.sql +++ /dev/null @@ -1,142 +0,0 @@ --- 招聘模块表结构 --- 创建于 2025-07-02 - --- 职位表 -DROP TABLE IF EXISTS `rs_job`; -CREATE TABLE `rs_job` ( - `job_id` bigint(20) NOT NULL COMMENT '职位ID', - `title` varchar(100) NOT NULL COMMENT '职位名称', - `department` varchar(50) NOT NULL COMMENT '部门', - `salary_range` varchar(50) DEFAULT NULL COMMENT '薪资范围', - `location` varchar(100) DEFAULT NULL COMMENT '工作地点', - `description` text COMMENT '职位描述', - `requirements` text COMMENT '任职要求', - `is_active` char(1) DEFAULT '0' COMMENT '是否在招(0是 1否)', - `publish_time` datetime DEFAULT NULL COMMENT '发布时间', - `view_count` int(11) DEFAULT '0' COMMENT '浏览次数', - `tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户编号', - `create_dept` bigint(20) DEFAULT NULL COMMENT '创建部门', - `create_by` bigint(20) DEFAULT NULL COMMENT '创建者', - `create_time` datetime DEFAULT NULL COMMENT '创建时间', - `update_by` bigint(20) DEFAULT NULL COMMENT '更新者', - `update_time` datetime DEFAULT NULL COMMENT '更新时间', - `remark` varchar(500) DEFAULT NULL COMMENT '备注', - `deleted` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 1代表删除)', - PRIMARY KEY (`job_id`), - KEY `idx_department` (`department`), - KEY `idx_is_active` (`is_active`) -) ENGINE=InnoDB COMMENT='招聘职位表'; - --- 应聘者表 -DROP TABLE IF EXISTS `rs_applicant`; -CREATE TABLE `rs_applicant` ( - `applicant_id` bigint(20) NOT NULL COMMENT '应聘者ID', - `user_id` bigint(20) DEFAULT NULL COMMENT '关联用户ID', - `name` varchar(50) NOT NULL COMMENT '姓名', - `phone` varchar(20) NOT NULL COMMENT '手机号码', - `email` varchar(100) NOT NULL COMMENT '电子邮箱', - `resume_file_path` varchar(255) DEFAULT NULL COMMENT '简历文件路径', - `self_intro` text COMMENT '自我介绍', - `current_status` varchar(20) DEFAULT 'active' COMMENT '当前状态(active-可申请 hired-已录用 rejected-已拒绝)', - `tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户编号', - `create_by` bigint(20) DEFAULT NULL COMMENT '创建者', - `create_time` datetime DEFAULT NULL COMMENT '创建时间', - `update_by` bigint(20) DEFAULT NULL COMMENT '更新者', - `update_time` datetime DEFAULT NULL COMMENT '更新时间', - `remark` varchar(500) DEFAULT NULL COMMENT '备注', - `deleted` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 1代表删除)', - PRIMARY KEY (`applicant_id`), - KEY `idx_user_id` (`user_id`), - KEY `idx_phone` (`phone`) -) ENGINE=InnoDB COMMENT='应聘者信息表'; - --- 教育经历表 -DROP TABLE IF EXISTS `rs_education`; -CREATE TABLE `rs_education` ( - `edu_id` bigint(20) NOT NULL COMMENT '教育经历ID', - `applicant_id` bigint(20) NOT NULL COMMENT '应聘者ID', - `school_name` varchar(100) NOT NULL COMMENT '学校名称', - `major` varchar(100) DEFAULT NULL COMMENT '专业', - `degree` varchar(50) DEFAULT NULL COMMENT '学位', - `start_date` date DEFAULT NULL COMMENT '开始日期', - `end_date` date DEFAULT NULL COMMENT '结束日期', - `gpa` varchar(20) DEFAULT NULL COMMENT '绩点', - `description` text COMMENT '描述', - `tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户编号', - `create_by` bigint(20) DEFAULT NULL COMMENT '创建者', - `create_time` datetime DEFAULT NULL COMMENT '创建时间', - `update_by` bigint(20) DEFAULT NULL COMMENT '更新者', - `update_time` datetime DEFAULT NULL COMMENT '更新时间', - `deleted` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 1代表删除)', - PRIMARY KEY (`edu_id`), - KEY `idx_applicant_id` (`applicant_id`) -) ENGINE=InnoDB COMMENT='教育经历表'; - --- 工作经历表 -DROP TABLE IF EXISTS `rs_work_experience`; -CREATE TABLE `rs_work_experience` ( - `exp_id` bigint(20) NOT NULL COMMENT '工作经历ID', - `applicant_id` bigint(20) NOT NULL COMMENT '应聘者ID', - `company_name` varchar(100) NOT NULL COMMENT '公司名称', - `position` varchar(100) NOT NULL COMMENT '职位', - `start_date` date DEFAULT NULL COMMENT '开始日期', - `end_date` date DEFAULT NULL COMMENT '结束日期', - `is_current` char(1) DEFAULT '0' COMMENT '是否当前工作(0是 1否)', - `description` text COMMENT '工作描述', - `tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户编号', - `create_by` bigint(20) DEFAULT NULL COMMENT '创建者', - `create_time` datetime DEFAULT NULL COMMENT '创建时间', - `update_by` bigint(20) DEFAULT NULL COMMENT '更新者', - `update_time` datetime DEFAULT NULL COMMENT '更新时间', - `deleted` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 1代表删除)', - PRIMARY KEY (`exp_id`), - KEY `idx_applicant_id` (`applicant_id`) -) ENGINE=InnoDB COMMENT='工作经历表'; - --- 申请记录表 -DROP TABLE IF EXISTS `rs_application`; -CREATE TABLE `rs_application` ( - `application_id` bigint(20) NOT NULL COMMENT '申请ID', - `job_id` bigint(20) NOT NULL COMMENT '职位ID', - `applicant_id` bigint(20) NOT NULL COMMENT '应聘者ID', - `apply_time` datetime NOT NULL COMMENT '申请时间', - `status` varchar(20) DEFAULT 'pending' COMMENT '状态(pending-待处理 interviewed-已面试 offered-已录用 rejected-已拒绝)', - `hr_notes` text COMMENT 'HR备注', - `tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户编号', - `create_by` bigint(20) DEFAULT NULL COMMENT '创建者', - `create_time` datetime DEFAULT NULL COMMENT '创建时间', - `update_by` bigint(20) DEFAULT NULL COMMENT '更新者', - `update_time` datetime DEFAULT NULL COMMENT '更新时间', - `remark` varchar(500) DEFAULT NULL COMMENT '备注', - `deleted` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 1代表删除)', - PRIMARY KEY (`application_id`), - KEY `idx_job_id` (`job_id`), - KEY `idx_applicant_id` (`applicant_id`), - KEY `idx_status` (`status`) -) ENGINE=InnoDB COMMENT='申请记录表'; - --- 附件表 -DROP TABLE IF EXISTS `rs_attachment`; -CREATE TABLE `rs_attachment` ( - `attachment_id` bigint(20) NOT NULL COMMENT '附件ID', - `application_id` bigint(20) NOT NULL COMMENT '申请ID', - `file_name` varchar(100) NOT NULL COMMENT '文件名', - `file_path` varchar(255) NOT NULL COMMENT '文件路径', - `file_type` varchar(50) DEFAULT NULL COMMENT '文件类型', - `file_size` bigint(20) DEFAULT NULL COMMENT '文件大小(字节)', - `tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户编号', - `create_by` bigint(20) DEFAULT NULL COMMENT '创建者', - `create_time` datetime DEFAULT NULL COMMENT '创建时间', - `update_by` bigint(20) DEFAULT NULL COMMENT '更新者', - `update_time` datetime DEFAULT NULL COMMENT '更新时间', - `deleted` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 1代表删除)', - PRIMARY KEY (`attachment_id`), - KEY `idx_application_id` (`application_id`) -) ENGINE=InnoDB COMMENT='申请附件表'; - --- 插入初始测试数据 -INSERT INTO `rs_job` (`job_id`, `title`, `department`, `salary_range`, `location`, `description`, `requirements`, `is_active`, `create_time`, `create_by`, `update_time`, `update_by`, `remark`, `deleted`, `tenant_id`) -VALUES -(1, '高级Java开发工程师', '教学', '15k-25k', '杭州', '负责校园教学管理系统的开发和维护,参与核心模块设计与实现,优化系统性能,解决技术难题。\n作为团队核心成员,参与技术方案评审,指导初级开发人员,确保项目按时高质量交付。', '计算机相关专业本科及以上学历,3年以上Java开发经验\n精通Java核心技术,熟悉Spring Boot、MyBatis等主流框架\n熟练掌握MySQL数据库设计与优化\n有教育行业相关系统开发经验者优先\n良好的沟通能力和团队协作精神', '0', NOW(), 1, NOW(), 1, '示例职位', '0', '000000'), -(2, '前端工程师', '教学', '12k-18k', '杭州', '负责校园教学管理系统前端开发,使用Vue3+TypeScript技术栈,参与UI设计与实现', '计算机相关专业本科及以上学历,2年以上前端开发经验\n精通HTML5/CSS3/JavaScript,熟悉Vue3/React等主流框架\n熟练使用TypeScript进行开发\n有教育行业相关系统开发经验者优先', '0', NOW(), 1, NOW(), 1, '示例职位', '0', '000000'), -(3, '行政助理', '行政', '5k-8k', '杭州', '协助部门主管处理日常行政事务,包括文件整理、会议安排、接待来访等工作', '大专及以上学历,行政管理相关专业优先\n熟悉办公软件,有较强的文字处理能力\n工作认真负责,有较强的组织协调能力\n有相关工作经验者优先', '0', NOW(), 1, NOW(), 1, '示例职位', '0', '000000'); \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/remove-submit-count-field.sql b/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/remove-submit-count-field.sql deleted file mode 100644 index c52e4f53..00000000 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/remove-submit-count-field.sql +++ /dev/null @@ -1,11 +0,0 @@ --- ----------------------------- --- 删除培训申请表中的提交次数字段 --- ----------------------------- - --- 删除 rs_training_application 表中的 submit_count 字段 --- 注意:MySQL 5.7及以下版本不支持 DROP COLUMN IF EXISTS 语法 --- 需要先检查字段是否存在,然后删除 -ALTER TABLE rs_training_application DROP COLUMN submit_count; - --- 验证字段已删除 -DESCRIBE rs_training_application; \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training-data-consistency-check.sql b/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training-data-consistency-check.sql deleted file mode 100644 index cc29e1c1..00000000 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training-data-consistency-check.sql +++ /dev/null @@ -1,62 +0,0 @@ --- ----------------------------- --- 培训审批数据一致性检查和修复 --- ----------------------------- - --- 检查审批记录表是否为空 -SELECT COUNT(*) AS approval_count FROM rs_training_approval WHERE deleted = '0'; - --- 检查审批记录与申请记录是否一致 -SELECT - COUNT(*) AS inconsistent_count -FROM - rs_training_approval a -LEFT JOIN - rs_training_application app ON a.application_id = app.application_id -WHERE - a.deleted = '0' - AND (app.application_id IS NULL OR app.deleted = '1'); - --- 插入测试数据(如果没有审批记录) -INSERT INTO rs_training_approval ( - approval_id, application_id, approver_id, approver_name, - approval_level, status, opinion, approval_time, tenant_id, - create_by, create_time, deleted -) -SELECT - FLOOR(RAND() * 1000000) + 100000, -- 随机审批ID - app.application_id, -- 使用真实申请ID - app.create_by, -- 使用申请的创建者ID作为审批人ID - (SELECT user_name FROM sys_user WHERE user_id = app.create_by LIMIT 1), -- 获取用户名 - 1, -- 审批级别 - app.status, -- 使用申请状态 - '系统自动生成的测试审批记录', -- 审批意见 - app.update_time, -- 使用申请更新时间作为审批时间 - app.tenant_id, -- 租户ID - app.create_by, -- 创建者 - NOW(), -- 创建时间 - '0' -- 未删除 -FROM - rs_training_application app -WHERE - app.deleted = '0' - AND app.status IN ('1', '2') -- 已通过或已拒绝 - AND NOT EXISTS ( - SELECT 1 - FROM rs_training_approval a - WHERE a.application_id = app.application_id AND a.deleted = '0' - ) -LIMIT 5; -- 限制生成5条测试数据 - --- 查询生成的测试数据 -SELECT - a.approval_id, a.application_id, a.approver_name, a.status, a.opinion, a.approval_time, - app.user_name AS applicant_name, app.reason -FROM - rs_training_approval a -JOIN - rs_training_application app ON a.application_id = app.application_id -WHERE - a.deleted = '0' - AND a.create_time > DATE_SUB(NOW(), INTERVAL 1 HOUR) -- 最近一小时创建的记录 -ORDER BY - a.create_time DESC; \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training-demo-data.sql b/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training-demo-data.sql deleted file mode 100644 index 47200bdb..00000000 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training-demo-data.sql +++ /dev/null @@ -1,38 +0,0 @@ --- ---------------------------- --- 培训管理模块测试数据 --- ---------------------------- - --- ---------------------------- --- 培训课程表测试数据 --- ---------------------------- -INSERT INTO `rs_training_course` (`course_id`, `title`, `description`, `instructor`, `duration`, `location`, `capacity`, `status`, `start_time`, `end_time`, `tenant_id`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`, `deleted`) -VALUES -(10001, '新入职教师培训', '针对新入职教师的岗前培训,包括教学理念、师德师风、教学方法等内容', '张教授', 16, '学术会议厅', 50, '1', '2025-08-01 09:00:00', '2025-08-03 17:00:00', '000000', 1, NOW(), 1, NOW(), '重要培训', '0'), -(10002, '教育技术与信息化', '介绍现代教育技术在教学中的应用,包括多媒体教学、在线教育平台等', '李博士', 12, '多媒体教室', 30, '1', '2025-08-10 09:00:00', '2025-08-12 17:00:00', '000000', 1, NOW(), 1, NOW(), '技术培训', '0'), -(10003, '教学方法与技能培训', '提升教师教学技能,包括课堂管理、学生互动、教学评估等', '王老师', 8, '实训楼', 40, '1', '2025-08-05 14:00:00', '2025-08-05 18:00:00', '000000', 1, NOW(), 1, NOW(), '实用培训', '0'), -(10004, '学生管理与心理健康', '帮助教师了解学生心理,掌握学生管理技巧和心理疏导方法', '陈心理师', 6, '学术报告厅', 60, '1', '2025-08-15 14:00:00', '2025-08-15 18:00:00', '000000', 1, NOW(), 1, NOW(), '心理培训', '0'), -(10005, '科研方法与学术写作', '指导教师进行科学研究,提升学术写作和论文发表能力', '赵教授', 20, '图书馆报告厅', 25, '1', '2025-08-25 09:00:00', '2025-08-30 17:00:00', '000000', 1, NOW(), 1, NOW(), '科研培训', '0'); - --- ---------------------------- --- 培训申请表测试数据 --- ---------------------------- -INSERT INTO `rs_training_application` (`application_id`, `course_id`, `user_id`, `user_name`, `department`, `position`, `reason`, `status`, `apply_time`, `current_approver`, `process_instance_id`, `tenant_id`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`, `deleted`) -VALUES -(20001, 10001, 2, '张老师', '教务处', '教师', '新入职需要培训', '1', '2025-07-25 12:03:49', 'admin', NULL, '000000', 1, NOW(), 1, NOW(), NULL, '0'), -(20002, 10002, 3, '王老师', '信息技术中心', '工程师', '学习新技术', '1', '2025-08-01 12:37:17', 'admin', NULL, '000000', 1, NOW(), 1, NOW(), '未完成', '0'), -(20003, 10003, 4, '刘老师', '计算机学院', '讲师', '提升教学技能', '1', '2025-07-25 12:04:06', 'admin', NULL, '000000', 1, NOW(), 1, NOW(), '培训尚未结束', '0'), -(20004, 10004, 5, '李老师', '学生处', '辅导员', '提升学生管理能力', '1', '2025-08-06 14:24:37', 'admin', NULL, '000000', 1, NOW(), 1, NOW(), '未完成', '0'), -(20005, 10005, 6, '陈老师', '科研处', '副教授', '提升科研能力', '1', '2025-08-22 15:25:51', 'admin', NULL, '000000', 1, NOW(), 1, NOW(), '未完成', '0'); - --- ---------------------------- --- 培训审批表测试数据 --- ---------------------------- -INSERT INTO `rs_training_approval` (`approval_id`, `application_id`, `approver_id`, `approver_name`, `approval_level`, `status`, `opinion`, `approval_time`, `tenant_id`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`, `deleted`) -VALUES -(30001, 20001, 101, '王主任', 1, '1', '同意参加,安排好工作交接', '2025-07-21 09:30:00', '000000', 101, NOW(), 101, NOW(), NULL, '0'), -(30002, 20003, 102, '孙院长', 1, '1', '批准参加,对提升教学质量有帮助', '2025-07-23 10:15:00', '000000', 102, NOW(), 102, NOW(), NULL, '0'), -(30003, 20004, 103, '周主任', 1, '2', '近期有重要工作交接,建议下次再参加', '2025-07-24 14:20:00', '000000', 103, NOW(), 103, NOW(), NULL, '0'); - --- ---------------------------- --- 培训记录表测试数据已删除,请通过申请界面新增 --- ---------------------------- \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training-fix-approval-column.sql b/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training-fix-approval-column.sql deleted file mode 100644 index e60a5c97..00000000 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training-fix-approval-column.sql +++ /dev/null @@ -1,43 +0,0 @@ --- ----------------------------- --- 培训审批数据修复脚本 --- ----------------------------- - --- 检查comment列是否存在,如果存在则迁移数据到opinion列 -SET @columnExists = 0; -SELECT COUNT(*) INTO @columnExists -FROM INFORMATION_SCHEMA.COLUMNS -WHERE TABLE_SCHEMA = DATABASE() - AND TABLE_NAME = 'rs_training_approval' - AND COLUMN_NAME = 'comment'; - --- 如果comment列存在,将数据从comment迁移到opinion -SET @sqlStmt = IF(@columnExists > 0, - 'UPDATE rs_training_approval SET opinion = comment WHERE opinion IS NULL AND comment IS NOT NULL', - 'SELECT "comment列不存在,无需迁移" AS message'); - -PREPARE stmt FROM @sqlStmt; -EXECUTE stmt; -DEALLOCATE PREPARE stmt; - --- 添加测试数据(如果表为空) -INSERT INTO rs_training_approval ( - approval_id, application_id, approver_id, approver_name, - approval_level, status, opinion, approval_time, tenant_id, - create_by, create_time, deleted -) -SELECT - FLOOR(RAND() * 1000000) + 100000, -- 随机ID - app.application_id, -- 关联到真实的申请ID - 1, -- 管理员ID - '系统管理员', -- 管理员名称 - 1, -- 第一级审批 - '1', -- 已通过状态 - '系统自动生成的测试审批记录', -- 审批意见 - NOW(), -- 当前时间 - app.tenant_id, -- 使用申请的租户ID - app.create_by, -- 使用申请的创建者 - NOW(), -- 当前时间 - '0' -- 未删除 -FROM rs_training_application app -WHERE NOT EXISTS (SELECT 1 FROM rs_training_approval) -LIMIT 1; \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training-update-approval-history-complete.sql b/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training-update-approval-history-complete.sql deleted file mode 100644 index 4e10367f..00000000 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training-update-approval-history-complete.sql +++ /dev/null @@ -1,58 +0,0 @@ --- ----------------------------- --- 培训审批历史记录完整性更新脚本 --- ----------------------------- - --- 1. 在培训申请表中添加提交次数字段 -ALTER TABLE rs_training_application ADD COLUMN IF NOT EXISTS submit_count int DEFAULT 1 COMMENT '提交次数'; - --- 2. 检查并移除审批表可能存在的唯一约束(允许一个申请有多条审批记录) -ALTER TABLE rs_training_approval DROP INDEX IF EXISTS uk_application_approver; -ALTER TABLE rs_training_approval DROP INDEX IF EXISTS uk_application_level; - --- 3. 确保审批表中有足够的索引来高效查询 -CREATE INDEX IF NOT EXISTS idx_training_approval_application ON rs_training_approval(application_id); -CREATE INDEX IF NOT EXISTS idx_training_approval_status ON rs_training_approval(status); -CREATE INDEX IF NOT EXISTS idx_training_approval_time ON rs_training_approval(approval_time); - --- 4. 添加审批记录表注释说明 -ALTER TABLE rs_training_approval COMMENT = '培训审批表(记录所有审批动作,包括通过和拒绝记录)'; - --- 5. 更新已有数据的提交次数为1(如果有) -UPDATE rs_training_application SET submit_count = 1 WHERE submit_count IS NULL; - --- 6. 检查并修复可能的数据不一致问题 --- 查找有申请状态为已拒绝但没有对应拒绝记录的数据 -SELECT - app.application_id, - app.course_id, - app.user_name, - app.status, - COUNT(appr.approval_id) as approval_count -FROM - rs_training_application app -LEFT JOIN - rs_training_approval appr ON app.application_id = appr.application_id -WHERE - app.status = '2' AND app.deleted = '0' -GROUP BY - app.application_id -HAVING - approval_count = 0; - --- 查找有申请状态为已通过但没有对应通过记录的数据 -SELECT - app.application_id, - app.course_id, - app.user_name, - app.status, - COUNT(appr.approval_id) as approval_count -FROM - rs_training_application app -LEFT JOIN - rs_training_approval appr ON app.application_id = appr.application_id -WHERE - app.status = '1' AND app.deleted = '0' -GROUP BY - app.application_id -HAVING - approval_count = 0; \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training-update-approval-history.sql b/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training-update-approval-history.sql deleted file mode 100644 index 8de2b2fc..00000000 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training-update-approval-history.sql +++ /dev/null @@ -1,19 +0,0 @@ --- ----------------------------- --- 培训审批历史记录更新 --- ----------------------------- - --- 确保审批表中没有唯一约束,允许一个申请有多条审批记录 -ALTER TABLE rs_training_approval DROP INDEX IF EXISTS uk_application_approver; - --- 添加审批意见列(如果不存在) -ALTER TABLE rs_training_approval ADD COLUMN IF NOT EXISTS opinion varchar(500) COMMENT '审批意见' AFTER status; - --- 添加comment列的别名以保持向后兼容(如果存在) -ALTER TABLE rs_training_approval CHANGE COLUMN comment opinion varchar(500) COMMENT '审批意见'; - --- 更新审批记录表注释 -ALTER TABLE rs_training_approval COMMENT = '培训审批表(记录所有审批动作,包括拒绝记录)'; - --- 添加相关索引以提高查询效率 -CREATE INDEX IF NOT EXISTS idx_training_approval_application ON rs_training_approval(application_id); -CREATE INDEX IF NOT EXISTS idx_training_approval_status ON rs_training_approval(status); \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training-update.sql b/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training-update.sql deleted file mode 100644 index 3dc82b1d..00000000 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training-update.sql +++ /dev/null @@ -1,17 +0,0 @@ --- 更新培训课程表,添加create_dept字段 -ALTER TABLE rs_training_course ADD COLUMN create_dept bigint(20) COMMENT '创建部门' AFTER tenant_id; - --- 更新培训申请表,添加create_dept字段 -ALTER TABLE rs_training_application ADD COLUMN create_dept bigint(20) COMMENT '创建部门' AFTER tenant_id; - --- 更新培训申请表,添加location、duration、startTime和endTime字段 -ALTER TABLE rs_training_application ADD COLUMN location varchar(200) COMMENT '培训地点' AFTER current_approver; -ALTER TABLE rs_training_application ADD COLUMN duration int(11) COMMENT '学时' AFTER location; -ALTER TABLE rs_training_application ADD COLUMN start_time datetime COMMENT '开始时间' AFTER duration; -ALTER TABLE rs_training_application ADD COLUMN end_time datetime COMMENT '结束时间' AFTER start_time; - --- 更新培训审批表,添加create_dept字段 -ALTER TABLE rs_training_approval ADD COLUMN create_dept bigint(20) COMMENT '创建部门' AFTER tenant_id; - --- 更新培训记录表,添加create_dept字段 -ALTER TABLE rs_training_record ADD COLUMN create_dept bigint(20) COMMENT '创建部门' AFTER tenant_id; \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training.sql b/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training.sql deleted file mode 100644 index 05843e86..00000000 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/training.sql +++ /dev/null @@ -1,122 +0,0 @@ --- ---------------------------- --- 培训管理模块数据库表 --- ---------------------------- - --- ---------------------------- --- 培训课程表 --- ---------------------------- -drop table if exists rs_training_course; -create table rs_training_course ( - course_id bigint(20) not null comment '课程ID', - title varchar(100) not null comment '课程名称', - category varchar(50) not null comment '课程类别', - description text comment '课程描述', - start_time datetime not null comment '开始时间', - end_time datetime not null comment '结束时间', - duration int(11) not null comment '学时数', - location varchar(200) comment '培训地点', - max_participants int(11) default 0 comment '最大参与人数', - current_participants int(11) default 0 comment '当前报名人数', - status char(1) default '0' comment '状态(0正常 1停用)', - tenant_id varchar(20) default '000000' comment '租户编号', - create_by bigint(20) comment '创建者', - create_time datetime comment '创建时间', - update_by bigint(20) comment '更新者', - update_time datetime comment '更新时间', - remark varchar(500) comment '备注', - deleted char(1) default '0' comment '删除标志', - primary key (course_id) -) engine=innodb comment = '培训课程表'; - --- ---------------------------- --- 培训申请表 --- ---------------------------- -drop table if exists rs_training_application; -create table rs_training_application ( - application_id bigint(20) not null comment '申请ID', - course_name varchar(100) not null comment '培训名称', - user_id bigint(20) not null comment '申请人ID', - user_name varchar(50) not null comment '申请人姓名', - department varchar(50) not null comment '所属部门', - position varchar(50) comment '职位', - reason text comment '申请理由', - status char(1) default '0' comment '状态(0待审核 1已通过 2已拒绝 3进行中 4已完成)', - apply_time datetime comment '申请时间', - current_approver varchar(50) comment '当前审批人', - location varchar(200) comment '培训地点', - duration int(11) comment '学时', - start_time datetime comment '开始时间', - end_time datetime comment '结束时间', - tenant_id varchar(20) default '000000' comment '租户编号', - create_dept bigint(20) comment '创建部门', - reject_time datetime comment '拒绝时间', - create_by bigint(20) comment '创建者', - create_time datetime comment '创建时间', - update_by bigint(20) comment '更新者', - update_time datetime comment '更新时间', - remark varchar(500) comment '备注', - deleted char(1) default '0' comment '删除标志', - primary key (application_id) -) engine=innodb comment = '培训申请表'; - --- ---------------------------- --- 培训审批表 --- ---------------------------- -drop table if exists rs_training_approval; -create table rs_training_approval ( - approval_id bigint(20) not null comment '审批ID', - application_id bigint(20) not null comment '申请ID', - approver_id bigint(20) not null comment '审批人ID', - approver_name varchar(50) not null comment '审批人姓名', - approval_level int(11) not null comment '审批级别(1部门主管 2分管领导 3校领导)', - status char(1) default '0' comment '状态(0待审核 1已通过 2已拒绝)', - opinion varchar(500) comment '审批意见', - approval_time datetime comment '审批时间', - tenant_id varchar(20) default '000000' comment '租户编号', - create_by bigint(20) comment '创建者', - create_time datetime comment '创建时间', - update_by bigint(20) comment '更新者', - update_time datetime comment '更新时间', - remark varchar(500) comment '备注', - deleted char(1) default '0' comment '删除标志', - primary key (approval_id) -) engine=innodb comment = '培训审批表'; - --- ---------------------------- --- 培训记录表 --- ---------------------------- -drop table if exists rs_training_record; -create table rs_training_record ( - record_id bigint(20) not null comment '记录ID', - application_id bigint(20) not null comment '申请ID', - course_id bigint(20) not null comment '课程ID', - user_id bigint(20) not null comment '用户ID', - user_name varchar(50) not null comment '用户姓名', - completion_status char(1) default '0' comment '完成状态(0进行中 1已完成 2未完成)', - completion_time datetime comment '完成时间', - score decimal(5,2) comment '成绩', - certificate_path varchar(255) comment '证书路径', - certificate_number varchar(50) comment '证书编号', - attachment_ids varchar(255) comment '证书附件ID(多个用逗号分隔)', - feedback text comment '培训反馈', - location varchar(255) comment '培训地点', - duration int(11) comment '学时', - apply_time datetime comment '申请时间', - status varchar(10) comment '申请状态', - course_name varchar(255) comment '课程名称', - department varchar(255) comment '所属部门', - position varchar(255) comment '职位', - reason varchar(255) comment '申请理由', - current_approver varchar(255) comment '当前审批人', - start_time datetime comment '开始时间', - end_time datetime comment '结束时间', - tenant_id varchar(20) default '000000' comment '租户编号', - create_dept bigint(20) comment '创建部门', - create_by bigint(20) comment '创建者', - create_time datetime comment '创建时间', - update_by bigint(20) comment '更新者', - update_time datetime comment '更新时间', - remark varchar(500) comment '备注', - deleted char(1) default '0' comment '删除标志', - primary key (record_id) -) engine=innodb comment = '培训记录表'; \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/update-custom-course-name.sql b/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/update-custom-course-name.sql deleted file mode 100644 index af90c53a..00000000 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/update-custom-course-name.sql +++ /dev/null @@ -1,5 +0,0 @@ --- 添加自定义课程名称字段 --- 创建于 2025-07-24 - --- 在rs_training_application表中添加course_name列,用于存储自定义培训名称 -ALTER TABLE `rs_training_application` ADD COLUMN `course_name` varchar(100) DEFAULT NULL COMMENT '自定义课程名称' AFTER `course_id`; \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/update-rs-application-table.sql b/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/update-rs-application-table.sql deleted file mode 100644 index 4407c026..00000000 --- a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/update-rs-application-table.sql +++ /dev/null @@ -1,7 +0,0 @@ --- 为rs_application表添加附件相关字段 -ALTER TABLE `rs_application` -ADD COLUMN `attachment_path` varchar(255) DEFAULT NULL COMMENT '附件路径' AFTER `self_description`, -ADD COLUMN `attachment_name` varchar(255) DEFAULT NULL COMMENT '附件名称' AFTER `attachment_path`; - --- 更新表注释 -ALTER TABLE `rs_application` COMMENT '应聘申请记录表'; \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-rs/src/main/resources/sql/update_party_retention_file_oss_id.sql b/backend/ruoyi-modules/ruoyi-rs/src/main/resources/sql/update_party_retention_file_oss_id.sql new file mode 100644 index 00000000..f6bb1ab1 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-rs/src/main/resources/sql/update_party_retention_file_oss_id.sql @@ -0,0 +1,15 @@ +-- ============================================= +-- 修改 rs_overseas_application 表结构 +-- 将 party_retention_file_oss_id 字段从 bigint 改为 varchar(500) +-- 以支持存储多个附件OSS ID(逗号分隔) +-- 创建时间: 2025-11-27 +-- ============================================= + +-- 修改字段类型(MySQL/MariaDB) +ALTER TABLE rs_overseas_application +MODIFY COLUMN party_retention_file_oss_id VARCHAR(500) COMMENT '党籍保留附件OSS ID(多个ID用逗号分隔)'; + +-- 注意: +-- 1. 此脚本会保留现有数据,bigint值会自动转换为字符串 +-- 2. 建议在执行前备份数据库 +-- 3. 如果字段当前有NOT NULL约束,会被保留 diff --git a/docs/开发文档/API/出国申请管理API.md b/docs/开发文档/API/出国申请管理API.md index 7cc420f4..e8044246 100644 --- a/docs/开发文档/API/出国申请管理API.md +++ b/docs/开发文档/API/出国申请管理API.md @@ -316,7 +316,7 @@ interface OverseasApprovalVO { approvalId: number | string; // 审批ID applicationId: number; // 关联申请ID approverId: number; // 审批人ID - approverName: string; // 审批人姓名 + approverName: string; // 审批人姓名(优先返回昵称,缺省时回退账号) approvalLevel: string; // 审批级别编码 approvalStatus: string; // 审批状态编码 approvalOpinion: string; // 审批意见 @@ -329,6 +329,8 @@ interface OverseasApprovalVO { } ``` +> 🆕 **2025-11-28 更新**:`approverName` 字段统一返回审批人的昵称,若用户未设置昵称则自动回退到账号,确保审批记录界面展示真实姓名。 + --- ## 🔧 核心接口详细说明 @@ -1048,6 +1050,7 @@ PDF模板包含以下空白填写区域(完全手工填写): - 申请表单:党员且出国天数>30天时自动显示 - 申请详情:符合条件时在弹窗底部显示绿色按钮 - **前端实现**:必须使用 `request` 工具而不是 `fetch` API,以确保包含所有必要的认证请求头 +- **附件格式**:自 2025-11-27 起,党籍保留附件支持任意文件类型上传,仅保留 10MB 单文件大小限制 #### 错误处理 @@ -1242,4 +1245,20 @@ import { getCurrentApproversForTask } from '@/api/workflow/common'; **更新时间**:2025-10-28 **维护人员**:开发团队 -**变更记录**:新增工作流动态审批人方案说明 \ No newline at end of file +**变更记录**:新增工作流动态审批人方案说明 + +--- + +### 2025-11-28 更新:出国记录回执下载 + +- **前端**:`web/src/views/rs/overseas/index.vue` 中的 “下载因私出国(境)情况回执” 按钮改为直接基于 Word 模板生成文件,摆脱后端 PDF 下载接口依赖。 +- **模板**:`web/public/chuguoqingkuanghuizhi.docx`,占位符 `{{applicantName}}`、`{{startYear}}`、`{{startMonth}}`、`{{startDay}}`、`{{endYear}}`、`{{endMonth}}`、`{{endDay}}`、`{{destinationText}}`、`{{exitReasonText}}`、`{{durationDays}}`、`{{expenseType}}`。 +- **数据映射**:优先使用记录中的实际出行信息(`actualStartDate`、`actualEndDate`、`destinationCountry`、`exitReason` 等),缺省时回落到申请单数据,行程天数自动根据起止日期计算,默认费用说明为“费用自理”。 +- **交互提示**:增加起止日期缺失时的前端校验,模板生成失败会提示用户检查模板或稍后再试。 + +### 2025-11-28 更新:审批监控页面审批表导出 + +- **前端**:`web/src/views/rs/overseas/monitor/index.vue` 的 “下载审批表” 按钮从 PDF 调整为 Word 导出,保持统一的 docxtemplater + image module 生成逻辑。 +- **模板**:`web/public/chuguojingshenpibiao.docx`,覆盖个人信息、行程信息、申请人签名及五级审批节点(部门负责人、人事处、纪委、学院分管领导、学院党委书记)的签名、意见、日期占位符,支持 `{{%signature}}` 形式的图片变量。 +- **数据来源**:优先取 `currentDetail` 详情接口返回值;审批节点数据由 `approvalHistory` 根据 `approvalLevel` 映射,若无签名/日期则留空。详情接口新增 `signature` 字段(申请人手写签名 base64),供模板直接使用。 +- **交互提示**:仅在审批完成(状态为 `1`)时显示导出按钮,生成失败时给出友好提示并引导检查模板。 \ No newline at end of file diff --git a/docs/开发文档/API/出国行程变更管理API.md b/docs/开发文档/API/出国行程变更管理API.md index 43d7cb6b..0f9599fb 100644 --- a/docs/开发文档/API/出国行程变更管理API.md +++ b/docs/开发文档/API/出国行程变更管理API.md @@ -144,6 +144,10 @@ } ``` +#### 字段补充说明(2025-11-28) + +- `approverName`:后台统一返回审批人的 **昵称**,若用户未设置昵称则自动回退到账号;前端无需再做用户名到昵称的手动映射。 + --- ### 3. 查询待审批列表 diff --git a/web/package.json b/web/package.json index 5e29e2d3..b636f0ca 100644 --- a/web/package.json +++ b/web/package.json @@ -34,9 +34,12 @@ "await-to-js": "3.0.0", "axios": "1.8.4", "crypto-js": "4.2.0", + "docx": "^9.5.1", + "docxtemplater": "^3.67.5", + "docxtemplater-image-module-free": "^1.1.1", "echarts": "^5.6.0", "element-plus": "2.9.8", - "file-saver": "2.0.5", + "file-saver": "^2.0.5", "highlight.js": "11.9.0", "html2canvas": "^1.4.1", "image-conversion": "2.1.1", @@ -46,6 +49,7 @@ "jspdf-font": "^1.0.7", "nprogress": "0.2.0", "pinia": "3.0.2", + "pizzip": "^3.2.0", "qrcode": "^1.5.4", "screenfull": "6.0.2", "vue": "3.5.13", diff --git a/web/public/baoliudangji.docx b/web/public/baoliudangji.docx new file mode 100644 index 0000000000000000000000000000000000000000..d54fc870e8e121474e54248a615cdbe1c4f3e257 GIT binary patch literal 11686 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 J%lRAh{{fD%Qw9J4 literal 0 HcmV?d00001 diff --git a/web/public/chuguojingshenpibiao.docx b/web/public/chuguojingshenpibiao.docx new file mode 100644 index 0000000000000000000000000000000000000000..04e9e13014ca0e9428c3a1d5cb68b4aae427ea93 GIT binary patch literal 18968 zcma&Ob986V(l;90=ESyb+cv(jZQHhOn-kkkCU!EhlS%TzUp>zuRhyVl)*?B1)o zdhhD`^y=!as@{q+pkOdS|6Jkn2ZH~U|9e6IeK55*QFO9*aAr{a%ZB!s0QE06@nRWw z9bh0Jdk`QXqa7(ahxpz7Z5_9cFwNRY>H_4Y80XWI)&xkw+H&_=Kb}3UO+Jh!_$U-G zpe-A-ePxf-*JokKqGPt_j+C*-A5Fv@*5ZXf5kdl3$Bc5Vt7n1oQJK|!9*!>ho%gOk zLjETZ42A9x+JAxY{|f~2zXM@n?*#YchGpVQM@gabNzC6-V@fp>qtU$*T(lFrpJb|6|NWKxMyLdHQy zWD={@qUtbN{DXK+GEr(M(Mv2KA>^75i$gS0(sAfrwF29?u;vm|3VWm%HaDY;ZC-Im zSpuCYDd=VBGF?mc_oME-+d%L__pxLeWErO%Kp(5~BDhQT_?o=t#bA-}M1WCJ*fPMt z{JrTcEWd0lQUg^^+%!6QTph!0240MBcCvanR$cLz9dU<_DPGiBQ^NDai~cJJz(5X!rY9NIU=Q+9`ao957tXT(9lD?nf)2Gm3r7$tLTr6htO zPPM&vGH+o1ClF81tteD~fk6BV1nPeP;p*&SZ~G4vrHMi^LyTx+H$l5ZM?2(lTeaA> zV_7zYKhT!-N=wewEs~Q;mWce^Sx(Rp^&ekf1^jMacBaznMGz4d*X650#EHTLsSC5^ zuV7vuJD{1NYO2*#UbH7bew2nz-M>@!b9kkUx-NpmE4BrApN@hHtN%V$+?pSoq^M;V zx$?1elWe!8AYC)YDRGK01?>+tePA|C z`%e>0?~;Be!UmLBMvB6w_t5Ad<~chONB}_v5cqpVlH6pPcP30M@9u6t@a2IqqfIO= z`}=fkzAe1m2ayp)n+}$L(ae43^MAf65NMQp-r?N+0u3M)hz+jnr;U1aVV(#ki`VL- zQOWxm4x2er6Og{KL~6FN#=H$}n$sjaEk+u(HZicX(`Ya-UTI)9VF})d8cmqosf%`z zq|&Jw5uo9JLDOXGF_VhPOlgvbmgSgq2W68W)rV%OUp%78YH`p6;n~J^z!i6KQ`<(Q zGeIlMf_=*yo-v%r#BWAyY4{w^9)L4;6?8~m_J#@$o_=$4{;loy`(}f-?pIx19o2(H za3jekc@&3nUgLBKHcA1&Ju5%mljsmuy#d0$Rr{)L(T78lx>-kQOCaZ`ggxe)K3!ej*kimd*xttJxN8s=SX7Mvth zWmhvRxOqS(n#%Vc77QBBIOcc9qAT0eCC88CQmD6jnLOST-Vp7&KF-6&4h*AlHD8Sz zKEKYnkm-W=vafK&m23{?*U2F*;9(dwE~?3)sq}#ID*1qC1Ge%}sx_!qqEZi$Rr@14 zuTSwo)H15?i7_#q;G z7hyPQo5YYmr4;+(K-cm^$N}BS&R~1jDfqx0Jc$y2NjLwH{Qi=Zv4ArQ1YxihQ^JGD zC=tMA6mekE3K-F7g|sL&gNhUzz{m;>p<#pvP*Q@=p*#)t%t@$5zqzy}wMd)x^al)} z_Ow9uys*SX3%U$=i|=5;>)zh#xnpz9>RpSvy>o$=e6QsLgqrj#5|bz}cZ7LUvEfd- zz4n5!JnpASu;9ID;Q|Aahz`N0F=BQ@$}E`s_OPeJZ|}IYQ$@y6B162Tu;-|p_y7tn zd%V!CcF5f2Wd(_&Ws90xv@4oAimE!(hG8~;e$$dZ*buWz64{B$->Q@-_BJy|aiYKC zE8$yCLBH4Gq!&C?ImVAFaL3Bb`>u45vrvXX*?LghYpkkp^#+yS_fp|8?`qtt_SNrI z`WOtjfB4llbDEAm385hi37qgIU5pODS}~$3KnH(qBW2H^*|D8pZ#T#A0ciEStgiii zqUTIsFZdE-nsF?e0*lleoo}NhCyD59GH?7L70SR`Khad!Z*&ApA>?4Yo(vWHFEPYT zs;2z512RbZ2}$f3eBWSH*q3w zXD)&^-LcEGb9~HwU96RSv>R~#diH``^FlaTG=;Azi{feGg6jQFyR!Vp!0-5>Kkg>` zGJFg7hGlw(w4Mo_dj+nh?Ne9rk1CD@Hp(9uYYQy62r%+P(t(~v^4HA%S+d|(<>I*- z>sRj#q!k)}7|9S6eD~u?yq2ct$%O1c)|WZm^5{ynke`AfHT7{FcP{W43~8}CB>O_ zQjqo1^48#7lq#^#1__Iq<(-r%EJ36REJ2(wmCUN_fCv7>P=)pbh)bdy#3^9w#VMj| z|CTl4<+0QQI92MiNVKI^g{UoBxf@sPSO!=mf5_XQ|5Ajr2&#?1{R2#FCYin=h@ry?7cIXUNZVFWLvyLv-zoW9v97k&jdEeNh$qFu`UzjUr4W^>*lmQa|2-P0msbe_okZqyCL#%L4Go;I3JF#8}D&?r`} z(9@`kbvNq770r=0uGP~hI(raH=7eESo7n6@68UQwSH@T+RQz;x7wA>GzqPBkoAHnVXJ)+dpNZSTv8^w_}f|1Mrx0gnLyK($Ktw&M*z00Id zsP?qb;(wLFZfn8X`tQB$jOK7sD=hXf(Xs*lAKh{L{*`Djf+KC}zZCW_@uZH(cQjFA z{tEtA{}xs{e|7o~0GkK3k$V;I&y^1s7H*$0keczTKkH^H^H(9yD#)ZAAbPot`ngs0 z{qLgr-SqX!-4qAi^RRg-3w)QoTPgJIV|{t3`@T-{v|J%64ryXip^l|%fo4rxo?~}| z-eWsMPU(U0*7fxu%0H{Co83=8F2N&J1TZ3}>sA`Ss=(iUzGoFK9zM4S^wrJ3s%e6I z6lw5iezG9Ln7nD^RXX3hHdk*!y|tgjT8U|XUFq_<|MaQ+anHzE+UFHd!4uCiqO8O8 z)KBL=d$X+LT#)rnyOrr%r(K&3Te}9l4yV-+k^yejPN%z#%KR`N;x=wWLEcM;-z?U+ zTO@o_e${>=DBxLOE7yu>z@WqCw=)YNx#elv1pLKys^l`|Vu!{I?&t9*tZ&!OiO0tX zg6AN5(5I`voXz#hd7RV3Ozit9Q@^-szZ>W$ldZ|L4B!akhjr9%harheYp+wdA0T%x zPo|{Qoo(53drU;PoY<+6qchxdE67F-WVk&T6?#btg8-tHsXtrG9QZLQ;P0KNW?Mbb zAQzkDf)&-5w}tc=1++x{VQ%mpe=vU?lPBZ6xRa^p8o!l)gZ}3tKeKyO9_{abcZ}!T{-WF1c*TXFU5|H92{Fq^#%vXNh5Iq`LmFCAVI~zJ8NqW~JJZO=ovb zBY#)_^HE9vMWep0+sIXbcD4HMYGtXT_kGFT|21ELf5&EQ8=>CTz$j|v*^JBE`>A2e z)~>F;y*b>G%VxKR;}B+8;nI@p_wCK&n-`a?!E^Rn^OFAZMN86ZnEj&dq}hSO%tUOoTK6(WvHh5VD|yT?;r<9GK}zPRbj4L`0rw{Z0Oa!24FtJPOS zKFxe{ZGRNZhcF0clYKsW<$Uz$XK(o0W^Q^tH?I%9y1v;Dc8%&0)@3g@I<|#{rbZmQ zr;PgE+2yJk$lu8?IFO#69Ir09(Zkwj_SaZWw?;-himr+=cIR%&jJT_?A#Q`BXV|aW zyPud5yZ5(z;^X7(e{gJZG_OQPVrpBjuu0AZ*Xh%lbKLOPuP!;X2YB+gykER$oeY7<}9IPCZ<$8V^9em&^u!M(B1 zZ|ioXx?HjI#`1-<{HfuTd*mh{=X(2c7+`+7FyqMTt+aZ%Jreyw5&g#q#8JH#nI)E{ zW7?)B?fv@WVhn?1rV+byOw!ZCz zHt6uW-$3EPpt0E~F@Fq0aA2t&nIaK?S3u$8yQf$CyTM?_?IYOy(=|t(L5owK(;KVI z2tq4|Q+Cf-n_+fJ-6q4Zi{XSTmM1`bc2!luLE;M^1gcEP@_@VD~q_(OyS z7Z3N2Zv(8Wk2kA7emVYr>%BTcRMD72etBUtV|VG>a}V1?MyPX_-!1r44cnntKAIzz zczu653}PgN?Pi5$cL@c z^jK%!jE6Zm@Fg@m7nM!((tClow6H=5%`IfN9rKlmK z6O0)H0ABhmTXK}LGzTHfp_Z2X615ROCLx8j5&zi7#*19(ShNW6T-GzgZ*9B=0}9sW zKn02(-P~E4LTx2MT9w3Ej;cc;wJ^&pZMUzTHIwFh+DJC2L%&fbNGl4k9C3|{MJ})W zHk7^7Lok(XEXpxvrlj{=YH%e4+0r@~kPZsj%Y+l8VrRjY1Sw?XNzlbn$$%>nt<8X> zNvsn!VuwLOm3tyFti1~Xy^@9sGl~e?beVIhDn5T-MmCsle8pqlmpjGE){K}tPr@%C z*0Yk2R*7XzK_Rs1B8Y5cJDt8MwYT1Dwd665#33xhZ^l;?y^PaLwcLZ%BUA^VA+d8Et&=zG{MqFWC5-!GB%&tStnVlKiOlFO89XjC#NT)Zk^Bar6Z&fl3P{~jU z;t$O)0wv%_~Wu_SsQwUV9L2NZ9hkzf8E@uqG+h;o9fk9Bdd6EiLzaLZYo-T^b*ewKAc zL2A-IfQ6t8OI`OP<+;MsE2p&7G8HCH$!cSa4mU^NLYdiE9Z|>9*9keT$q=m-PRhtg z*K8<&pX#74wbvv!-H+ve5rtT-jFFAdjJV2u7;R+Qk%6S_e&o6|2|Pu8Y$u9(mMv$O zykOf_c+^TE-fU|G8x|aOrQV=P3t#_TJYsv#yG6sI1P+uiIaB2 zIV`J`cs4drM<+Z=W-tE)4jelU6sFmNXv3bHxYaa%1eYYEeas1@+a@Q&>7ds}iG?LL zfWVqq;S2%S0ydGTmdek#>sG;l<}$o0YT}fyMYfN}T=&xl zI3+4Fg@YFX&OImD!q_Yew6r45a!X1|99eeQCMn?)_54v1Qb*2b%d@I22EUq)SSt_FrlGkx$kH(LaM;L#Dinu`&7v<#VB zALC3ZF?plSJb;v^3eZE8V~w8C3kb){pTt2^os%ucF7=?9Mqr-wx|4OTaPk`I0z;_C z5i#Zg2M6G8DKNpDjM_l5@9A7Pk93-9*|>}hlxcoJj5!gJn-R^s0R2JUqAHSpgwuo3 zMi@sr!&<{wbq@sXjvEb?7TTP0(Izdp8z9wA=mHz_6j=)t)24v;VQOX?zd*9WhJ?bT z%zMi?P?_R|%}geYBwa*S5y6-s%+0Vty4}Fi$x0*c)D(;`dW9%B^Hq2Og}k;j6&7{R zRIBr#=TeS_6nh`!g^ee3ihNEADnfaZHe8p5AhI^d;3!gAYOSFFi-@;1KjXgj0+Vl( zpQZwY*@HkZ$HEpwTx2px%C{}S)dx!8*OpN$S0YuyFgd)`${yz? z@j=NmVb8dNUr8tl2j<(TB~Vb<6|xa(;FJ|Ym-*8W(p$}O*xbSko05ufDLCwrC&H1^ z#fF5JP=vCucV4V{^JH-@vZ2(g&=8LM-pA8_^dk!0dncpyNH!C1QVl?@w;OI9Psd{q<532*ur z6yhy{e9;3;>YoiZjO6(?sFC=hroloyZ%Y!)BBk0L=jY=hJ#_+A6f!6M{%)r7;)|v#+XqKp#rG{ex5 zqS#9f+Nk)FyI5(q0ZG!y8ES>q(G^1OQM{DoCjz(PqTJLsAAkyM)Qlv02UC1@% zQN@}q51M`n_}qPb)tU3#k-(=W>3F6cZC7-SNN8t^ToN6#y_z|%$dN^*+FfB%6vcy3 zl@$Expm9#J6g&L>k$qt$1 zrP*{Sq)p(C;qe6tGTH)DV9*(|;S<)I9`ZLYL`z6Kp(uaU2{MAAagq)NFA{E_Ycc@I z`A25C@SgMpj7iygImJi zy~C_7G^)_xIUk1{@(<$j|4jJn0|{a+fKQ8+YCSNcRJVEnGOsqxjR^)B=>ap^3LloI znJ4Yq{11{V{! z){IxF++v=pR9dh#$qS2C_$it~vsjwv0FO12$jP%MMmWKlK-L8DlbD<3yuc!w^P32R z7$#KRCZd;!PlzQ*3te=H#Gi;q40l3nq>Hn4AjBp@lQ#$i#!rT~I;ejF?lZ6++d#kZ zQ&Te)fbD3@SIA2I943n`%P1rIBg9$T%?~CD4G{4Sg~v(iCJzha6TPofTEf4ufx>@h z{E1{pMocnQEr=XDS47_kGnJi}=Mx-+z+H_%Gt~+yX+E^TGgZ$7VeVd21`h5CRXq$Y z$3DywWN#67utYOga+qv7*W~)DGaXo1x(ACsOdceeY8*&nS`M5~rKOHvv{j8WHLD<0 z8H(G?k(17vC zC$YA92bFT%XX6};lC!K;cB|k_T%~TI3q2~04G2e_0|eQTe9(#C;e`3hmhd>`$L<7D zC$Jms>@gKtEB<9NNw`~|OIWN>gpf+AGEy*nq;o6S2qdU1cmLZ8%_tfxAmf|xDyGw1 zQV9r^Q<|b@zZw3DN__>a&cvJm>WNdwY!^BZByllAEf7)dD_-Um(?nj|Q<92s>k_%KsG=tY63f@v;Ji)EDb6s)fy&j^PAWw~E;(#DH@1A9(dGc69tzikYKMgj+c)3(X`d)qoMI;)(o z7+l%xL#z0X7N?$P2mQL0vAN}RjtBI*ZrZuI_K)tZhYIv-*IvSX?&aNndDp>9^E=+1 z-wyb<4!%d9w>|`xy1ifLZThEJ3Yf+_`k(K&+@G~^qa%?cE;{zeaRgyACmx|^*Ulf$ zR!a82T3pu=zV}a7(}x7y^Se=NdFF2oO7Xw2&0L#3J|2tv9R5VM%M0Y?S3a;j{eg2o zj=MQOH9z??%`4D+YThyP>hTa@O|xX&_a4;Eb(;V5R{mt%=!bXHFCgIK?Rs}c88TFx z;$FRB>@P4V=%8WqVdvUCq&G>ue~O#2`-%F0k7mLCZ>Y+}0$>aHub9?$E6zO(1Q1X+ z77!5ne=`1yo#~&*)|J*wJSqoTzsAKkV;0YE(jybCM6v960Bm;yG4Sxh1Sg9b-{H8M z+eB@UNvX7&<%&q6*JYtqG?+5OWBo>2RVm81aQ@ZAuzXHkL4|Ncju~(N%O8Hfn>Uhl z`rSDThnP-6TKmw{2PLdMpJC%9irU*%!YPKwJw2ay66y14vW{V( z33DSDR-u(C$+fZYOhHDZv(3=6`&$`za8e$}6XMeg&}TDe;%rekxY)#=SVxqgt?mdP z`_H=O@j(Z=Fl>xPJ@u>yWQIPfO8FzQE|iFI$hM&cJZ2Q2J_cdHV#0&r>R9In9gV)0 zptk73s&$F2M{ZB`2{d2_#Z`I96hlsn+O_)Dzf<1X0w&dT>o=?)aZ35&1&fh8ENvWJ z?AWby-2z($PZo;1+qG)1xjkgQL(#L{tFLk^F80{Q&+2RUvY*`APY&3|_bzE(VU;MX z*6==YOmAb+ed_fum%S16gO#Q4i_u-s)Np&f0_llN#9w-D&FY)Fm+1Cj(HaU%(z1-F zozBU$!>*b7U=7fth^6iYXURQc#09qyicH6)8WfULtGMPd#8=%}SW#$1thxkaI5Wr9 zJzRqdkTA3j_`dH}9?A#KgU!nk{kVKzZyyKd#1lzc!|#XFyxu-B{RO^QUoX$y83=rj z$6#^4KNRvmpD)w>2vz3?b1)4A-VQgCF?W5wP7&jIZ}H>8*nI?lfT$7C2n5N^qDyPW#tn*K(;C@7MfR7@5cQVv}2jwLKyMRe4MANKQO&-?=LOyM4ZnJuzpqYZlhN)={X| z_xjv8cJ1B@xN$hB&Fn1|??q_;mB$k^cC>vntH(Nn$b>e0f$~(y>&@4cI|w(_uL zKM_q22d`}lF{7_@)5G#T>McuoHHAI$A$-#dPRn~o4@XVpJ2`wzWs@{mOl>lMy@wJf zohgczg@P=b5zuVZp`Q9KSiTW^x1K{sGvkFeLot7`6r9NR7LmQrA|BC`IYA&t!PAf(9*6v|6~AK zSFr*rbO1_Ddml^Q*!AgKo3 zE(*P;jNx&9j}$-VL27HJO%err(rUqG z1JiO|eka9PqT+`NtVW|$TF8lq3T*!E1?Hm6;Gm&akG|4R==3wQN5phfVFBzKclJy( zLSEUl0#>D_^7kTXF2t0!!^&mUv+D>;+!z`N@LHIIKu0m266)Zw5gY?%)R@SMS*wP# zfA!=9z<;C$J)TxmpvGl=fPj`}*5jvn9OsA#8Vc}i84Ui{?PRoQ5Fs}d@}nOfD2 zoDd5Djnxt7V>zqk>F}V8WSvn_I$u`Ue4W~Aw%*|8%c~>l37W3INZD1fXv1A3cporg z)={)pTXZtn*=jNi_b}U(vKbYp)rmKYxx}(=r^SZIRP)uQ7M3iCi>06x152O^%EA8= zrT>;ld3*9QxO)`0R{PwC!cfAK$}oBYZz*NuK~TGlPF!#41OWu|*KJo>ZtJIJl&DFa z84}0lKVuahv5u zF2&NiHS2j1KuUU$0mcbLDYj}V9(5CCa>{Dug2!`r{OY#(y13p7n2_?51yAz2H#pnu zNB{#<1_F!196&6CO2S?Du5W#AzwZBjolTf0w0+L{roTXJQh=7sH!fL9-b1BuKG|7~;ol!k_SD z{4Kn}0UQ63)jzFGWBAKa8=3kRuBjw#MB5z4NFtaj?Mg!XH%H`%)yUV5GJN^-c0C_J zz&k4SI;&6%v|sk>bXNrKV=gU!>&kACe-8;W2Ff0 zkuX4K zcCwr-?ZI=ZVZunR1U>N1mKc3&hs>sS}q3IZV#bSLR`H~c04&1;uzw4*TXHykS`Nem0+QNc0ydQ%Ws_gX^^7%KrJ$J|W z8^DGd;WBZ5^CSKE5OhFoN1Vy#sWoT7xkP+|Om}Z@fBJqi_*~sSIc(j&VtAa|daupj z>F+Mu75M(zR{PxO9bIX!^;Y0*m!wfe8v7Yd1+y2wwCN<)AeF(Yfk6KaO;0yNo`f|+ z3|&CCCitAf-&z9Zv!9~M+z-Myb@{-W!x1nNvo%4OE&D@guL5Mz(XxzE!}KSGk6!YE z&eJcOAm*=%JF$+`8JWQzjOTW6Ezkkn=e4K~MFd}KCFPN%+pUHdcK@$22LTDeP6T!;txy+#uhV&E=?0%v-8B;FLBDaupVr)Wd#(6QHn~zJ!ZzyJ9ywgK}K^z52CXwyr;I^K%^_RNUfLC;d&B^_n=AmFeXjs3Xpmln8M;~f zPGz^NHNna%el#vD;4(=OFq8fDB)Wsv1jHGJh_>Hl7El32t?^ zu7{jgcq?a0wa7Vy;f9U8HU1~_=5W7~HKL0=AYAw|WYoYqQkxx-vrJ&Hy4J$kxi6kP zl~#Jf*pR?0sqH*gn+Dgc&>P2P zHphI_UVk?um5dJ>f#F^aY6n`z%@|?LFomxL#Gx;2DEk;|!Yctxo*Mm*Z-^w3i3*@3 z6DPwVCq{?1d5pcX>#Bsya;xniMC@6J0-AMrkCWkq7tzlvpT| zlN-bN0trh#cQuh7zi&?9o)-U#a_^wv*?BuH6tWzCp7yUkj5}r z<#lM`T0GplIUmM-J*yo)ry?z)mnZ2TtjJZQfnebQ8M@@1tj}91k%Elxyf`jI5obOo z2G0gnSR8+&cpNv+m1(HQxDy1r_W$zCK2FyI!EU@7XhJg|XzL;X>9AaG!h$GMf>TiD zB99FhgJAcRgVW{d8*FL3lQ|sA?Y+5f_v^tB&maz7^%I7dO?1g4+HfIPUkUbj9#yeg zHHE4J(!hN$R4 z)$5?s$SP&oF|x+=E!tM=*a-Teh-eZq1wdlKiyK)p6~Yl3qriH$#*A!E8;y&J-Oh`b zJOSWA38zXHowRqUEJf_<~1gYNCQEp%WF5G z2{Ga>%3wu_(k84T0O4#*lLahxoQrX}JvJ?HlQ`NZ(CA)TPSUi|p8~d84!<+lG;(l* zL*txr8=fGC7FnePUk%64iZl=8qS zqSg-SoQD*e>_bJ}qD~=i=7=uru^S5~8`E-8h&yRBKa6IdY_}psq!RM6nEsMh(-hzu zDWjaf7uh;9A>?5fT$LC8?$D=$LYiO5#_}G}YJfC4H=TWic$VHOm{~7ow8RD8|1A;J zG%ru50yf^O5%UL0^48&q_jUZyknlEcvJ9`$9lVh=sLFygTJzla5IxRaUGWz*L@u1l zp!Cl9EF)P>1fzi^Q3I<*C?Sgkmm@de_!x^ij$wD;R-wa)4w2dLOrw9n)_b0u8?Qza?Iwf*YHZiu z$oUX`3kt6guAC{TI06rQRU26(^DdCtVLduYNtm2ujZ6+2J6PILq)3fjrmO-F7uJPD zhHVr#5_pga)Lsxh{NuDRN5uJ%fXI|PW91KHR4UmIEZ=4>h=XB9$oJLPAgy144C{n= z=umfT>)4}Uj)*^9x7T~t*EfT1nIY9g z&Y%X#gMWP%KZz7d+p|<@b(KI=)fLdfM0QqEeK*H}ZR1^)5YY>%&W*c@sa9t;De}^! z=&DD1$VjhR@;wTFj%7tncw;Fk6^zC)HZd%gyM}JsD_AADplc@vci>)<84)T zE+I{i9brju?h=k9*{Wj~I#9WA;^!cy!PT?BZ$e>E2FjwwRe|4$6YA~EpoA+%`CNgC zn_^SLRxax)f#1n{T!V2UA+nc-$Q=Tjej>We76!+mhW$wUbd&x0+>i#DyB2%-;@<8L zrpg~m_=`X+qQDBYqp~lY%NSIlnRvFT1Aqms0;<@6;796#V!MMl)Xs-Jx1wb2=(TMSE@$C3(KNh&j51@l->mNeF9c z-${%lx$hA~j+o2ycVG3d{;8n9eyivo3(-a$&Z)5A;5a1AA(jN!= zh?ylBw4gxb>Ynegc|tnCN~M~AKg-PO`8TvyyO)b1%pVcCJ#oJ_cGtgd|~ooFSe?|cVXT~Lf@HKQNg5bgJKe* zowzh&;J5ApF|mhnOo98fA?F4`63u2NVyaAHLWkbX_CEd|*phOQ(3ZDUF3&je+1LO8 zxTuOC-ALZ$J{&}&A&zSCk&&^CqDtA%Z-^Dc&9C;Co4PAITMoP3K%jSJJtk^=ppnkp zWnc8ej3Z;@6SX|(&dNu~@9+ARIs6V7u9rdM%wXAWA#WRf1|Fg2ELC1yFfa}&N=LA~)=w@~_`fUrTF>qLp{|_3v{AKfi0oFdzE^RQ+L``rfWW zuP+%@olh@E-Q6GS>n`K@!-XDoc8d=T?F}w9^aa;)Mz_6VpTciBy3UK|Uex`=ZhqVO zR(?NXx*S(q$HH;_J%t-K5@V^Jc&eYR;vUKvyfsGuNCs9dwG|hl9bx)JS z^iZn`f@IKz_^MSidBfm>>|-@gtuQ>waAYg^ujbqJ`M=a2cAUfNLD)s$2iRz8y`dXa zs})@3m*iq#7I3TSIkqpH@tj-SX7Nd5l~TW}og0zmG<~(MvJ(nbcHYgcKHS6s2dW+i zX(IDeO)1M_?SR{oYl}V_LVP>7d44#EgL$VcBp^AzKZ~0@6;eS1=?aY=+LJBaiI#lEnLXg@5&04ywv; zeHbr@`j(2Puy8e8EkL-II?6Zi24{1@#4&W0>)+J= z2IdnD*<$E^iA5TBsxsd{&jZRWkH|;9nY{nn;ZXdZ<0EygUkaIN!$rgnLQvG~; z3K$Segn-o3_(Y4xlfQwFJZj*do?xq42Qi2XmsBs$FJrISAZZHSgDx!drMx8*def6j z@oIH+mEk8(mjZBdE2c4xpopuWiVpWK#k{un;*oxOP;ghr>?biAeH%XdGjm-R;r7S+htnKsrw=%nuNr@yCXZ zBfc-&De>X4sP>D{PRBeZPPW0b1YaLI!_|r!mGNqu6)>`#?X)zq+=pZ`hRi{<9|%Se zizRtX&Qp3pFr(~SON*P18lB-~N(%vJ)6bNHLYd(I(m8H!ZI!w|o&in?fzUS`wY=7~ zxE8D}t42C37wBH==sp(uwo45wf{yxYlgLX<>=8o@7mRS6u|N(fxsz|oMD5w*hS`m# zf{<@nVj;K*A|x8|#`^{jxT40(@oFV{q<-%7MkXGy!+X$7YDSqPb(Rdxb3sJi&ttPu z+O@bF4TCxb%Udg7$pgqTdLv1hg36Q;mhlGfy{1K#4a_1!MvB=RYid$b_pzdXJ5#|` zK@F`W8_H7CG$KR0mb$Y4##0(~sz~uA3!Z@)3bru9s;UWaoc<=2a7x!Do1$oRmUF zq{xXTNA4KomT^g}Qcj{J`9aAG<2sG$xsJ%HU*JJ(lD0`oFdY^`5y_icjE~Sf9^v$sNjD6Z2lyO!;m#k$mJrTrkGI=9U1Ho@ z5?#!Y(kRX#LB7Kv2@67t5^JP11=oiuixHfVBUcb9Q`Eq!;5b&mOMF#sAB)|UGj6aX zN}vlXec+sZi^${2OJQ_uhETp+UB_GT#;Xe^5v2ZfX+Hxme*vkfS%)*dxO(5x$dyS0 zfqKKswGQ(Otg_`{(<3$71P~Z@MoW%wLWx=rStkjO@?r)+>!t(CsHjrUaa+}n-oA4V zT}JN)UE04~6|Xj9h6k)k6(6-lb8p{fox|2|z{N7*U5gp}F1Ia$Uryb=Y9)5l! zq#A4vfHGP`#8#8SSk^j~QH9Uv50;0;*Lx41icME6ywv$~l;U@3_i%@ELF4%CWAeb( zk{p16=*>aETHj~OtFu-wVjt1{9&E3;3;FsnIkR2sN?8cE9rV~bHIIJq*{u_2@}qXs zZ;<#LEst0#9D{hJy3A6ihE`p0iAlReGgoe}S%vHa$F6XE7BNPgs)8HdD$E$iYk{X) zAS#*H9Ch~PiXreA6KxH-wGMK71)6+yl!GA)m*MH@MrUr#&0W7;odJYS*gR#mm*NEd z^I!Xv?b?;mZQKf}iQfJfD-n<;WfqQ}Hi!3|^U=XOQZrRST2Lk?yopRb4l7uKJDA1( zV;tS`0t~DkrQuNKA}&=L=p77Gx#a5Hc)F+-emY!0!A}KoT=a<(fEH@XhA6Y;*%Pem zjE6O{k5XO1;=ENXgYY3i6r*uy;GU7ggQv999jKTnuhE4rWtp@XV&K=S=yKHUE2$lv zUI@ET`Apz%43X#IKv@_k%Z pAk=0gqF_V5dY~-72o3um;ZaN&-ZUQ4&{GCadxh@ z#sH_kiNF82Q%lvh?6w%;dougYEWOwv?V3AClfVw$X)91)EaRYNCXJx`%Z_RK2% zOlOZ$v<5a01w_-~><^P=EvkZOl*as=9i9A*8aN|TY#vb8Xu>m^$phr(#Wzp^!X|!F zT%d(?q<1eg-y!?sggDCF19GmFIJRK%DJHTIkCoEWx-8zP2NPbnhC=E}MyMr7!7X>T zj{a46yuNZ^a}Pn<5v(V`n;iRikMuPG(qPv_EuYxzTxK8!{SHZJ$eJ|ASza2 zNNI0mOHD{O#sf&LUriswNi>&qPqRgb$xBPAkN3Pw)7?_|(w7ILv+%?DJzhZjSJ1%0 z#NLw7x5!pIp)+B)qvfw7-o?S19@PDYk`U-=Na`)s6;oR_au-gBAUa<-dV?m;oNwY} zzVcMuxF>?cA(O?it9YY~as%%`zxJxeK$Mq{DD2DYOn;Ws4@0m_>8_se5cHJCkK`V2 zYAEQ6y|r>aZ|%(3wS`lGClFu=+H%*|EzPh0I^s*#)?)t~6bPvLZ{qp?JNnG*?Oari zjBWmP#`jo5C~k`#Z5wkOAMA>yh_wlTdk6Mo4vG#0Ovo&%#xk=lGNMRm@^BV8%1q@E zK?_VW;Cd6HcZO8ZE>S_b36RNLW4TC6Je2T9%r9BtrvK&X?2duf)h;3-#av`fK&Vcn zDMZ1NouEs=U<}c_j>I`f&T*23*~LE6g&?2rb$`K&@lcB%KO=tvSSIY6crG1uE_ot| zkyha~$<)Dl6nu#551Mnl(VbJPPliL=Fjww;%1OI^fZ`h6VuVT4?M4jwQ4j@IuYf0S~)r1D9d*7bzh%Y9Cd&!|Cpiyd#+|&rP8n~ z{t;b9FPtnm{{$P;BMHRKTEJxvW-sT7a0o7FEd}|Um(2Q<%barwG>!8x>6P&WqFe+M z{Pw&^Wm$;G2ebo5PXQo}p(&YU4?&EYXC%I~#Z2v@&*vxgV5&4~-%wy>WENNA+|ihX z%IdP8SYWXnt&4gWZih&GK}ifuJBcTV$am?2x?qBmD}i`-s2Ljwbwp)$?&=jL>>c6b z@v^nu?{7iqkRNM8)}wEs(yzYiX~vrIV)m*;p%GC~IYdA#ArMWvzdGOPk@lnwMgCNd3? zaO#*qz3QA8o&r4rkoxT}509B8H=)IhrQU%po+(35`&s;^vlMHk4%X8A`y#^L`h+Eml5C;wQ&W zh1{n>PiGeZ)$XFatVeY}cA4q`>qs0w0P z$)V_iCX8$ghGtBY=C_|P%P3VYlod+_fs~hsQ*oF&a*dX*GZm0Kx@()WRnLI@&8vX<@ES1F;Sr8GNd zABEd*t6KQj;?n2ngyMI_SdkC03i-I$LCw^*jOE3`{J@7{&TJlyLx^yG$eDGnzAige zSTda0!&l-RhLro6O6X{B7n(H>;2XzS#)_}i9>OaAm+A{}M#6PV`>|WFu8L+zk$HjT zmcTlpa4lahFp1^3n!Nq8UOM`t4fFqdHpyS(1Z3a@u=($QH2@P66MX)y+y869{>eD` zA0GODIBS!(r9qjHLT|u#iKcxywy230ZR|zQawUBOh%&d~xBt)nOz_!STism0W5c?= zABv39&(##4s46aLu2?uF_XF3oP~N6Je8+zI9sK+*v5;HIxT@Lt!;%Lsl4fV09{ttH z_QYsPwe)5UJv;3eGat{L68Zg!LAc^1{kdi;?J=jWob0ITmpvI8d;i3j%gnPTF25ng znwh=M@r~ccoPW2ix2(NdBLC&!#OIx&MQb$9&9F*hU9TvaX~(|0iKl2*TB+UH_tU-H zd)OqG@^y+CPMsrkd(s>8fW%6j^FLz^*|*rpXVm-&u_;OU)ON;3=$M6j$=CTVzb07j zsGP@tR(z67?U`r1xI+@YE(cq1dh7h~`UVU}U0{(c1Ej%wfFKHyCs?83do~Mr_(IG3 z)_u)4s&%dZE}U{==M<-PM{605#{Cb7G`i)x^8UVVML(v4dw(e15S;Z>;%(XY+ty#3 zC+>Q{o8rmERV%sboH^URuKxD$fFsw!ud(*CxvAWE`n~+Nf626`f?Mx8c*WRAX3T6i zU%N7A7e8KN{-o`{bv@6K+OIsTB-lOw-M79{ z8vdwSqu;h_pWwpR4|Z-~Z%@ifZq7-VCZxz(EBz?bm!tj-zr@l9#z%B?9c|We9{l7i zuIsaS-npF4#La5Vikq)EJeTu3CFHbCi>cqwtm+=ytY^V{_C6>sagXkXA*#Oaps%qBugGsZyaQg%dv z{*(3yhW)ZU?{<`!HT7n$F}_jpg{5`HsTnrwUw*!(;?Sv6dB<-}q^0n^=}}!C%W6`u z%BkKJG>W=;=HSwA70yyU@~T&q?=zRZzPDo50#TJZq`SJssBB4`$%VjQ> zxGq!;a7$g$#Pdp5J7DE<4~{_R=QWdbV;ep{%ys|zYICQkTl%^5cexQ}hW0J>yB56K zw>>uYR_Rb)N|UKpG^vgeQM`)XO4j*^Reg|-_6tY0c-)Iw^BB0!jFCx% zK?E2Vki%TiV+bh0z{sEgB!gVA#uvl@>?bsUN+AdUHdh$dJHpBvbW71oC6FG7rI+D) zzwYlEI&gYH`NV^$ElHvp41+)~(~E9lzM z4+lYLw*;0V2<`BrLhyMSbOr_lfIK~!1%4t1$ivtp1yrCx07%PECzMhP-LdEe97rQ1 zq?DXdG-4EY=q91BLP3~xToq~(vKi>w(Hp)9?UQYx+EJRp==#ySZ3z9M)=>Q@oj7#; z=({};`sIL!j9~2jMAwdfC<#LQHdve?jwpd>CMNb0<)L=N7LB5&bM$4S2wS^QH6j*~ W26(fAbFCoo1R(*S(>}R^Gywo`(S3XX literal 0 HcmV?d00001 diff --git a/web/public/chuguoqingkuanghuizhi.docx b/web/public/chuguoqingkuanghuizhi.docx new file mode 100644 index 0000000000000000000000000000000000000000..7c1e4e19821481abb96436c967c2149b02d77f39 GIT binary patch literal 16514 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 IJikHzALJ1AqW}N^ literal 0 HcmV?d00001 diff --git a/web/public/zunshoudangjichengnuoshu.docx b/web/public/zunshoudangjichengnuoshu.docx new file mode 100644 index 0000000000000000000000000000000000000000..ed40ebc979b7af5b5a19c168f5179a93089a147a GIT binary patch literal 14822 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 literal 0 HcmV?d00001 diff --git a/web/src/api/rs/overseas-registration.ts b/web/src/api/rs/overseas-registration.ts index b97c832c..fdc069ae 100644 --- a/web/src/api/rs/overseas-registration.ts +++ b/web/src/api/rs/overseas-registration.ts @@ -70,6 +70,7 @@ export interface OverseasRegistrationForm { officePhone?: string; // 办公电话 mobilePhone?: string; // 移动电话 remarks?: string; // 备注 + status?: string; // 登记状态:0-草稿,1-待审核,2-已通过,3-已拒绝 } // 登记历史查询参数类型 @@ -142,6 +143,17 @@ export function getOverseasRegistrationList(query: OverseasRegistrationQuery) { }); } +/** + * 查询我的出国登记列表(只查询当前登录人创建的记录) + */ +export function getMyOverseasRegistrationList(query: OverseasRegistrationQuery) { + return request({ + url: '/rs/overseas/registration/my/list', + method: 'get', + params: query + }); +} + /** * 查询出国登记详细 */ @@ -433,4 +445,4 @@ export function batchRejectOverseasRegistration(registrationIds: Array; } @@ -842,8 +853,8 @@ export interface ItineraryChangeVO { status: string; statusText: string; approvalOpinion?: string; - approvalBy?: number; - approvalByName?: string; + approverId?: number; + approverName?: string; approvalTime?: string; createTime: string; updateTime?: string; diff --git a/web/src/permission.ts b/web/src/permission.ts index 30dea85a..48d071f7 100644 --- a/web/src/permission.ts +++ b/web/src/permission.ts @@ -34,7 +34,7 @@ router.beforeEach(async (to, from, next) => { const [err] = await tos(useUserStore().getInfo()); if (err) { await useUserStore().logout(); - ElMessage.error(err); + // ElMessage.error(err.message || '获取用户信息失败'); next({ path: '/' }); } else { isRelogin.show = false; diff --git a/web/src/utils/request.ts b/web/src/utils/request.ts index 9559c259..7ae6f71b 100644 --- a/web/src/utils/request.ts +++ b/web/src/utils/request.ts @@ -204,8 +204,14 @@ service.interceptors.response.use( err.response = res; return Promise.reject(err); } else if (code !== HttpStatus.SUCCESS) { - ElNotification.error({ title: msg }); - const err = new Error(msg) as any; + // 确保错误消息不为空,避免只显示图标没有文字 + const errorMsg = msg || errorCode['default'] || '操作失败,请稍后重试'; + ElNotification.error({ + title: '错误提示', + message: errorMsg, + duration: 5000 + }); + const err = new Error(errorMsg) as any; err.response = res; return Promise.reject(err); } else { diff --git a/web/src/views/login.vue b/web/src/views/login.vue index c80e5f5e..73e68f6f 100644 --- a/web/src/views/login.vue +++ b/web/src/views/login.vue @@ -192,13 +192,29 @@ const getLoginData = () => { * 获取租户列表 */ const initTenantList = async () => { - const { data } = await getTenantList(false); - tenantEnabled.value = data.tenantEnabled === undefined ? true : data.tenantEnabled; - if (tenantEnabled.value) { - tenantList.value = data.voList; - if (tenantList.value != null && tenantList.value.length !== 0) { - loginForm.value.tenantId = tenantList.value[0].tenantId; + try { + const [err, res] = await to(getTenantList(false)); + if (err) { + // 静默处理租户列表获取失败,不影响登录功能 + console.warn('获取租户列表失败:', err); + // 设置默认值,确保登录功能正常 + tenantEnabled.value = true; + return; } + if (res && res.data) { + const { data } = res; + tenantEnabled.value = data.tenantEnabled === undefined ? true : data.tenantEnabled; + if (tenantEnabled.value) { + tenantList.value = data.voList; + if (tenantList.value != null && tenantList.value.length !== 0) { + loginForm.value.tenantId = tenantList.value[0].tenantId; + } + } + } + } catch (error) { + // 捕获其他可能的错误,静默处理 + console.warn('初始化租户列表时发生错误:', error); + tenantEnabled.value = true; } }; diff --git a/web/src/views/rs/overseas-registration/index-all.vue b/web/src/views/rs/overseas-registration/index-all.vue new file mode 100644 index 00000000..1c775028 --- /dev/null +++ b/web/src/views/rs/overseas-registration/index-all.vue @@ -0,0 +1,1138 @@ + + + + + diff --git a/web/src/views/rs/overseas-registration/index.vue b/web/src/views/rs/overseas-registration/index.vue index 1b898610..28ba279c 100644 --- a/web/src/views/rs/overseas-registration/index.vue +++ b/web/src/views/rs/overseas-registration/index.vue @@ -1,316 +1,171 @@ - \ No newline at end of file + diff --git a/web/src/views/rs/overseas/approval.vue b/web/src/views/rs/overseas/approval.vue index b8171c2c..6a5a326a 100644 --- a/web/src/views/rs/overseas/approval.vue +++ b/web/src/views/rs/overseas/approval.vue @@ -9,12 +9,7 @@ - + @@ -49,254 +44,317 @@
- - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + +
- + - - - {{ viewData.applicantName || '-' }} - {{ viewData.gender === '0' ? '男' : viewData.gender === '1' ? '女' : '-' }} - - {{ formatDate(viewData.birthDate) || '-' }} - {{ viewData.politicalStatusText || '-' }} - - - {{ viewData.department || '-' }} - {{ viewData.professionalTitle || '-' }} - - {{ viewData.position || '-' }} - {{ viewData.documentName || '-' }} - - - {{ viewData.documentNumber || '-' }} - {{ viewData.destinationCountry || viewData.destination || '-' }} - - - {{ viewData.exitReason || '-' }} - - - {{ viewData.visitRelationship || '-' }} - - - - {{ viewData.itinerary || '-' }} - {{ viewData.duration || 0 }}天 - - - {{ viewData.remarks }} - - - {{ formatDate(viewData.startDate) || '-' }} - {{ formatDate(viewData.endDate) || '-' }} - - - {{ viewData.mobilePhone || viewData.contactPhone || '-' }} - {{ viewData.emergencyContact || '-' }} - - - - {{ viewData.statusText }} - - {{ viewData.applyTime || '-' }} - - - {{ viewData.adminRemark }} - +
+ +
+
个人信息
+ + + + {{ viewData.applicantName || '-' }} + + + + + {{ viewData.gender === '0' ? '男' : viewData.gender === '1' ? '女' : '-' }} + + + + + {{ formatYearMonth(viewData.birthDate) || '-' }} + + + + + + + {{ viewData.politicalStatusText || '-' }} + + + + + {{ viewData.registeredAddress || '-' }} + + + + + {{ viewData.professionalTitle || '-' }} + + + +
- -
- -
-
- - 出国境登记表盖章文件 +
+
岗位与证件
+ + + + {{ viewData.position || '-' }} + + + + + {{ viewData.documentName || '-' }} + + + + + {{ viewData.documentNumber || '-' }} + + +
-
-
- - - {{ viewData.stampedRegistrationFileName }} - - - 上传时间: {{ viewData.stampedFileUploadTime }} - -
-
- +
出行信息
+ + + + {{ viewData.destinationCountry || viewData.destination || '-' }} + + + + + {{ viewData.exitReason || '-' }} + + + + + {{ viewData.visitRelationship || '-' }} + + + + + + + {{ viewData.itinerary && viewData.itinerary !== '未填写' ? viewData.itinerary : '-' }} + + + +
+ +
+
承诺声明
+ + +
+ 本人承诺,在出国(境)期间遵守当地法律法规和中国的法律法规,结束后如期回国并到学校报到,如逾期不归,同意作自动离职处理。 +
+
+
+
+ +
+
手写签字
+ + +
+ 手写签名 +
+
+
+
+ +
+
时间安排
+ + + + {{ formatDate(viewData.startDate) || '-' }} + + + + + {{ formatDate(viewData.endDate) || '-' }} + + + + + {{ viewData.duration || 0 }}天 + + + +
+ +
+
联系方式
+ + + + {{ viewData.mobilePhone || viewData.contactPhone || '-' }} + + + + + + {{ viewData.emergencyContact || '-' }} + + + +
+ +
+
状态信息
+ + + + {{ viewData.statusText || '-' }} + + + + + {{ viewData.applyTime || '-' }} + + + + + + + {{ viewData.remarks }} + + + + + {{ viewData.adminRemark }} + + + +
+ + +
+
+ + 审批流程信息 +
+
+ + + - 查看 - - - 下载 - +
+
+ {{ getApprovalLevelText(record) }} + + {{ record.approvalStatusText || getApprovalResultText(record.approvalStatus) }} + +
+
+ 审批人:{{ record.approverName || '-' }} + 审批时间:{{ parseTime(record.approvalTime) }} +
+
审批意见:{{ record.approvalOpinion }}
+
+
+
+
+
+ +
+
+ + 申请附件 +
+
+
+
+ +
+ + {{ getFileExt(file.name || '') }} +
+
+
+ {{ file.name || '附件' }} +
+
上传时间:{{ parseTime(file.uploadTime) }}
+
+ 下载 +
- - -
-
- - 党籍保留附件 -
-
-
- - - {{ viewData.partyRetentionFileName }} - - - 上传时间: {{ viewData.partyRetentionUploadTime }} - -
-
- - 查看 - - - 下载 - -
-
+ +
+
- - -
-
- - 其他申请附件 -
- - - - - - - - - - - - - -
- - -
- -
diff --git a/web/src/views/rs/overseas/itinerary-change-approval.vue b/web/src/views/rs/overseas/itinerary-change-approval.vue index a39dc44b..b2bcf37f 100644 --- a/web/src/views/rs/overseas/itinerary-change-approval.vue +++ b/web/src/views/rs/overseas/itinerary-change-approval.vue @@ -1,30 +1,6 @@ - - - 前往待办页面 - - - - + + @@ -190,7 +161,7 @@ - + @@ -522,7 +518,8 @@ import { ref, reactive, toRefs, onMounted, computed, watch } from 'vue'; import { ElMessage, ElMessageBox } from 'element-plus'; import animate from '@/animate'; import { parseTime } from '@/utils/ruoyi'; -import { +import { useUserStore } from '@/store/modules/user'; +import { listItineraryChange, listMyItineraryChange, getItineraryChange, @@ -535,6 +532,7 @@ import { getOverseasApplication, getItineraryChangeHistory, getOverseasRecordByApplication, + listOverseasApplication, type ItineraryChangeQuery, type ItineraryChangeVO, type ItineraryChangeForm, @@ -553,7 +551,7 @@ const formRef = ref(); const state = reactive({ // Tab切换 activeTab: 'all', - + // 全部变更列表 loading: false, showSearch: true, @@ -565,9 +563,10 @@ const state = reactive({ pageSize: 10, applicantName: '', destination: '', - status: '' + status: '', + applicantId: undefined as number | undefined } as ItineraryChangeQuery, - + // 我的变更列表 myLoading: false, showMySearch: true, @@ -577,9 +576,10 @@ const state = reactive({ pageNum: 1, pageSize: 10, destination: '', - status: '' + status: '', + applicantId: undefined as number | undefined } as ItineraryChangeQuery, - + // 对话框 dialog: { visible: false, @@ -591,21 +591,21 @@ const state = reactive({ historyDialog: { visible: false }, - + // 表单数据 form: {} as ItineraryChangeForm, viewData: null as ItineraryChangeVO | null, - + // 变更历史 historyLoading: false, historyList: [] as ItineraryChangeVO[], - + // 可用的出国申请列表(已审批通过) availableApplicationList: [] as OverseasApplicationVO[], - + // 变更类型(多选) changeTypes: [] as string[], - + // 表单验证规则 rules: { applicationId: [{ required: true, message: '请选择原出国申请', trigger: 'change' }], @@ -690,7 +690,7 @@ onMounted(() => { const handleTabClick = (tab: any) => { const tabName = tab.props?.name || tab.paneName; state.activeTab = tabName; - + if (tabName === 'all') { getList(); } else if (tabName === 'my') { @@ -698,18 +698,36 @@ const handleTabClick = (tab: any) => { } }; -// 查询变更申请列表(只显示待审核的数据) +// 查询变更申请列表(只显示当前登录人的待审核和已撤回的数据) const getList = async () => { state.loading = true; try { - // 如果用户没有手动选择状态,默认只显示待审核的申请 - const params = { ...state.queryParams }; - if (!params.status) { - params.status = '0'; // 默认只显示待审核状态 - } + const userStore = useUserStore(); + // 先获取所有当前登录人的数据(不分页),然后在前端过滤和分页 + const params = { + pageNum: 1, + pageSize: 10000, // 设置一个较大的值,获取所有数据 + applicantId: Number(userStore.userId), + applicantName: state.queryParams.applicantName, + destination: state.queryParams.destination + }; + const response = await listItineraryChange(params); - state.changeList = response.rows; - state.total = response.total; + + // 前端过滤:只显示状态为 0(待审核)和 3(已撤回)的数据 + const filteredRows = response.rows.filter((item: ItineraryChangeVO) => { + return item.status === '0' || item.status === '3'; + }); + + // 前端分页 + const pageNum = state.queryParams.pageNum; + const pageSize = state.queryParams.pageSize; + const total = filteredRows.length; + const start = (pageNum - 1) * pageSize; + const end = Math.min(start + pageSize, total); + + state.changeList = filteredRows.slice(start, end); + state.total = total; } catch (error) { console.error('获取变更行程列表失败:', error); } finally { @@ -717,13 +735,35 @@ const getList = async () => { } }; -// 查询我的变更记录列表(只显示已通过的变更申请) +// 查询我的变更记录列表(只显示当前登录人的已通过和已拒绝的数据) const getMyList = async () => { state.myLoading = true; try { - const response = await listMyItineraryChange(state.myQueryParams); - state.myChangeList = response.rows; - state.myTotal = response.total; + const userStore = useUserStore(); + // 先获取所有当前登录人的数据(不分页),然后在前端过滤和分页 + const params = { + pageNum: 1, + pageSize: 10000, // 设置一个较大的值,获取所有数据 + applicantId: Number(userStore.userId), + destination: state.myQueryParams.destination + }; + + const response = await listItineraryChange(params); + + // 前端过滤:只显示状态为 1(已通过)和 2(已拒绝)的数据 + const filteredRows = response.rows.filter((item: ItineraryChangeVO) => { + return item.status === '1' || item.status === '2'; + }); + + // 前端分页 + const pageNum = state.myQueryParams.pageNum; + const pageSize = state.myQueryParams.pageSize; + const total = filteredRows.length; + const start = (pageNum - 1) * pageSize; + const end = Math.min(start + pageSize, total); + + state.myChangeList = filteredRows.slice(start, end); + state.myTotal = total; } catch (error) { console.error('获取我的变更记录失败:', error); } finally { @@ -731,13 +771,25 @@ const getMyList = async () => { } }; -// 获取已审批通过的出国申请列表 +// 获取已审批通过的出国申请列表(只显示当前登录人的) const getApprovedApplicationList = async () => { try { - const response = await getApprovedOverseasApplications(); - state.availableApplicationList = response.data; + const userStore = useUserStore(); + // 使用listOverseasApplication查询当前登录人的已通过申请 + const response = await listOverseasApplication({ + pageNum: 1, + pageSize: 1000, // 设置一个较大的值,获取所有数据 + applicantId: Number(userStore.userId), // 只查询当前登录人的申请 + status: '1' // 只查询已通过的状态 + }); + + // 确保只返回已通过审批的申请 + state.availableApplicationList = response.rows.filter((item: OverseasApplicationVO) => { + return item.status === '1'; + }); } catch (error) { console.error('获取已审批通过的出国申请列表失败:', error); + ElMessage.error('获取已审批通过的出国申请列表失败'); } }; @@ -798,30 +850,30 @@ const handleApplicationChange = async (applicationId: number) => { // 1. 获取出国申请详情 const response = await getOverseasApplication(applicationId); const selectedApplication = response.data; - + // 2. 根据申请ID获取出国记录(包含recordId) const recordResponse = await getOverseasRecordByApplication(applicationId); const recordData = recordResponse.data; - + if (selectedApplication && recordData) { // 设置recordId(关键字段,必填) state.form.recordId = recordData.recordId; - + // 设置申请人ID(关键字段,必填) state.form.applicantId = selectedApplication.applicantId; - + // 填充本人基本情况 state.form.applicantName = selectedApplication.applicantName; state.form.gender = selectedApplication.gender; state.form.birthDate = selectedApplication.birthDate; // 政治面貌:将字典值转换为文本标签 - state.form.politicalStatus = getPoliticalStatusLabel(selectedApplication.politicalStatus); + state.form.politicalStatus = selectedApplication.politicalStatus ? getPoliticalStatusLabel(selectedApplication.politicalStatus) : ''; state.form.professionalTitle = selectedApplication.professionalTitle; state.form.position = selectedApplication.position; state.form.documentName = selectedApplication.documentName; state.form.documentNumber = selectedApplication.documentNumber; state.form.department = selectedApplication.department; - + // 填充申请行程情况 // 前往国家:优先使用destinationCountry,其次使用destination state.form.originalDestination = selectedApplication.destinationCountry || selectedApplication.destination; @@ -833,7 +885,7 @@ const handleApplicationChange = async (applicationId: number) => { state.form.originalStartDate = selectedApplication.startDate; state.form.originalEndDate = selectedApplication.endDate; state.form.originalDuration = selectedApplication.duration; - + // 默认将原信息复制到新信息 state.form.newDestination = selectedApplication.destinationCountry || selectedApplication.destination; state.form.newDestinationCity = selectedApplication.destinationCity; @@ -867,22 +919,24 @@ const handleCancel = (row: ItineraryChangeVO) => { confirmButtonText: '确定', cancelButtonText: '取消', inputPlaceholder: '请输入撤回原因' - }).then(async ({ value }) => { - try { - await cancelItineraryChange(row.changeId, value); - ElMessage.success('撤回成功'); - - // 根据当前Tab刷新列表 - if (state.activeTab === 'all') { - getList(); - } else if (state.activeTab === 'my') { - getMyList(); + }) + .then(async ({ value }) => { + try { + await cancelItineraryChange(row.changeId, value); + ElMessage.success('撤回成功'); + + // 根据当前Tab刷新列表 + if (state.activeTab === 'all') { + getList(); + } else if (state.activeTab === 'my') { + getMyList(); + } + } catch (error) { + console.error('撤回失败:', error); + ElMessage.error('撤回失败'); } - } catch (error) { - console.error('撤回失败:', error); - ElMessage.error('撤回失败'); - } - }).catch(() => {}); + }) + .catch(() => {}); }; // 重新提交 @@ -890,12 +944,12 @@ const handleResubmit = async (row: ItineraryChangeVO) => { try { const response = await getItineraryChange(row.changeId); Object.assign(state.form, response.data); - + // 解析变更类型 if (response.data.changeType) { state.changeTypes = response.data.changeType.split(','); } - + state.dialog.title = '重新提交行程变更申请'; state.dialog.visible = true; } catch (error) { @@ -910,30 +964,32 @@ const handleDelete = (row: ItineraryChangeVO) => { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' - }).then(async () => { - try { - await delItineraryChange(row.changeId); - ElMessage.success('删除成功'); - - // 根据当前Tab刷新列表(如果删除的是当前页最后一条且不是第1页,返回上一页) - if (state.activeTab === 'all') { - // 如果删除的是当前页最后一条且不是第1页,返回上一页 - if (state.changeList.length === 1 && state.queryParams.pageNum > 1) { - state.queryParams.pageNum--; + }) + .then(async () => { + try { + await delItineraryChange(row.changeId); + ElMessage.success('删除成功'); + + // 根据当前Tab刷新列表(如果删除的是当前页最后一条且不是第1页,返回上一页) + if (state.activeTab === 'all') { + // 如果删除的是当前页最后一条且不是第1页,返回上一页 + if (state.changeList.length === 1 && state.queryParams.pageNum > 1) { + state.queryParams.pageNum--; + } + getList(); + } else if (state.activeTab === 'my') { + // 如果删除的是当前页最后一条且不是第1页,返回上一页 + if (state.myChangeList.length === 1 && state.myQueryParams.pageNum > 1) { + state.myQueryParams.pageNum--; + } + getMyList(); } - getList(); - } else if (state.activeTab === 'my') { - // 如果删除的是当前页最后一条且不是第1页,返回上一页 - if (state.myChangeList.length === 1 && state.myQueryParams.pageNum > 1) { - state.myQueryParams.pageNum--; - } - getMyList(); + } catch (error) { + console.error('删除失败:', error); + ElMessage.error('删除失败'); } - } catch (error) { - console.error('删除失败:', error); - ElMessage.error('删除失败'); - } - }).catch(() => {}); + }) + .catch(() => {}); }; // 查看变更历史 @@ -963,15 +1019,14 @@ const submitForm = () => { if (state.form.newExitReason !== state.form.originalExitReason) { changeTypeList.push('出境事由变更'); } - if (state.form.newStartDate !== state.form.originalStartDate || - state.form.newEndDate !== state.form.originalEndDate) { + if (state.form.newStartDate !== state.form.originalStartDate || state.form.newEndDate !== state.form.originalEndDate) { changeTypeList.push('时间变更'); } if (state.form.newItinerary && state.form.newItinerary !== state.form.originalItinerary) { changeTypeList.push('行程变更'); } state.form.changeType = changeTypeList.length > 0 ? changeTypeList.join(',') : '其他变更'; - + state.submitLoading = true; try { if (state.form.changeId) { @@ -982,7 +1037,7 @@ const submitForm = () => { ElMessage.success('提交成功!变更申请已进入审批流程。'); } state.dialog.visible = false; - + // 根据当前Tab刷新列表(重置到第1页) if (state.activeTab === 'all') { state.queryParams.pageNum = 1; @@ -1056,11 +1111,11 @@ const getStatusTagType = (status: string): 'primary' | 'success' | 'info' | 'war const typeMap: Record = { '0': 'warning', // 待审核 '1': 'success', // 已通过 - '2': 'danger' // 已拒绝 + '2': 'danger', // 已拒绝 + '3': 'info' // 已撤回 }; return typeMap[status] || 'info'; }; - - - diff --git a/web/src/views/rs/overseas/monitor/index.vue b/web/src/views/rs/overseas/monitor/index.vue index 6dede570..7bcc83d5 100644 --- a/web/src/views/rs/overseas/monitor/index.vue +++ b/web/src/views/rs/overseas/monitor/index.vue @@ -49,24 +49,24 @@
- - - + + + - - + + @@ -350,8 +350,16 @@ -