南大三十年教职工疗休养

This commit is contained in:
2026-06-25 20:54:58 +08:00
parent f39dad393f
commit a1547b133e
16 changed files with 328 additions and 62 deletions
@@ -16,6 +16,7 @@ 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.AuthUtil;
import com.budwk.app.web.commons.auth.utils.SecurityUtil; import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.activity.basic.models.ActivityUserScope; import com.budwk.app.zhgh.activity.basic.models.ActivityUserScope;
import com.budwk.app.zhgh.dayofficework.thirtyTeachTour.models.ThirtyTeachTourSetting;
import com.budwk.app.zhgh.dayofficework.tour.models.TourSetting; import com.budwk.app.zhgh.dayofficework.tour.models.TourSetting;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import org.jsoup.Jsoup; import org.jsoup.Jsoup;
@@ -310,7 +311,30 @@ public class SysHomeController {
.addv("year", setting.getYear()) .addv("year", setting.getYear())
.addv("title", setting.getYear() + "暑期疗休养") .addv("title", setting.getYear() + "暑期疗休养")
.addv("imageUrl", imageUrl) .addv("imageUrl", imageUrl)
.addv("href", "/platform/tour/signup")); .addv("href", "/platform/tour/signup")
.addv("vx", 0.028)
.addv("vy", 0.022));
}
Cnd thirtyTeachCnd = Cnd.where(ThirtyTeachTourSetting::getDelFlag, "=", false)
.and(ThirtyTeachTourSetting::getEnabled, "=", true)
.and(ThirtyTeachTourSetting::getYear, "=", currentYear)
.and(ThirtyTeachTourSetting::getHomeSignupEntryEnabled, "=", true);
thirtyTeachCnd.asc(ThirtyTeachTourSetting::getSortNo);
thirtyTeachCnd.desc(ThirtyTeachTourSetting::getUpdatedAt);
thirtyTeachCnd.desc(ThirtyTeachTourSetting::getCreatedAt);
List<ThirtyTeachTourSetting> thirtyTeachSettings = dao.query(ThirtyTeachTourSetting.class, thirtyTeachCnd);
for (ThirtyTeachTourSetting setting : thirtyTeachSettings) {
// 30年教龄疗休养与暑期疗休养共用首页默认图,但跳转地址和浮动方向保持独立。
String imageUrl = StrUtil.blankToDefault(setting.getHomeSignupEntryImage(), Globals.AppDomain + "/assets/platform/images/thirtyTeachTour/疗休养logo.png");
rows.add(NutMap.NEW()
.addv("id", "thirtyTeachTour-" + setting.getId())
.addv("year", setting.getYear())
.addv("title", setting.getYear() + "30年教龄疗休养")
.addv("imageUrl", imageUrl)
.addv("href", "/platform/thirtyTeachTour/signup")
.addv("startCorner", "rightTop")
.addv("vx", -0.026)
.addv("vy", 0.024));
} }
return Result.success(rows); return Result.success(rows);
} }
@@ -50,10 +50,10 @@ public class ThirtyTeachTourBranchUserAssignmentController {
*/ */
@At @At
@SaCheckPermission("thirtyTeachTour.branchUserAssignment") @SaCheckPermission("thirtyTeachTour.branchUserAssignment")
public Result pageData(PageForm pageForm, Integer year, String settingId, String matterId, public Result pageData(PageForm pageForm, Integer year, String settingId, String matterId, String periodId,
String personType, String keyword) { String personType, String keyword) {
return Result.success(tourUserAssignmentService.branchAssignmentPage(pageForm, year, settingId, matterId, return Result.success(tourUserAssignmentService.branchAssignmentPage(pageForm, year, settingId, matterId,
personType, keyword)); periodId, personType, keyword));
} }
/** /**
@@ -62,10 +62,10 @@ public class ThirtyTeachTourBranchUserAssignmentController {
@At @At
@SaCheckPermission("thirtyTeachTour.branchUserAssignment") @SaCheckPermission("thirtyTeachTour.branchUserAssignment")
@Ok("void") @Ok("void")
public void exportData(PageForm pageForm, Integer year, String settingId, String matterId, public void exportData(PageForm pageForm, Integer year, String settingId, String matterId, String periodId,
String personType, String keyword, HttpServletResponse response) { String personType, String keyword, HttpServletResponse response) {
List<NutMap> rows = tourUserAssignmentService.branchAssignmentExportRows(pageForm, year, settingId, matterId, List<NutMap> rows = tourUserAssignmentService.branchAssignmentExportRows(pageForm, year, settingId, matterId,
personType, keyword); periodId, personType, keyword);
ExportParams exportParams = new ExportParams(); ExportParams exportParams = new ExportParams();
exportParams.setSheetName("分工会人员分配"); exportParams.setSheetName("分工会人员分配");
exportParams.setType(ExcelType.XSSF); exportParams.setType(ExcelType.XSSF);
@@ -113,7 +113,7 @@ public class ThirtyTeachTourBranchUserAssignmentController {
* 查询当前配置下已启用的出行时间段,供人员分配弹窗按钮组使用。 * 查询当前配置下已启用的出行时间段,供人员分配弹窗按钮组使用。
*/ */
@At @At
@SaCheckPermission("thirtyTeachTour.branchUserAssignment.assign") @SaCheckPermission("thirtyTeachTour.branchUserAssignment")
public Result travelPeriodOptions(String settingId) { public Result travelPeriodOptions(String settingId) {
return Result.success(tourUserAssignmentService.listBranchEnabledTravelPeriods(settingId)); return Result.success(tourUserAssignmentService.listBranchEnabledTravelPeriods(settingId));
} }
@@ -367,6 +367,7 @@ public class ThirtyTeachTourLedgerController {
entities.add(new ExcelExportEntity("携带家属", "familyCountText", 14)); entities.add(new ExcelExportEntity("携带家属", "familyCountText", 14));
entities.add(new ExcelExportEntity("是否参加", "joinedText", 14)); entities.add(new ExcelExportEntity("是否参加", "joinedText", 14));
entities.add(new ExcelExportEntity("报销超出费用", "overCostReimbursedText", 18)); entities.add(new ExcelExportEntity("报销超出费用", "overCostReimbursedText", 18));
entities.add(new ExcelExportEntity("图片材料", "photoFiles", 36));
List<NutMap> rows = queryLedgerExportRows(startYear, endYear, keyword, unionId, lineId, travelPeriod, List<NutMap> rows = queryLedgerExportRows(startYear, endYear, keyword, unionId, lineId, travelPeriod,
lineType, directFamilyOnly, overCostOnly); lineType, directFamilyOnly, overCostOnly);
@@ -406,6 +407,7 @@ public class ThirtyTeachTourLedgerController {
END AS travelPeriod, END AS travelPeriod,
t.lineType, t.lineType,
t.signupTime, t.signupTime,
t.photoFiles,
IFNULL(f.familyCount, 0) AS familyCount, IFNULL(f.familyCount, 0) AS familyCount,
t.joined, t.joined,
t.overCostReimbursed t.overCostReimbursed
@@ -51,10 +51,10 @@ public class ThirtyTeachTourSchoolUserAssignmentController {
*/ */
@At @At
@SaCheckPermission("thirtyTeachTour.schoolUserAssignment") @SaCheckPermission("thirtyTeachTour.schoolUserAssignment")
public Result pageData(PageForm pageForm, Integer year, String settingId, String matterId, String unionId, public Result pageData(PageForm pageForm, Integer year, String settingId, String matterId, String periodId,
String personType, String keyword, String assignSource, Boolean cancelled) { String unionId, String personType, String keyword, String assignSource, Boolean cancelled) {
return Result.success(tourUserAssignmentService.schoolAssignmentPage(pageForm, year, settingId, matterId, return Result.success(tourUserAssignmentService.schoolAssignmentPage(pageForm, year, settingId, matterId,
unionId, personType, keyword, assignSource, cancelled)); periodId, unionId, personType, keyword, assignSource, cancelled));
} }
/** /**
@@ -63,10 +63,11 @@ public class ThirtyTeachTourSchoolUserAssignmentController {
@At @At
@SaCheckPermission("thirtyTeachTour.schoolUserAssignment") @SaCheckPermission("thirtyTeachTour.schoolUserAssignment")
@Ok("void") @Ok("void")
public void exportData(PageForm pageForm, Integer year, String settingId, String matterId, String unionId, public void exportData(PageForm pageForm, Integer year, String settingId, String matterId, String periodId,
String personType, String keyword, String assignSource, Boolean cancelled, HttpServletResponse response) { String unionId, String personType, String keyword, String assignSource, Boolean cancelled,
HttpServletResponse response) {
List<NutMap> rows = tourUserAssignmentService.schoolAssignmentExportRows(pageForm, year, settingId, matterId, List<NutMap> rows = tourUserAssignmentService.schoolAssignmentExportRows(pageForm, year, settingId, matterId,
unionId, personType, keyword, assignSource, cancelled); periodId, unionId, personType, keyword, assignSource, cancelled);
ExportParams exportParams = new ExportParams(); ExportParams exportParams = new ExportParams();
exportParams.setSheetName("校工会人员分配"); exportParams.setSheetName("校工会人员分配");
exportParams.setType(ExcelType.XSSF); exportParams.setType(ExcelType.XSSF);
@@ -82,14 +83,15 @@ public class ThirtyTeachTourSchoolUserAssignmentController {
@Aop(TransAop.READ_COMMITTED) @Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("thirtyTeachTour.schoolUserAssignment") @SaCheckPermission("thirtyTeachTour.schoolUserAssignment")
@SLog(type = "tour", tag = "疗休养校工会人员分配", msg = "发送校工会人员分配提醒") @SLog(type = "tour", tag = "疗休养校工会人员分配", msg = "发送校工会人员分配提醒")
public Result sendReminder(PageForm pageForm, Integer year, String settingId, String matterId, String unionId, public Result sendReminder(PageForm pageForm, Integer year, String settingId, String matterId, String periodId,
String personType, String keyword, String assignSource, Boolean cancelled, String content) { String unionId, String personType, String keyword, String assignSource,
Boolean cancelled, String content) {
if (StrUtil.isBlank(content)) { if (StrUtil.isBlank(content)) {
return Result.error("请输入提醒内容"); return Result.error("请输入提醒内容");
} }
try { try {
return Result.success(tourUserAssignmentService.sendSchoolAssignmentReminder(pageForm, year, settingId, return Result.success(tourUserAssignmentService.sendSchoolAssignmentReminder(pageForm, year, settingId,
matterId, unionId, personType, keyword, assignSource, cancelled, content)); matterId, periodId, unionId, personType, keyword, assignSource, cancelled, content));
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
return Result.error(e.getMessage()); return Result.error(e.getMessage());
} }
@@ -124,6 +126,15 @@ public class ThirtyTeachTourSchoolUserAssignmentController {
return Result.success(tourUserAssignmentService.listMatterOptions(settingId)); return Result.success(tourUserAssignmentService.listMatterOptions(settingId));
} }
/**
* 查询当前配置下已启用的出行时间段,供校工会人员分配列表筛选使用。
*/
@At
@SaCheckPermission("thirtyTeachTour.schoolUserAssignment")
public Result travelPeriodOptions(String settingId) {
return Result.success(tourUserAssignmentService.listBranchEnabledTravelPeriods(settingId));
}
/** /**
* 查询分工会选项,供筛选和候选人员过滤使用。 * 查询分工会选项,供筛选和候选人员过滤使用。
*/ */
@@ -155,7 +155,7 @@ public class ThirtyTeachTourSettingController {
if (StrUtil.isNotBlank(quotaLimitMessage)) { if (StrUtil.isNotBlank(quotaLimitMessage)) {
return Result.error(quotaLimitMessage); return Result.error(quotaLimitMessage);
} }
String periodQuotaLimitMessage = tourSettingService.checkPeriodQuotaLimit(periodQuotas, unionQuotas); String periodQuotaLimitMessage = tourSettingService.checkPeriodQuotaLimit(tourSetting.getId(), periodQuotas, unionQuotas);
if (StrUtil.isNotBlank(periodQuotaLimitMessage)) { if (StrUtil.isNotBlank(periodQuotaLimitMessage)) {
return Result.error(periodQuotaLimitMessage); return Result.error(periodQuotaLimitMessage);
} }
@@ -1129,6 +1129,11 @@ public class ThirtyTeachTourSignupController {
if (scopeResult != null) { if (scopeResult != null) {
return scopeResult; return scopeResult;
} }
String periodPermissionMessage = tourUserAssignmentService.checkSignupAssignmentPeriodPermission(
setting.getId(), matter.getTravelStartTime(), matter.getTravelEndTime());
if (StrUtil.isNotBlank(periodPermissionMessage)) {
return Result.error(periodPermissionMessage);
}
CycleAllowedTimes cycleAllowedTimes = calculateCycleAllowedTimes(matter, setting, oldLedger); CycleAllowedTimes cycleAllowedTimes = calculateCycleAllowedTimes(matter, setting, oldLedger);
if (!cycleAllowedTimes.canApply()) { if (!cycleAllowedTimes.canApply()) {
return Result.error(buildCycleAllowedTimesNotice(cycleAllowedTimes)); return Result.error(buildCycleAllowedTimesNotice(cycleAllowedTimes));
@@ -63,4 +63,9 @@ public class ThirtyTeachTourSettingUnionQuota extends BaseModel implements Seria
* 页面展示用实时会员数,不落库。 * 页面展示用实时会员数,不落库。
*/ */
private Integer memberCount; private Integer memberCount;
/**
* Page-only count of formal users already assigned by school union for this branch union.
*/
private Integer schoolFormalAssignedCount;
} }
@@ -76,11 +76,12 @@ public interface ThirtyTeachTourSettingService extends BaseService<ThirtyTeachTo
/** /**
* 校验时间段名额合计是否超过对应分工会正式名额。 * 校验时间段名额合计是否超过对应分工会正式名额。
* *
* @param settingId 疗休养配置ID,用于统计校工会已占用的分工会正式名额
* @param periodQuotas 时间段名额 JSON * @param periodQuotas 时间段名额 JSON
* @param unionQuotas 分工会名额 JSON * @param unionQuotas 分工会名额 JSON
* @return 通过返回空字符串;不通过返回业务提示 * @return 通过返回空字符串;不通过返回业务提示
*/ */
String checkPeriodQuotaLimit(String periodQuotas, String unionQuotas); String checkPeriodQuotaLimit(String settingId, String periodQuotas, String unionQuotas);
/** /**
* 清理指定疗休养配置下的分工会名额。 * 清理指定疗休养配置下的分工会名额。
@@ -25,8 +25,8 @@ public interface ThirtyTeachTourUserAssignmentService extends BaseService<Thirty
* @return 校工会分配记录分页数据 * @return 校工会分配记录分页数据
*/ */
Pagination<NutMap> schoolAssignmentPage(PageForm pageForm, Integer year, String settingId, String matterId, Pagination<NutMap> schoolAssignmentPage(PageForm pageForm, Integer year, String settingId, String matterId,
String unionId, String personType, String keyword, String assignSource, String periodId, String unionId, String personType, String keyword,
Boolean cancelled); String assignSource, Boolean cancelled);
/** /**
* 按校工会人员分配页面当前筛选条件查询导出数据,不做分页截断。 * 按校工会人员分配页面当前筛选条件查询导出数据,不做分页截断。
@@ -43,8 +43,8 @@ public interface ThirtyTeachTourUserAssignmentService extends BaseService<Thirty
* @return 符合当前页面查询条件的人员分配导出数据 * @return 符合当前页面查询条件的人员分配导出数据
*/ */
List<NutMap> schoolAssignmentExportRows(PageForm pageForm, Integer year, String settingId, String matterId, List<NutMap> schoolAssignmentExportRows(PageForm pageForm, Integer year, String settingId, String matterId,
String unionId, String personType, String keyword, String assignSource, String periodId, String unionId, String personType, String keyword,
Boolean cancelled); String assignSource, Boolean cancelled);
/** /**
* 按校工会人员分配页面当前筛选条件发送报名提醒。 * 按校工会人员分配页面当前筛选条件发送报名提醒。
@@ -62,8 +62,8 @@ public interface ThirtyTeachTourUserAssignmentService extends BaseService<Thirty
* @return 发送结果,receiverCount 表示本次去重后的接收人数 * @return 发送结果,receiverCount 表示本次去重后的接收人数
*/ */
NutMap sendSchoolAssignmentReminder(PageForm pageForm, Integer year, String settingId, String matterId, NutMap sendSchoolAssignmentReminder(PageForm pageForm, Integer year, String settingId, String matterId,
String unionId, String personType, String keyword, String assignSource, String periodId, String unionId, String personType, String keyword,
Boolean cancelled, String content); String assignSource, Boolean cancelled, String content);
/** /**
* 分页查询校工会可分配候选人,候选人来自疗休养配置的可参加人员范围,并排除同一配置下已分配人员。 * 分页查询校工会可分配候选人,候选人来自疗休养配置的可参加人员范围,并排除同一配置下已分配人员。
@@ -92,7 +92,7 @@ public interface ThirtyTeachTourUserAssignmentService extends BaseService<Thirty
* @return 分工会分配记录分页数据 * @return 分工会分配记录分页数据
*/ */
Pagination<NutMap> branchAssignmentPage(PageForm pageForm, Integer year, String settingId, String matterId, Pagination<NutMap> branchAssignmentPage(PageForm pageForm, Integer year, String settingId, String matterId,
String personType, String keyword); String periodId, String personType, String keyword);
/** /**
* 按分工会人员分配页面当前筛选条件查询导出数据,不做分页截断。 * 按分工会人员分配页面当前筛选条件查询导出数据,不做分页截断。
@@ -106,7 +106,7 @@ public interface ThirtyTeachTourUserAssignmentService extends BaseService<Thirty
* @return 当前登录人所在分工会下符合页面查询条件的人员分配导出数据 * @return 当前登录人所在分工会下符合页面查询条件的人员分配导出数据
*/ */
List<NutMap> branchAssignmentExportRows(PageForm pageForm, Integer year, String settingId, String matterId, List<NutMap> branchAssignmentExportRows(PageForm pageForm, Integer year, String settingId, String matterId,
String personType, String keyword); String periodId, String personType, String keyword);
/** /**
* 分页查询当前登录人所在分工会可分配候选人,候选人来自疗休养配置的可参加人员范围。 * 分页查询当前登录人所在分工会可分配候选人,候选人来自疗休养配置的可参加人员范围。
@@ -263,6 +263,17 @@ public interface ThirtyTeachTourUserAssignmentService extends BaseService<Thirty
*/ */
NutMap currentUserAssignmentInfo(String settingId); NutMap currentUserAssignmentInfo(String settingId);
/**
* 校验当前报名人员的分配时间段是否允许报名当前线路。
* 人员分配记录中未维护 periodId、periodName 时不限制;维护后必须与线路出行起止日期完全一致。
*
* @param settingId 30年教龄疗休养配置ID
* @param matterTravelStartTime 线路出行开始时间
* @param matterTravelEndTime 线路出行结束时间
* @return 为空表示允许报名;不为空时返回前端提示文案
*/
String checkSignupAssignmentPeriodPermission(String settingId, String matterTravelStartTime, String matterTravelEndTime);
/** /**
* 用户报名写入台账后,回填该用户在同一疗休养配置下已存在的人员分配记录。 * 用户报名写入台账后,回填该用户在同一疗休养配置下已存在的人员分配记录。
* 仅更新事项、线路、旅行社快照字段,不新增记录,不修改 assignSource 和人员类型。 * 仅更新事项、线路、旅行社快照字段,不新增记录,不修改 assignSource 和人员类型。
@@ -7,6 +7,7 @@ import com.budwk.app.zhgh.dayofficework.thirtyTeachTour.models.ThirtyTeachTourSe
import com.budwk.app.zhgh.dayofficework.thirtyTeachTour.models.ThirtyTeachTourSettingPeriodQuota; import com.budwk.app.zhgh.dayofficework.thirtyTeachTour.models.ThirtyTeachTourSettingPeriodQuota;
import com.budwk.app.zhgh.dayofficework.thirtyTeachTour.models.ThirtyTeachTourSettingTravelPeriod; import com.budwk.app.zhgh.dayofficework.thirtyTeachTour.models.ThirtyTeachTourSettingTravelPeriod;
import com.budwk.app.zhgh.dayofficework.thirtyTeachTour.models.ThirtyTeachTourSettingUnionQuota; import com.budwk.app.zhgh.dayofficework.thirtyTeachTour.models.ThirtyTeachTourSettingUnionQuota;
import com.budwk.app.zhgh.dayofficework.thirtyTeachTour.models.ThirtyTeachTourUserAssignment;
import com.budwk.app.zhgh.dayofficework.thirtyTeachTour.service.ThirtyTeachTourSettingService; import com.budwk.app.zhgh.dayofficework.thirtyTeachTour.service.ThirtyTeachTourSettingService;
import org.nutz.dao.Cnd; import org.nutz.dao.Cnd;
import org.nutz.dao.Dao; import org.nutz.dao.Dao;
@@ -47,7 +48,8 @@ public class ThirtyTeachTourSettingServiceImpl extends BaseServiceImpl<ThirtyTea
un.name AS unionName, un.name AS unionName,
COALESCE(quota.formalQuota, 0) AS formalQuota, COALESCE(quota.formalQuota, 0) AS formalQuota,
COALESCE(quota.backupQuota, 0) AS backupQuota, COALESCE(quota.backupQuota, 0) AS backupQuota,
COALESCE(user_count.memberCount, 0) AS memberCount COALESCE(user_count.memberCount, 0) AS memberCount,
COALESCE(school_assigned.schoolFormalAssignedCount, 0) AS schoolFormalAssignedCount
FROM sys_union un FROM sys_union un
LEFT JOIN thirty_teach_tour_setting_union_quota quota LEFT JOIN thirty_teach_tour_setting_union_quota quota
ON quota.unionId = un.id ON quota.unionId = un.id
@@ -70,12 +72,21 @@ public class ThirtyTeachTourSettingServiceImpl extends BaseServiceImpl<ThirtyTea
SELECT 1 SELECT 1
FROM tour_ledger summer_ledger FROM tour_ledger summer_ledger
WHERE summer_ledger.delFlag = 0 WHERE summer_ledger.delFlag = 0
AND summer_ledger.joined = 1
AND summer_ledger.`year` = @year AND summer_ledger.`year` = @year
AND summer_ledger.jobNo = u.loginname AND summer_ledger.jobNo = u.loginname
) )
GROUP BY u.unionid GROUP BY u.unionid
) user_count ON user_count.unionId = un.id ) user_count ON user_count.unionId = un.id
LEFT JOIN (
SELECT assignment.unionId AS unionId, COUNT(1) AS schoolFormalAssignedCount
FROM thirty_teach_tour_user_assignment assignment
WHERE assignment.settingId = @quotaSettingId
AND assignment.delFlag = 0
AND assignment.assignSource = 'SCHOOL_UNION'
AND assignment.personType = 'FORMAL'
AND (assignment.cancelled IS NULL OR assignment.cancelled = 0)
GROUP BY assignment.unionId
) school_assigned ON school_assigned.unionId = un.id
ORDER BY un.unionCode ASC ORDER BY un.unionCode ASC
"""); """);
sql.setParam("quotaSettingId", quotaSettingId); sql.setParam("quotaSettingId", quotaSettingId);
@@ -92,6 +103,7 @@ public class ThirtyTeachTourSettingServiceImpl extends BaseServiceImpl<ThirtyTea
quota.setFormalQuota(defaultInt(row.getInt("formalQuota"))); quota.setFormalQuota(defaultInt(row.getInt("formalQuota")));
quota.setBackupQuota(defaultInt(row.getInt("backupQuota"))); quota.setBackupQuota(defaultInt(row.getInt("backupQuota")));
quota.setMemberCount(defaultInt(row.getInt("memberCount"))); quota.setMemberCount(defaultInt(row.getInt("memberCount")));
quota.setSchoolFormalAssignedCount(defaultInt(row.getInt("schoolFormalAssignedCount")));
return quota; return quota;
}).collect(Collectors.toList()); }).collect(Collectors.toList());
} }
@@ -242,7 +254,7 @@ public class ThirtyTeachTourSettingServiceImpl extends BaseServiceImpl<ThirtyTea
} }
@Override @Override
public String checkPeriodQuotaLimit(String periodQuotas, String unionQuotas) { public String checkPeriodQuotaLimit(String settingId, String periodQuotas, String unionQuotas) {
if (StrUtil.isBlank(periodQuotas)) { if (StrUtil.isBlank(periodQuotas)) {
return ""; return "";
} }
@@ -260,18 +272,47 @@ public class ThirtyTeachTourSettingServiceImpl extends BaseServiceImpl<ThirtyTea
.filter(item -> item != null && StrUtil.isNotBlank(item.getUnionId())) .filter(item -> item != null && StrUtil.isNotBlank(item.getUnionId()))
.collect(Collectors.groupingBy(ThirtyTeachTourSettingPeriodQuota::getUnionId, .collect(Collectors.groupingBy(ThirtyTeachTourSettingPeriodQuota::getUnionId,
Collectors.summingInt(item -> defaultInt(item.getFormalQuota())))); Collectors.summingInt(item -> defaultInt(item.getFormalQuota()))));
Map<String, Integer> schoolAssignedCountMap = countSchoolFormalAssignedByUnion(settingId);
for (Map.Entry<String, Integer> entry : periodQuotaTotalMap.entrySet()) { for (Map.Entry<String, Integer> entry : periodQuotaTotalMap.entrySet()) {
ThirtyTeachTourSettingUnionQuota unionQuota = unionQuotaMap.get(entry.getKey()); ThirtyTeachTourSettingUnionQuota unionQuota = unionQuotaMap.get(entry.getKey());
int formalQuota = unionQuota == null ? 0 : defaultInt(unionQuota.getFormalQuota()); int formalQuota = unionQuota == null ? 0 : defaultInt(unionQuota.getFormalQuota());
if (entry.getValue() > formalQuota) { int schoolAssignedCount = schoolAssignedCountMap.getOrDefault(entry.getKey(), 0);
// Time-period quota can only use branch-union quota that has not been occupied by school-union assignment.
int availableFormalQuota = Math.max(formalQuota - schoolAssignedCount, 0);
if (entry.getValue() > availableFormalQuota) {
String unionName = unionQuota == null ? "" : unionQuota.getUnionName(); String unionName = unionQuota == null ? "" : unionQuota.getUnionName();
return "分工会【" + StrUtil.blankToDefault(unionName, entry.getKey()) + "】时间段名额合计 " return "分工会【" + StrUtil.blankToDefault(unionName, entry.getKey()) + "】时间段名额合计 "
+ entry.getValue() + ",不能大于当前分工会正式名额 " + formalQuota; + entry.getValue() + ",不能大于扣除校工会已分配后的可分配正式名额 " + availableFormalQuota;
} }
} }
return ""; return "";
} }
/**
* Count active formal users assigned by school union, grouped by branch union, for quota validation.
*/
private Map<String, Integer> countSchoolFormalAssignedByUnion(String settingId) {
if (StrUtil.isBlank(settingId)) {
return Collections.emptyMap();
}
Sql sql = Sqls.create("""
SELECT unionId, COUNT(1) AS assignedCount
FROM thirty_teach_tour_user_assignment
WHERE settingId = @settingId
AND delFlag = 0
AND assignSource = @assignSource
AND personType = @personType
AND (cancelled IS NULL OR cancelled = 0)
GROUP BY unionId
""");
sql.setParam("settingId", settingId);
sql.setParam("assignSource", ThirtyTeachTourUserAssignment.ASSIGN_SOURCE_SCHOOL_UNION);
sql.setParam("personType", ThirtyTeachTourUserAssignment.PERSON_TYPE_FORMAL);
return listMap(sql).stream()
.filter(row -> StrUtil.isNotBlank(row.getString("unionId")))
.collect(Collectors.toMap(row -> row.getString("unionId"), row -> defaultInt(row.getInt("assignedCount")), (a, b) -> a));
}
@Override @Override
public void clearUnionQuotas(String settingId) { public void clearUnionQuotas(String settingId) {
if (StrUtil.isNotBlank(settingId)) { if (StrUtil.isNotBlank(settingId)) {
@@ -51,8 +51,8 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
@Override @Override
public Pagination<NutMap> schoolAssignmentPage(PageForm pageForm, Integer year, String settingId, String matterId, public Pagination<NutMap> schoolAssignmentPage(PageForm pageForm, Integer year, String settingId, String matterId,
String unionId, String personType, String keyword, String assignSource, String periodId, String unionId, String personType, String keyword,
Boolean cancelled) { String assignSource, Boolean cancelled) {
Sql sql = Sqls.create(""" Sql sql = Sqls.create("""
SELECT SELECT
a.*, a.*,
@@ -75,6 +75,8 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
cnd.andEX("s.`year`", "=", year); cnd.andEX("s.`year`", "=", year);
cnd.andEX("a.settingId", "=", settingId); cnd.andEX("a.settingId", "=", settingId);
cnd.andEX("a.matterId", "=", matterId); cnd.andEX("a.matterId", "=", matterId);
// 校工会人员分配列表支持按配置中的出行时间段精确筛选。
cnd.andEX("a.periodId", "=", periodId);
cnd.andEX("a.unionId", "=", unionId); cnd.andEX("a.unionId", "=", unionId);
cnd.andEX("a.personType", "=", personType); cnd.andEX("a.personType", "=", personType);
appendCancelledCondition(cnd, cancelled); appendCancelledCondition(cnd, cancelled);
@@ -91,21 +93,23 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
@Override @Override
public List<NutMap> schoolAssignmentExportRows(PageForm pageForm, Integer year, String settingId, String matterId, public List<NutMap> schoolAssignmentExportRows(PageForm pageForm, Integer year, String settingId, String matterId,
String unionId, String personType, String keyword, String assignSource, String periodId, String unionId, String personType, String keyword,
Boolean cancelled) { String assignSource, Boolean cancelled) {
List<NutMap> rows = assignmentExportRows(pageForm, year, settingId, matterId, unionId, personType, keyword, assignSource, cancelled); List<NutMap> rows = assignmentExportRows(pageForm, year, settingId, matterId, unionId, personType, keyword,
assignSource, cancelled, periodId);
fillAssignmentExportText(rows); fillAssignmentExportText(rows);
return rows; return rows;
} }
@Override @Override
public NutMap sendSchoolAssignmentReminder(PageForm pageForm, Integer year, String settingId, String matterId, public NutMap sendSchoolAssignmentReminder(PageForm pageForm, Integer year, String settingId, String matterId,
String unionId, String personType, String keyword, String assignSource, String periodId, String unionId, String personType, String keyword,
Boolean cancelled, String content) { String assignSource, Boolean cancelled, String content) {
if (StrUtil.isBlank(content)) { if (StrUtil.isBlank(content)) {
throw new IllegalArgumentException("请输入提醒内容"); throw new IllegalArgumentException("请输入提醒内容");
} }
List<NutMap> rows = assignmentExportRows(pageForm, year, settingId, matterId, unionId, personType, keyword, assignSource, cancelled); List<NutMap> rows = assignmentExportRows(pageForm, year, settingId, matterId, unionId, personType, keyword,
assignSource, cancelled, periodId);
List<String> receiverIds = rows.stream() List<String> receiverIds = rows.stream()
.map(row -> row.getString("userId")) .map(row -> row.getString("userId"))
.filter(StrUtil::isNotBlank) .filter(StrUtil::isNotBlank)
@@ -144,24 +148,24 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
@Override @Override
public Pagination<NutMap> branchAssignmentPage(PageForm pageForm, Integer year, String settingId, String matterId, public Pagination<NutMap> branchAssignmentPage(PageForm pageForm, Integer year, String settingId, String matterId,
String personType, String keyword) { String periodId, String personType, String keyword) {
String unionId = SecurityUtil.getUnionId(); String unionId = SecurityUtil.getUnionId();
if (StrUtil.isBlank(unionId)) { if (StrUtil.isBlank(unionId)) {
return emptyPagination(pageForm); return emptyPagination(pageForm);
} }
return assignmentPage(pageForm, year, settingId, matterId, unionId, personType, keyword, return assignmentPage(pageForm, year, settingId, matterId, periodId, unionId, personType, keyword,
ThirtyTeachTourUserAssignment.ASSIGN_SOURCE_BRANCH_UNION); ThirtyTeachTourUserAssignment.ASSIGN_SOURCE_BRANCH_UNION);
} }
@Override @Override
public List<NutMap> branchAssignmentExportRows(PageForm pageForm, Integer year, String settingId, String matterId, public List<NutMap> branchAssignmentExportRows(PageForm pageForm, Integer year, String settingId, String matterId,
String personType, String keyword) { String periodId, String personType, String keyword) {
String unionId = SecurityUtil.getUnionId(); String unionId = SecurityUtil.getUnionId();
if (StrUtil.isBlank(unionId)) { if (StrUtil.isBlank(unionId)) {
return Collections.emptyList(); return Collections.emptyList();
} }
List<NutMap> rows = assignmentExportRows(pageForm, year, settingId, matterId, unionId, personType, keyword, List<NutMap> rows = assignmentExportRows(pageForm, year, settingId, matterId, unionId, personType, keyword,
ThirtyTeachTourUserAssignment.ASSIGN_SOURCE_BRANCH_UNION, null); ThirtyTeachTourUserAssignment.ASSIGN_SOURCE_BRANCH_UNION, null, periodId);
fillAssignmentExportText(rows); fillAssignmentExportText(rows);
return rows; return rows;
} }
@@ -178,9 +182,15 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
/** /**
* 构建人员分配导出查询,筛选条件与页面分页列表保持一致,但导出时不追加分页。 * 构建人员分配导出查询,筛选条件与页面分页列表保持一致,但导出时不追加分页。
*/ */
private List<NutMap> assignmentExportRows(PageForm pageForm, Integer year, String settingId, String matterId,
String unionId, String personType, String keyword, String assignSource,
Boolean cancelled) {
return assignmentExportRows(pageForm, year, settingId, matterId, unionId, personType, keyword, assignSource, cancelled, null);
}
private List<NutMap> assignmentExportRows(PageForm pageForm, Integer year, String settingId, String matterId, private List<NutMap> assignmentExportRows(PageForm pageForm, Integer year, String settingId, String matterId,
String unionId, String personType, String keyword, String assignSource, String unionId, String personType, String keyword, String assignSource,
Boolean cancelled) { Boolean cancelled, String periodId) {
Sql sql = Sqls.create(""" Sql sql = Sqls.create("""
SELECT SELECT
a.*, a.*,
@@ -204,6 +214,7 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
cnd.andEX("a.matterId", "=", matterId); cnd.andEX("a.matterId", "=", matterId);
cnd.andEX("a.unionId", "=", unionId); cnd.andEX("a.unionId", "=", unionId);
cnd.andEX("a.personType", "=", personType); cnd.andEX("a.personType", "=", personType);
cnd.andEX("a.periodId", "=", periodId);
appendCancelledCondition(cnd, cancelled); appendCancelledCondition(cnd, cancelled);
if (StrUtil.isNotBlank(keyword)) { if (StrUtil.isNotBlank(keyword)) {
SqlExpressionGroup group = new SqlExpressionGroup(); SqlExpressionGroup group = new SqlExpressionGroup();
@@ -875,6 +886,8 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
unionName, unionName,
boardingPlace, boardingPlace,
photoFiles, photoFiles,
periodId,
periodName,
personType, personType,
assignSource, assignSource,
IFNULL(cancelled, 0) AS cancelled IFNULL(cancelled, 0) AS cancelled
@@ -893,6 +906,61 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
return map == null ? NutMap.NEW() : map; return map == null ? NutMap.NEW() : map;
} }
/**
* 校验当前报名人员的分配时间段是否允许报名当前线路。
* 只有人员分配记录维护了 periodId 或 periodName 时才限制线路,未维护时沿用原报名规则。
*/
@Override
public String checkSignupAssignmentPeriodPermission(String settingId, String matterTravelStartTime, String matterTravelEndTime) {
if (StrUtil.isBlank(settingId)) {
return "";
}
NutMap assignment = currentUserAssignmentInfo(settingId);
if (assignment == null || assignment.isEmpty()) {
return "";
}
String periodId = assignment.getString("periodId", "");
String periodName = assignment.getString("periodName", "");
if (StrUtil.isBlank(periodId) && StrUtil.isBlank(periodName)) {
return "";
}
ThirtyTeachTourSettingTravelPeriod period = fetchAssignmentTravelPeriod(settingId, periodId, periodName);
if (period == null || !sameTravelDate(period.getStartDate(), matterTravelStartTime)
|| !sameTravelDate(period.getEndDate(), matterTravelEndTime)) {
return "没有该路线报名权限";
}
return "";
}
/**
* 优先按 periodId 获取人员分配时间段;历史数据只有 periodName 时再按名称兜底匹配。
*/
private ThirtyTeachTourSettingTravelPeriod fetchAssignmentTravelPeriod(String settingId, String periodId, String periodName) {
Cnd cnd = Cnd.where("settingId", "=", settingId)
.and("enabled", "=", true)
.and("delFlag", "=", false);
if (StrUtil.isNotBlank(periodId)) {
cnd.and("id", "=", periodId);
} else {
cnd.and("periodName", "=", periodName);
}
return dao().fetch(ThirtyTeachTourSettingTravelPeriod.class, cnd);
}
/**
* 事项时间可能带时分秒,时间段配置只有日期;统一截取日期部分后比较。
*/
private boolean sameTravelDate(String periodDate, String matterDateTime) {
if (StrUtil.isBlank(periodDate) || StrUtil.isBlank(matterDateTime)) {
return false;
}
String normalizedMatterDate = matterDateTime.trim();
if (normalizedMatterDate.length() >= 10) {
normalizedMatterDate = normalizedMatterDate.substring(0, 10);
}
return periodDate.trim().equals(normalizedMatterDate);
}
@Override @Override
public int backfillExistingAssignmentAfterSignup(String settingId, String matterId, String userId, String boardingPlace, String photoFiles) { public int backfillExistingAssignmentAfterSignup(String settingId, String matterId, String userId, String boardingPlace, String photoFiles) {
if (StrUtil.isBlank(settingId) || StrUtil.isBlank(matterId) || StrUtil.isBlank(userId)) { if (StrUtil.isBlank(settingId) || StrUtil.isBlank(matterId) || StrUtil.isBlank(userId)) {
@@ -1113,7 +1181,12 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
} }
private Pagination<NutMap> assignmentPage(PageForm pageForm, Integer year, String settingId, String matterId, private Pagination<NutMap> assignmentPage(PageForm pageForm, Integer year, String settingId, String matterId,
String unionId, String personType, String keyword, String assignSource) { String unionId, String personType, String keyword, String assignSource) {
return assignmentPage(pageForm, year, settingId, matterId, null, unionId, personType, keyword, assignSource);
}
private Pagination<NutMap> assignmentPage(PageForm pageForm, Integer year, String settingId, String matterId,
String periodId, String unionId, String personType, String keyword, String assignSource) {
Sql sql = Sqls.create(""" Sql sql = Sqls.create("""
SELECT SELECT
a.*, a.*,
@@ -1135,6 +1208,7 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
cnd.andEX("s.`year`", "=", year); cnd.andEX("s.`year`", "=", year);
cnd.andEX("a.settingId", "=", settingId); cnd.andEX("a.settingId", "=", settingId);
cnd.andEX("a.matterId", "=", matterId); cnd.andEX("a.matterId", "=", matterId);
cnd.andEX("a.periodId", "=", periodId);
cnd.andEX("a.unionId", "=", unionId); cnd.andEX("a.unionId", "=", unionId);
cnd.andEX("a.personType", "=", personType); cnd.andEX("a.personType", "=", personType);
if (StrUtil.isNotBlank(keyword)) { if (StrUtil.isNotBlank(keyword)) {
@@ -1179,7 +1253,6 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
SELECT jobNo, COUNT(1) AS joinedCount SELECT jobNo, COUNT(1) AS joinedCount
FROM tour_ledger FROM tour_ledger
WHERE delFlag = 0 WHERE delFlag = 0
AND joined = 1
AND `year` = @currentYear AND `year` = @currentYear
GROUP BY jobNo GROUP BY jobNo
) summer_joined ON summer_joined.jobNo = u.loginname ) summer_joined ON summer_joined.jobNo = u.loginname
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.5 MiB

@@ -238,13 +238,16 @@ layout("/layouts/v4/baseLayout.html"){
const bounds = this.getTourHomeFloatBounds(width, height) const bounds = this.getTourHomeFloatBounds(width, height)
this.tourHomeFloatEntries = (rows || []).map((item, index) => { this.tourHomeFloatEntries = (rows || []).map((item, index) => {
const offset = index * 28 const offset = index * 28
const backendVx = Number(item.vx)
const backendVy = Number(item.vy)
const startRight = item.startCorner === "rightTop"
return Object.assign({}, item, { return Object.assign({}, item, {
width: width, width: width,
height: height, height: height,
x: Math.min(bounds.minX + offset, bounds.maxX), x: startRight ? Math.max(bounds.maxX - offset, bounds.minX) : Math.min(bounds.minX + offset, bounds.maxX),
y: Math.min(bounds.minY + offset, bounds.maxY), y: Math.min(bounds.minY + offset, bounds.maxY),
vx: (index % 2 === 0 ? 1 : -1) * (0.028 + index * 0.004), vx: Number.isFinite(backendVx) && backendVx !== 0 ? backendVx : (index % 2 === 0 ? 1 : -1) * (0.028 + index * 0.004),
vy: (index % 3 === 0 ? 1 : -1) * (0.022 + index * 0.003) vy: Number.isFinite(backendVy) && backendVy !== 0 ? backendVy : (index % 3 === 0 ? 1 : -1) * (0.022 + index * 0.003)
}) })
}) })
if (this.tourHomeFloatEntries.length > 0) { if (this.tourHomeFloatEntries.length > 0) {
@@ -26,6 +26,11 @@ layout("/layouts/platform.html"){
<el-option v-for="item in pageMatterOptions" :key="item.matterId" :label="matterOptionLabel(item)" :value="item.matterId"></el-option> <el-option v-for="item in pageMatterOptions" :key="item.matterId" :label="matterOptionLabel(item)" :value="item.matterId"></el-option>
</el-select> </el-select>
</search-item> </search-item>
<search-item label="出行时间段">
<el-select v-model="pageForm.periodId" clearable filterable placeholder="请选择出行时间段" style="width: 100%">
<el-option v-for="item in pagePeriodOptions" :key="item.id" :label="item.label || item.periodName" :value="item.id"></el-option>
</el-select>
</search-item>
<search-item label="人员类型"> <search-item label="人员类型">
<el-select v-model="pageForm.personType" clearable placeholder="请选择人员类型" style="width: 100%"> <el-select v-model="pageForm.personType" clearable placeholder="请选择人员类型" style="width: 100%">
<el-option label="正式人员" value="FORMAL"></el-option> <el-option label="正式人员" value="FORMAL"></el-option>
@@ -69,7 +74,7 @@ layout("/layouts/platform.html"){
<el-table-column label="所在单位" prop="unitName" min-width="180" align="center" header-align="center" show-overflow-tooltip></el-table-column> <el-table-column label="所在单位" prop="unitName" min-width="180" align="center" header-align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="线路" prop="lineName" min-width="200" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column> <el-table-column label="线路" prop="lineName" min-width="200" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="出行时间" prop="travelPeriod" min-width="180" align="center" header-align="center" show-overflow-tooltip></el-table-column> <el-table-column label="出行时间" prop="travelPeriod" min-width="180" align="center" header-align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="出行时" prop="travelPeriod" min-width="180" align="center" header-align="center" show-overflow-tooltip></el-table-column> <el-table-column label="出行时" prop="periodName" min-width="180" align="center" header-align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="乘车地点" prop="boardingPlace" min-width="140" align="center" header-align="center" show-overflow-tooltip></el-table-column> <el-table-column label="乘车地点" prop="boardingPlace" min-width="140" align="center" header-align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="已上传图片数量" width="140" align="center" header-align="center"> <el-table-column label="已上传图片数量" width="140" align="center" header-align="center">
<template slot-scope="{row}"> <template slot-scope="{row}">
@@ -426,6 +431,7 @@ layout("/layouts/platform.html"){
return { return {
settingOptions: [], settingOptions: [],
pageMatterOptions: [], pageMatterOptions: [],
pagePeriodOptions: [],
assignSettingOptions: [], assignSettingOptions: [],
assignMatterOptions: [], assignMatterOptions: [],
assignPeriodOptions: [], assignPeriodOptions: [],
@@ -473,6 +479,7 @@ layout("/layouts/platform.html"){
year: currentYear, year: currentYear,
settingId: "", settingId: "",
matterId: "", matterId: "",
periodId: "",
personType: "", personType: "",
keyword: "" keyword: ""
}, },
@@ -548,6 +555,7 @@ layout("/layouts/platform.html"){
this.pageForm.year = currentYear this.pageForm.year = currentYear
this.pageForm.settingId = "" this.pageForm.settingId = ""
this.pageForm.matterId = "" this.pageForm.matterId = ""
this.pageForm.periodId = ""
this.pageForm.personType = "" this.pageForm.personType = ""
this.pageForm.keyword = "" this.pageForm.keyword = ""
this.loadSettingOptions() this.loadSettingOptions()
@@ -555,24 +563,38 @@ layout("/layouts/platform.html"){
pageYearChange() { pageYearChange() {
this.pageForm.settingId = "" this.pageForm.settingId = ""
this.pageForm.matterId = "" this.pageForm.matterId = ""
this.pageForm.periodId = ""
this.pageMatterOptions = [] this.pageMatterOptions = []
this.pagePeriodOptions = []
this.loadSettingOptions() this.loadSettingOptions()
}, },
pageSettingChange() { pageSettingChange() {
this.pageForm.matterId = "" this.pageForm.matterId = ""
this.pageForm.periodId = ""
this.loadPageMatterOptions() this.loadPageMatterOptions()
this.loadPageTravelPeriodOptions()
}, },
loadSettingOptions() { loadSettingOptions() {
this.$axios.post(loc() + "/settingOptions", {year: this.pageForm.year}).then((res) => { this.$axios.post(loc() + "/settingOptions", {year: this.pageForm.year}).then((res) => {
if (res.code === 0) { if (res.code === 0) {
this.settingOptions = res.data || [] this.settingOptions = res.data || []
const hasCurrentSetting = this.settingOptions.some(item => item.id === this.pageForm.settingId)
if (this.pageForm.settingId && !hasCurrentSetting) {
this.pageForm.settingId = ""
this.pageForm.matterId = ""
this.pageForm.periodId = ""
}
// 主页面默认选择当前年度第一条配置,避免进入页面后查询范围为空。 // 主页面默认选择当前年度第一条配置,避免进入页面后查询范围为空。
if (!this.pageForm.settingId && this.settingOptions.length > 0) { if (!this.pageForm.settingId && this.settingOptions.length > 0) {
this.pageForm.settingId = this.settingOptions[0].id this.pageForm.settingId = this.settingOptions[0].id
}
if (this.pageForm.settingId) {
this.loadPageMatterOptions() this.loadPageMatterOptions()
this.loadPageTravelPeriodOptions()
this.doSearch() this.doSearch()
} else { } else {
this.pageMatterOptions = [] this.pageMatterOptions = []
this.pagePeriodOptions = []
this.doSearch() this.doSearch()
} }
} }
@@ -589,9 +611,28 @@ layout("/layouts/platform.html"){
} }
}) })
}, },
loadPageTravelPeriodOptions() {
if (!this.pageForm.settingId) {
this.pagePeriodOptions = []
this.pageForm.periodId = ""
return
}
this.$axios.post(loc() + "/travelPeriodOptions", {settingId: this.pageForm.settingId}).then((res) => {
if (res.code === 0) {
this.pagePeriodOptions = res.data || []
const hasCurrentPeriod = this.pagePeriodOptions.some(item => item.id === this.pageForm.periodId)
if (this.pageForm.periodId && !hasCurrentPeriod) {
this.pageForm.periodId = ""
}
} else {
this.pagePeriodOptions = []
this.pageForm.periodId = ""
}
})
},
exportData() { exportData() {
const params = new URLSearchParams() const params = new URLSearchParams()
;["year", "settingId", "matterId", "personType", "keyword", "pageOrderName", "pageOrderBy"].forEach(key => { ;["year", "settingId", "matterId", "periodId", "personType", "keyword", "pageOrderName", "pageOrderBy"].forEach(key => {
const value = this.pageForm[key] const value = this.pageForm[key]
if (value !== undefined && value !== null && value !== "") { if (value !== undefined && value !== null && value !== "") {
params.append(key, value) params.append(key, value)
@@ -26,6 +26,11 @@ layout("/layouts/platform.html"){
<el-option v-for="item in pageMatterOptions" :key="item.matterId" :label="matterOptionLabel(item)" :value="item.matterId"></el-option> <el-option v-for="item in pageMatterOptions" :key="item.matterId" :label="matterOptionLabel(item)" :value="item.matterId"></el-option>
</el-select> </el-select>
</search-item> </search-item>
<search-item label="出行时间段">
<el-select v-model="pageForm.periodId" clearable filterable placeholder="请选择出行时间段" style="width: 100%">
<el-option v-for="item in pagePeriodOptions" :key="item.id" :label="item.label || item.periodName" :value="item.id"></el-option>
</el-select>
</search-item>
<search-item label="所属分工会"> <search-item label="所属分工会">
<el-select v-model="pageForm.unionId" clearable filterable placeholder="请选择分工会" style="width: 100%"> <el-select v-model="pageForm.unionId" clearable filterable placeholder="请选择分工会" style="width: 100%">
<el-option v-for="item in unionOptions" :key="item.id" :label="item.name" :value="item.id"></el-option> <el-option v-for="item in unionOptions" :key="item.id" :label="item.name" :value="item.id"></el-option>
@@ -87,6 +92,7 @@ layout("/layouts/platform.html"){
<el-table-column label="所在单位" prop="unitName" min-width="180" align="center" header-align="center" show-overflow-tooltip></el-table-column> <el-table-column label="所在单位" prop="unitName" min-width="180" align="center" header-align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="线路" prop="lineName" min-width="200" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column> <el-table-column label="线路" prop="lineName" min-width="200" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="出行时间" prop="travelPeriod" min-width="180" align="center" header-align="center" show-overflow-tooltip></el-table-column> <el-table-column label="出行时间" prop="travelPeriod" min-width="180" align="center" header-align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="出行时段" prop="periodName" min-width="180" align="center" header-align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="乘车地点" prop="boardingPlace" min-width="140" align="center" header-align="center" show-overflow-tooltip></el-table-column> <el-table-column label="乘车地点" prop="boardingPlace" min-width="140" align="center" header-align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="已上传图片数量" width="140" align="center" header-align="center"> <el-table-column label="已上传图片数量" width="140" align="center" header-align="center">
<template slot-scope="{row}"> <template slot-scope="{row}">
@@ -195,7 +201,7 @@ layout("/layouts/platform.html"){
<el-select v-model="candidateForm.unionId" clearable filterable placeholder="所属分工会" style="width: 220px" @change="candidateSearch"> <el-select v-model="candidateForm.unionId" clearable filterable placeholder="所属分工会" style="width: 220px" @change="candidateSearch">
<el-option v-for="item in unionOptions" :key="item.id" :label="item.name" :value="item.id"></el-option> <el-option v-for="item in unionOptions" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select> </el-select>
<el-select v-model="candidateForm.summerJoined" clearable placeholder="今年是否已参加暑期疗休养" style="width: 230px" @change="candidateSearch"> <el-select v-model="candidateForm.summerJoined" clearable placeholder="今年是否已暑期疗休养台账" style="width: 230px" @change="candidateSearch">
<el-option label="否" value="false"></el-option> <el-option label="否" value="false"></el-option>
<el-option label="是" value="true"></el-option> <el-option label="是" value="true"></el-option>
</el-select> </el-select>
@@ -424,6 +430,7 @@ layout("/layouts/platform.html"){
return { return {
settingOptions: [], settingOptions: [],
pageMatterOptions: [], pageMatterOptions: [],
pagePeriodOptions: [],
assignSourceOptions: [ assignSourceOptions: [
{label: "校工会分配", value: "SCHOOL_UNION"}, {label: "校工会分配", value: "SCHOOL_UNION"},
{label: "分工会分配", value: "BRANCH_UNION"} {label: "分工会分配", value: "BRANCH_UNION"}
@@ -468,6 +475,7 @@ layout("/layouts/platform.html"){
year: currentYear, year: currentYear,
settingId: "", settingId: "",
matterId: "", matterId: "",
periodId: "",
unionId: "", unionId: "",
personType: "", personType: "",
assignSource: "SCHOOL_UNION", assignSource: "SCHOOL_UNION",
@@ -548,6 +556,7 @@ layout("/layouts/platform.html"){
this.pageForm.year = currentYear this.pageForm.year = currentYear
this.pageForm.settingId = "" this.pageForm.settingId = ""
this.pageForm.matterId = "" this.pageForm.matterId = ""
this.pageForm.periodId = ""
this.pageForm.unionId = "" this.pageForm.unionId = ""
this.pageForm.personType = "" this.pageForm.personType = ""
// 页面打开和重置后默认查看校工会分配,清空该筛选项时后端会查询全部来源。 // 页面打开和重置后默认查看校工会分配,清空该筛选项时后端会查询全部来源。
@@ -559,24 +568,38 @@ layout("/layouts/platform.html"){
pageYearChange() { pageYearChange() {
this.pageForm.settingId = "" this.pageForm.settingId = ""
this.pageForm.matterId = "" this.pageForm.matterId = ""
this.pageForm.periodId = ""
this.pageMatterOptions = [] this.pageMatterOptions = []
this.pagePeriodOptions = []
this.loadSettingOptions() this.loadSettingOptions()
}, },
pageSettingChange() { pageSettingChange() {
this.pageForm.matterId = "" this.pageForm.matterId = ""
this.pageForm.periodId = ""
this.loadPageMatterOptions() this.loadPageMatterOptions()
this.loadPageTravelPeriodOptions()
}, },
loadSettingOptions() { loadSettingOptions() {
this.$axios.post(loc() + "/settingOptions", {year: this.pageForm.year}).then((res) => { this.$axios.post(loc() + "/settingOptions", {year: this.pageForm.year}).then((res) => {
if (res.code === 0) { if (res.code === 0) {
this.settingOptions = res.data || [] this.settingOptions = res.data || []
const hasCurrentSetting = this.settingOptions.some(item => item.id === this.pageForm.settingId)
if (this.pageForm.settingId && !hasCurrentSetting) {
this.pageForm.settingId = ""
this.pageForm.matterId = ""
this.pageForm.periodId = ""
}
// 主页面查询条件默认选择当前年度第一条配置,并以该配置刷新列表。 // 主页面查询条件默认选择当前年度第一条配置,并以该配置刷新列表。
if (!this.pageForm.settingId && this.settingOptions.length > 0) { if (!this.pageForm.settingId && this.settingOptions.length > 0) {
this.pageForm.settingId = this.settingOptions[0].id this.pageForm.settingId = this.settingOptions[0].id
}
if (this.pageForm.settingId) {
this.loadPageMatterOptions() this.loadPageMatterOptions()
this.loadPageTravelPeriodOptions()
this.doSearch() this.doSearch()
} else { } else {
this.pageMatterOptions = [] this.pageMatterOptions = []
this.pagePeriodOptions = []
this.doSearch() this.doSearch()
} }
} }
@@ -593,6 +616,25 @@ layout("/layouts/platform.html"){
} }
}) })
}, },
loadPageTravelPeriodOptions() {
if (!this.pageForm.settingId) {
this.pagePeriodOptions = []
this.pageForm.periodId = ""
return
}
this.$axios.post(loc() + "/travelPeriodOptions", {settingId: this.pageForm.settingId}).then((res) => {
if (res.code === 0) {
this.pagePeriodOptions = res.data || []
const hasCurrentPeriod = this.pagePeriodOptions.some(item => item.id === this.pageForm.periodId)
if (this.pageForm.periodId && !hasCurrentPeriod) {
this.pageForm.periodId = ""
}
} else {
this.pagePeriodOptions = []
this.pageForm.periodId = ""
}
})
},
loadUnionOptions() { loadUnionOptions() {
this.$axios.post(loc() + "/unionOptions").then((res) => { this.$axios.post(loc() + "/unionOptions").then((res) => {
if (res.code === 0) { if (res.code === 0) {
@@ -617,7 +659,7 @@ layout("/layouts/platform.html"){
}, },
exportData() { exportData() {
const params = new URLSearchParams() const params = new URLSearchParams()
;["year", "settingId", "matterId", "unionId", "personType", "assignSource", "cancelled", "keyword", "pageOrderName", "pageOrderBy"].forEach(key => { ;["year", "settingId", "matterId", "periodId", "unionId", "personType", "assignSource", "cancelled", "keyword", "pageOrderName", "pageOrderBy"].forEach(key => {
const value = this.pageForm[key] const value = this.pageForm[key]
if (value !== undefined && value !== null && value !== "") { if (value !== undefined && value !== null && value !== "") {
params.append(key, value) params.append(key, value)
@@ -627,7 +669,7 @@ layout("/layouts/platform.html"){
}, },
buildCurrentQueryParams() { buildCurrentQueryParams() {
const params = {} const params = {}
;["year", "settingId", "matterId", "unionId", "personType", "assignSource", "cancelled", "keyword", "pageOrderName", "pageOrderBy"].forEach(key => { ;["year", "settingId", "matterId", "periodId", "unionId", "personType", "assignSource", "cancelled", "keyword", "pageOrderName", "pageOrderBy"].forEach(key => {
const value = this.pageForm[key] const value = this.pageForm[key]
if (value !== undefined && value !== null && value !== "") { if (value !== undefined && value !== null && value !== "") {
params[key] = value params[key] = value
@@ -491,8 +491,8 @@ layout("/layouts/platform.html"){
</el-table-column> </el-table-column>
<el-table-column label="已分配合计" width="150" align="center" header-align="center"> <el-table-column label="已分配合计" width="150" align="center" header-align="center">
<template slot-scope="{row}"> <template slot-scope="{row}">
<span :class="periodQuotaRowTotal(row.unionId) > toNonNegativeInteger(row.formalQuota) ? 'text-danger' : 'text-success'"> <span :class="periodQuotaRowTotal(row.unionId) > periodQuotaAvailable(row.unionId) ? 'text-danger' : 'text-success'">
{{ periodQuotaRowTotal(row.unionId) }} / {{ toNonNegativeInteger(row.formalQuota) }} {{ periodQuotaRowTotal(row.unionId) }} / {{ periodQuotaAvailable(row.unionId) }}
</span> </span>
</template> </template>
</el-table-column> </el-table-column>
@@ -742,7 +742,8 @@ layout("/layouts/platform.html"){
return Object.assign({}, item, { return Object.assign({}, item, {
formalQuota: this.toNonNegativeInteger(item.formalQuota), formalQuota: this.toNonNegativeInteger(item.formalQuota),
backupQuota: this.toNonNegativeInteger(item.backupQuota), backupQuota: this.toNonNegativeInteger(item.backupQuota),
memberCount: this.toNonNegativeInteger(item.memberCount) memberCount: this.toNonNegativeInteger(item.memberCount),
schoolFormalAssignedCount: this.toNonNegativeInteger(item.schoolFormalAssignedCount)
}) })
}) })
}, },
@@ -908,10 +909,16 @@ layout("/layouts/platform.html"){
return sum + this.toNonNegativeInteger(item.formalQuota) return sum + this.toNonNegativeInteger(item.formalQuota)
}, 0) }, 0)
}, },
periodQuotaRemaining(unionId) { periodQuotaAvailable(unionId) {
const unionRow = (this.formData.unionQuotas || []).find((item) => item.unionId === unionId) const unionRow = (this.formData.unionQuotas || []).find((item) => item.unionId === unionId)
const formalQuota = unionRow ? this.toNonNegativeInteger(unionRow.formalQuota) : 0 const formalQuota = unionRow ? this.toNonNegativeInteger(unionRow.formalQuota) : 0
const remaining = formalQuota - this.periodQuotaRowTotal(unionId) const schoolAssignedCount = unionRow ? this.toNonNegativeInteger(unionRow.schoolFormalAssignedCount) : 0
// Time-period quota can only distribute the branch quota left after school-union assignment.
const availableQuota = formalQuota - schoolAssignedCount
return availableQuota > 0 ? availableQuota : 0
},
periodQuotaRemaining(unionId) {
const remaining = this.periodQuotaAvailable(unionId) - this.periodQuotaRowTotal(unionId)
return remaining > 0 ? remaining : 0 return remaining > 0 ? remaining : 0
}, },
buildPeriodQuotasForSubmit() { buildPeriodQuotasForSubmit() {
@@ -1448,10 +1455,10 @@ layout("/layouts/platform.html"){
const rows = this.formData.unionQuotas || [] const rows = this.formData.unionQuotas || []
for (let i = 0; i < rows.length; i++) { for (let i = 0; i < rows.length; i++) {
const row = rows[i] const row = rows[i]
const formalQuota = this.toNonNegativeInteger(row.formalQuota) const formalQuota = this.periodQuotaAvailable(row.unionId)
const usedQuota = this.periodQuotaRowTotal(row.unionId) const usedQuota = this.periodQuotaRowTotal(row.unionId)
if (usedQuota > formalQuota) { if (usedQuota > formalQuota) {
this.$message.warning("分工会【" + row.unionName + "】时间段名额合计 " + usedQuota + ",不能大于正式名额 " + formalQuota) this.$message.warning("分工会【" + row.unionName + "】时间段名额合计 " + usedQuota + ",不能大于扣除校工会已分配后的可分配正式名额 " + formalQuota)
return false return false
} }
} }