From 850f4742a2f9bbd4050ed1b025ad0e00b2dba48d Mon Sep 17 00:00:00 2001 From: zhangrui Date: Mon, 15 Jun 2026 17:43:33 +0800 Subject: [PATCH] =?UTF-8?q?=E7=96=97=E4=BC=91=E5=85=BB=E4=BC=98=E5=8C=961.?= =?UTF-8?q?2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/sys/controller/SysHomeController.java | 37 +++ .../TourBranchUserAssignmentController.java | 8 +- .../tour/controller/TourLedgerController.java | 4 +- .../tour/controller/TourMatterController.java | 7 +- .../controller/TourMySignupController.java | 51 +++- .../TourSchoolUserAssignmentController.java | 6 +- .../controller/TourSettingController.java | 17 ++ .../tour/controller/TourSignupController.java | 108 +++++++- .../dayofficework/tour/models/TourLedger.java | 5 + .../tour/models/TourSetting.java | 11 + .../tour/models/TourUserAssignment.java | 10 + .../service/TourUserAssignmentService.java | 44 ++- .../impl/TourUserAssignmentServiceImpl.java | 256 +++++++++++++++++- src/main/resources/db/tour_ledger_add_age.sql | 3 + .../db/tour_setting_add_home_signup_entry.sql | 4 + src/main/resources/db/tour_setting_create.sql | 4 + .../tour_user_assignment_add_confirm_info.sql | 4 + .../db/tour_user_assignment_create.sql | 2 + .../platform/images/tour/疗休养图片.jpeg | Bin 0 -> 22492 bytes .../views/layouts/v4/home/index.html | 155 +++++++++++ .../Tour/branchUserAssignment/index.html | 87 +++++- .../zhgh/dayofficework/Tour/matter/index.html | 10 +- .../Tour/schoolUserAssignment/index.html | 88 +++++- .../dayofficework/Tour/setting/index.html | 56 ++++ .../recuperation/lineSelect/select.js | 7 +- .../dayofficework/tour/mysignup/index.html | 8 +- .../dayofficework/tour/signup/apply.html | 37 ++- .../dayofficework/tour/signup/confirm.html | 234 ++++++++++++++++ .../dayofficework/tour/signup/entry.html | 42 +++ .../dayofficework/tour/signup/index.html | 138 +++------- .../dayofficework/tour/signup/lineInfo.html | 4 +- .../dayofficework/tour/signup/list.html | 9 +- .../views/platform/zhghh5/sys/home/home.js | 27 +- 33 files changed, 1303 insertions(+), 180 deletions(-) create mode 100644 src/main/resources/db/tour_ledger_add_age.sql create mode 100644 src/main/resources/db/tour_setting_add_home_signup_entry.sql create mode 100644 src/main/resources/db/tour_user_assignment_add_confirm_info.sql create mode 100644 src/main/resources/static/assets/platform/images/tour/疗休养图片.jpeg create mode 100644 src/main/resources/views/platform/zhghh5/dayofficework/tour/signup/confirm.html create mode 100644 src/main/resources/views/platform/zhghh5/dayofficework/tour/signup/entry.html diff --git a/src/main/java/com/budwk/app/sys/controller/SysHomeController.java b/src/main/java/com/budwk/app/sys/controller/SysHomeController.java index 91e35037..0c154eff 100644 --- a/src/main/java/com/budwk/app/sys/controller/SysHomeController.java +++ b/src/main/java/com/budwk/app/sys/controller/SysHomeController.java @@ -12,9 +12,11 @@ import com.budwk.app.sys.models.Sys_home_template; import com.budwk.app.sys.models.Sys_menu; import com.budwk.app.sys.services.SysMenuService; import com.budwk.app.sys.services.SysUserService; +import com.budwk.app.web.commons.base.Globals; import com.budwk.app.web.commons.auth.utils.AuthUtil; import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.zhgh.activity.basic.models.ActivityUserScope; +import com.budwk.app.zhgh.dayofficework.tour.models.TourSetting; import io.swagger.annotations.ApiOperation; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; @@ -40,6 +42,7 @@ import org.nutz.mvc.annotation.Param; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; +import java.time.LocalDate; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -278,6 +281,40 @@ public class SysHomeController { return Result.success(list); } + /** + * 查询PC首页疗休养报名浮动入口。 + * 只返回本年度启用、开启首页入口且已上传图片的疗休养配置,避免首页出现空图片入口。 + * + * @return 当前年度可展示的疗休养报名入口 + */ + @At + @SaCheckLogin + @ApiOperation("PC首页疗休养报名浮动入口") + @Ok("json") + public Result listTourHomeSignupEntry() { + int currentYear = LocalDate.now().getYear(); + Cnd cnd = Cnd.where(TourSetting::getDelFlag, "=", false) + .and(TourSetting::getEnabled, "=", true) + .and(TourSetting::getYear, "=", currentYear) + .and(TourSetting::getHomeSignupEntryEnabled, "=", true); + cnd.asc(TourSetting::getSortNo); + cnd.desc(TourSetting::getUpdatedAt); + cnd.desc(TourSetting::getCreatedAt); + List settings = dao.query(TourSetting.class, cnd); + List rows = new ArrayList<>(); + for (TourSetting setting : settings) { + // 优先使用配置上传图片,未上传时使用系统默认疗休养图片,避免首页浮动入口空图。 + String imageUrl = StrUtil.blankToDefault(setting.getHomeSignupEntryImage(), Globals.AppDomain + "/assets/platform/images/tour/疗休养图片.jpeg"); + rows.add(NutMap.NEW() + .addv("id", setting.getId()) + .addv("year", setting.getYear()) + .addv("title", setting.getYear() + "暑期教职工疗休养") + .addv("imageUrl", imageUrl) + .addv("href", "/platform/tour/signup")); + } + return Result.success(rows); + } + @At @SaCheckLogin diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourBranchUserAssignmentController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourBranchUserAssignmentController.java index 2d1cdbf1..6944e60c 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourBranchUserAssignmentController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourBranchUserAssignmentController.java @@ -48,12 +48,12 @@ public class TourBranchUserAssignmentController { } /** - * 分页查询候选人员,候选范围由疗休养配置可参加人员范围和当前登录人所在分工会共同决定。 + * 分页查询候选人员,候选范围由疗休养配置可参加人员范围和当前登录人所在分工会共同决定;userIds 仅作为人员选择器筛选条件。 */ @At @SaCheckPermission("tour.branchUserAssignment") - public Result candidatePageData(PageForm pageForm, String settingId, String keyword) { - return Result.success(tourUserAssignmentService.branchCandidatePage(pageForm, settingId, keyword)); + public Result candidatePageData(PageForm pageForm, String settingId, String keyword, @Param("userIds") String userIds) { + return Result.success(tourUserAssignmentService.branchCandidatePage(pageForm, settingId, keyword, parseUserIds(userIds))); } /** @@ -71,7 +71,7 @@ public class TourBranchUserAssignmentController { @At @SaCheckPermission("tour.branchUserAssignment") public Result matterOptions(String settingId) { - return Result.success(tourUserAssignmentService.listMatterOptions(settingId)); + return Result.success(tourUserAssignmentService.listSignupOpenMatterOptions(settingId)); } /** diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourLedgerController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourLedgerController.java index fc184703..103bc4a1 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourLedgerController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourLedgerController.java @@ -132,7 +132,7 @@ public class TourLedgerController { dr.id AS directRelativeId, COALESCE(NULLIF(l.lineName, ''), t.lineName) AS currentLineName, COALESCE(NULLIF(vu.sex, ''), t.gender, '') AS currentGender, - vu.age AS age, + COALESCE(t.age, vu.age) AS age, COALESCE(NULLIF(t.idCard, ''), NULLIF(vu.idCard, ''), '') AS currentIdCard, COALESCE(NULLIF(t.mobile, ''), NULLIF(vu.mobile, ''), '') AS currentMobile, IFNULL(f.familyCount, 0) AS familyCount, @@ -393,7 +393,7 @@ public class TourLedgerController { t.jobNo, t.userName, COALESCE(NULLIF(vu.sex, ''), t.gender, '') AS gender, - vu.age AS age, + COALESCE(t.age, vu.age) AS age, COALESCE(NULLIF(t.idCard, ''), NULLIF(vu.idCard, ''), '') AS idCard, COALESCE(NULLIF(t.mobile, ''), NULLIF(vu.mobile, ''), '') AS mobile, t.unitName, diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourMatterController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourMatterController.java index 45f227b8..11354a72 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourMatterController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourMatterController.java @@ -37,8 +37,6 @@ import java.util.stream.Collectors; @At("/platform/tour/matter") public class TourMatterController { - private static final String MOBILE_PATTERN = "^1[3-9]\\d{9}$"; - @Inject private TourMatterService tourMatterService; @@ -353,8 +351,9 @@ public class TourMatterController { if (StrUtil.isBlank(matter.getContactName())) { return Result.error("联系人不能为空"); } - if (StrUtil.isBlank(matter.getContactPhone()) || !matter.getContactPhone().matches(MOBILE_PATTERN)) { - return Result.error("联系方式格式不正确"); + // 线路联系人电话允许填写座机、分机或其他联系说明,后端只保留必填校验。 + if (StrUtil.isBlank(matter.getContactPhone())) { + return Result.error("联系方式不能为空"); } if (matter.getMinGroupPeople() == null || matter.getMinGroupPeople() <= 0) { return Result.error("最少成团人数必须大于0"); diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourMySignupController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourMySignupController.java index f49d64e5..97e3cf89 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourMySignupController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourMySignupController.java @@ -154,6 +154,7 @@ public class TourMySignupController { IFNULL(f.familyCount, 0) AS familyCount, a.id AS assignmentId, IFNULL(a.cancelled, 0) AS assignmentCancelled, + m.signupEndTime AS actionSignupEndTime, m.travelStartTime AS actionTravelStartTime, COALESCE(m.travelEndTime, tp.travelEndTime) AS travelEndTime, CASE @@ -309,6 +310,7 @@ public class TourMySignupController { m.matterName, m.unionId AS matterUnionId, m.defaultBoardingPlace, + m.signupEndTime, m.travelStartTime, m.travelEndTime, CASE @@ -345,6 +347,8 @@ public class TourMySignupController { if (isTravelEnded(matter.getString("travelEndTime"))) { return Result.error("线路出行已结束,不能修改"); } + // 修改弹窗内的取消按钮也由后端返回的报名截止状态控制,避免报名结束后仍可操作。 + matter.put("canCancelSignup", isWithinSignupCancelTime(matter.getString("signupEndTime", ""), LocalDateTime.now())); Cnd familyCnd = Cnd.where(TourLedgerFamily::getDelFlag, "=", false) .and(TourLedgerFamily::getLedgerId, "=", ledger.getId()); familyCnd.asc(TourLedgerFamily::getCreatedAt); @@ -549,6 +553,10 @@ public class TourMySignupController { if (ledger == null) { return Result.error("报名记录不存在"); } + TourMatter matter = StrUtil.isBlank(ledger.getMatterId()) ? null : tourMatterService.fetch(ledger.getMatterId()); + if (!isWithinSignupCancelTime(matter)) { + return Result.error("报名已结束,不能取消报名"); + } tourLedgerFamilyService.clear(Cnd.where(TourLedgerFamily::getLedgerId, "=", ledger.getId())); tourLedgerDirectRelativeService.clear(Cnd.where(TourLedgerDirectRelative::getLedgerId, "=", ledger.getId())); tourLedgerService.clear(Cnd.where(TourLedger::getId, "=", ledger.getId())); @@ -676,22 +684,51 @@ public class TourMySignupController { item.put("cancelDeadlineDays", cancelDeadlineDays); item.put("canLeaveTour", StrUtil.isNotBlank(assignmentId) && !assignmentCancelled - && isWithinLeaveTimeRange(item.getString("actionTravelStartTime", ""), cancelDeadlineDays, now)); + && isWithinLeaveTimeRange(item.getString("actionSignupEndTime", ""), item.getString("actionTravelStartTime", ""), cancelDeadlineDays, now)); + item.put("canCancelSignup", isWithinSignupCancelTime(item.getString("actionSignupEndTime", ""), now)); } } /** - * 判断当前时间是否处于“出行开始前N天”到“出行开始前”的可退出范围内。 + * 判断当前报名是否还处于允许取消的报名截止时间内,报名结束后不再显示或执行取消报名。 */ - private boolean isWithinLeaveTimeRange(String travelStartTime, int cancelDeadlineDays, LocalDateTime now) { + private boolean isWithinSignupCancelTime(TourMatter matter) { + if (matter == null) { + return false; + } + return isWithinSignupCancelTime(matter.getSignupEndTime(), LocalDateTime.now()); + } + + /** + * 报名结束时间为空时不允许取消;当前时间小于等于报名结束时间才允许取消。 + */ + private boolean isWithinSignupCancelTime(String signupEndTime, LocalDateTime now) { try { - String normalized = normalizeDateTime(travelStartTime, false); - if (StrUtil.isBlank(normalized)) { + String normalizedSignupEnd = normalizeDateTime(signupEndTime, true); + if (StrUtil.isBlank(normalizedSignupEnd)) { return false; } - LocalDateTime startTime = LocalDateTime.parse(normalized, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + LocalDateTime endTime = LocalDateTime.parse(normalizedSignupEnd, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + return !now.isAfter(endTime); + } catch (Exception e) { + return false; + } + } + + /** + * 判断当前时间是否处于“报名截止时间 < 当前时间 < 出行开始前N天”的可退出范围内。 + */ + private boolean isWithinLeaveTimeRange(String signupEndTime, String travelStartTime, int cancelDeadlineDays, LocalDateTime now) { + try { + String normalizedSignupEnd = normalizeDateTime(signupEndTime, true); + String normalizedTravelStart = normalizeDateTime(travelStartTime, false); + if (StrUtil.isBlank(normalizedSignupEnd) || StrUtil.isBlank(normalizedTravelStart)) { + return false; + } + LocalDateTime signupEnd = LocalDateTime.parse(normalizedSignupEnd, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + LocalDateTime startTime = LocalDateTime.parse(normalizedTravelStart, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); LocalDateTime deadline = startTime.minusDays(cancelDeadlineDays); - return !now.isBefore(deadline) && now.isBefore(startTime); + return now.isAfter(signupEnd) && now.isBefore(deadline); } catch (Exception e) { return false; } diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourSchoolUserAssignmentController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourSchoolUserAssignmentController.java index 129afd82..bfd9d91f 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourSchoolUserAssignmentController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourSchoolUserAssignmentController.java @@ -50,12 +50,12 @@ public class TourSchoolUserAssignmentController { } /** - * 分页查询候选人员,候选范围由疗休养配置的可参加人员范围决定。 + * 分页查询候选人员,候选范围由疗休养配置的可参加人员范围决定;userIds 仅作为人员选择器筛选条件。 */ @At @SaCheckPermission("tour.schoolUserAssignment") - public Result candidatePageData(PageForm pageForm, String settingId, String unionId, String keyword) { - return Result.success(tourUserAssignmentService.schoolCandidatePage(pageForm, settingId, unionId, keyword)); + public Result candidatePageData(PageForm pageForm, String settingId, String unionId, String keyword, @Param("userIds") String userIds) { + return Result.success(tourUserAssignmentService.schoolCandidatePage(pageForm, settingId, unionId, keyword, parseUserIds(userIds))); } /** diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourSettingController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourSettingController.java index a58966a8..9c8e650d 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourSettingController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourSettingController.java @@ -155,6 +155,14 @@ public class TourSettingController { if (tourSetting.getFillBedInfo() == null) { tourSetting.setFillBedInfo(true); } + if (tourSetting.getHomeSignupEntryEnabled() == null) { + tourSetting.setHomeSignupEntryEnabled(false); + } + if (!Boolean.TRUE.equals(tourSetting.getHomeSignupEntryEnabled())) { + tourSetting.setHomeSignupEntryImage(""); + } else { + tourSetting.setHomeSignupEntryImage(normalizeSingleHomeSignupEntryImage(tourSetting.getHomeSignupEntryImage())); + } // 报名资格校验方式默认按人员分配表校验,后续报名校验切换会读取该配置。 if (StrUtil.isBlank(tourSetting.getSignupEligibilityMode())) { tourSetting.setSignupEligibilityMode(SIGNUP_ELIGIBILITY_MODE_ASSIGNED_USER); @@ -226,6 +234,15 @@ public class TourSettingController { .collect(Collectors.toList()); } + private String normalizeSingleHomeSignupEntryImage(String image) { + // 首页报名入口图片仅允许保存一张,兼容逗号分隔的历史多图路径时只保留第一张。 + if (StrUtil.isBlank(image)) { + return ""; + } + List images = StrUtil.splitTrim(image, ","); + return Lang.isEmpty(images) ? "" : images.get(0); + } + private void saveLots(TourSetting tourSetting) { List lots = tourSetting.getLots(); if (Lang.isEmpty(lots)) { diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourSignupController.java b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourSignupController.java index a4c412c5..ccf70882 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourSignupController.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/controller/TourSignupController.java @@ -93,11 +93,76 @@ public class TourSignupController { } @At("/h5") - @Ok("beetl:/platform/zhghh5/dayofficework/tour/signup/index.html") + @Ok("beetl:/platform/zhghh5/dayofficework/tour/signup/entry.html") @SaCheckPermission(value = {"tour.signup", "h5.tour.signup"}, mode = SaMode.OR) public void h5() { } + /** + * 移动端疗休养报名入口的信息确认页。 + * 用户先确认基础信息并保存到人员分配表,再进入线路选择页面。 + */ + @At("/h5/confirm") + @Ok("beetl:/platform/zhghh5/dayofficework/tour/signup/confirm.html") + @SaCheckPermission(value = {"tour.signup", "h5.tour.signup"}, mode = SaMode.OR) + public void h5Confirm() { + } + + /** + * 移动端疗休养报名范围外提示页。 + * 当前登录人不存在人员分配记录时,从报名入口跳转到此页并引导返回首页。 + */ + @At("/h5/noAssignment") + @Ok("beetl:/platform/zhghh5/dayofficework/tour/signup/noAssignment.html") + @SaCheckPermission(value = {"tour.signup", "h5.tour.signup"}, mode = SaMode.OR) + public void h5NoAssignment() { + } + + /** + * 移动端报名成功后展示服务须知页。 + * 用户点击“我已阅读”后返回线路选择页面。 + */ + @At("/h5/notice") + @Ok("beetl:/platform/zhghh5/dayofficework/tour/signup/index.html") + @SaCheckPermission(value = {"tour.signup", "h5.tour.signup"}, mode = SaMode.OR) + public void h5Notice() { + } + + /** + * 查询移动端信息确认页数据。 + * 数据优先来自人员分配表;人员分配表不存在时返回 vw_user 基础信息供展示。 + * + * @return 信息确认表单数据和乘车地点选项 + */ + @At("/h5/confirmInfo") + @SaCheckPermission(value = {"tour.signup", "h5.tour.signup"}, mode = SaMode.OR) + public Result h5ConfirmInfo() { + return Result.success(tourUserAssignmentService.currentH5ConfirmInfo()); + } + + /** + * 保存移动端信息确认页数据。 + * 保存范围限制为当前年度既有人员分配记录,避免新增分配资格影响报名校验。 + * + * @param userName 姓名 + * @param gender 性别 + * @param age 年龄 + * @param idCard 身份证号 + * @param mobile 手机号 + * @param boardingPlace 乘车地点 + * @return 保存结果 + */ + @At("/h5/saveConfirmInfo") + @SaCheckPermission(value = {"tour.signup", "h5.tour.signup"}, mode = SaMode.OR) + public Result h5SaveConfirmInfo(String userName, String gender, Integer age, String idCard, String mobile, String boardingPlace) { + try { + tourUserAssignmentService.saveCurrentH5ConfirmInfo(userName, gender, age, idCard, mobile, boardingPlace); + return Result.success(); + } catch (IllegalArgumentException e) { + return Result.error(e.getMessage()); + } + } + @At("/h5/serviceNotice") @SaCheckPermission(value = {"tour.signup", "h5.tour.signup"}, mode = SaMode.OR) public Result h5ServiceNotice() { @@ -333,6 +398,7 @@ public class TourSignupController { SELECT m.id AS matterId, m.`year`, + m.settingId, m.matterName, m.minGroupPeople, m.maxGroupPeople, @@ -502,12 +568,14 @@ public class TourSignupController { SELECT m.id AS matterId, m.`year`, + m.settingId, m.matterName, m.unionId AS matterUnionId, m.defaultBoardingPlace, m.travelStartTime, m.travelEndTime, m.contactName, + m.contactPhone, CASE WHEN IFNULL(m.travelStartTime, '') <> '' AND IFNULL(m.travelEndTime, '') <> '' THEN CONCAT(m.travelStartTime, ' 至 ', m.travelEndTime) @@ -569,6 +637,7 @@ public class TourSignupController { .addv("unitName", user == null ? "" : defaultIfBlank(user.getUnitName(), "")) .addv("unionId", user == null ? SecurityUtil.getUnionId() : defaultIfBlank(user.getUnionId(), SecurityUtil.getUnionId())) .addv("unionName", user == null ? "" : defaultIfBlank(user.getUnionName(), "")); + NutMap assignment = tourUserAssignmentService.currentUserAssignmentInfo(matter.getString("settingId", "")); Cnd ledgerCnd = Cnd.where(TourLedger::getDelFlag, "=", false) .and(TourLedger::getMatterId, "=", matter.getString("matterId")) .and(TourLedger::getJobNo, "=", staff.getString("jobNo")); @@ -589,6 +658,7 @@ public class TourSignupController { return Result.success(NutMap.NEW() .addv("matter", matter) .addv("staff", staff) + .addv("assignment", assignment) .addv("ledger", ledger) .addv("process", getSignupProcessInfo(ledger)) .addv("families", families) @@ -803,6 +873,7 @@ public class TourSignupController { TourLine line = tourLedgerService.dao().fetch(TourLine.class, matter.getLineId()); boolean directFamilyLine = line != null && Boolean.TRUE.equals(line.getDirectFamilyUnitLine()); TourSetting setting = tourLedgerService.dao().fetch(TourSetting.class, matter.getSettingId()); + fillLedgerFromAssignmentConfirmInfo(ledger, matter.getSettingId()); Result boardingPlaceResult = normalizeBoardingPlace(ledger, matter, setting); if (boardingPlaceResult != null) { return boardingPlaceResult; @@ -838,6 +909,7 @@ public class TourSignupController { } ledger.setOverCostReimbursed(Boolean.TRUE.equals(ledger.getOverCostReimbursed())); fillStaffInfo(ledger); + fillLedgerFromAssignmentConfirmInfo(ledger, matter.getSettingId()); if (update) { tourLedgerService.updateIgnoreNull(ledger); @@ -1288,19 +1360,21 @@ public class TourSignupController { // Button visibility is calculated on the server so H5 follows the same time rules as backend submit actions. item.put("cancelDeadlineDays", cancelDeadlineDays); item.put("canLeaveTour", signed && StrUtil.isNotBlank(assignmentId) && !assignmentCancelled - && isWithinLeaveTimeRange(item.getString("travelStartTime", ""), cancelDeadlineDays, now)); + && isWithinLeaveTimeRange(item.getString("signupEndTime", ""), item.getString("travelStartTime", ""), cancelDeadlineDays, now)); item.put("canCancelLine", signed && StrUtil.isNotBlank(ledgerId) && isWithinSignupRange(item.getString("signupStartTime", ""), item.getString("signupEndTime", ""), now)); } } - private boolean isWithinLeaveTimeRange(String travelStartTime, int cancelDeadlineDays, LocalDateTime now) { + private boolean isWithinLeaveTimeRange(String signupEndTime, String travelStartTime, int cancelDeadlineDays, LocalDateTime now) { + LocalDateTime signupEnd = parseDateTimeValue(signupEndTime, true); LocalDateTime startTime = parseDateTimeValue(travelStartTime, false); - if (startTime == null) { + if (signupEnd == null || startTime == null) { return false; } LocalDateTime deadline = startTime.minusDays(cancelDeadlineDays); - return !now.isBefore(deadline) && now.isBefore(startTime); + // 退出疗休养按钮严格限定在“报名截止时间 < 当前时间 < 出行开始前N天”范围内。 + return now.isAfter(signupEnd) && now.isBefore(deadline); } private boolean isWithinSignupRange(String signupStartTime, String signupEndTime, LocalDateTime now) { @@ -1638,6 +1712,30 @@ public class TourSignupController { ledger.setUnionName(user == null ? ledger.getUnionName() : defaultIfBlank(user.getUnionName(), ledger.getUnionName())); } + private void fillLedgerFromAssignmentConfirmInfo(TourLedger ledger, String settingId) { + if (ledger == null || StrUtil.isBlank(settingId)) { + return; + } + NutMap assignment = tourUserAssignmentService.currentUserAssignmentInfo(settingId); + if (assignment == null || assignment.isEmpty()) { + return; + } + // 移动端信息确认页保存到人员分配表,最终报名时以该确认信息回写台账。 + ledger.setUserName(defaultIfBlank(assignment.getString("userName", ""), ledger.getUserName())); + ledger.setGender(defaultIfBlank(assignment.getString("gender", ""), ledger.getGender())); + Integer assignmentAge = assignment.getInt("age"); + if (assignmentAge != null) { + ledger.setAge(assignmentAge); + } + ledger.setIdCard(defaultIfBlank(assignment.getString("idCard", ""), ledger.getIdCard())); + ledger.setMobile(defaultIfBlank(assignment.getString("mobile", ""), ledger.getMobile())); + ledger.setBoardingPlace(defaultIfBlank(assignment.getString("boardingPlace", ""), ledger.getBoardingPlace())); + ledger.setUnitId(defaultIfBlank(assignment.getString("unitId", ""), ledger.getUnitId())); + ledger.setUnitName(defaultIfBlank(assignment.getString("unitName", ""), ledger.getUnitName())); + ledger.setUnionId(defaultIfBlank(assignment.getString("unionId", ""), ledger.getUnionId())); + ledger.setUnionName(defaultIfBlank(assignment.getString("unionName", ""), ledger.getUnionName())); + } + private void fillLedgerContactFallback(TourLedger ledger, NutMap staff) { if (ledger == null || staff == null) { return; diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/models/TourLedger.java b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/models/TourLedger.java index bd7f2597..34e63be8 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/models/TourLedger.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/models/TourLedger.java @@ -46,6 +46,11 @@ public class TourLedger extends BaseModel implements Serializable { @ColDefine(type = ColType.VARCHAR, width = 20) private String gender; + @Column + @Comment("年龄") + @ColDefine(type = ColType.INT, width = 3) + private Integer age; + @Column @Comment("身份证号") @ColDefine(type = ColType.VARCHAR, width = 30) diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/models/TourSetting.java b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/models/TourSetting.java index 289882ed..8bd064ee 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/models/TourSetting.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/models/TourSetting.java @@ -138,6 +138,17 @@ public class TourSetting extends BaseModel implements Serializable { @Default("1") private Boolean enabled; + @Column + @Comment("是否展示PC首页报名入口") + @ColDefine(type = ColType.BOOLEAN) + @Default("0") + private Boolean homeSignupEntryEnabled; + + @Column + @Comment("PC首页报名入口图片") + @ColDefine(type = ColType.VARCHAR, width = 500) + private String homeSignupEntryImage; + /** * 标段管理沿用老疗休养配置的子表设计,后续线路、旅行社等模块可通过标段ID继续关联。 */ diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/models/TourUserAssignment.java b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/models/TourUserAssignment.java index 465a5398..5074ef6e 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/models/TourUserAssignment.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/models/TourUserAssignment.java @@ -97,6 +97,16 @@ public class TourUserAssignment extends BaseModel implements Serializable { @ColDefine(type = ColType.VARCHAR, width = 20) private String gender; + @Column + @Comment("年龄") + @ColDefine(type = ColType.INT, width = 3) + private Integer age; + + @Column + @Comment("身份证号") + @ColDefine(type = ColType.VARCHAR, width = 30) + private String idCard; + @Column @Comment("手机号") @ColDefine(type = ColType.VARCHAR, width = 30) diff --git a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/service/TourUserAssignmentService.java b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/service/TourUserAssignmentService.java index cf2c9dc2..65efb16e 100644 --- a/src/main/java/com/budwk/app/zhgh/dayofficework/tour/service/TourUserAssignmentService.java +++ b/src/main/java/com/budwk/app/zhgh/dayofficework/tour/service/TourUserAssignmentService.java @@ -33,9 +33,10 @@ public interface TourUserAssignmentService extends BaseService schoolCandidatePage(PageForm pageForm, String settingId, String unionId, String keyword); + Pagination schoolCandidatePage(PageForm pageForm, String settingId, String unionId, String keyword, List userIds); /** * 分页查询当前登录人所在分工会的人员分配记录,列表只读取人员分配表,不读取报名台账。 @@ -57,9 +58,10 @@ public interface TourUserAssignmentService extends BaseService branchCandidatePage(PageForm pageForm, String settingId, String keyword); + Pagination branchCandidatePage(PageForm pageForm, String settingId, String keyword, List userIds); /** * 查询启用的疗休养配置选项,供人员分配列表筛选和分配弹窗复用。 @@ -77,6 +79,14 @@ public interface TourUserAssignmentService extends BaseService listMatterOptions(String settingId); + /** + * 查询指定配置下当前处于报名时间内的分配线路选项,供分工会人员选择线路时使用。 + * + * @param settingId 疗休养配置ID + * @return 当前报名时间内的事项选项列表 + */ + List listSignupOpenMatterOptions(String settingId); + /** * 查询分工会选项,供校工会分配候选人和列表筛选使用。 * @@ -148,6 +158,36 @@ public interface TourUserAssignmentService extends BaseService schoolCandidatePage(PageForm pageForm, String settingId, String unionId, String keyword) { + public Pagination schoolCandidatePage(PageForm pageForm, String settingId, String unionId, String keyword, List userIds) { TourSetting setting = fetchSettingForAssignment(settingId); if (setting == null || StrUtil.isBlank(setting.getActivityGroupId())) { return emptyPagination(pageForm); } Sql sql = buildCandidateSql(settingId); Cnd cnd = buildCandidateCnd(settingId, setting.getActivityGroupId(), unionId, keyword); + List normalizedUserIds = normalizeUserIds(userIds); + // 人员选择器多选查询时,候选范围仍受活动组、分工会、已分配排除规则约束,再按指定人员ID精确过滤。 + if (!Lang.isEmpty(normalizedUserIds)) { + cnd.and("u.id", "in", normalizedUserIds); + } cnd.asc("u.unionname"); cnd.asc("u.unitname"); cnd.asc("u.loginname"); @@ -108,12 +113,12 @@ public class TourUserAssignmentServiceImpl extends BaseServiceImpl branchCandidatePage(PageForm pageForm, String settingId, String keyword) { + public Pagination branchCandidatePage(PageForm pageForm, String settingId, String keyword, List userIds) { String unionId = SecurityUtil.getUnionId(); if (StrUtil.isBlank(unionId)) { return emptyPagination(pageForm); } - return schoolCandidatePage(pageForm, settingId, unionId, keyword); + return schoolCandidatePage(pageForm, settingId, unionId, keyword, userIds); } @Override @@ -134,6 +139,18 @@ public class TourUserAssignmentServiceImpl extends BaseServiceImpl listMatterOptions(String settingId) { + return listMatterOptions(settingId, false); + } + + @Override + public List listSignupOpenMatterOptions(String settingId) { + return listMatterOptions(settingId, true); + } + + /** + * 统一查询人员分配可选线路;分工会选择线路时只返回当前报名期内的事项。 + */ + private List listMatterOptions(String settingId, boolean onlySignupOpen) { if (StrUtil.isBlank(settingId)) { return Collections.emptyList(); } @@ -143,6 +160,8 @@ public class TourUserAssignmentServiceImpl extends BaseServiceImpl=", now); + } sql.setCondition(cnd); return listMap(sql); } @@ -336,6 +360,9 @@ public class TourUserAssignmentServiceImpl extends BaseServiceImpl users = Collections.singletonList(fetchAssignmentUserSnapshot(assignment)); checkProxySignupMaxGroupPeople(matterInfo, users); updateAssignmentMatterSnapshot(id, matterInfo, TourUserAssignment.ASSIGN_SOURCE_BRANCH_UNION); @@ -487,6 +514,103 @@ public class TourUserAssignmentServiceImpl extends BaseServiceImpl= 0; + } + private TourUserAssignment buildSchoolAssignment(String settingId, NutMap matterInfo, String personType, NutMap user) { return buildAssignment(settingId, matterInfo, personType, user, TourUserAssignment.ASSIGN_SOURCE_SCHOOL_UNION); } @@ -973,6 +1171,9 @@ public class TourUserAssignmentServiceImpl extends BaseServiceImpl= 14) { + String normalizedIdCard = idCard.trim(); + birthDate = LocalDate.parse(normalizedIdCard.substring(6, 14), DateTimeFormatter.ofPattern("yyyyMMdd")); + } else if (StrUtil.isNotBlank(birthday) && birthday.length() >= 10) { + birthDate = LocalDate.parse(birthday.substring(0, 10), DateTimeFormatter.ofPattern("yyyy-MM-dd")); + } + } catch (Exception e) { + birthDate = null; + } + if (birthDate == null) { + return null; + } + LocalDate today = LocalDate.now(); + int age = today.getYear() - birthDate.getYear(); + if (today.getMonthValue() < birthDate.getMonthValue() + || (today.getMonthValue() == birthDate.getMonthValue() && today.getDayOfMonth() < birthDate.getDayOfMonth())) { + age--; + } + return Math.max(age, 0); + } + + private boolean isValidIdCard(String idCard) { + return StrUtil.isNotBlank(idCard) + && idCard.matches("^[1-9]\\d{5}(18|19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[\\dX]$"); + } } diff --git a/src/main/resources/db/tour_ledger_add_age.sql b/src/main/resources/db/tour_ledger_add_age.sql new file mode 100644 index 00000000..f3913e0f --- /dev/null +++ b/src/main/resources/db/tour_ledger_add_age.sql @@ -0,0 +1,3 @@ +-- Add signup ledger age field for mobile confirmation snapshot. +ALTER TABLE `tour_ledger` + ADD COLUMN `age` int DEFAULT NULL COMMENT '年龄' AFTER `gender`; diff --git a/src/main/resources/db/tour_setting_add_home_signup_entry.sql b/src/main/resources/db/tour_setting_add_home_signup_entry.sql new file mode 100644 index 00000000..78385433 --- /dev/null +++ b/src/main/resources/db/tour_setting_add_home_signup_entry.sql @@ -0,0 +1,4 @@ +-- 疗休养配置增加PC首页报名浮动入口控制字段。 +ALTER TABLE `tour_setting` + ADD COLUMN `homeSignupEntryEnabled` tinyint(1) DEFAULT 0 COMMENT '是否展示PC首页报名入口' AFTER `enabled`, + ADD COLUMN `homeSignupEntryImage` varchar(500) DEFAULT NULL COMMENT 'PC首页报名入口图片' AFTER `homeSignupEntryEnabled`; diff --git a/src/main/resources/db/tour_setting_create.sql b/src/main/resources/db/tour_setting_create.sql index 05caa494..6a6daed4 100644 --- a/src/main/resources/db/tour_setting_create.sql +++ b/src/main/resources/db/tour_setting_create.sql @@ -23,6 +23,8 @@ CREATE TABLE IF NOT EXISTS `tour_setting` ( `allowFamily` tinyint(1) DEFAULT 0 COMMENT '是否允许携带家属', `fillBedInfo` tinyint(1) DEFAULT 1 COMMENT '是否填报床位信息', `enabled` tinyint(1) DEFAULT 1 COMMENT '是否启用', + `homeSignupEntryEnabled` tinyint(1) DEFAULT 0 COMMENT '是否展示PC首页报名入口', + `homeSignupEntryImage` varchar(500) DEFAULT NULL COMMENT 'PC首页报名入口图片', `serviceNotice` text COMMENT '服务须知', `createdBy` varchar(32) DEFAULT NULL COMMENT '创建人', `createdAt` bigint DEFAULT NULL COMMENT '创建时间', @@ -87,6 +89,8 @@ CREATE TABLE IF NOT EXISTS `tour_user_assignment` ( `loginName` varchar(50) DEFAULT NULL COMMENT '工号', `userName` varchar(100) DEFAULT NULL COMMENT '姓名', `gender` varchar(20) DEFAULT NULL COMMENT '性别', + `age` int DEFAULT NULL COMMENT '年龄', + `idCard` varchar(30) DEFAULT NULL COMMENT '身份证号', `mobile` varchar(30) DEFAULT NULL COMMENT '手机号', `unitId` varchar(32) DEFAULT NULL COMMENT '所在单位ID', `unitName` varchar(100) DEFAULT NULL COMMENT '所在单位', diff --git a/src/main/resources/db/tour_user_assignment_add_confirm_info.sql b/src/main/resources/db/tour_user_assignment_add_confirm_info.sql new file mode 100644 index 00000000..852793e7 --- /dev/null +++ b/src/main/resources/db/tour_user_assignment_add_confirm_info.sql @@ -0,0 +1,4 @@ +-- Add mobile H5 signup confirmation fields to tour user assignment. +ALTER TABLE `tour_user_assignment` + ADD COLUMN `age` int DEFAULT NULL COMMENT '年龄' AFTER `gender`, + ADD COLUMN `idCard` varchar(30) DEFAULT NULL COMMENT '身份证号' AFTER `age`; diff --git a/src/main/resources/db/tour_user_assignment_create.sql b/src/main/resources/db/tour_user_assignment_create.sql index 0beb81ff..e91cdc87 100644 --- a/src/main/resources/db/tour_user_assignment_create.sql +++ b/src/main/resources/db/tour_user_assignment_create.sql @@ -13,6 +13,8 @@ CREATE TABLE IF NOT EXISTS `tour_user_assignment` ( `loginName` varchar(50) DEFAULT NULL COMMENT '工号', `userName` varchar(100) DEFAULT NULL COMMENT '姓名', `gender` varchar(20) DEFAULT NULL COMMENT '性别', + `age` int DEFAULT NULL COMMENT '年龄', + `idCard` varchar(30) DEFAULT NULL COMMENT '身份证号', `mobile` varchar(30) DEFAULT NULL COMMENT '手机号', `unitId` varchar(32) DEFAULT NULL COMMENT '所在单位ID', `unitName` varchar(100) DEFAULT NULL COMMENT '所在单位', diff --git a/src/main/resources/static/assets/platform/images/tour/疗休养图片.jpeg b/src/main/resources/static/assets/platform/images/tour/疗休养图片.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..9df824ed5d33bc72d56fee91cf21c4c44a8ef4d2 GIT binary patch literal 22492 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
@@ -133,6 +168,16 @@ layout("/layouts/v4/baseLayout.html"){
+
+ +
{{ item.title }}
+
+ + + diff --git a/src/main/resources/views/platform/zhghh5/dayofficework/tour/signup/entry.html b/src/main/resources/views/platform/zhghh5/dayofficework/tour/signup/entry.html new file mode 100644 index 00000000..2c0c2b15 --- /dev/null +++ b/src/main/resources/views/platform/zhghh5/dayofficework/tour/signup/entry.html @@ -0,0 +1,42 @@ + + + + +
+ 加载中... +
+ + + + diff --git a/src/main/resources/views/platform/zhghh5/dayofficework/tour/signup/index.html b/src/main/resources/views/platform/zhghh5/dayofficework/tour/signup/index.html index 43194769..477d1132 100644 --- a/src/main/resources/views/platform/zhghh5/dayofficework/tour/signup/index.html +++ b/src/main/resources/views/platform/zhghh5/dayofficework/tour/signup/index.html @@ -6,83 +6,32 @@ layout("/layouts/platform_h5.html"){ .tour-signup-h5 { min-height: 100vh; background: #f5f7fb; - padding-bottom: 80px; + padding: 10px 10px 80px; box-sizing: border-box; } - .tour-signup-banner { - position: relative; - height: 193px; - overflow: hidden; - background: #0f74bc; - color: #ffffff; - } - - .tour-signup-swipe { - width: 100%; - height: 100%; - } - - .tour-signup-swipe img { - display: block; - width: 100%; - height: 193px; - object-fit: cover; - } - - .tour-signup-banner::after { - content: ""; - position: absolute; - inset: 0; - background: linear-gradient(90deg, rgba(5, 83, 143, 0.72) 0%, rgba(5, 83, 143, 0.32) 48%, rgba(5, 83, 143, 0.08) 100%); - pointer-events: none; - } - - .tour-signup-banner__text { - position: absolute; - left: 16px; - right: 16px; - bottom: 20px; - z-index: 1; - } - - .tour-signup-title { - font-size: 22px; - font-weight: 700; - line-height: 1.35; - } - - .tour-signup-subtitle { - margin-top: 8px; - color: rgba(255, 255, 255, 0.86); - font-size: 13px; - line-height: 1.5; - } - .tour-signup-card { - margin: 10px 12px 0; + display: flex; + flex-direction: column; + min-height: calc(100vh - 160px); + max-height: calc(100vh - 160px); + margin: 0; padding: 14px 14px 16px; border-radius: 8px; background: #ffffff; box-shadow: 0 8px 20px rgba(15, 23, 42, 0.08); + box-sizing: border-box; } .tour-signup-card__header { display: flex; align-items: center; - justify-content: space-between; + justify-content: flex-end; gap: 10px; padding-bottom: 12px; border-bottom: 1px solid #eef2f7; } - .tour-signup-card__title { - color: #111827; - font-size: 17px; - font-weight: 700; - line-height: 1.4; - } - .tour-signup-card__year { flex-shrink: 0; color: #0f74bc; @@ -91,13 +40,19 @@ layout("/layouts/platform_h5.html"){ } .tour-signup-notice { - margin-top: 14px; color: #334155; font-size: 15px; line-height: 1.8; word-break: break-word; } + .tour-signup-card__body { + flex: 1; + min-height: 0; + margin-top: 14px; + overflow-y: auto; + } + .tour-signup-notice /deep/ img, .tour-signup-notice /deep/ video { max-width: 100%; @@ -140,7 +95,10 @@ layout("/layouts/platform_h5.html"){ } .tour-signup-empty { - padding: 34px 0 26px; + min-height: 100%; + display: flex; + align-items: center; + justify-content: center; } .tour-signup-footer { @@ -156,48 +114,38 @@ layout("/layouts/platform_h5.html"){ @@ -306,7 +306,7 @@ layout("/layouts/platform_h5.html"){ } return this.canModifySignup() ? "修改报名" : "审核中" } - return "我要核对信息" + return "我要报名" }, detailActionType() { return this.canRevokeSignup() ? "danger" : "info" diff --git a/src/main/resources/views/platform/zhghh5/dayofficework/tour/signup/list.html b/src/main/resources/views/platform/zhghh5/dayofficework/tour/signup/list.html index e8ab274d..2e75e526 100644 --- a/src/main/resources/views/platform/zhghh5/dayofficework/tour/signup/list.html +++ b/src/main/resources/views/platform/zhghh5/dayofficework/tour/signup/list.html @@ -462,7 +462,7 @@ layout("/layouts/platform_h5.html"){
- +
@@ -668,6 +668,9 @@ layout("/layouts/platform_h5.html"){ } }, methods: { + goHome() { + window.location.replace("/platform/h5/home") + }, thumbUrl(row) { const thumb = this.resolveThumbPath(row && (row.lineMobileThumb || row.agencyMobileThumb)) if (!thumb) { @@ -817,7 +820,7 @@ layout("/layouts/platform_h5.html"){ } vant.Dialog.confirm({ title: "提示", - message: "是否确认退出本次疗休养,退出后将取消报名资格!", + message: "是否确认退出本次疗休,退出后将取消报名资格!", confirmButtonColor: "#ee2f2f" }).then(() => { this.$axios.post("/platform/tour/signup/h5/doLeaveTour", { assignmentId: row.assignmentId }).then((res) => { @@ -837,7 +840,7 @@ layout("/layouts/platform_h5.html"){ } vant.Dialog.confirm({ title: "提示", - message: "是否确认取消本线路?", + message: "是否确认取消本线路报名?", confirmButtonColor: "#ee2f2f" }).then(() => { this.$axios.post("/platform/tour/signup/h5/doCancelLine", { ledgerId: row.ledgerId }).then((res) => { diff --git a/src/main/resources/views/platform/zhghh5/sys/home/home.js b/src/main/resources/views/platform/zhghh5/sys/home/home.js index 8d757f57..4efec188 100644 --- a/src/main/resources/views/platform/zhghh5/sys/home/home.js +++ b/src/main/resources/views/platform/zhghh5/sys/home/home.js @@ -73,7 +73,7 @@ const home = {
-
+
政策文件
更多 @@ -120,6 +120,7 @@ const home = { return { activityOptions: [], quickEntries: [], + disabledHomeModules: ["劳模先进", "普惠信息", "我的课堂", "政策文件"], classroomCourses: [], classroomCourseIndex: 0, classroomCourseTimer: null, @@ -374,16 +375,40 @@ const home = { }, 3000) }, menuClick(item) { + if (this.isHomeModuleDisabled(item && item.name)) { + this.showDisabledHomeModuleNotice() + return + } this.$pjaxReplace(item.href) }, featureClick(item) { + if (this.isHomeModuleDisabled(item && item.name)) { + this.showDisabledHomeModuleNotice() + return + } if (item.href) { this.$pjaxReplace(item.href) } }, enterClassroom() { + if (this.isHomeModuleDisabled("我的课堂")) { + this.showDisabledHomeModuleNotice() + return + } this.$pjaxReplace("/platform/learning/course/h5") }, + enterPolicy() { + if (this.isHomeModuleDisabled("政策文件")) { + this.showDisabledHomeModuleNotice() + } + }, + isHomeModuleDisabled(moduleName) { + // 首页临时停用模块统一在点击入口拦截,保留原模块展示和后台数据配置。 + return moduleName && this.disabledHomeModules.indexOf(moduleName) !== -1 + }, + showDisabledHomeModuleNotice() { + this.$toast("暂未启用") + }, bannerChange(index) { this.activeBannerIndex = index },