南大三十年教职工疗休养
This commit is contained in:
+77
-4
@@ -109,6 +109,24 @@ public class ThirtyTeachTourBranchUserAssignmentController {
|
||||
return Result.success(tourUserAssignmentService.branchQuotaInfo(settingId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询当前配置下已启用的出行时间段,供人员分配弹窗按钮组使用。
|
||||
*/
|
||||
@At
|
||||
@SaCheckPermission("thirtyTeachTour.branchUserAssignment.assign")
|
||||
public Result travelPeriodOptions(String settingId) {
|
||||
return Result.success(tourUserAssignmentService.listBranchEnabledTravelPeriods(settingId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询当前分工会在指定出行时间段内的正式名额,正式人员分配按该口径控制。
|
||||
*/
|
||||
@At
|
||||
@SaCheckPermission("thirtyTeachTour.branchUserAssignment.assign")
|
||||
public Result periodQuotaInfo(String settingId, String periodId) {
|
||||
return Result.success(tourUserAssignmentService.branchPeriodQuotaInfo(settingId, periodId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存分工会人员分配,保存时数据来源固定为 BRANCH_UNION。
|
||||
*/
|
||||
@@ -116,7 +134,22 @@ public class ThirtyTeachTourBranchUserAssignmentController {
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
@SaCheckPermission("thirtyTeachTour.branchUserAssignment.assign")
|
||||
@SLog(type = "tour", tag = "疗休养分工会人员分配", msg = "保存分工会人员分配")
|
||||
public Result doAssign(String settingId, String matterId, String personType, @Param("userIds") String userIds) {
|
||||
public Result doAssign(String settingId, String matterId, String periodId, String personType,
|
||||
@Param("userIds") String userIds, String photoFiles,
|
||||
@Param("assignItems") String assignItems) {
|
||||
if (StrUtil.isNotBlank(assignItems)) {
|
||||
List<NutMap> parsedItems;
|
||||
try {
|
||||
parsedItems = parseAssignItems(assignItems);
|
||||
} catch (Exception e) {
|
||||
return Result.error("人员分配明细参数错误");
|
||||
}
|
||||
try {
|
||||
return Result.success(tourUserAssignmentService.assignBranchUsers(settingId, matterId, periodId, personType, parsedItems));
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
List<String> parsedUserIds;
|
||||
try {
|
||||
parsedUserIds = parseUserIds(userIds);
|
||||
@@ -124,7 +157,8 @@ public class ThirtyTeachTourBranchUserAssignmentController {
|
||||
return Result.error("人员参数错误");
|
||||
}
|
||||
try {
|
||||
return Result.success(tourUserAssignmentService.assignBranchUsers(settingId, matterId, personType, parsedUserIds));
|
||||
return Result.success(tourUserAssignmentService.assignBranchUsers(settingId, matterId, periodId, personType,
|
||||
buildAssignItems(parsedUserIds, photoFiles)));
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
@@ -152,9 +186,25 @@ public class ThirtyTeachTourBranchUserAssignmentController {
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
@SaCheckPermission("thirtyTeachTour.branchUserAssignment.selectMatter")
|
||||
@SLog(type = "tour", tag = "疗休养分工会人员分配", msg = "选择分工会分配事项")
|
||||
public Result selectMatter(String id, String matterId) {
|
||||
public Result selectMatter(String id, String matterId, String photoFiles) {
|
||||
try {
|
||||
return Result.success(tourUserAssignmentService.selectCurrentBranchAssignmentMatter(id, matterId));
|
||||
return Result.success(tourUserAssignmentService.selectCurrentBranchAssignmentMatter(id, matterId, photoFiles));
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新当前分工会人员分配记录的图片材料,已生成台账时同步回写台账图片。
|
||||
*/
|
||||
@At
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
@SaCheckPermission("thirtyTeachTour.branchUserAssignment.assign")
|
||||
@SLog(type = "tour", tag = "疗休养分工会人员分配", msg = "上传分工会人员分配图片")
|
||||
public Result updatePhotoFiles(String id, String photoFiles) {
|
||||
try {
|
||||
tourUserAssignmentService.updateCurrentBranchAssignmentPhotoFiles(id, photoFiles);
|
||||
return Result.success();
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
@@ -215,6 +265,29 @@ public class ThirtyTeachTourBranchUserAssignmentController {
|
||||
return Lang.isEmpty(list) ? Collections.emptyList() : list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析前端批量人员分配明细,支持每个人携带独立图片材料。
|
||||
*/
|
||||
private List<NutMap> parseAssignItems(String assignItems) {
|
||||
if (StrUtil.isBlank(assignItems)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<NutMap> list = Json.fromJsonAsList(NutMap.class, assignItems);
|
||||
return Lang.isEmpty(list) ? Collections.emptyList() : list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 兼容旧的 userIds 入参,把公共图片材料转换成按人员明细的统一 service 入参。
|
||||
*/
|
||||
private List<NutMap> buildAssignItems(List<String> userIds, String photoFiles) {
|
||||
if (Lang.isEmpty(userIds)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<NutMap> list = new ArrayList<>();
|
||||
userIds.forEach(userId -> list.add(NutMap.NEW().addv("userId", userId).addv("photoFiles", photoFiles)));
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 定义人员分配导出字段,保持与页面列表核心字段一致并补充人员基础信息。
|
||||
*/
|
||||
|
||||
+2
-1
@@ -512,7 +512,8 @@ public class ThirtyTeachTourMySignupController {
|
||||
|
||||
tourLedgerService.updateIgnoreNull(ledger);
|
||||
// 报名台账更新后,仅回填已存在的人员分配记录事项信息,不新增记录、不改变分配来源。
|
||||
tourUserAssignmentService.backfillExistingAssignmentAfterSignup(matter.getSettingId(), matter.getId(), SecurityUtil.getUserId(), ledger.getBoardingPlace());
|
||||
tourUserAssignmentService.backfillExistingAssignmentAfterSignup(matter.getSettingId(), matter.getId(), SecurityUtil.getUserId(),
|
||||
ledger.getBoardingPlace(), ledger.getPhotoFiles());
|
||||
tourLedgerFamilyService.clear(Cnd.where(ThirtyTeachTourLedgerFamily::getLedgerId, "=", ledger.getId()));
|
||||
tourLedgerDirectRelativeService.clear(Cnd.where(ThirtyTeachTourLedgerDirectRelative::getLedgerId, "=", ledger.getId()));
|
||||
if (Lang.isNotEmpty(familyList)) {
|
||||
|
||||
+24
-6
@@ -100,8 +100,10 @@ public class ThirtyTeachTourSchoolUserAssignmentController {
|
||||
*/
|
||||
@At
|
||||
@SaCheckPermission("thirtyTeachTour.schoolUserAssignment")
|
||||
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)));
|
||||
public Result candidatePageData(PageForm pageForm, String settingId, String unionId, String keyword, @Param("userIds") String userIds,
|
||||
Boolean summerJoined, Boolean lastThirtyJoined) {
|
||||
return Result.success(tourUserAssignmentService.schoolCandidatePage(pageForm, settingId, unionId, keyword,
|
||||
parseUserIds(userIds), summerJoined, lastThirtyJoined));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -138,7 +140,7 @@ public class ThirtyTeachTourSchoolUserAssignmentController {
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
@SaCheckPermission("thirtyTeachTour.schoolUserAssignment")
|
||||
@SLog(type = "tour", tag = "疗休养校工会人员分配", msg = "保存校工会人员分配")
|
||||
public Result doAssign(String settingId, String matterId, String personType, @Param("userIds") String userIds) {
|
||||
public Result doAssign(String settingId, String matterId, String personType, @Param("userIds") String userIds, String photoFiles) {
|
||||
List<String> parsedUserIds;
|
||||
try {
|
||||
parsedUserIds = parseUserIds(userIds);
|
||||
@@ -146,7 +148,7 @@ public class ThirtyTeachTourSchoolUserAssignmentController {
|
||||
return Result.error("人员参数错误");
|
||||
}
|
||||
try {
|
||||
return Result.success(tourUserAssignmentService.assignSchoolUsers(settingId, matterId, personType, parsedUserIds));
|
||||
return Result.success(tourUserAssignmentService.assignSchoolUsers(settingId, matterId, personType, parsedUserIds, photoFiles));
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
@@ -180,9 +182,25 @@ public class ThirtyTeachTourSchoolUserAssignmentController {
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
@SaCheckPermission("thirtyTeachTour.schoolUserAssignment")
|
||||
@SLog(type = "tour", tag = "疗休养校工会人员分配", msg = "选择校工会分配事项")
|
||||
public Result selectMatter(String id, String matterId) {
|
||||
public Result selectMatter(String id, String matterId, String photoFiles) {
|
||||
try {
|
||||
return Result.success(tourUserAssignmentService.selectSchoolAssignmentMatter(id, matterId));
|
||||
return Result.success(tourUserAssignmentService.selectSchoolAssignmentMatter(id, matterId, photoFiles));
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新校工会人员分配记录的图片材料,已生成台账时同步回写台账图片。
|
||||
*/
|
||||
@At
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
@SaCheckPermission("thirtyTeachTour.schoolUserAssignment")
|
||||
@SLog(type = "tour", tag = "疗休养校工会人员分配", msg = "上传校工会人员分配图片")
|
||||
public Result updatePhotoFiles(String id, String photoFiles) {
|
||||
try {
|
||||
tourUserAssignmentService.updateSchoolAssignmentPhotoFiles(id, photoFiles);
|
||||
return Result.success();
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
|
||||
+66
-5
@@ -8,6 +8,7 @@ import com.budwk.app.base.param.PageForm;
|
||||
import com.budwk.app.base.result.Result;
|
||||
import com.budwk.app.zhgh.dayofficework.thirtyTeachTour.models.ThirtyTeachTourSetting;
|
||||
import com.budwk.app.zhgh.dayofficework.thirtyTeachTour.models.ThirtyTeachTourSettingLot;
|
||||
import com.budwk.app.zhgh.dayofficework.thirtyTeachTour.models.ThirtyTeachTourSettingTravelPeriod;
|
||||
import com.budwk.app.zhgh.dayofficework.thirtyTeachTour.service.ThirtyTeachTourSettingService;
|
||||
import com.budwk.app.zhgh.dayofficework.thirtyTeachTour.service.ThirtyTeachTourUserAssignmentService;
|
||||
import org.nutz.aop.interceptor.ioc.TransAop;
|
||||
@@ -22,8 +23,11 @@ import org.nutz.mvc.annotation.At;
|
||||
import org.nutz.mvc.annotation.Ok;
|
||||
import org.nutz.mvc.annotation.Param;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@IocBean
|
||||
@@ -77,7 +81,9 @@ public class ThirtyTeachTourSettingController {
|
||||
Cnd lotCnd = Cnd.NEW();
|
||||
lotCnd.desc(ThirtyTeachTourSettingLot::getLotValue);
|
||||
tourSettingService.dao().fetchLinks(tourSetting, "lots", lotCnd);
|
||||
tourSetting.setUnionQuotas(tourSettingService.listUnionQuotaRows(id));
|
||||
tourSetting.setUnionQuotas(tourSettingService.listUnionQuotaRows(id, tourSetting.getYear(), tourSetting.getActivityGroupId()));
|
||||
tourSetting.setTravelPeriods(tourSettingService.listTravelPeriods(id));
|
||||
tourSetting.setPeriodQuotas(tourSettingService.listPeriodQuotas(id));
|
||||
return Result.success(tourSetting);
|
||||
}
|
||||
|
||||
@@ -98,15 +104,17 @@ public class ThirtyTeachTourSettingController {
|
||||
Cnd lotCnd = Cnd.NEW();
|
||||
lotCnd.desc(ThirtyTeachTourSettingLot::getLotValue);
|
||||
tourSettingService.dao().fetchLinks(tourSetting, "lots", lotCnd);
|
||||
tourSetting.setUnionQuotas(tourSettingService.listUnionQuotaRows(tourSetting.getId()));
|
||||
tourSetting.setUnionQuotas(tourSettingService.listUnionQuotaRows(tourSetting.getId(), tourSetting.getYear(), tourSetting.getActivityGroupId()));
|
||||
tourSetting.setTravelPeriods(tourSettingService.listTravelPeriods(tourSetting.getId()));
|
||||
tourSetting.setPeriodQuotas(tourSettingService.listPeriodQuotas(tourSetting.getId()));
|
||||
return Result.success(tourSetting);
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("thirtyTeachTour.setting")
|
||||
public Result unionQuotaRows(String settingId) {
|
||||
// 新增配置时 settingId 为空,service 会返回所有分工会的空名额行;编辑时合并已保存名额。
|
||||
return Result.success(tourSettingService.listUnionQuotaRows(settingId));
|
||||
public Result unionQuotaRows(String settingId, Integer year, String activityGroupId) {
|
||||
// 新增配置时 settingId 为空,前端传入年度和可参加人员范围;编辑时 service 会兜底读取配置并合并已保存名额。
|
||||
return Result.success(tourSettingService.listUnionQuotaRows(settingId, year, activityGroupId));
|
||||
}
|
||||
|
||||
@At
|
||||
@@ -123,6 +131,8 @@ public class ThirtyTeachTourSettingController {
|
||||
public Result doSubmit(ThirtyTeachTourSetting tourSetting,
|
||||
@Param(value = "lots") String lots,
|
||||
@Param(value = "unionQuotas") String unionQuotas,
|
||||
@Param(value = "travelPeriods") String travelPeriods,
|
||||
@Param(value = "periodQuotas") String periodQuotas,
|
||||
@Param(value = "lotDeleteList") String[] lotDeleteList) {
|
||||
Result checkResult = check(tourSetting);
|
||||
if (checkResult != null) {
|
||||
@@ -145,6 +155,10 @@ public class ThirtyTeachTourSettingController {
|
||||
if (StrUtil.isNotBlank(quotaLimitMessage)) {
|
||||
return Result.error(quotaLimitMessage);
|
||||
}
|
||||
String periodQuotaLimitMessage = tourSettingService.checkPeriodQuotaLimit(periodQuotas, unionQuotas);
|
||||
if (StrUtil.isNotBlank(periodQuotaLimitMessage)) {
|
||||
return Result.error(periodQuotaLimitMessage);
|
||||
}
|
||||
|
||||
if (tourSetting.getEnabled() == null) {
|
||||
tourSetting.setEnabled(true);
|
||||
@@ -186,6 +200,10 @@ public class ThirtyTeachTourSettingController {
|
||||
if (lotCheckResult != null) {
|
||||
return lotCheckResult;
|
||||
}
|
||||
Result periodCheckResult = checkTravelPeriods(travelPeriods);
|
||||
if (periodCheckResult != null) {
|
||||
return periodCheckResult;
|
||||
}
|
||||
if (StrUtil.isBlank(tourSetting.getId())) {
|
||||
if (Lang.isEmpty(tourSetting.getLots())) {
|
||||
tourSettingService.insert(tourSetting);
|
||||
@@ -193,6 +211,8 @@ public class ThirtyTeachTourSettingController {
|
||||
tourSettingService.insertWith(tourSetting, "lots");
|
||||
}
|
||||
tourSettingService.saveUnionQuotas(tourSetting.getId(), unionQuotas);
|
||||
Set<String> validPeriodIds = tourSettingService.saveTravelPeriods(tourSetting.getId(), travelPeriods);
|
||||
tourSettingService.savePeriodQuotas(tourSetting.getId(), periodQuotas, validPeriodIds);
|
||||
} else {
|
||||
// 编辑时先处理页面删除的标段,再保存配置和当前标段行。
|
||||
if (Lang.isNotEmpty(lotDeleteList)) {
|
||||
@@ -201,6 +221,8 @@ public class ThirtyTeachTourSettingController {
|
||||
tourSettingService.updateIgnoreNull(tourSetting);
|
||||
saveLots(tourSetting);
|
||||
tourSettingService.saveUnionQuotas(tourSetting.getId(), unionQuotas);
|
||||
Set<String> validPeriodIds = tourSettingService.saveTravelPeriods(tourSetting.getId(), travelPeriods);
|
||||
tourSettingService.savePeriodQuotas(tourSetting.getId(), periodQuotas, validPeriodIds);
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
@@ -215,6 +237,7 @@ public class ThirtyTeachTourSettingController {
|
||||
}
|
||||
tourSettingService.dao().clear(ThirtyTeachTourSettingLot.class, Cnd.where(ThirtyTeachTourSettingLot::getSettingId, "=", id));
|
||||
tourSettingService.clearUnionQuotas(id);
|
||||
tourSettingService.clearTravelPeriodsAndQuotas(id);
|
||||
tourUserAssignmentService.clearBySettingId(id);
|
||||
tourSettingService.delete(id);
|
||||
return Result.success();
|
||||
@@ -339,4 +362,42 @@ public class ThirtyTeachTourSettingController {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Result checkTravelPeriods(String travelPeriods) {
|
||||
if (StrUtil.isBlank(travelPeriods)) {
|
||||
return null;
|
||||
}
|
||||
List<ThirtyTeachTourSettingTravelPeriod> periodList;
|
||||
try {
|
||||
periodList = Json.fromJsonAsList(ThirtyTeachTourSettingTravelPeriod.class, travelPeriods);
|
||||
} catch (Exception e) {
|
||||
return Result.error("时间段数据格式不正确");
|
||||
}
|
||||
if (Lang.isEmpty(periodList)) {
|
||||
return null;
|
||||
}
|
||||
for (int i = 0; i < periodList.size(); i++) {
|
||||
ThirtyTeachTourSettingTravelPeriod period = periodList.get(i);
|
||||
if (period == null) {
|
||||
continue;
|
||||
}
|
||||
String rowNo = "第" + (i + 1) + "行";
|
||||
if (StrUtil.isBlank(period.getPeriodName())) {
|
||||
return Result.error(rowNo + "时间段名称不能为空");
|
||||
}
|
||||
if (StrUtil.isBlank(period.getStartDate()) || StrUtil.isBlank(period.getEndDate())) {
|
||||
return Result.error(rowNo + "开始日期和结束日期不能为空");
|
||||
}
|
||||
try {
|
||||
LocalDate startDate = LocalDate.parse(period.getStartDate());
|
||||
LocalDate endDate = LocalDate.parse(period.getEndDate());
|
||||
if (startDate.isAfter(endDate)) {
|
||||
return Result.error(rowNo + "开始日期不能大于结束日期");
|
||||
}
|
||||
} catch (DateTimeParseException e) {
|
||||
return Result.error(rowNo + "日期格式不正确");
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -919,7 +919,8 @@ public class ThirtyTeachTourSignupController {
|
||||
tourLedgerService.insert(ledger);
|
||||
}
|
||||
// 报名台账落库后,仅回填已存在的人员分配记录事项信息,不新增记录、不改变分配来源。
|
||||
tourUserAssignmentService.backfillExistingAssignmentAfterSignup(matter.getSettingId(), matter.getId(), SecurityUtil.getUserId(), ledger.getBoardingPlace());
|
||||
tourUserAssignmentService.backfillExistingAssignmentAfterSignup(matter.getSettingId(), matter.getId(), SecurityUtil.getUserId(),
|
||||
ledger.getBoardingPlace(), ledger.getPhotoFiles());
|
||||
if (Lang.isNotEmpty(familyList)) {
|
||||
familyList.forEach(item -> {
|
||||
item.setLedgerId(ledger.getId());
|
||||
|
||||
+5
@@ -126,6 +126,11 @@ public class ThirtyTeachTourLedger extends BaseModel implements Serializable {
|
||||
@ColDefine(type = ColType.VARCHAR, width = 100)
|
||||
private String boardingPlace;
|
||||
|
||||
@Column
|
||||
@Comment("报名图片材料")
|
||||
@ColDefine(type = ColType.TEXT)
|
||||
private String photoFiles;
|
||||
|
||||
@Column
|
||||
@Comment("是否携带家属")
|
||||
@ColDefine(type = ColType.BOOLEAN)
|
||||
|
||||
+11
@@ -155,11 +155,22 @@ public class ThirtyTeachTourSetting extends BaseModel implements Serializable {
|
||||
@Many(field = "settingId")
|
||||
private List<ThirtyTeachTourSettingLot> lots;
|
||||
|
||||
/**
|
||||
* 时间段管理,仅用于配置弹窗回显与提交,不作为 thirty_teach_tour_setting 表字段保存。
|
||||
*/
|
||||
@Many(field = "settingId")
|
||||
private List<ThirtyTeachTourSettingTravelPeriod> travelPeriods;
|
||||
|
||||
/**
|
||||
* 分工会名额分配,仅用于配置弹窗回显与提交,不作为 thirty_teach_tour_setting 表字段保存。
|
||||
*/
|
||||
private List<ThirtyTeachTourSettingUnionQuota> unionQuotas;
|
||||
|
||||
/**
|
||||
* 时间段名额分配,仅用于配置弹窗回显与提交,不作为 thirty_teach_tour_setting 表字段保存。
|
||||
*/
|
||||
private List<ThirtyTeachTourSettingPeriodQuota> periodQuotas;
|
||||
|
||||
@Column
|
||||
@Comment("服务须知")
|
||||
@ColDefine(type = ColType.TEXT)
|
||||
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
package com.budwk.app.zhgh.dayofficework.thirtyTeachTour.models;
|
||||
|
||||
import com.budwk.app.base.model.BaseModel;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.nutz.dao.entity.annotation.ColDefine;
|
||||
import org.nutz.dao.entity.annotation.ColType;
|
||||
import org.nutz.dao.entity.annotation.Column;
|
||||
import org.nutz.dao.entity.annotation.Comment;
|
||||
import org.nutz.dao.entity.annotation.Default;
|
||||
import org.nutz.dao.entity.annotation.Name;
|
||||
import org.nutz.dao.entity.annotation.Table;
|
||||
import org.nutz.dao.entity.annotation.TableMeta;
|
||||
import org.nutz.dao.interceptor.annotation.PrevInsert;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 30年教龄疗休养配置时间段名额。
|
||||
* 该表把分工会正式名额继续拆分到各时间段,保存时会校验同一分工会合计不超过正式名额。
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Table("thirty_teach_tour_setting_period_quota")
|
||||
@TableMeta("{'mysql-charset':'utf8mb4'}")
|
||||
@Comment("30年教龄疗休养配置时间段名额")
|
||||
public class ThirtyTeachTourSettingPeriodQuota extends BaseModel implements Serializable {
|
||||
|
||||
@Column
|
||||
@Name
|
||||
@Comment("ID")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||
@PrevInsert(uu32 = true)
|
||||
private String id;
|
||||
|
||||
@Column
|
||||
@Comment("所属疗休养配置ID")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||
private String settingId;
|
||||
|
||||
@Column
|
||||
@Comment("分工会ID")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||
private String unionId;
|
||||
|
||||
@Column
|
||||
@Comment("分工会名称")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 100)
|
||||
private String unionName;
|
||||
|
||||
@Column
|
||||
@Comment("时间段ID")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||
private String periodId;
|
||||
|
||||
@Column
|
||||
@Comment("时间段名称")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 50)
|
||||
private String periodName;
|
||||
|
||||
@Column
|
||||
@Comment("正式人员名额")
|
||||
@ColDefine(type = ColType.INT)
|
||||
@Default("0")
|
||||
private Integer formalQuota;
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
package com.budwk.app.zhgh.dayofficework.thirtyTeachTour.models;
|
||||
|
||||
import com.budwk.app.base.model.BaseModel;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.nutz.dao.entity.annotation.ColDefine;
|
||||
import org.nutz.dao.entity.annotation.ColType;
|
||||
import org.nutz.dao.entity.annotation.Column;
|
||||
import org.nutz.dao.entity.annotation.Comment;
|
||||
import org.nutz.dao.entity.annotation.Default;
|
||||
import org.nutz.dao.entity.annotation.Name;
|
||||
import org.nutz.dao.entity.annotation.Table;
|
||||
import org.nutz.dao.entity.annotation.TableMeta;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 30年教龄疗休养配置时间段。
|
||||
* 时间段只负责配置端细分名额的维度,报名/线路占用校验由后续业务接入时再按该配置扩展。
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Table("thirty_teach_tour_setting_travel_period")
|
||||
@TableMeta("{'mysql-charset':'utf8mb4'}")
|
||||
@Comment("30年教龄疗休养配置时间段")
|
||||
public class ThirtyTeachTourSettingTravelPeriod extends BaseModel implements Serializable {
|
||||
|
||||
@Column
|
||||
@Name
|
||||
@Comment("ID")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||
private String id;
|
||||
|
||||
@Column
|
||||
@Comment("所属疗休养配置ID")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||
private String settingId;
|
||||
|
||||
@Column
|
||||
@Comment("时间段名称")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 50)
|
||||
private String periodName;
|
||||
|
||||
@Column
|
||||
@Comment("开始日期")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 10)
|
||||
private String startDate;
|
||||
|
||||
@Column
|
||||
@Comment("结束日期")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 10)
|
||||
private String endDate;
|
||||
|
||||
@Column
|
||||
@Comment("是否启用")
|
||||
@ColDefine(type = ColType.BOOLEAN)
|
||||
@Default("1")
|
||||
private Boolean enabled;
|
||||
}
|
||||
+15
@@ -77,6 +77,21 @@ public class ThirtyTeachTourUserAssignment extends BaseModel implements Serializ
|
||||
@ColDefine(type = ColType.VARCHAR, width = 100)
|
||||
private String boardingPlace;
|
||||
|
||||
@Column
|
||||
@Comment("报名图片材料")
|
||||
@ColDefine(type = ColType.TEXT)
|
||||
private String photoFiles;
|
||||
|
||||
@Column
|
||||
@Comment("出行时间段ID")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||
private String periodId;
|
||||
|
||||
@Column
|
||||
@Comment("出行时间段名称")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 50)
|
||||
private String periodName;
|
||||
|
||||
@Column
|
||||
@Comment("人员ID")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||
|
||||
+59
-3
@@ -2,19 +2,40 @@ package com.budwk.app.zhgh.dayofficework.thirtyTeachTour.service;
|
||||
|
||||
import com.budwk.app.base.service.BaseService;
|
||||
import com.budwk.app.zhgh.dayofficework.thirtyTeachTour.models.ThirtyTeachTourSetting;
|
||||
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.ThirtyTeachTourSettingUnionQuota;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public interface ThirtyTeachTourSettingService extends BaseService<ThirtyTeachTourSetting> {
|
||||
|
||||
/**
|
||||
* 查询所有分工会在指定疗休养配置下的名额,实时补充当前工会会员数供页面分配时参考。
|
||||
* 查询所有分工会在指定疗休养配置下的名额,并实时补充可参加人数供页面分配时参考。
|
||||
*
|
||||
* @param settingId 疗休养配置ID,新增时可为空
|
||||
* @param settingId 疗休养配置ID,新增时可为空
|
||||
* @param year 配置年度,新增未落库时用于计算历史参加情况
|
||||
* @param activityGroupId 可参加人员范围分组ID,新增未落库时用于计算可参加人员
|
||||
* @return 按分工会编码排序后的名额列表
|
||||
*/
|
||||
List<ThirtyTeachTourSettingUnionQuota> listUnionQuotaRows(String settingId);
|
||||
List<ThirtyTeachTourSettingUnionQuota> listUnionQuotaRows(String settingId, Integer year, String activityGroupId);
|
||||
|
||||
/**
|
||||
* 查询配置下维护的时间段,按开始日期和创建时间排序。
|
||||
*
|
||||
* @param settingId 疗休养配置ID
|
||||
* @return 时间段列表
|
||||
*/
|
||||
List<ThirtyTeachTourSettingTravelPeriod> listTravelPeriods(String settingId);
|
||||
|
||||
/**
|
||||
* 查询配置下已保存的时间段名额。
|
||||
*
|
||||
* @param settingId 疗休养配置ID
|
||||
* @return 时间段名额列表
|
||||
*/
|
||||
List<ThirtyTeachTourSettingPeriodQuota> listPeriodQuotas(String settingId);
|
||||
|
||||
/**
|
||||
* 保存指定疗休养配置下的分工会名额,前端传入的是 JSON 数组字符串。
|
||||
@@ -24,6 +45,25 @@ public interface ThirtyTeachTourSettingService extends BaseService<ThirtyTeachTo
|
||||
*/
|
||||
void saveUnionQuotas(String settingId, String unionQuotas);
|
||||
|
||||
/**
|
||||
* 保存指定配置下的时间段,前端传入的是 JSON 数组字符串。
|
||||
* 时间段ID由前端生成并由服务端原样保留,确保同一次提交里的时间段名额可以稳定关联。
|
||||
*
|
||||
* @param settingId 疗休养配置ID
|
||||
* @param travelPeriods 时间段 JSON
|
||||
* @return 本次实际保存且已启用的时间段ID集合
|
||||
*/
|
||||
Set<String> saveTravelPeriods(String settingId, String travelPeriods);
|
||||
|
||||
/**
|
||||
* 保存指定配置下的时间段名额,保存前会清理无效时间段和零名额行。
|
||||
*
|
||||
* @param settingId 疗休养配置ID
|
||||
* @param periodQuotas 时间段名额 JSON
|
||||
* @param validPeriodIds 当前配置下本次实际保存且已启用的时间段ID集合
|
||||
*/
|
||||
void savePeriodQuotas(String settingId, String periodQuotas, Set<String> validPeriodIds);
|
||||
|
||||
/**
|
||||
* 校验分工会正式人员名额合计是否超过分工会总名额。
|
||||
*
|
||||
@@ -33,10 +73,26 @@ public interface ThirtyTeachTourSettingService extends BaseService<ThirtyTeachTo
|
||||
*/
|
||||
String checkBranchFormalQuotaLimit(String unionQuotas, int branchTotalQuota);
|
||||
|
||||
/**
|
||||
* 校验时间段名额合计是否超过对应分工会正式名额。
|
||||
*
|
||||
* @param periodQuotas 时间段名额 JSON
|
||||
* @param unionQuotas 分工会名额 JSON
|
||||
* @return 通过返回空字符串;不通过返回业务提示
|
||||
*/
|
||||
String checkPeriodQuotaLimit(String periodQuotas, String unionQuotas);
|
||||
|
||||
/**
|
||||
* 清理指定疗休养配置下的分工会名额。
|
||||
*
|
||||
* @param settingId 疗休养配置ID
|
||||
*/
|
||||
void clearUnionQuotas(String settingId);
|
||||
|
||||
/**
|
||||
* 清理指定配置下的时间段和时间段名额。
|
||||
*
|
||||
* @param settingId 疗休养配置ID
|
||||
*/
|
||||
void clearTravelPeriodsAndQuotas(String settingId);
|
||||
}
|
||||
|
||||
+46
-8
@@ -73,9 +73,12 @@ public interface ThirtyTeachTourUserAssignmentService extends BaseService<Thirty
|
||||
* @param unionId 所属分工会ID
|
||||
* @param keyword 姓名或工号关键字
|
||||
* @param userIds 指定候选人员ID列表,人员选择器多选查询时使用
|
||||
* @param summerJoinedFilter 今年是否已参加暑期疗休养过滤:true 是,false 否,null 不过滤
|
||||
* @param lastThirtyJoinedFilter 去年是否参加30年教龄疗休养过滤:true 是,false 否,null 不过滤
|
||||
* @return 可分配候选人分页数据
|
||||
*/
|
||||
Pagination<NutMap> schoolCandidatePage(PageForm pageForm, String settingId, String unionId, String keyword, List<String> userIds);
|
||||
Pagination<NutMap> schoolCandidatePage(PageForm pageForm, String settingId, String unionId, String keyword, List<String> userIds,
|
||||
Boolean summerJoinedFilter, Boolean lastThirtyJoinedFilter);
|
||||
|
||||
/**
|
||||
* 分页查询当前登录人所在分工会的人员分配记录,列表只读取人员分配表,不读取报名台账。
|
||||
@@ -157,11 +160,11 @@ public interface ThirtyTeachTourUserAssignmentService extends BaseService<Thirty
|
||||
* @param userIds 待分配人员ID列表
|
||||
* @return 保存结果,包含实际分配数量、跳过数量和代报名台账写入数量
|
||||
*/
|
||||
NutMap assignSchoolUsers(String settingId, String matterId, String personType, List<String> userIds);
|
||||
NutMap assignSchoolUsers(String settingId, String matterId, String personType, List<String> userIds, String photoFiles);
|
||||
|
||||
/**
|
||||
* 保存校工会人员分配,支持每个候选人员单独选择分配线路。
|
||||
* assignItems 中每项包含 userId、matterId;matterId 为空时只保存人员分配,不写台账。
|
||||
* assignItems 中每项包含 userId、matterId、photoFiles;matterId 为空时只保存人员分配,不写台账。
|
||||
*
|
||||
* @param settingId 疗休养配置ID
|
||||
* @param assignItems 人员和分配线路明细
|
||||
@@ -177,7 +180,7 @@ public interface ThirtyTeachTourUserAssignmentService extends BaseService<Thirty
|
||||
* @param matterId 疗休养事项ID
|
||||
* @return 处理结果,包含台账写入数量
|
||||
*/
|
||||
NutMap selectSchoolAssignmentMatter(String id, String matterId);
|
||||
NutMap selectSchoolAssignmentMatter(String id, String matterId, String photoFiles);
|
||||
|
||||
/**
|
||||
* 给当前分工会已分配的正式人员补选分配路线,并按代报名写入疗休养台账。
|
||||
@@ -187,19 +190,38 @@ public interface ThirtyTeachTourUserAssignmentService extends BaseService<Thirty
|
||||
* @param matterId 疗休养事项ID
|
||||
* @return 处理结果,包含台账写入数量
|
||||
*/
|
||||
NutMap selectCurrentBranchAssignmentMatter(String id, String matterId);
|
||||
NutMap selectCurrentBranchAssignmentMatter(String id, String matterId, String photoFiles);
|
||||
|
||||
/**
|
||||
* 查询指定配置下已启用的出行时间段,供分工会人员分配弹窗按时间段选择正式名额。
|
||||
*
|
||||
* @param settingId 疗休养配置ID
|
||||
* @return 已启用出行时间段列表
|
||||
*/
|
||||
List<NutMap> listBranchEnabledTravelPeriods(String settingId);
|
||||
|
||||
/**
|
||||
* 查询当前登录人所在分工会在指定时间段下的正式名额使用情况。
|
||||
*
|
||||
* @param settingId 疗休养配置ID
|
||||
* @param periodId 出行时间段ID
|
||||
* @return 时间段正式名额、已分配人数和剩余人数
|
||||
*/
|
||||
NutMap branchPeriodQuotaInfo(String settingId, String periodId);
|
||||
|
||||
/**
|
||||
* 保存分工会人员分配。保存前会重新按配置可参加人员范围和当前登录人所在分工会过滤,并校验正式/替补名额。
|
||||
* 正式人员必须选择出行时间段,并按“当前分工会 + 出行时间段”校验正式名额;替补人员沿用分工会替补名额。
|
||||
* 疗休养事项为可选;正式人员选择事项时视为代报名并同步写入疗休养台账,替补人员不能分配事项。
|
||||
*
|
||||
* @param settingId 疗休养配置ID
|
||||
* @param matterId 疗休养事项ID,可为空
|
||||
* @param periodId 出行时间段ID,正式人员必填
|
||||
* @param personType 人员类型:FORMAL 正式人员,BACKUP 替补人员
|
||||
* @param userIds 待分配人员ID列表
|
||||
* @param assignItems 待分配人员明细,每项包含 userId、photoFiles
|
||||
* @return 保存结果,包含实际分配数量、跳过数量、代报名台账写入数量和剩余名额
|
||||
*/
|
||||
NutMap assignBranchUsers(String settingId, String matterId, String personType, List<String> userIds);
|
||||
NutMap assignBranchUsers(String settingId, String matterId, String periodId, String personType, List<NutMap> assignItems);
|
||||
|
||||
/**
|
||||
* 切换当前分工会人员分配记录的正式/替补状态。
|
||||
@@ -251,7 +273,7 @@ public interface ThirtyTeachTourUserAssignmentService extends BaseService<Thirty
|
||||
* @param boardingPlace 用户报名时最终选择的乘车地点
|
||||
* @return 更新记录数
|
||||
*/
|
||||
int backfillExistingAssignmentAfterSignup(String settingId, String matterId, String userId, String boardingPlace);
|
||||
int backfillExistingAssignmentAfterSignup(String settingId, String matterId, String userId, String boardingPlace, String photoFiles);
|
||||
|
||||
/**
|
||||
* 用户取消报名删除台账后,清空该用户在同一疗休养配置下已存在人员分配记录的事项快照。
|
||||
@@ -297,6 +319,22 @@ public interface ThirtyTeachTourUserAssignmentService extends BaseService<Thirty
|
||||
*/
|
||||
NutMap branchDeleteInfo(String id);
|
||||
|
||||
/**
|
||||
* 更新校工会人员分配记录的图片材料,并同步该人员已存在的报名台账图片。
|
||||
*
|
||||
* @param id 人员分配记录ID
|
||||
* @param photoFiles 图片材料,多个文件以逗号分隔
|
||||
*/
|
||||
void updateSchoolAssignmentPhotoFiles(String id, String photoFiles);
|
||||
|
||||
/**
|
||||
* 更新当前分工会人员分配记录的图片材料,并同步该人员已存在的报名台账图片。
|
||||
*
|
||||
* @param id 人员分配记录ID
|
||||
* @param photoFiles 图片材料,多个文件以逗号分隔
|
||||
*/
|
||||
void updateCurrentBranchAssignmentPhotoFiles(String id, String photoFiles);
|
||||
|
||||
/**
|
||||
* 删除指定来源的人员分配记录,避免校工会页面误删分工会分配的数据。
|
||||
*
|
||||
|
||||
+193
-9
@@ -4,6 +4,8 @@ import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.base.service.impl.BaseServiceImpl;
|
||||
import com.budwk.app.sys.models.Sys_union;
|
||||
import com.budwk.app.zhgh.dayofficework.thirtyTeachTour.models.ThirtyTeachTourSetting;
|
||||
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.ThirtyTeachTourSettingUnionQuota;
|
||||
import com.budwk.app.zhgh.dayofficework.thirtyTeachTour.service.ThirtyTeachTourSettingService;
|
||||
import org.nutz.dao.Cnd;
|
||||
@@ -15,8 +17,10 @@ import org.nutz.json.Json;
|
||||
import org.nutz.lang.Lang;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@IocBean(args = {"refer:dao"})
|
||||
@@ -27,12 +31,18 @@ public class ThirtyTeachTourSettingServiceImpl extends BaseServiceImpl<ThirtyTea
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ThirtyTeachTourSettingUnionQuota> listUnionQuotaRows(String settingId) {
|
||||
// 一次性查出所有分工会、已保存名额和实时会员数,避免页面打开时按分工会循环统计造成 N+1 查询。
|
||||
public List<ThirtyTeachTourSettingUnionQuota> listUnionQuotaRows(String settingId, Integer year, String activityGroupId) {
|
||||
ThirtyTeachTourSetting setting = resolveQuotaSetting(settingId);
|
||||
Integer effectiveYear = year == null && setting != null ? setting.getYear() : year;
|
||||
String effectiveActivityGroupId = StrUtil.isBlank(activityGroupId) && setting != null ? setting.getActivityGroupId() : activityGroupId;
|
||||
String quotaSettingId = settingId == null ? "" : settingId;
|
||||
int previousYear = effectiveYear == null ? 0 : effectiveYear - 1;
|
||||
// 一次性查出所有分工会、已保存名额和可参加人数,避免页面打开时按分工会循环统计造成 N+1 查询。
|
||||
// 可参加人数口径:当前活动人员范围内人员,扣除去年已参加30年教龄疗休养和本年度已参加暑期疗休养的人员。
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
quota.id AS id,
|
||||
@settingId AS settingId,
|
||||
@quotaSettingId AS settingId,
|
||||
un.id AS unionId,
|
||||
un.name AS unionName,
|
||||
COALESCE(quota.formalQuota, 0) AS formalQuota,
|
||||
@@ -41,16 +51,37 @@ public class ThirtyTeachTourSettingServiceImpl extends BaseServiceImpl<ThirtyTea
|
||||
FROM sys_union un
|
||||
LEFT JOIN thirty_teach_tour_setting_union_quota quota
|
||||
ON quota.unionId = un.id
|
||||
AND quota.settingId = @settingId
|
||||
AND quota.settingId = @quotaSettingId
|
||||
LEFT JOIN (
|
||||
SELECT unionId, COUNT(1) AS memberCount
|
||||
FROM vw_user
|
||||
WHERE member = 1
|
||||
GROUP BY unionId
|
||||
SELECT u.unionid AS unionId, COUNT(DISTINCT u.id) AS memberCount
|
||||
FROM activity_user_scope aus
|
||||
INNER JOIN vw_user u ON u.id = aus.userId
|
||||
WHERE aus.groupId = @activityGroupId
|
||||
AND @year > 0
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM thirty_teach_tour_ledger previous_ledger
|
||||
WHERE previous_ledger.delFlag = 0
|
||||
AND previous_ledger.joined = 1
|
||||
AND previous_ledger.`year` = @previousYear
|
||||
AND previous_ledger.jobNo = u.loginname
|
||||
)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM tour_ledger summer_ledger
|
||||
WHERE summer_ledger.delFlag = 0
|
||||
AND summer_ledger.joined = 1
|
||||
AND summer_ledger.`year` = @year
|
||||
AND summer_ledger.jobNo = u.loginname
|
||||
)
|
||||
GROUP BY u.unionid
|
||||
) user_count ON user_count.unionId = un.id
|
||||
ORDER BY un.unionCode ASC
|
||||
""");
|
||||
sql.setParam("settingId", settingId == null ? "" : settingId);
|
||||
sql.setParam("quotaSettingId", quotaSettingId);
|
||||
sql.setParam("activityGroupId", StrUtil.blankToDefault(effectiveActivityGroupId, ""));
|
||||
sql.setParam("year", effectiveYear == null ? 0 : effectiveYear);
|
||||
sql.setParam("previousYear", previousYear);
|
||||
List<NutMap> rows = listMap(sql);
|
||||
return rows.stream().map(row -> {
|
||||
ThirtyTeachTourSettingUnionQuota quota = new ThirtyTeachTourSettingUnionQuota();
|
||||
@@ -65,6 +96,37 @@ public class ThirtyTeachTourSettingServiceImpl extends BaseServiceImpl<ThirtyTea
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private ThirtyTeachTourSetting resolveQuotaSetting(String settingId) {
|
||||
// 编辑配置时允许前端只传 settingId,服务层兜底读取年度和可参加人员范围,保证接口兼容已有调用。
|
||||
if (StrUtil.isBlank(settingId)) {
|
||||
return null;
|
||||
}
|
||||
return fetch(settingId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ThirtyTeachTourSettingTravelPeriod> listTravelPeriods(String settingId) {
|
||||
if (StrUtil.isBlank(settingId)) {
|
||||
return List.of();
|
||||
}
|
||||
Cnd cnd = Cnd.NEW();
|
||||
cnd.and("settingId", "=", settingId)
|
||||
.and("delFlag", "=", false)
|
||||
.asc("startDate")
|
||||
.asc("createdAt");
|
||||
return dao().query(ThirtyTeachTourSettingTravelPeriod.class, cnd);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ThirtyTeachTourSettingPeriodQuota> listPeriodQuotas(String settingId) {
|
||||
if (StrUtil.isBlank(settingId)) {
|
||||
return List.of();
|
||||
}
|
||||
Cnd cnd = Cnd.where("settingId", "=", settingId)
|
||||
.and("delFlag", "=", false);
|
||||
return dao().query(ThirtyTeachTourSettingPeriodQuota.class, cnd);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveUnionQuotas(String settingId, String unionQuotas) {
|
||||
if (StrUtil.isBlank(settingId)) {
|
||||
@@ -90,6 +152,76 @@ public class ThirtyTeachTourSettingServiceImpl extends BaseServiceImpl<ThirtyTea
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> saveTravelPeriods(String settingId, String travelPeriods) {
|
||||
if (StrUtil.isBlank(settingId)) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
dao().clear(ThirtyTeachTourSettingTravelPeriod.class,
|
||||
Cnd.where("settingId", "=", settingId));
|
||||
if (StrUtil.isBlank(travelPeriods)) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
List<ThirtyTeachTourSettingTravelPeriod> periodList = Json.fromJsonAsList(ThirtyTeachTourSettingTravelPeriod.class, travelPeriods);
|
||||
if (Lang.isEmpty(periodList)) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
List<ThirtyTeachTourSettingTravelPeriod> saveList = periodList.stream()
|
||||
.filter(item -> item != null
|
||||
&& StrUtil.isNotBlank(item.getId())
|
||||
&& StrUtil.isNotBlank(item.getPeriodName())
|
||||
&& StrUtil.isNotBlank(item.getStartDate())
|
||||
&& StrUtil.isNotBlank(item.getEndDate()))
|
||||
.peek(item -> {
|
||||
// 前端会为新增时间段生成临时ID,服务端保留该ID以便时间段名额能稳定关联。
|
||||
item.setSettingId(settingId);
|
||||
item.setEnabled(Boolean.TRUE.equals(item.getEnabled()));
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
if (Lang.isNotEmpty(saveList)) {
|
||||
dao().insert(saveList);
|
||||
}
|
||||
return saveList.stream()
|
||||
.filter(item -> Boolean.TRUE.equals(item.getEnabled()))
|
||||
.map(ThirtyTeachTourSettingTravelPeriod::getId)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void savePeriodQuotas(String settingId, String periodQuotas, Set<String> validPeriodIds) {
|
||||
if (StrUtil.isBlank(settingId)) {
|
||||
return;
|
||||
}
|
||||
dao().clear(ThirtyTeachTourSettingPeriodQuota.class,
|
||||
Cnd.where("settingId", "=", settingId));
|
||||
if (StrUtil.isBlank(periodQuotas)) {
|
||||
return;
|
||||
}
|
||||
List<ThirtyTeachTourSettingPeriodQuota> quotaList = Json.fromJsonAsList(ThirtyTeachTourSettingPeriodQuota.class, periodQuotas);
|
||||
if (Lang.isEmpty(quotaList)) {
|
||||
return;
|
||||
}
|
||||
Set<String> periodIds = Lang.isEmpty(validPeriodIds)
|
||||
? listTravelPeriods(settingId).stream()
|
||||
.filter(item -> Boolean.TRUE.equals(item.getEnabled()))
|
||||
.map(ThirtyTeachTourSettingTravelPeriod::getId)
|
||||
.collect(Collectors.toSet())
|
||||
: validPeriodIds;
|
||||
Map<String, Sys_union> unionMap = dao().query(Sys_union.class, Cnd.NEW()).stream()
|
||||
.collect(Collectors.toMap(Sys_union::getId, item -> item, (a, b) -> a));
|
||||
List<ThirtyTeachTourSettingPeriodQuota> saveList = quotaList.stream()
|
||||
.filter(item -> item != null
|
||||
&& StrUtil.isNotBlank(item.getUnionId())
|
||||
&& StrUtil.isNotBlank(item.getPeriodId())
|
||||
&& periodIds.contains(item.getPeriodId())
|
||||
&& defaultInt(item.getFormalQuota()) > 0)
|
||||
.map(item -> normalizePeriodQuota(settingId, item, unionMap))
|
||||
.collect(Collectors.toList());
|
||||
if (Lang.isNotEmpty(saveList)) {
|
||||
dao().insert(saveList);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String checkBranchFormalQuotaLimit(String unionQuotas, int branchTotalQuota) {
|
||||
if (StrUtil.isBlank(unionQuotas)) {
|
||||
@@ -109,6 +241,37 @@ public class ThirtyTeachTourSettingServiceImpl extends BaseServiceImpl<ThirtyTea
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String checkPeriodQuotaLimit(String periodQuotas, String unionQuotas) {
|
||||
if (StrUtil.isBlank(periodQuotas)) {
|
||||
return "";
|
||||
}
|
||||
List<ThirtyTeachTourSettingPeriodQuota> periodQuotaList = Json.fromJsonAsList(ThirtyTeachTourSettingPeriodQuota.class, periodQuotas);
|
||||
if (Lang.isEmpty(periodQuotaList)) {
|
||||
return "";
|
||||
}
|
||||
List<ThirtyTeachTourSettingUnionQuota> unionQuotaList = StrUtil.isBlank(unionQuotas)
|
||||
? List.of()
|
||||
: Json.fromJsonAsList(ThirtyTeachTourSettingUnionQuota.class, unionQuotas);
|
||||
Map<String, ThirtyTeachTourSettingUnionQuota> unionQuotaMap = unionQuotaList.stream()
|
||||
.filter(item -> item != null && StrUtil.isNotBlank(item.getUnionId()))
|
||||
.collect(Collectors.toMap(ThirtyTeachTourSettingUnionQuota::getUnionId, item -> item, (a, b) -> a));
|
||||
Map<String, Integer> periodQuotaTotalMap = periodQuotaList.stream()
|
||||
.filter(item -> item != null && StrUtil.isNotBlank(item.getUnionId()))
|
||||
.collect(Collectors.groupingBy(ThirtyTeachTourSettingPeriodQuota::getUnionId,
|
||||
Collectors.summingInt(item -> defaultInt(item.getFormalQuota()))));
|
||||
for (Map.Entry<String, Integer> entry : periodQuotaTotalMap.entrySet()) {
|
||||
ThirtyTeachTourSettingUnionQuota unionQuota = unionQuotaMap.get(entry.getKey());
|
||||
int formalQuota = unionQuota == null ? 0 : defaultInt(unionQuota.getFormalQuota());
|
||||
if (entry.getValue() > formalQuota) {
|
||||
String unionName = unionQuota == null ? "" : unionQuota.getUnionName();
|
||||
return "分工会【" + StrUtil.blankToDefault(unionName, entry.getKey()) + "】时间段名额合计 "
|
||||
+ entry.getValue() + ",不能大于当前分工会正式名额 " + formalQuota;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearUnionQuotas(String settingId) {
|
||||
if (StrUtil.isNotBlank(settingId)) {
|
||||
@@ -116,6 +279,15 @@ public class ThirtyTeachTourSettingServiceImpl extends BaseServiceImpl<ThirtyTea
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearTravelPeriodsAndQuotas(String settingId) {
|
||||
if (StrUtil.isBlank(settingId)) {
|
||||
return;
|
||||
}
|
||||
dao().clear(ThirtyTeachTourSettingPeriodQuota.class, Cnd.where("settingId", "=", settingId));
|
||||
dao().clear(ThirtyTeachTourSettingTravelPeriod.class, Cnd.where("settingId", "=", settingId));
|
||||
}
|
||||
|
||||
private ThirtyTeachTourSettingUnionQuota normalizeUnionQuota(String settingId, ThirtyTeachTourSettingUnionQuota item, Map<String, Sys_union> unionMap) {
|
||||
Sys_union union = unionMap.get(item.getUnionId());
|
||||
ThirtyTeachTourSettingUnionQuota quota = new ThirtyTeachTourSettingUnionQuota();
|
||||
@@ -127,6 +299,18 @@ public class ThirtyTeachTourSettingServiceImpl extends BaseServiceImpl<ThirtyTea
|
||||
return quota;
|
||||
}
|
||||
|
||||
private ThirtyTeachTourSettingPeriodQuota normalizePeriodQuota(String settingId, ThirtyTeachTourSettingPeriodQuota item, Map<String, Sys_union> unionMap) {
|
||||
Sys_union union = unionMap.get(item.getUnionId());
|
||||
ThirtyTeachTourSettingPeriodQuota quota = new ThirtyTeachTourSettingPeriodQuota();
|
||||
quota.setSettingId(settingId);
|
||||
quota.setUnionId(item.getUnionId());
|
||||
quota.setUnionName(union == null ? item.getUnionName() : union.getName());
|
||||
quota.setPeriodId(item.getPeriodId());
|
||||
quota.setPeriodName(item.getPeriodName());
|
||||
quota.setFormalQuota(defaultInt(item.getFormalQuota()));
|
||||
return quota;
|
||||
}
|
||||
|
||||
private Integer defaultInt(Integer value) {
|
||||
return value == null || value < 0 ? 0 : value;
|
||||
}
|
||||
|
||||
+348
-31
@@ -122,13 +122,14 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pagination<NutMap> schoolCandidatePage(PageForm pageForm, String settingId, String unionId, String keyword, List<String> userIds) {
|
||||
public Pagination<NutMap> schoolCandidatePage(PageForm pageForm, String settingId, String unionId, String keyword, List<String> userIds,
|
||||
Boolean summerJoinedFilter, Boolean lastThirtyJoinedFilter) {
|
||||
ThirtyTeachTourSetting 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);
|
||||
Sql sql = buildCandidateSql(settingId, setting.getYear());
|
||||
Cnd cnd = buildCandidateCnd(setting.getActivityGroupId(), unionId, keyword, summerJoinedFilter, lastThirtyJoinedFilter);
|
||||
List<String> normalizedUserIds = normalizeUserIds(userIds);
|
||||
// 人员选择器多选查询时,候选范围仍受活动组、分工会、已分配排除规则约束,再按指定人员ID精确过滤。
|
||||
if (!Lang.isEmpty(normalizedUserIds)) {
|
||||
@@ -171,7 +172,7 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
|
||||
if (StrUtil.isBlank(unionId)) {
|
||||
return emptyPagination(pageForm);
|
||||
}
|
||||
return schoolCandidatePage(pageForm, settingId, unionId, keyword, userIds);
|
||||
return schoolCandidatePage(pageForm, settingId, unionId, keyword, userIds, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -342,7 +343,7 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
|
||||
}
|
||||
|
||||
@Override
|
||||
public NutMap assignSchoolUsers(String settingId, String matterId, String personType, List<String> userIds) {
|
||||
public NutMap assignSchoolUsers(String settingId, String matterId, String personType, List<String> userIds, String photoFiles) {
|
||||
List<String> distinctUserIds = normalizeUserIds(userIds);
|
||||
if (StrUtil.isBlank(settingId) || Lang.isEmpty(distinctUserIds)) {
|
||||
throw new IllegalArgumentException("请选择疗休养配置和分配人员");
|
||||
@@ -359,11 +360,12 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
|
||||
}
|
||||
|
||||
// 保存前重新按“配置人员范围 + 未分配”查询,防止前端绕过候选列表提交范围外人员。
|
||||
Sql userSql = buildCandidateSql(settingId);
|
||||
Cnd userCnd = buildCandidateCnd(settingId, setting.getActivityGroupId(), null, null);
|
||||
Sql userSql = buildCandidateSql(settingId, setting.getYear());
|
||||
Cnd userCnd = buildCandidateCnd(setting.getActivityGroupId(), null, null, false, false);
|
||||
userCnd.and("u.id", "in", distinctUserIds);
|
||||
userSql.setCondition(userCnd);
|
||||
List<NutMap> candidateUsers = listMap(userSql);
|
||||
fillCandidatePhotoFiles(candidateUsers, normalizePhotoFiles(photoFiles));
|
||||
List<ThirtyTeachTourUserAssignment> assignments = candidateUsers.stream()
|
||||
.map(user -> buildSchoolAssignment(settingId, matterInfo, normalizedPersonType, user))
|
||||
.collect(Collectors.toList());
|
||||
@@ -392,6 +394,7 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
|
||||
}
|
||||
|
||||
Map<String, String> matterByUserId = new HashMap<>();
|
||||
Map<String, String> photoFilesByUserId = new HashMap<>();
|
||||
List<String> userIds = assignItems.stream()
|
||||
.map(item -> item == null ? "" : item.getString("userId", ""))
|
||||
.filter(StrUtil::isNotBlank)
|
||||
@@ -406,6 +409,7 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
|
||||
return;
|
||||
}
|
||||
matterByUserId.put(userId, item.getString("matterId", ""));
|
||||
photoFilesByUserId.put(userId, normalizePhotoFiles(item.getString("photoFiles")));
|
||||
});
|
||||
if (Lang.isEmpty(userIds)) {
|
||||
throw new IllegalArgumentException("请选择分配人员");
|
||||
@@ -424,11 +428,12 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
|
||||
});
|
||||
|
||||
// 保存前重新按“配置人员范围 + 未分配”查询,防止前端绕过候选列表提交范围外人员。
|
||||
Sql userSql = buildCandidateSql(settingId);
|
||||
Cnd userCnd = buildCandidateCnd(settingId, setting.getActivityGroupId(), null, null);
|
||||
Sql userSql = buildCandidateSql(settingId, setting.getYear());
|
||||
Cnd userCnd = buildCandidateCnd(setting.getActivityGroupId(), null, null, false, false);
|
||||
userCnd.and("u.id", "in", userIds);
|
||||
userSql.setCondition(userCnd);
|
||||
List<NutMap> candidateUsers = listMap(userSql);
|
||||
candidateUsers.forEach(user -> user.put("photoFiles", photoFilesByUserId.get(user.getString("userId", ""))));
|
||||
|
||||
List<ThirtyTeachTourUserAssignment> assignments = candidateUsers.stream()
|
||||
.map(user -> {
|
||||
@@ -456,7 +461,7 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
|
||||
}
|
||||
|
||||
@Override
|
||||
public NutMap selectSchoolAssignmentMatter(String id, String matterId) {
|
||||
public NutMap selectSchoolAssignmentMatter(String id, String matterId, String photoFiles) {
|
||||
if (StrUtil.isBlank(id) || StrUtil.isBlank(matterId)) {
|
||||
throw new IllegalArgumentException("请选择人员分配记录和分配线路");
|
||||
}
|
||||
@@ -477,13 +482,14 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
|
||||
if (matterInfo == null) {
|
||||
throw new IllegalArgumentException("分配线路不存在或未配置线路");
|
||||
}
|
||||
updateAssignmentMatterSnapshot(id, matterInfo, ThirtyTeachTourUserAssignment.ASSIGN_SOURCE_SCHOOL_UNION);
|
||||
updateAssignmentMatterSnapshot(id, matterInfo, ThirtyTeachTourUserAssignment.ASSIGN_SOURCE_SCHOOL_UNION, photoFiles);
|
||||
assignment.setPhotoFiles(normalizePhotoFiles(photoFiles));
|
||||
int ledgerCount = insertProxySignupLedgers(matterInfo, Collections.singletonList(fetchAssignmentUserSnapshot(assignment)));
|
||||
return NutMap.NEW().addv("ledgerCount", ledgerCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NutMap selectCurrentBranchAssignmentMatter(String id, String matterId) {
|
||||
public NutMap selectCurrentBranchAssignmentMatter(String id, String matterId, String photoFiles) {
|
||||
if (StrUtil.isBlank(id) || StrUtil.isBlank(matterId)) {
|
||||
throw new IllegalArgumentException("请选择人员分配记录和分配路线");
|
||||
}
|
||||
@@ -505,23 +511,43 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
|
||||
if (!isMatterSignupOpen(matterInfo)) {
|
||||
throw new IllegalArgumentException("当前线路不在报名时间内,不能选择");
|
||||
}
|
||||
assignment.setPhotoFiles(normalizePhotoFiles(photoFiles));
|
||||
List<NutMap> users = Collections.singletonList(fetchAssignmentUserSnapshot(assignment));
|
||||
checkProxySignupMaxGroupPeople(matterInfo, users);
|
||||
updateAssignmentMatterSnapshot(id, matterInfo, ThirtyTeachTourUserAssignment.ASSIGN_SOURCE_BRANCH_UNION);
|
||||
updateAssignmentMatterSnapshot(id, matterInfo, ThirtyTeachTourUserAssignment.ASSIGN_SOURCE_BRANCH_UNION, photoFiles);
|
||||
int ledgerCount = insertProxySignupLedgers(matterInfo, users);
|
||||
return NutMap.NEW().addv("ledgerCount", ledgerCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NutMap assignBranchUsers(String settingId, String matterId, String personType, List<String> userIds) {
|
||||
public NutMap assignBranchUsers(String settingId, String matterId, String periodId, String personType, List<NutMap> assignItems) {
|
||||
String unionId = SecurityUtil.getUnionId();
|
||||
if (StrUtil.isBlank(unionId)) {
|
||||
throw new IllegalArgumentException("当前登录人未绑定分工会");
|
||||
}
|
||||
List<String> distinctUserIds = normalizeUserIds(userIds);
|
||||
if (StrUtil.isBlank(settingId) || Lang.isEmpty(distinctUserIds)) {
|
||||
if (StrUtil.isBlank(settingId) || Lang.isEmpty(assignItems)) {
|
||||
throw new IllegalArgumentException("请选择疗休养配置和分配人员");
|
||||
}
|
||||
Map<String, String> photoFilesByUserId = new HashMap<>();
|
||||
List<String> distinctUserIds = assignItems.stream()
|
||||
.map(item -> item == null ? "" : item.getString("userId", ""))
|
||||
.filter(StrUtil::isNotBlank)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
assignItems.forEach(item -> {
|
||||
if (item == null) {
|
||||
return;
|
||||
}
|
||||
String userId = item.getString("userId", "");
|
||||
if (StrUtil.isBlank(userId)) {
|
||||
return;
|
||||
}
|
||||
// 图片材料来自人员分配弹窗的行级暂存,按人员写入后续分配表和台账。
|
||||
photoFilesByUserId.put(userId, normalizePhotoFiles(item.getString("photoFiles")));
|
||||
});
|
||||
if (Lang.isEmpty(distinctUserIds)) {
|
||||
throw new IllegalArgumentException("请选择分配人员");
|
||||
}
|
||||
String normalizedPersonType = normalizePersonType(personType);
|
||||
NutMap matterInfo = prepareMatterInfoForAssignment(settingId, matterId, normalizedPersonType);
|
||||
// 分工会代报名占用事项最大成团人数;在同一事务中锁定事项行,避免多个分工会同时校验通过后并发写入导致超额。
|
||||
@@ -539,26 +565,47 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
|
||||
throw new IllegalArgumentException("疗休养配置未设置可参加人员范围");
|
||||
}
|
||||
NutMap quotaInfo = branchQuotaInfo(settingId);
|
||||
NutMap periodInfo = NutMap.NEW();
|
||||
ThirtyTeachTourSettingTravelPeriod selectedPeriod = null;
|
||||
int remainingQuota = defaultInt(quotaInfo.getInt(remainingQuotaKey(normalizedPersonType)));
|
||||
if (ThirtyTeachTourUserAssignment.PERSON_TYPE_FORMAL.equals(normalizedPersonType)) {
|
||||
if (StrUtil.isBlank(periodId)) {
|
||||
throw new IllegalArgumentException("请选择出行时间");
|
||||
}
|
||||
selectedPeriod = fetchEnabledTravelPeriod(settingId, periodId);
|
||||
if (selectedPeriod == null) {
|
||||
throw new IllegalArgumentException("出行时间不存在或已禁用");
|
||||
}
|
||||
lockBranchPeriodQuota(settingId, periodId, unionId);
|
||||
// 正式人员按“当前分工会 + 已启用时间段”读取细分名额,避免继续占用配置页签中的分工会总正式名额。
|
||||
periodInfo = branchPeriodQuotaInfo(settingId, periodId);
|
||||
remainingQuota = defaultInt(periodInfo.getInt("formalRemaining"));
|
||||
}
|
||||
if (remainingQuota <= 0) {
|
||||
throw new IllegalArgumentException("当前分工会该人员类型暂无剩余名额");
|
||||
}
|
||||
|
||||
// 保存前重新按“配置人员范围 + 当前分工会 + 未分配”查询,防止前端提交范围外或其它分工会人员。
|
||||
Sql userSql = buildCandidateSql(settingId);
|
||||
Cnd userCnd = buildCandidateCnd(settingId, setting.getActivityGroupId(), unionId, null);
|
||||
Sql userSql = buildCandidateSql(settingId, setting.getYear());
|
||||
Cnd userCnd = buildCandidateCnd(setting.getActivityGroupId(), unionId, null, false, false);
|
||||
userCnd.and("u.id", "in", distinctUserIds);
|
||||
userSql.setCondition(userCnd);
|
||||
List<NutMap> candidateUsers = listMap(userSql);
|
||||
candidateUsers.forEach(user -> user.put("photoFiles", photoFilesByUserId.get(user.getString("userId", ""))));
|
||||
if (candidateUsers.size() > remainingQuota) {
|
||||
throw new IllegalArgumentException("选择人数超过当前分工会剩余名额");
|
||||
}
|
||||
// 分工会代报名会占用线路成团人数,人员分配和台账写入前先做后端兜底校验。
|
||||
checkProxySignupMaxGroupPeople(matterInfo, candidateUsers);
|
||||
final NutMap assignmentMatterInfo = matterInfo;
|
||||
final ThirtyTeachTourSettingTravelPeriod assignmentPeriod = selectedPeriod;
|
||||
List<ThirtyTeachTourUserAssignment> assignments = candidateUsers.stream()
|
||||
.map(user -> buildAssignment(settingId, assignmentMatterInfo, normalizedPersonType, user,
|
||||
ThirtyTeachTourUserAssignment.ASSIGN_SOURCE_BRANCH_UNION))
|
||||
.map(user -> {
|
||||
ThirtyTeachTourUserAssignment assignment = buildAssignment(settingId, assignmentMatterInfo, normalizedPersonType, user,
|
||||
ThirtyTeachTourUserAssignment.ASSIGN_SOURCE_BRANCH_UNION);
|
||||
fillTravelPeriodSnapshot(assignment, assignmentPeriod);
|
||||
return assignment;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
if (Lang.isNotEmpty(assignments)) {
|
||||
dao().insert(assignments);
|
||||
@@ -566,11 +613,14 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
|
||||
// 事项为空时只写人员分配表;正式人员选择事项时按代报名写入台账。
|
||||
int ledgerCount = insertProxySignupLedgers(matterInfo, candidateUsers);
|
||||
NutMap newQuotaInfo = branchQuotaInfo(settingId);
|
||||
NutMap newPeriodQuotaInfo = ThirtyTeachTourUserAssignment.PERSON_TYPE_FORMAL.equals(normalizedPersonType)
|
||||
? branchPeriodQuotaInfo(settingId, periodId) : NutMap.NEW();
|
||||
return NutMap.NEW()
|
||||
.addv("assignCount", assignments.size())
|
||||
.addv("skipCount", distinctUserIds.size() - assignments.size())
|
||||
.addv("ledgerCount", ledgerCount)
|
||||
.addv("formalRemaining", defaultInt(newQuotaInfo.getInt("formalRemaining")))
|
||||
.addv("formalRemaining", ThirtyTeachTourUserAssignment.PERSON_TYPE_FORMAL.equals(normalizedPersonType)
|
||||
? defaultInt(newPeriodQuotaInfo.getInt("formalRemaining")) : defaultInt(newQuotaInfo.getInt("formalRemaining")))
|
||||
.addv("backupRemaining", defaultInt(newQuotaInfo.getInt("backupRemaining")));
|
||||
}
|
||||
|
||||
@@ -625,6 +675,79 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
|
||||
.addv("backupRemaining", Math.max(backupQuota - backupUsed, 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NutMap> listBranchEnabledTravelPeriods(String settingId) {
|
||||
if (StrUtil.isBlank(settingId)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<ThirtyTeachTourSettingTravelPeriod> periods = dao().query(ThirtyTeachTourSettingTravelPeriod.class,
|
||||
Cnd.where("settingId", "=", settingId)
|
||||
.and("enabled", "=", true)
|
||||
.and("delFlag", "=", false)
|
||||
.asc("startDate")
|
||||
.asc("createdAt"));
|
||||
return periods.stream()
|
||||
.map(period -> NutMap.NEW()
|
||||
.addv("id", period.getId())
|
||||
.addv("periodName", period.getPeriodName())
|
||||
.addv("startDate", period.getStartDate())
|
||||
.addv("endDate", period.getEndDate())
|
||||
.addv("label", travelPeriodButtonLabel(period)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public NutMap branchPeriodQuotaInfo(String settingId, String periodId) {
|
||||
String unionId = SecurityUtil.getUnionId();
|
||||
if (StrUtil.isBlank(settingId) || StrUtil.isBlank(periodId) || StrUtil.isBlank(unionId)) {
|
||||
return emptyPeriodQuotaInfo(periodId);
|
||||
}
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
COALESCE(q.formalQuota, 0) AS formalQuota,
|
||||
COALESCE(used_quota.formalUsed, 0) AS formalUsed
|
||||
FROM thirty_teach_tour_setting_period_quota q
|
||||
LEFT JOIN (
|
||||
SELECT
|
||||
settingId,
|
||||
unionId,
|
||||
periodId,
|
||||
COUNT(1) AS formalUsed
|
||||
FROM thirty_teach_tour_user_assignment
|
||||
WHERE delFlag = 0
|
||||
AND assignSource = @assignSource
|
||||
AND personType = @formalType
|
||||
AND settingId = @settingId
|
||||
AND unionId = @unionId
|
||||
AND periodId = @periodId
|
||||
AND (cancelled = 0 OR cancelled IS NULL)
|
||||
GROUP BY settingId, unionId, periodId
|
||||
) used_quota ON used_quota.settingId = q.settingId
|
||||
AND used_quota.unionId = q.unionId
|
||||
AND used_quota.periodId = q.periodId
|
||||
WHERE q.settingId = @settingId
|
||||
AND q.unionId = @unionId
|
||||
AND q.periodId = @periodId
|
||||
AND q.delFlag = 0
|
||||
LIMIT 1
|
||||
""");
|
||||
sql.setParam("assignSource", ThirtyTeachTourUserAssignment.ASSIGN_SOURCE_BRANCH_UNION);
|
||||
sql.setParam("formalType", ThirtyTeachTourUserAssignment.PERSON_TYPE_FORMAL);
|
||||
sql.setParam("settingId", settingId);
|
||||
sql.setParam("unionId", unionId);
|
||||
sql.setParam("periodId", periodId);
|
||||
List<NutMap> rows = listMap(sql);
|
||||
NutMap quotaInfo = Lang.isEmpty(rows) ? emptyPeriodQuotaInfo(periodId) : rows.get(0);
|
||||
int formalQuota = defaultInt(quotaInfo.getInt("formalQuota"));
|
||||
int formalUsed = defaultInt(quotaInfo.getInt("formalUsed"));
|
||||
// NutMap.addv 对已存在的同名 key 会形成数组,这里直接构造最终返回值,避免前端显示 [0, 12]。
|
||||
return NutMap.NEW()
|
||||
.addv("periodId", periodId)
|
||||
.addv("formalQuota", formalQuota)
|
||||
.addv("formalUsed", formalUsed)
|
||||
.addv("formalRemaining", Math.max(formalQuota - formalUsed, 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public NutMap switchCurrentBranchPersonType(String id, String personType) {
|
||||
String unionId = SecurityUtil.getUnionId();
|
||||
@@ -651,6 +774,14 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
|
||||
}
|
||||
NutMap quotaInfo = branchQuotaInfo(assignment.getSettingId());
|
||||
int remainingQuota = defaultInt(quotaInfo.getInt(remainingQuotaKey(targetPersonType)));
|
||||
if (ThirtyTeachTourUserAssignment.PERSON_TYPE_FORMAL.equals(targetPersonType)) {
|
||||
if (StrUtil.isBlank(assignment.getPeriodId())) {
|
||||
throw new IllegalArgumentException("当前人员未记录出行时间,不能直接转为正式人员");
|
||||
}
|
||||
// 转为正式人员会重新占用时间段名额,按人员原有时间段快照做后端兜底校验。
|
||||
NutMap periodQuotaInfo = branchPeriodQuotaInfo(assignment.getSettingId(), assignment.getPeriodId());
|
||||
remainingQuota = defaultInt(periodQuotaInfo.getInt("formalRemaining"));
|
||||
}
|
||||
if (remainingQuota <= 0) {
|
||||
throw new IllegalArgumentException("当前分工会目标人员类型暂无剩余名额");
|
||||
}
|
||||
@@ -743,6 +874,7 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
|
||||
unionId,
|
||||
unionName,
|
||||
boardingPlace,
|
||||
photoFiles,
|
||||
personType,
|
||||
assignSource,
|
||||
IFNULL(cancelled, 0) AS cancelled
|
||||
@@ -762,7 +894,7 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
|
||||
}
|
||||
|
||||
@Override
|
||||
public int backfillExistingAssignmentAfterSignup(String settingId, String matterId, String userId, String boardingPlace) {
|
||||
public int backfillExistingAssignmentAfterSignup(String settingId, String matterId, String userId, String boardingPlace, String photoFiles) {
|
||||
if (StrUtil.isBlank(settingId) || StrUtil.isBlank(matterId) || StrUtil.isBlank(userId)) {
|
||||
return 0;
|
||||
}
|
||||
@@ -777,7 +909,8 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
|
||||
.add("lineName", matterInfo.getString("lineName"))
|
||||
.add("travelAgencyId", matterInfo.getString("travelAgencyId"))
|
||||
.add("travelAgencyName", matterInfo.getString("travelAgencyName"))
|
||||
.add("boardingPlace", StrUtil.blankToDefault(boardingPlace, matterInfo.getString("defaultBoardingPlace"))),
|
||||
.add("boardingPlace", StrUtil.blankToDefault(boardingPlace, matterInfo.getString("defaultBoardingPlace")))
|
||||
.add("photoFiles", normalizePhotoFiles(photoFiles)),
|
||||
Cnd.where(ThirtyTeachTourUserAssignment::getSettingId, "=", settingId)
|
||||
.and(ThirtyTeachTourUserAssignment::getUserId, "=", userId)
|
||||
.and(ThirtyTeachTourUserAssignment::getDelFlag, "=", false));
|
||||
@@ -834,6 +967,24 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
|
||||
return assignmentDeleteInfo(fetchCurrentBranchAssignment(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSchoolAssignmentPhotoFiles(String id, String photoFiles) {
|
||||
ThirtyTeachTourUserAssignment assignment = fetchAssignmentBySource(id, ThirtyTeachTourUserAssignment.ASSIGN_SOURCE_SCHOOL_UNION);
|
||||
if (assignment == null) {
|
||||
throw new IllegalArgumentException("人员分配记录不存在或无权限操作");
|
||||
}
|
||||
updateAssignmentPhotoFiles(assignment, photoFiles);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateCurrentBranchAssignmentPhotoFiles(String id, String photoFiles) {
|
||||
ThirtyTeachTourUserAssignment assignment = fetchCurrentBranchAssignment(id);
|
||||
if (assignment == null) {
|
||||
throw new IllegalArgumentException("人员分配记录不存在或无权限操作");
|
||||
}
|
||||
updateAssignmentPhotoFiles(assignment, photoFiles);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteBySource(String id, String assignSource) {
|
||||
deleteBySource(id, assignSource, false);
|
||||
@@ -997,7 +1148,11 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
|
||||
return listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
|
||||
}
|
||||
|
||||
private Sql buildCandidateSql(String settingId) {
|
||||
/**
|
||||
* 构建人员分配候选人查询,额外带出今年暑期疗休养和去年30年教龄疗休养参加标记。
|
||||
*/
|
||||
private Sql buildCandidateSql(String settingId, Integer year) {
|
||||
int currentYear = year == null ? LocalDate.now().getYear() : year;
|
||||
return Sqls.create("""
|
||||
SELECT
|
||||
u.id AS userId,
|
||||
@@ -1011,23 +1166,49 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
|
||||
u.unitid AS unitId,
|
||||
u.unitname AS unitName,
|
||||
u.unionid AS unionId,
|
||||
u.unionname AS unionName
|
||||
u.unionname AS unionName,
|
||||
CASE WHEN IFNULL(summer_joined.joinedCount, 0) > 0 THEN 1 ELSE 0 END AS summerJoined,
|
||||
CASE WHEN IFNULL(last_thirty_joined.joinedCount, 0) > 0 THEN 1 ELSE 0 END AS lastThirtyJoined
|
||||
FROM activity_user_scope aus
|
||||
LEFT JOIN vw_user u ON u.id = aus.userId
|
||||
LEFT JOIN thirty_teach_tour_user_assignment a
|
||||
ON a.settingId = @settingId
|
||||
AND a.userId = aus.userId
|
||||
AND a.delFlag = 0
|
||||
LEFT JOIN (
|
||||
SELECT jobNo, COUNT(1) AS joinedCount
|
||||
FROM tour_ledger
|
||||
WHERE delFlag = 0
|
||||
AND joined = 1
|
||||
AND `year` = @currentYear
|
||||
GROUP BY jobNo
|
||||
) summer_joined ON summer_joined.jobNo = u.loginname
|
||||
LEFT JOIN (
|
||||
SELECT jobNo, COUNT(1) AS joinedCount
|
||||
FROM thirty_teach_tour_ledger
|
||||
WHERE delFlag = 0
|
||||
AND joined = 1
|
||||
AND `year` = @lastThirtyYear
|
||||
GROUP BY jobNo
|
||||
) last_thirty_joined ON last_thirty_joined.jobNo = u.loginname
|
||||
$condition
|
||||
""").setParam("settingId", settingId);
|
||||
""").setParam("settingId", settingId)
|
||||
.setParam("currentYear", currentYear)
|
||||
.setParam("lastThirtyYear", currentYear - 1);
|
||||
}
|
||||
|
||||
private Cnd buildCandidateCnd(String settingId, String activityGroupId, String unionId, String keyword) {
|
||||
/**
|
||||
* 组装候选人过滤条件,参游记录过滤为null时表示不限制该条件。
|
||||
*/
|
||||
private Cnd buildCandidateCnd(String activityGroupId, String unionId, String keyword,
|
||||
Boolean summerJoinedFilter, Boolean lastThirtyJoinedFilter) {
|
||||
Cnd cnd = Cnd.NEW();
|
||||
cnd.and("aus.groupId", "=", activityGroupId);
|
||||
cnd.and("u.id", "is not", null);
|
||||
cnd.and("a.id", "is", null);
|
||||
cnd.andEX("u.unionid", "=", unionId);
|
||||
appendJoinedFilter(cnd, "summer_joined.joinedCount", summerJoinedFilter);
|
||||
appendJoinedFilter(cnd, "last_thirty_joined.joinedCount", lastThirtyJoinedFilter);
|
||||
if (StrUtil.isNotBlank(keyword)) {
|
||||
SqlExpressionGroup group = new SqlExpressionGroup();
|
||||
group.orLike("u.username", keyword);
|
||||
@@ -1037,6 +1218,16 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
|
||||
return cnd;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按是否参加过对应疗休养活动追加过滤:true查已参加,false查未参加,null不追加条件。
|
||||
*/
|
||||
private void appendJoinedFilter(Cnd cnd, String countColumn, Boolean joinedFilter) {
|
||||
if (joinedFilter == null) {
|
||||
return;
|
||||
}
|
||||
cnd.and("IFNULL(" + countColumn + ", 0)", joinedFilter ? ">" : "=", 0);
|
||||
}
|
||||
|
||||
private NutMap assignmentDeleteInfo(ThirtyTeachTourUserAssignment assignment) {
|
||||
int ledgerCount = countAssignmentLedgers(assignment);
|
||||
return NutMap.NEW()
|
||||
@@ -1089,6 +1280,28 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
|
||||
.and(ThirtyTeachTourLedger::getDelFlag, "=", false));
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新人员分配图片材料,并同步同一事项和工号下已生成的报名台账。
|
||||
* 未选择线路或尚未生成台账时,只更新人员分配表,保证主列表上传不会强制产生台账数据。
|
||||
*/
|
||||
private void updateAssignmentPhotoFiles(ThirtyTeachTourUserAssignment assignment, String photoFiles) {
|
||||
if (assignment == null || StrUtil.isBlank(assignment.getId())) {
|
||||
throw new IllegalArgumentException("人员分配记录不存在或无权限操作");
|
||||
}
|
||||
String normalizedPhotoFiles = normalizePhotoFiles(photoFiles);
|
||||
update(Chain.make("photoFiles", normalizedPhotoFiles),
|
||||
Cnd.where(ThirtyTeachTourUserAssignment::getId, "=", assignment.getId())
|
||||
.and(ThirtyTeachTourUserAssignment::getDelFlag, "=", false));
|
||||
if (StrUtil.isBlank(assignment.getMatterId()) || StrUtil.isBlank(assignment.getLoginName())) {
|
||||
return;
|
||||
}
|
||||
dao().update(ThirtyTeachTourLedger.class,
|
||||
Chain.make("photoFiles", normalizedPhotoFiles),
|
||||
Cnd.where(ThirtyTeachTourLedger::getMatterId, "=", assignment.getMatterId())
|
||||
.and(ThirtyTeachTourLedger::getJobNo, "=", assignment.getLoginName())
|
||||
.and(ThirtyTeachTourLedger::getDelFlag, "=", false));
|
||||
}
|
||||
|
||||
private void deleteAssignmentLedgers(ThirtyTeachTourUserAssignment assignment) {
|
||||
List<ThirtyTeachTourLedger> ledgers = dao().query(ThirtyTeachTourLedger.class, Cnd.where(ThirtyTeachTourLedger::getMatterId, "=", assignment.getMatterId())
|
||||
.and(ThirtyTeachTourLedger::getJobNo, "=", assignment.getLoginName())
|
||||
@@ -1145,6 +1358,11 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
|
||||
String personType = normalizePersonType(assignment.getPersonType());
|
||||
NutMap quotaInfo = branchQuotaInfo(assignment.getSettingId());
|
||||
int remainingQuota = defaultInt(quotaInfo.getInt(remainingQuotaKey(personType)));
|
||||
if (ThirtyTeachTourUserAssignment.PERSON_TYPE_FORMAL.equals(personType) && StrUtil.isNotBlank(assignment.getPeriodId())) {
|
||||
// 恢复已退出正式人员时,同步按该人员原时间段重新占用细分名额。
|
||||
NutMap periodQuotaInfo = branchPeriodQuotaInfo(assignment.getSettingId(), assignment.getPeriodId());
|
||||
remainingQuota = defaultInt(periodQuotaInfo.getInt("formalRemaining"));
|
||||
}
|
||||
// 恢复已退出人员会重新占用当前人员类型名额,因此恢复前必须按正式/替补剩余名额校验。
|
||||
if (remainingQuota <= 0) {
|
||||
throw new IllegalArgumentException("当前分工会目标人员类型暂无剩余名额");
|
||||
@@ -1332,6 +1550,8 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
|
||||
ThirtyTeachTourUserAssignment assignment = new ThirtyTeachTourUserAssignment();
|
||||
assignment.setSettingId(settingId);
|
||||
fillMatterSnapshot(assignment, matterInfo);
|
||||
// 图片材料来自人员分配/报名提交快照,不能放在事项快照方法中读取。
|
||||
assignment.setPhotoFiles(normalizePhotoFiles(user.getString("photoFiles")));
|
||||
assignment.setUserId(user.getString("userId"));
|
||||
assignment.setLoginName(user.getString("loginName"));
|
||||
assignment.setUserName(user.getString("userName"));
|
||||
@@ -1365,7 +1585,18 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
|
||||
assignment.setBoardingPlace(matterInfo.getString("defaultBoardingPlace"));
|
||||
}
|
||||
|
||||
private void updateAssignmentMatterSnapshot(String id, NutMap matterInfo, String assignSource) {
|
||||
/**
|
||||
* 保存分工会正式人员时保留时间段快照,后续名额使用统计直接按人员分配表 periodId 汇总。
|
||||
*/
|
||||
private void fillTravelPeriodSnapshot(ThirtyTeachTourUserAssignment assignment, ThirtyTeachTourSettingTravelPeriod period) {
|
||||
if (period == null) {
|
||||
return;
|
||||
}
|
||||
assignment.setPeriodId(period.getId());
|
||||
assignment.setPeriodName(period.getPeriodName());
|
||||
}
|
||||
|
||||
private void updateAssignmentMatterSnapshot(String id, NutMap matterInfo, String assignSource, String photoFiles) {
|
||||
// 补选事项时同时回写人员分配表事项快照,后续列表筛选无需再跨表追溯事项信息。
|
||||
update(Chain.make("matterId", matterInfo.getString("matterId"))
|
||||
.add("matterName", matterInfo.getString("matterName"))
|
||||
@@ -1373,7 +1604,8 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
|
||||
.add("lineName", matterInfo.getString("lineName"))
|
||||
.add("travelAgencyId", matterInfo.getString("travelAgencyId"))
|
||||
.add("travelAgencyName", matterInfo.getString("travelAgencyName"))
|
||||
.add("boardingPlace", matterInfo.getString("defaultBoardingPlace")),
|
||||
.add("boardingPlace", matterInfo.getString("defaultBoardingPlace"))
|
||||
.add("photoFiles", normalizePhotoFiles(photoFiles)),
|
||||
Cnd.where(ThirtyTeachTourUserAssignment::getId, "=", id)
|
||||
.and(ThirtyTeachTourUserAssignment::getAssignSource, "=", assignSource));
|
||||
}
|
||||
@@ -1484,7 +1716,9 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
|
||||
sql.setCondition(cnd);
|
||||
List<NutMap> users = listMap(sql);
|
||||
if (Lang.isNotEmpty(users)) {
|
||||
return users.get(0);
|
||||
NutMap user = users.get(0);
|
||||
user.put("photoFiles", assignment.getPhotoFiles());
|
||||
return user;
|
||||
}
|
||||
// 视图中找不到人员时使用分配表快照兜底,保证补选事项不会因非关键字段缺失中断。
|
||||
return NutMap.NEW()
|
||||
@@ -1496,7 +1730,8 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
|
||||
.addv("unitId", assignment.getUnitId())
|
||||
.addv("unitName", assignment.getUnitName())
|
||||
.addv("unionId", assignment.getUnionId())
|
||||
.addv("unionName", assignment.getUnionName());
|
||||
.addv("unionName", assignment.getUnionName())
|
||||
.addv("photoFiles", assignment.getPhotoFiles());
|
||||
}
|
||||
|
||||
private ThirtyTeachTourLedger buildProxySignupLedger(NutMap matterInfo, NutMap user) {
|
||||
@@ -1518,6 +1753,7 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
|
||||
ledger.setTravelAgencyId(matterInfo.getString("travelAgencyId"));
|
||||
ledger.setTravelAgencyName(matterInfo.getString("travelAgencyName"));
|
||||
ledger.setBoardingPlace(matterInfo.getString("defaultBoardingPlace"));
|
||||
ledger.setPhotoFiles(normalizePhotoFiles(user.getString("photoFiles")));
|
||||
ledger.setHasFamily(false);
|
||||
ledger.setJoined(false);
|
||||
ledger.setReimbursed(false);
|
||||
@@ -1575,6 +1811,33 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 清洗多图上传返回值,统一保存为逗号分隔路径,避免空白项和重复路径写入快照表。
|
||||
*/
|
||||
private String normalizePhotoFiles(String photoFiles) {
|
||||
if (StrUtil.isBlank(photoFiles)) {
|
||||
return "";
|
||||
}
|
||||
List<String> photos = StrUtil.splitTrim(photoFiles, ",");
|
||||
if (Lang.isEmpty(photos)) {
|
||||
return "";
|
||||
}
|
||||
return photos.stream()
|
||||
.filter(StrUtil::isNotBlank)
|
||||
.distinct()
|
||||
.collect(Collectors.joining(","));
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量人员共用同一组上传图片时,将图片路径写入候选人快照,后续构建分配表和台账时直接读取。
|
||||
*/
|
||||
private void fillCandidatePhotoFiles(List<NutMap> candidateUsers, String photoFiles) {
|
||||
if (Lang.isEmpty(candidateUsers)) {
|
||||
return;
|
||||
}
|
||||
candidateUsers.forEach(user -> user.put("photoFiles", photoFiles));
|
||||
}
|
||||
|
||||
private Pagination<NutMap> emptyPagination(PageForm pageForm) {
|
||||
int pageNumber = pageForm == null || pageForm.getPageNumber() == null ? 1 : pageForm.getPageNumber();
|
||||
int pageSize = pageForm == null || pageForm.getPageSize() == null ? 10 : pageForm.getPageSize();
|
||||
@@ -1591,6 +1854,60 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
|
||||
.addv("backupRemaining", 0);
|
||||
}
|
||||
|
||||
private NutMap emptyPeriodQuotaInfo(String periodId) {
|
||||
return NutMap.NEW()
|
||||
.addv("periodId", periodId)
|
||||
.addv("formalQuota", 0)
|
||||
.addv("formalUsed", 0)
|
||||
.addv("formalRemaining", 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 正式分配只允许选择当前配置下已启用的时间段,避免前端传入历史或其它配置的时间段ID。
|
||||
*/
|
||||
private ThirtyTeachTourSettingTravelPeriod fetchEnabledTravelPeriod(String settingId, String periodId) {
|
||||
if (StrUtil.isBlank(settingId) || StrUtil.isBlank(periodId)) {
|
||||
return null;
|
||||
}
|
||||
return dao().fetch(ThirtyTeachTourSettingTravelPeriod.class,
|
||||
Cnd.where("id", "=", periodId)
|
||||
.and("settingId", "=", settingId)
|
||||
.and("enabled", "=", true)
|
||||
.and("delFlag", "=", false));
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存正式人员前锁定当前分工会当前时间段的名额行,使名额校验和人员插入在同一事务内串行执行。
|
||||
*/
|
||||
private void lockBranchPeriodQuota(String settingId, String periodId, String unionId) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT id
|
||||
FROM thirty_teach_tour_setting_period_quota
|
||||
WHERE settingId = @settingId
|
||||
AND periodId = @periodId
|
||||
AND unionId = @unionId
|
||||
AND delFlag = 0
|
||||
FOR UPDATE
|
||||
""");
|
||||
sql.setParam("settingId", settingId);
|
||||
sql.setParam("periodId", periodId);
|
||||
sql.setParam("unionId", unionId);
|
||||
List<NutMap> rows = listMap(sql);
|
||||
if (Lang.isEmpty(rows)) {
|
||||
throw new IllegalArgumentException("当前分工会未配置该出行时间名额");
|
||||
}
|
||||
}
|
||||
|
||||
private String travelPeriodButtonLabel(ThirtyTeachTourSettingTravelPeriod period) {
|
||||
if (period == null) {
|
||||
return "";
|
||||
}
|
||||
if (StrUtil.isNotBlank(period.getStartDate()) && StrUtil.isNotBlank(period.getEndDate())) {
|
||||
return period.getStartDate().replace("-", "") + "-" + period.getEndDate().replace("-", "");
|
||||
}
|
||||
return StrUtil.blankToDefault(period.getPeriodName(), "");
|
||||
}
|
||||
|
||||
private String remainingQuotaKey(String personType) {
|
||||
return ThirtyTeachTourUserAssignment.PERSON_TYPE_BACKUP.equals(personType) ? "backupRemaining" : "formalRemaining";
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
-- 30年教龄疗休养报名图片材料字段。
|
||||
-- PC端选择线路、移动端提交报名上传的多张图片统一保存为逗号分隔路径。
|
||||
ALTER TABLE `thirty_teach_tour_user_assignment`
|
||||
ADD COLUMN `photoFiles` text DEFAULT NULL COMMENT '报名图片材料' AFTER `boardingPlace`;
|
||||
|
||||
ALTER TABLE `thirty_teach_tour_ledger`
|
||||
ADD COLUMN `photoFiles` text DEFAULT NULL COMMENT '报名图片材料' AFTER `boardingPlace`;
|
||||
@@ -22,6 +22,7 @@ CREATE TABLE IF NOT EXISTS `thirty_teach_tour_ledger` (
|
||||
`travelAgencyId` varchar(32) DEFAULT NULL COMMENT '旅行社ID',
|
||||
`travelAgencyName` varchar(100) DEFAULT NULL COMMENT '旅行社',
|
||||
`boardingPlace` varchar(100) DEFAULT NULL COMMENT '乘车地点',
|
||||
`photoFiles` text DEFAULT NULL COMMENT '报名图片材料',
|
||||
`hasFamily` tinyint(1) DEFAULT 0 COMMENT '是否携带家属',
|
||||
`intendedRoommate` varchar(100) DEFAULT NULL COMMENT '意向拼床人',
|
||||
`bedType` varchar(50) DEFAULT NULL COMMENT '床型',
|
||||
|
||||
@@ -74,6 +74,42 @@ CREATE TABLE IF NOT EXISTS `thirty_teach_tour_setting_union_quota` (
|
||||
KEY `idx_thirty_teach_tour_setting_union_quota_union` (`unionId`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='疗休养配置分工会名额分配';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `thirty_teach_tour_setting_travel_period` (
|
||||
`id` varchar(32) NOT NULL COMMENT 'ID',
|
||||
`settingId` varchar(32) DEFAULT NULL COMMENT '所属疗休养配置ID',
|
||||
`periodName` varchar(50) DEFAULT NULL COMMENT '时间段名称',
|
||||
`startDate` varchar(10) DEFAULT NULL COMMENT '开始日期',
|
||||
`endDate` varchar(10) DEFAULT NULL COMMENT '结束日期',
|
||||
`enabled` tinyint(1) DEFAULT 1 COMMENT '是否启用',
|
||||
`createdBy` varchar(32) DEFAULT NULL COMMENT '创建人',
|
||||
`createdAt` bigint DEFAULT NULL COMMENT '创建时间',
|
||||
`updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人',
|
||||
`updatedAt` bigint DEFAULT NULL COMMENT '修改时间',
|
||||
`delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_thirty_teach_tour_setting_period_setting` (`settingId`),
|
||||
KEY `idx_thirty_teach_tour_setting_period_date` (`startDate`, `endDate`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='30年教龄疗休养配置时间段';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `thirty_teach_tour_setting_period_quota` (
|
||||
`id` varchar(32) NOT NULL COMMENT 'ID',
|
||||
`settingId` varchar(32) DEFAULT NULL COMMENT '所属疗休养配置ID',
|
||||
`unionId` varchar(32) DEFAULT NULL COMMENT '分工会ID',
|
||||
`unionName` varchar(100) DEFAULT NULL COMMENT '分工会名称',
|
||||
`periodId` varchar(32) DEFAULT NULL COMMENT '时间段ID',
|
||||
`periodName` varchar(50) DEFAULT NULL COMMENT '时间段名称',
|
||||
`formalQuota` int DEFAULT 0 COMMENT '正式人员名额',
|
||||
`createdBy` varchar(32) DEFAULT NULL COMMENT '创建人',
|
||||
`createdAt` bigint DEFAULT NULL COMMENT '创建时间',
|
||||
`updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人',
|
||||
`updatedAt` bigint DEFAULT NULL COMMENT '修改时间',
|
||||
`delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_thirty_teach_tour_period_quota_setting` (`settingId`),
|
||||
KEY `idx_thirty_teach_tour_period_quota_union` (`unionId`),
|
||||
KEY `idx_thirty_teach_tour_period_quota_period` (`periodId`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='30年教龄疗休养配置时间段名额';
|
||||
|
||||
-- 疗休养人员分配表。
|
||||
CREATE TABLE IF NOT EXISTS `thirty_teach_tour_user_assignment` (
|
||||
`id` varchar(32) NOT NULL COMMENT 'ID',
|
||||
@@ -85,6 +121,8 @@ CREATE TABLE IF NOT EXISTS `thirty_teach_tour_user_assignment` (
|
||||
`lineId` varchar(32) DEFAULT NULL COMMENT '线路ID',
|
||||
`lineName` varchar(100) DEFAULT NULL COMMENT '线路名称',
|
||||
`boardingPlace` varchar(100) DEFAULT NULL COMMENT '乘车地点',
|
||||
`periodId` varchar(32) DEFAULT NULL COMMENT '出行时间段ID',
|
||||
`periodName` varchar(50) DEFAULT NULL COMMENT '出行时间段名称',
|
||||
`userId` varchar(32) DEFAULT NULL COMMENT '人员ID',
|
||||
`loginName` varchar(50) DEFAULT NULL COMMENT '工号',
|
||||
`userName` varchar(100) DEFAULT NULL COMMENT '姓名',
|
||||
@@ -112,6 +150,7 @@ CREATE TABLE IF NOT EXISTS `thirty_teach_tour_user_assignment` (
|
||||
KEY `idx_thirty_teach_tour_user_assignment_setting` (`settingId`),
|
||||
KEY `idx_thirty_teach_tour_user_assignment_user` (`userId`),
|
||||
KEY `idx_thirty_teach_tour_user_assignment_union` (`unionId`),
|
||||
KEY `idx_thirty_teach_tour_user_assignment_period` (`periodId`),
|
||||
KEY `idx_thirty_teach_tour_user_assignment_source` (`assignSource`),
|
||||
KEY `idx_thirty_teach_tour_user_assignment_person_type` (`personType`),
|
||||
KEY `idx_thirty_teach_tour_user_assignment_cancelled` (`cancelled`)
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
CREATE TABLE IF NOT EXISTS `thirty_teach_tour_setting_travel_period` (
|
||||
`id` varchar(32) NOT NULL COMMENT 'ID',
|
||||
`settingId` varchar(32) DEFAULT NULL COMMENT '所属疗休养配置ID',
|
||||
`periodName` varchar(50) DEFAULT NULL COMMENT '时间段名称',
|
||||
`startDate` varchar(10) DEFAULT NULL COMMENT '开始日期',
|
||||
`endDate` varchar(10) DEFAULT NULL COMMENT '结束日期',
|
||||
`enabled` tinyint(1) DEFAULT 1 COMMENT '是否启用',
|
||||
`createdBy` varchar(32) DEFAULT NULL COMMENT '创建人',
|
||||
`createdAt` bigint DEFAULT NULL COMMENT '创建时间',
|
||||
`updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人',
|
||||
`updatedAt` bigint DEFAULT NULL COMMENT '修改时间',
|
||||
`delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_thirty_teach_tour_setting_period_setting` (`settingId`),
|
||||
KEY `idx_thirty_teach_tour_setting_period_date` (`startDate`, `endDate`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='30年教龄疗休养配置时间段';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `thirty_teach_tour_setting_period_quota` (
|
||||
`id` varchar(32) NOT NULL COMMENT 'ID',
|
||||
`settingId` varchar(32) DEFAULT NULL COMMENT '所属疗休养配置ID',
|
||||
`unionId` varchar(32) DEFAULT NULL COMMENT '分工会ID',
|
||||
`unionName` varchar(100) DEFAULT NULL COMMENT '分工会名称',
|
||||
`periodId` varchar(32) DEFAULT NULL COMMENT '时间段ID',
|
||||
`periodName` varchar(50) DEFAULT NULL COMMENT '时间段名称',
|
||||
`formalQuota` int DEFAULT 0 COMMENT '正式人员名额',
|
||||
`createdBy` varchar(32) DEFAULT NULL COMMENT '创建人',
|
||||
`createdAt` bigint DEFAULT NULL COMMENT '创建时间',
|
||||
`updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人',
|
||||
`updatedAt` bigint DEFAULT NULL COMMENT '修改时间',
|
||||
`delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_thirty_teach_tour_period_quota_setting` (`settingId`),
|
||||
KEY `idx_thirty_teach_tour_period_quota_union` (`unionId`),
|
||||
KEY `idx_thirty_teach_tour_period_quota_period` (`periodId`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='30年教龄疗休养配置时间段名额';
|
||||
@@ -0,0 +1,5 @@
|
||||
-- 分工会正式人员分配按配置时间段占用名额,人员分配表保留所选时间段快照。
|
||||
ALTER TABLE `thirty_teach_tour_user_assignment`
|
||||
ADD COLUMN `periodId` varchar(32) DEFAULT NULL COMMENT '出行时间段ID' AFTER `boardingPlace`,
|
||||
ADD COLUMN `periodName` varchar(50) DEFAULT NULL COMMENT '出行时间段名称' AFTER `periodId`,
|
||||
ADD KEY `idx_thirty_teach_tour_user_assignment_period` (`periodId`);
|
||||
@@ -9,6 +9,9 @@ CREATE TABLE IF NOT EXISTS `thirty_teach_tour_user_assignment` (
|
||||
`lineId` varchar(32) DEFAULT NULL COMMENT '线路ID',
|
||||
`lineName` varchar(100) DEFAULT NULL COMMENT '线路名称',
|
||||
`boardingPlace` varchar(100) DEFAULT NULL COMMENT '乘车地点',
|
||||
`photoFiles` text DEFAULT NULL COMMENT '报名图片材料',
|
||||
`periodId` varchar(32) DEFAULT NULL COMMENT '出行时间段ID',
|
||||
`periodName` varchar(50) DEFAULT NULL COMMENT '出行时间段名称',
|
||||
`userId` varchar(32) DEFAULT NULL COMMENT '人员ID',
|
||||
`loginName` varchar(50) DEFAULT NULL COMMENT '工号',
|
||||
`userName` varchar(100) DEFAULT NULL COMMENT '姓名',
|
||||
@@ -36,6 +39,7 @@ CREATE TABLE IF NOT EXISTS `thirty_teach_tour_user_assignment` (
|
||||
KEY `idx_thirty_teach_tour_user_assignment_setting` (`settingId`),
|
||||
KEY `idx_thirty_teach_tour_user_assignment_user` (`userId`),
|
||||
KEY `idx_thirty_teach_tour_user_assignment_union` (`unionId`),
|
||||
KEY `idx_thirty_teach_tour_user_assignment_period` (`periodId`),
|
||||
KEY `idx_thirty_teach_tour_user_assignment_source` (`assignSource`),
|
||||
KEY `idx_thirty_teach_tour_user_assignment_person_type` (`personType`),
|
||||
KEY `idx_thirty_teach_tour_user_assignment_cancelled` (`cancelled`)
|
||||
|
||||
+307
-14
@@ -69,7 +69,13 @@ 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="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="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">
|
||||
<template slot-scope="{row}">
|
||||
{{ photoFileCount(row) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="人员类型" prop="personType" width="110" sortable="custom" align="center" header-align="center">
|
||||
<template slot-scope="{row}">
|
||||
<el-tag size="mini" :type="row.personType === 'BACKUP' ? 'warning' : 'success'">{{ personTypeText(row.personType) }}</el-tag>
|
||||
@@ -81,9 +87,10 @@ layout("/layouts/platform.html"){
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="分配时间" prop="assignedAt" width="170" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="操作" width="380" align="center" header-align="center" fixed="right">
|
||||
<el-table-column label="操作" width="470" align="center" header-align="center" fixed="right">
|
||||
<template slot-scope="{row}">
|
||||
<el-button v-if="!row.matterId && row.personType !== 'BACKUP' && $auth.hasPermission('thirtyTeachTour.branchUserAssignment.selectMatter')" size="mini" type="primary" @click="openSelectMatter(row)">选择路线</el-button>
|
||||
<el-button v-if="$auth.hasPermission('thirtyTeachTour.branchUserAssignment.assign')" size="mini" type="primary" @click="openListPhotoUpload(row)">{{ row.photoFiles ? "已上传" : "上传图片" }}</el-button>
|
||||
<el-button v-if="$auth.hasPermission('thirtyTeachTour.branchUserAssignment.switchPersonType')" size="mini" type="primary" :loading="personTypeSwitching === row.id" @click="switchPersonType(row, row.personType === 'BACKUP' ? 'FORMAL' : 'BACKUP')">{{ row.personType === 'BACKUP' ? '转为正式' : '转为替补' }}</el-button>
|
||||
<el-button v-if="isCancelled(row) && $auth.hasPermission('thirtyTeachTour.branchUserAssignment.cancelRestore')" size="mini" type="warning" @click="restoreCancel(row)">取消退出</el-button>
|
||||
<el-button v-if="$auth.hasPermission('thirtyTeachTour.branchUserAssignment.delete')" size="mini" type="danger" @click="doDelete(row)">删除</el-button>
|
||||
@@ -129,7 +136,7 @@ layout("/layouts/platform.html"){
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="人员类型" prop="personType">
|
||||
<el-radio-group v-model="assignForm.personType" @change="assignPersonTypeChange">
|
||||
<el-radio-button label="FORMAL">正式人员</el-radio-button>
|
||||
@@ -137,7 +144,22 @@ layout("/layouts/platform.html"){
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-col :span="18" v-if="assignForm.personType === 'FORMAL'">
|
||||
<el-form-item label="出行时间">
|
||||
<div class="period-button-list">
|
||||
<el-button
|
||||
v-for="item in assignPeriodOptions"
|
||||
:key="item.id"
|
||||
size="mini"
|
||||
:type="assignForm.periodId === item.id ? 'primary' : 'default'"
|
||||
@click="toggleAssignPeriod(item)">
|
||||
{{ item.label || item.periodName }}
|
||||
</el-button>
|
||||
<span v-if="assignPeriodOptions.length === 0" class="period-empty">暂无启用时间段</span>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<div class="quota-bar">
|
||||
@@ -190,6 +212,13 @@ layout("/layouts/platform.html"){
|
||||
<el-table-column label="手机号" prop="mobile" width="130" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="所属分工会" prop="unionName" min-width="160" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="所在单位" prop="unitName" min-width="220" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="材料" width="110" align="center" header-align="center">
|
||||
<template slot-scope="{row}">
|
||||
<el-button type="text" size="mini" :disabled="!isCandidateSelected(row)" @click="openPhotoUpload(row)">
|
||||
{{ row.photoFiles ? "已上传" : "上传" }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-row class="el-pagination-container candidate-pagination">
|
||||
<el-pagination
|
||||
@@ -209,6 +238,64 @@ layout("/layouts/platform.html"){
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog
|
||||
title="上传图片材料"
|
||||
:visible.sync="photoUploadDialogVisible"
|
||||
:close-on-click-modal="false"
|
||||
width="520px"
|
||||
@closed="resetPhotoUploadDialog">
|
||||
<el-form :model="photoUploadForm" label-width="100px">
|
||||
<el-form-item label="人员">
|
||||
<el-input v-model="photoUploadForm.userName" disabled></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="图片材料">
|
||||
<file-upload
|
||||
style="--upload-width: 96px;--upload-height:96px"
|
||||
:upload_number="9"
|
||||
:upload_size="20971520"
|
||||
:value.sync="photoUploadForm.photoFiles"
|
||||
accept=".jpg,.jpeg,.png"
|
||||
complete_result
|
||||
upload_mode="image"
|
||||
upload_result_category="interval">
|
||||
</file-upload>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="photoUploadDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="confirmPhotoUpload">确定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog
|
||||
title="上传图片材料"
|
||||
:visible.sync="listPhotoDialogVisible"
|
||||
:close-on-click-modal="false"
|
||||
width="520px"
|
||||
@closed="resetListPhotoUploadDialog">
|
||||
<el-form :model="listPhotoForm" label-width="100px">
|
||||
<el-form-item label="人员">
|
||||
<el-input v-model="listPhotoForm.userName" disabled></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="图片材料">
|
||||
<file-upload
|
||||
style="--upload-width: 96px;--upload-height:96px"
|
||||
:upload_number="9"
|
||||
:upload_size="20971520"
|
||||
:value.sync="listPhotoForm.photoFiles"
|
||||
accept=".jpg,.jpeg,.png"
|
||||
complete_result
|
||||
upload_mode="image"
|
||||
upload_result_category="interval">
|
||||
</file-upload>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="listPhotoDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="listPhotoSubmitting" @click="saveListPhotoFiles">保存</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog
|
||||
title="选择分配路线"
|
||||
:visible.sync="selectMatterDialogVisible"
|
||||
@@ -224,6 +311,18 @@ layout("/layouts/platform.html"){
|
||||
<el-option v-for="item in selectMatterOptions" :key="item.matterId" :label="matterOptionLabel(item)" :value="item.matterId"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="图片材料">
|
||||
<file-upload
|
||||
style="--upload-width: 96px;--upload-height:96px"
|
||||
:upload_number="9"
|
||||
:upload_size="20971520"
|
||||
:value.sync="selectMatterForm.photoFiles"
|
||||
accept=".jpg,.jpeg,.png"
|
||||
complete_result
|
||||
upload_mode="image"
|
||||
upload_result_category="interval">
|
||||
</file-upload>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="selectMatterDialogVisible = false">取消</el-button>
|
||||
@@ -272,6 +371,24 @@ layout("/layouts/platform.html"){
|
||||
color: #606266;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.period-button-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
min-height: 32px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.period-button-list .el-button + .el-button {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.period-empty {
|
||||
color: #909399;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.candidate-pagination {
|
||||
margin-top: 12px;
|
||||
margin-bottom: 0;
|
||||
@@ -311,6 +428,7 @@ layout("/layouts/platform.html"){
|
||||
pageMatterOptions: [],
|
||||
assignSettingOptions: [],
|
||||
assignMatterOptions: [],
|
||||
assignPeriodOptions: [],
|
||||
quotaInfo: {
|
||||
formalQuota: 0,
|
||||
backupQuota: 0,
|
||||
@@ -327,9 +445,24 @@ layout("/layouts/platform.html"){
|
||||
selectedCandidates: [],
|
||||
selectedCandidateIds: [],
|
||||
assignSubmitting: false,
|
||||
photoUploadDialogVisible: false,
|
||||
photoUploadRow: null,
|
||||
photoUploadForm: {
|
||||
userId: "",
|
||||
userName: "",
|
||||
photoFiles: ""
|
||||
},
|
||||
selectMatterDialogVisible: false,
|
||||
selectMatterSubmitting: false,
|
||||
selectMatterOptions: [],
|
||||
listPhotoDialogVisible: false,
|
||||
listPhotoSubmitting: false,
|
||||
listPhotoRow: null,
|
||||
listPhotoForm: {
|
||||
id: "",
|
||||
userName: "",
|
||||
photoFiles: ""
|
||||
},
|
||||
personTypeSwitching: "",
|
||||
pageForm: {
|
||||
pageNumber: 1,
|
||||
@@ -347,6 +480,7 @@ layout("/layouts/platform.html"){
|
||||
year: currentYear,
|
||||
settingId: "",
|
||||
matterId: "",
|
||||
periodId: "",
|
||||
personType: "FORMAL"
|
||||
},
|
||||
candidateForm: {
|
||||
@@ -359,7 +493,8 @@ layout("/layouts/platform.html"){
|
||||
id: "",
|
||||
settingId: "",
|
||||
matterId: "",
|
||||
userName: ""
|
||||
userName: "",
|
||||
photoFiles: ""
|
||||
},
|
||||
assignRules: {
|
||||
year: [{required: true, message: "请选择年度", trigger: ["change", "blur"]}],
|
||||
@@ -377,6 +512,7 @@ layout("/layouts/platform.html"){
|
||||
year: currentYear || moment().format("YYYY"),
|
||||
settingId: "",
|
||||
matterId: "",
|
||||
periodId: "",
|
||||
personType: "FORMAL"
|
||||
}
|
||||
},
|
||||
@@ -403,7 +539,8 @@ layout("/layouts/platform.html"){
|
||||
id: "",
|
||||
settingId: "",
|
||||
matterId: "",
|
||||
userName: ""
|
||||
userName: "",
|
||||
photoFiles: ""
|
||||
}
|
||||
},
|
||||
resetSearch() {
|
||||
@@ -466,6 +603,7 @@ layout("/layouts/platform.html"){
|
||||
const year = this.pageForm.year || moment().format("YYYY")
|
||||
this.assignForm = this.defaultAssignForm(year)
|
||||
this.candidateForm = this.defaultCandidateForm()
|
||||
this.assignPeriodOptions = []
|
||||
this.quotaInfo = this.defaultQuotaInfo()
|
||||
this.candidateData = []
|
||||
this.selectedCandidates = []
|
||||
@@ -478,6 +616,7 @@ layout("/layouts/platform.html"){
|
||||
this.assignForm = this.defaultAssignForm(moment().format("YYYY"))
|
||||
this.candidateForm = this.defaultCandidateForm()
|
||||
this.assignMatterOptions = []
|
||||
this.assignPeriodOptions = []
|
||||
this.quotaInfo = this.defaultQuotaInfo()
|
||||
this.candidateData = []
|
||||
this.selectedCandidates = []
|
||||
@@ -488,7 +627,9 @@ layout("/layouts/platform.html"){
|
||||
assignYearChange() {
|
||||
this.assignForm.settingId = ""
|
||||
this.assignForm.matterId = ""
|
||||
this.assignForm.periodId = ""
|
||||
this.assignMatterOptions = []
|
||||
this.assignPeriodOptions = []
|
||||
this.quotaInfo = this.defaultQuotaInfo()
|
||||
this.selectedCandidateIds = []
|
||||
this.candidateUserOptions = []
|
||||
@@ -498,19 +639,22 @@ layout("/layouts/platform.html"){
|
||||
},
|
||||
assignSettingChange() {
|
||||
this.assignForm.matterId = ""
|
||||
this.assignForm.periodId = ""
|
||||
this.assignMatterOptions = []
|
||||
this.assignPeriodOptions = []
|
||||
this.selectedCandidateIds = []
|
||||
this.candidateUserOptions = []
|
||||
this.candidateForm.keyword = ""
|
||||
this.clearCandidateSelection()
|
||||
this.loadAssignMatterOptions()
|
||||
this.loadQuotaInfo()
|
||||
this.loadAssignTravelPeriods(true)
|
||||
this.loadCandidatePageData()
|
||||
},
|
||||
assignPersonTypeChange() {
|
||||
if (this.assignForm.personType === "BACKUP") {
|
||||
this.assignForm.matterId = ""
|
||||
}
|
||||
this.loadQuotaInfo()
|
||||
},
|
||||
loadAssignSettingOptions() {
|
||||
this.$axios.post(loc() + "/settingOptions", {year: this.assignForm.year}).then((res) => {
|
||||
@@ -520,14 +664,15 @@ layout("/layouts/platform.html"){
|
||||
if (!this.assignForm.settingId && this.assignSettingOptions.length > 0) {
|
||||
this.assignForm.settingId = this.assignSettingOptions[0].id
|
||||
this.loadAssignMatterOptions()
|
||||
this.loadQuotaInfo()
|
||||
this.loadAssignTravelPeriods(true)
|
||||
this.loadCandidatePageData()
|
||||
} else if (this.assignForm.settingId) {
|
||||
this.loadAssignMatterOptions()
|
||||
this.loadQuotaInfo()
|
||||
this.loadAssignTravelPeriods(true)
|
||||
this.loadCandidatePageData()
|
||||
} else {
|
||||
this.assignMatterOptions = []
|
||||
this.assignPeriodOptions = []
|
||||
this.quotaInfo = this.defaultQuotaInfo()
|
||||
this.candidateData = []
|
||||
this.candidateForm.totalCount = 0
|
||||
@@ -535,6 +680,29 @@ layout("/layouts/platform.html"){
|
||||
}
|
||||
})
|
||||
},
|
||||
loadAssignTravelPeriods(defaultFirst) {
|
||||
if (!this.assignForm.settingId) {
|
||||
this.assignPeriodOptions = []
|
||||
this.assignForm.periodId = ""
|
||||
this.loadQuotaInfo()
|
||||
return
|
||||
}
|
||||
this.$axios.post(loc() + "/travelPeriodOptions", {settingId: this.assignForm.settingId}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.assignPeriodOptions = res.data || []
|
||||
const hasCurrent = this.assignPeriodOptions.some(item => item.id === this.assignForm.periodId)
|
||||
if ((defaultFirst || !hasCurrent) && this.assignPeriodOptions.length > 0) {
|
||||
this.assignForm.periodId = this.assignPeriodOptions[0].id
|
||||
} else if (!hasCurrent) {
|
||||
this.assignForm.periodId = ""
|
||||
}
|
||||
} else {
|
||||
this.assignPeriodOptions = []
|
||||
this.assignForm.periodId = ""
|
||||
}
|
||||
this.loadQuotaInfo()
|
||||
})
|
||||
},
|
||||
loadAssignMatterOptions() {
|
||||
if (!this.assignForm.settingId) {
|
||||
this.assignMatterOptions = []
|
||||
@@ -553,10 +721,41 @@ layout("/layouts/platform.html"){
|
||||
}
|
||||
this.$axios.post(loc() + "/quotaInfo", {settingId: this.assignForm.settingId}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.quotaInfo = Object.assign(this.defaultQuotaInfo(), res.data || {})
|
||||
const baseQuota = Object.assign(this.defaultQuotaInfo(), res.data || {})
|
||||
if (this.assignForm.personType === "FORMAL") {
|
||||
baseQuota.formalQuota = 0
|
||||
baseQuota.formalUsed = 0
|
||||
baseQuota.formalRemaining = 0
|
||||
}
|
||||
this.quotaInfo = baseQuota
|
||||
if (this.assignForm.personType === "FORMAL" && this.assignForm.periodId) {
|
||||
this.loadPeriodQuotaInfo()
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
loadPeriodQuotaInfo() {
|
||||
this.$axios.post(loc() + "/periodQuotaInfo", {
|
||||
settingId: this.assignForm.settingId,
|
||||
periodId: this.assignForm.periodId
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
const periodQuota = res.data || {}
|
||||
this.quotaInfo = Object.assign({}, this.quotaInfo, {
|
||||
formalQuota: periodQuota.formalQuota || 0,
|
||||
formalUsed: periodQuota.formalUsed || 0,
|
||||
formalRemaining: periodQuota.formalRemaining || 0
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
toggleAssignPeriod(item) {
|
||||
this.assignForm.periodId = this.assignForm.periodId === item.id ? "" : item.id
|
||||
this.candidateForm.pageNumber = 1
|
||||
this.clearCandidateSelection()
|
||||
this.loadQuotaInfo()
|
||||
this.loadCandidatePageData()
|
||||
},
|
||||
loadCandidatePageData() {
|
||||
if (!this.assignForm.settingId) {
|
||||
this.candidateData = []
|
||||
@@ -573,7 +772,9 @@ layout("/layouts/platform.html"){
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
const data = res.data || {}
|
||||
this.candidateData = data.list || []
|
||||
this.candidateData = (data.list || []).map(item => Object.assign({}, item, {
|
||||
photoFiles: ""
|
||||
}))
|
||||
this.candidateForm.totalCount = data.totalCount || 0
|
||||
this.mergeCandidateOptions(this.candidateData)
|
||||
} else {
|
||||
@@ -654,6 +855,80 @@ layout("/layouts/platform.html"){
|
||||
})
|
||||
return Object.keys(map).map(key => map[key])
|
||||
},
|
||||
isCandidateSelected(row) {
|
||||
return !!row && this.selectedCandidates.some(item => item.userId === row.userId)
|
||||
},
|
||||
openPhotoUpload(row) {
|
||||
if (!this.isCandidateSelected(row)) {
|
||||
this.$message.warning("请先勾选人员")
|
||||
return
|
||||
}
|
||||
this.photoUploadRow = row
|
||||
// 行级图片只是暂存到候选人员对象中,再次打开时从当前行回显。
|
||||
this.photoUploadForm = {
|
||||
userId: row.userId || "",
|
||||
userName: row.userName || "",
|
||||
photoFiles: row.photoFiles || ""
|
||||
}
|
||||
this.photoUploadDialogVisible = true
|
||||
},
|
||||
confirmPhotoUpload() {
|
||||
if (this.photoUploadRow) {
|
||||
this.$set(this.photoUploadRow, "photoFiles", this.photoUploadForm.photoFiles || "")
|
||||
}
|
||||
this.photoUploadDialogVisible = false
|
||||
},
|
||||
resetPhotoUploadDialog() {
|
||||
this.photoUploadRow = null
|
||||
this.photoUploadForm = {
|
||||
userId: "",
|
||||
userName: "",
|
||||
photoFiles: ""
|
||||
}
|
||||
},
|
||||
openListPhotoUpload(row) {
|
||||
this.listPhotoRow = row
|
||||
// 主列表上传读取当前行 photoFiles;人员分配弹窗已保存过图片时可直接回显。
|
||||
this.listPhotoForm = {
|
||||
id: row.id || "",
|
||||
userName: row.userName || "",
|
||||
photoFiles: row.photoFiles || ""
|
||||
}
|
||||
this.listPhotoDialogVisible = true
|
||||
},
|
||||
resetListPhotoUploadDialog() {
|
||||
this.listPhotoSubmitting = false
|
||||
this.listPhotoRow = null
|
||||
this.listPhotoForm = {
|
||||
id: "",
|
||||
userName: "",
|
||||
photoFiles: ""
|
||||
}
|
||||
},
|
||||
saveListPhotoFiles() {
|
||||
if (!this.listPhotoForm.id) {
|
||||
this.$message.warning("人员分配记录不存在")
|
||||
return
|
||||
}
|
||||
this.listPhotoSubmitting = true
|
||||
this.$axios.post(loc() + "/updatePhotoFiles", {
|
||||
id: this.listPhotoForm.id,
|
||||
photoFiles: this.listPhotoForm.photoFiles || ""
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
if (this.listPhotoRow) {
|
||||
this.$set(this.listPhotoRow, "photoFiles", this.listPhotoForm.photoFiles || "")
|
||||
}
|
||||
this.$message.success("图片材料保存成功")
|
||||
this.listPhotoDialogVisible = false
|
||||
this.doSearch()
|
||||
} else {
|
||||
this.$message.warning(res.msg || "图片材料保存失败")
|
||||
}
|
||||
}).finally(() => {
|
||||
this.listPhotoSubmitting = false
|
||||
})
|
||||
},
|
||||
clearCandidateSelection() {
|
||||
this.selectedCandidates = []
|
||||
if (this.$refs.candidateTable) {
|
||||
@@ -667,18 +942,28 @@ layout("/layouts/platform.html"){
|
||||
this.$message.warning("请选择需要分配的人员")
|
||||
return
|
||||
}
|
||||
if (this.assignForm.personType === "FORMAL" && !this.assignForm.periodId) {
|
||||
this.$message.warning("请选择出行时间")
|
||||
return
|
||||
}
|
||||
this.$confirm("确定保存当前人员分配吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
const userIds = this.selectedCandidates.map(item => item.userId)
|
||||
const assignItems = this.selectedCandidates.map(item => {
|
||||
return {
|
||||
userId: item.userId,
|
||||
photoFiles: item.photoFiles || ""
|
||||
}
|
||||
})
|
||||
this.assignSubmitting = true
|
||||
this.$axios.post(loc() + "/doAssign", {
|
||||
settingId: this.assignForm.settingId,
|
||||
matterId: this.assignForm.personType === "BACKUP" ? "" : this.assignForm.matterId,
|
||||
periodId: this.assignForm.personType === "BACKUP" ? "" : this.assignForm.periodId,
|
||||
personType: this.assignForm.personType,
|
||||
userIds: JSON.stringify(userIds)
|
||||
assignItems: JSON.stringify(assignItems)
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
const data = res.data || {}
|
||||
@@ -715,7 +1000,8 @@ layout("/layouts/platform.html"){
|
||||
id: row.id,
|
||||
settingId: row.settingId,
|
||||
matterId: "",
|
||||
userName: row.userName || ""
|
||||
userName: row.userName || "",
|
||||
photoFiles: ""
|
||||
}
|
||||
this.selectMatterOptions = []
|
||||
this.selectMatterDialogVisible = true
|
||||
@@ -743,7 +1029,8 @@ layout("/layouts/platform.html"){
|
||||
this.selectMatterSubmitting = true
|
||||
this.$axios.post(loc() + "/selectMatter", {
|
||||
id: this.selectMatterForm.id,
|
||||
matterId: this.selectMatterForm.matterId
|
||||
matterId: this.selectMatterForm.matterId,
|
||||
photoFiles: this.selectMatterForm.photoFiles
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
const data = res.data || {}
|
||||
@@ -792,6 +1079,12 @@ layout("/layouts/platform.html"){
|
||||
isCancelled(row) {
|
||||
return row && (row.cancelled === true || row.cancelled === 1)
|
||||
},
|
||||
photoFileCount(row) {
|
||||
if (!row || !row.photoFiles) {
|
||||
return 0
|
||||
}
|
||||
return String(row.photoFiles).split(",").map(item => item.trim()).filter(item => item).length
|
||||
},
|
||||
restoreCancel(row) {
|
||||
this.$confirm("确定将【" + row.userName + "】恢复为未退出状态吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
|
||||
+206
-7
@@ -88,6 +88,11 @@ layout("/layouts/platform.html"){
|
||||
<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="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">
|
||||
<template slot-scope="{row}">
|
||||
{{ photoFileCount(row) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="分配类别" prop="assignSource" width="120" sortable="custom" align="center" header-align="center">
|
||||
<template slot-scope="{row}">
|
||||
<el-tag size="mini" :type="row.assignSource === 'BRANCH_UNION' ? 'warning' : 'primary'">{{ assignSourceText(row.assignSource) }}</el-tag>
|
||||
@@ -104,9 +109,10 @@ layout("/layouts/platform.html"){
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="分配时间" prop="assignedAt" width="170" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="操作" width="320" align="center" header-align="center" fixed="right">
|
||||
<el-table-column label="操作" width="410" align="center" header-align="center" fixed="right">
|
||||
<template slot-scope="{row}">
|
||||
<el-button v-if="isSchoolUnionAssignment(row) && !row.matterId && row.personType !== 'BACKUP'" size="mini" type="primary" @click="openSelectMatter(row)">选择线路</el-button>
|
||||
<el-button v-if="isSchoolUnionAssignment(row)" size="mini" type="primary" @click="openListPhotoUpload(row)">{{ row.photoFiles ? "已上传" : "上传图片" }}</el-button>
|
||||
<el-button v-if="isSchoolUnionAssignment(row) && isCancelled(row) && $auth.hasPermission('thirtyTeachTour.schoolUserAssignment.cancelRestore')" size="mini" type="warning" @click="restoreCancel(row)">取消退出</el-button>
|
||||
<el-button v-if="isSchoolUnionAssignment(row)" size="mini" type="danger" @click="doDelete(row)">删除</el-button>
|
||||
</template>
|
||||
@@ -189,6 +195,14 @@ layout("/layouts/platform.html"){
|
||||
<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-select>
|
||||
<el-select v-model="candidateForm.summerJoined" clearable placeholder="今年是否已参加暑期疗休养" style="width: 230px" @change="candidateSearch">
|
||||
<el-option label="否" value="false"></el-option>
|
||||
<el-option label="是" value="true"></el-option>
|
||||
</el-select>
|
||||
<el-select v-model="candidateForm.lastThirtyJoined" clearable placeholder="去年是否参加30年教龄疗休养" style="width: 250px" @change="candidateSearch">
|
||||
<el-option label="否" value="false"></el-option>
|
||||
<el-option label="是" value="true"></el-option>
|
||||
</el-select>
|
||||
<el-select
|
||||
v-model="selectedCandidateIds"
|
||||
multiple
|
||||
@@ -238,6 +252,13 @@ layout("/layouts/platform.html"){
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="材料" width="110" align="center" header-align="center">
|
||||
<template slot-scope="{row}">
|
||||
<el-button type="text" size="mini" :disabled="!isCandidateSelected(row)" @click="openPhotoUpload(row)">
|
||||
{{ row.photoFiles ? "已上传" : "上传" }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-row class="el-pagination-container candidate-pagination">
|
||||
<el-pagination
|
||||
@@ -257,6 +278,64 @@ layout("/layouts/platform.html"){
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog
|
||||
title="上传图片材料"
|
||||
:visible.sync="photoUploadDialogVisible"
|
||||
:close-on-click-modal="false"
|
||||
width="520px"
|
||||
@closed="resetPhotoUploadDialog">
|
||||
<el-form :model="photoUploadForm" label-width="100px">
|
||||
<el-form-item label="人员">
|
||||
<el-input v-model="photoUploadForm.userName" disabled></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="图片材料">
|
||||
<file-upload
|
||||
style="--upload-width: 96px;--upload-height:96px"
|
||||
:upload_number="9"
|
||||
:upload_size="20971520"
|
||||
:value.sync="photoUploadForm.photoFiles"
|
||||
accept=".jpg,.jpeg,.png"
|
||||
complete_result
|
||||
upload_mode="image"
|
||||
upload_result_category="interval">
|
||||
</file-upload>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="photoUploadDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="confirmPhotoUpload">确定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog
|
||||
title="上传图片材料"
|
||||
:visible.sync="listPhotoDialogVisible"
|
||||
:close-on-click-modal="false"
|
||||
width="520px"
|
||||
@closed="resetListPhotoUploadDialog">
|
||||
<el-form :model="listPhotoForm" label-width="100px">
|
||||
<el-form-item label="人员">
|
||||
<el-input v-model="listPhotoForm.userName" disabled></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="图片材料">
|
||||
<file-upload
|
||||
style="--upload-width: 96px;--upload-height:96px"
|
||||
:upload_number="9"
|
||||
:upload_size="20971520"
|
||||
:value.sync="listPhotoForm.photoFiles"
|
||||
accept=".jpg,.jpeg,.png"
|
||||
complete_result
|
||||
upload_mode="image"
|
||||
upload_result_category="interval">
|
||||
</file-upload>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="listPhotoDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="listPhotoSubmitting" @click="saveListPhotoFiles">保存</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog
|
||||
title="选择分配线路"
|
||||
:visible.sync="selectMatterDialogVisible"
|
||||
@@ -272,6 +351,18 @@ layout("/layouts/platform.html"){
|
||||
<el-option v-for="item in selectMatterOptions" :key="item.matterId" :label="matterOptionLabel(item)" :value="item.matterId"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="图片材料">
|
||||
<file-upload
|
||||
style="--upload-width: 96px;--upload-height:96px"
|
||||
:upload_number="9"
|
||||
:upload_size="20971520"
|
||||
:value.sync="selectMatterForm.photoFiles"
|
||||
accept=".jpg,.jpeg,.png"
|
||||
complete_result
|
||||
upload_mode="image"
|
||||
upload_result_category="interval">
|
||||
</file-upload>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="selectMatterDialogVisible = false">取消</el-button>
|
||||
@@ -350,9 +441,24 @@ layout("/layouts/platform.html"){
|
||||
selectedCandidates: [],
|
||||
selectedCandidateIds: [],
|
||||
assignSubmitting: false,
|
||||
photoUploadDialogVisible: false,
|
||||
photoUploadRow: null,
|
||||
photoUploadForm: {
|
||||
userId: "",
|
||||
userName: "",
|
||||
photoFiles: ""
|
||||
},
|
||||
selectMatterDialogVisible: false,
|
||||
selectMatterSubmitting: false,
|
||||
selectMatterOptions: [],
|
||||
listPhotoDialogVisible: false,
|
||||
listPhotoSubmitting: false,
|
||||
listPhotoRow: null,
|
||||
listPhotoForm: {
|
||||
id: "",
|
||||
userName: "",
|
||||
photoFiles: ""
|
||||
},
|
||||
pageForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
@@ -378,13 +484,16 @@ layout("/layouts/platform.html"){
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
unionId: "",
|
||||
summerJoined: "false",
|
||||
lastThirtyJoined: "false",
|
||||
keyword: ""
|
||||
},
|
||||
selectMatterForm: {
|
||||
id: "",
|
||||
settingId: "",
|
||||
matterId: "",
|
||||
userName: ""
|
||||
userName: "",
|
||||
photoFiles: ""
|
||||
},
|
||||
remindForm: {
|
||||
content: ""
|
||||
@@ -415,6 +524,8 @@ layout("/layouts/platform.html"){
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
unionId: "",
|
||||
summerJoined: "false",
|
||||
lastThirtyJoined: "false",
|
||||
keyword: ""
|
||||
}
|
||||
},
|
||||
@@ -423,7 +534,8 @@ layout("/layouts/platform.html"){
|
||||
id: "",
|
||||
settingId: "",
|
||||
matterId: "",
|
||||
userName: ""
|
||||
userName: "",
|
||||
photoFiles: ""
|
||||
}
|
||||
},
|
||||
defaultRemindForm() {
|
||||
@@ -649,13 +761,16 @@ layout("/layouts/platform.html"){
|
||||
pageSize: this.candidateForm.pageSize,
|
||||
settingId: this.assignForm.settingId,
|
||||
unionId: this.candidateForm.unionId,
|
||||
summerJoined: this.candidateForm.summerJoined,
|
||||
lastThirtyJoined: this.candidateForm.lastThirtyJoined,
|
||||
keyword: (this.selectedCandidateIds || []).length > 0 ? "" : this.candidateForm.keyword,
|
||||
userIds: JSON.stringify(this.selectedCandidateIds || [])
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
const data = res.data || {}
|
||||
this.candidateData = (data.list || []).map(item => Object.assign({}, item, {
|
||||
assignmentMatterId: ""
|
||||
assignmentMatterId: "",
|
||||
photoFiles: ""
|
||||
}))
|
||||
this.candidateForm.totalCount = data.totalCount || 0
|
||||
this.mergeCandidateOptions(this.candidateData)
|
||||
@@ -678,6 +793,8 @@ layout("/layouts/platform.html"){
|
||||
},
|
||||
resetCandidateSearch() {
|
||||
this.candidateForm.unionId = ""
|
||||
this.candidateForm.summerJoined = "false"
|
||||
this.candidateForm.lastThirtyJoined = "false"
|
||||
this.candidateForm.keyword = ""
|
||||
this.candidateForm.pageNumber = 1
|
||||
this.selectedCandidateIds = []
|
||||
@@ -723,6 +840,8 @@ layout("/layouts/platform.html"){
|
||||
pageSize: 20,
|
||||
settingId: this.assignForm.settingId,
|
||||
unionId: this.candidateForm.unionId,
|
||||
summerJoined: this.candidateForm.summerJoined,
|
||||
lastThirtyJoined: this.candidateForm.lastThirtyJoined,
|
||||
keyword: keyword || ""
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
@@ -758,6 +877,77 @@ layout("/layouts/platform.html"){
|
||||
isCandidateSelected(row) {
|
||||
return !!row && this.selectedCandidates.some(item => item.userId === row.userId)
|
||||
},
|
||||
openPhotoUpload(row) {
|
||||
if (!this.isCandidateSelected(row)) {
|
||||
this.$message.warning("请先勾选人员")
|
||||
return
|
||||
}
|
||||
this.photoUploadRow = row
|
||||
// 行级图片只是暂存到候选人员对象中,再次打开时从当前行回显。
|
||||
this.photoUploadForm = {
|
||||
userId: row.userId || "",
|
||||
userName: row.userName || "",
|
||||
photoFiles: row.photoFiles || ""
|
||||
}
|
||||
this.photoUploadDialogVisible = true
|
||||
},
|
||||
confirmPhotoUpload() {
|
||||
if (this.photoUploadRow) {
|
||||
this.$set(this.photoUploadRow, "photoFiles", this.photoUploadForm.photoFiles || "")
|
||||
}
|
||||
this.photoUploadDialogVisible = false
|
||||
},
|
||||
resetPhotoUploadDialog() {
|
||||
this.photoUploadRow = null
|
||||
this.photoUploadForm = {
|
||||
userId: "",
|
||||
userName: "",
|
||||
photoFiles: ""
|
||||
}
|
||||
},
|
||||
openListPhotoUpload(row) {
|
||||
this.listPhotoRow = row
|
||||
// 主列表上传读取当前行 photoFiles;人员分配弹窗已保存过图片时可直接回显。
|
||||
this.listPhotoForm = {
|
||||
id: row.id || "",
|
||||
userName: row.userName || "",
|
||||
photoFiles: row.photoFiles || ""
|
||||
}
|
||||
this.listPhotoDialogVisible = true
|
||||
},
|
||||
resetListPhotoUploadDialog() {
|
||||
this.listPhotoSubmitting = false
|
||||
this.listPhotoRow = null
|
||||
this.listPhotoForm = {
|
||||
id: "",
|
||||
userName: "",
|
||||
photoFiles: ""
|
||||
}
|
||||
},
|
||||
saveListPhotoFiles() {
|
||||
if (!this.listPhotoForm.id) {
|
||||
this.$message.warning("人员分配记录不存在")
|
||||
return
|
||||
}
|
||||
this.listPhotoSubmitting = true
|
||||
this.$axios.post(loc() + "/updatePhotoFiles", {
|
||||
id: this.listPhotoForm.id,
|
||||
photoFiles: this.listPhotoForm.photoFiles || ""
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
if (this.listPhotoRow) {
|
||||
this.$set(this.listPhotoRow, "photoFiles", this.listPhotoForm.photoFiles || "")
|
||||
}
|
||||
this.$message.success("图片材料保存成功")
|
||||
this.listPhotoDialogVisible = false
|
||||
this.doSearch()
|
||||
} else {
|
||||
this.$message.warning(res.msg || "图片材料保存失败")
|
||||
}
|
||||
}).finally(() => {
|
||||
this.listPhotoSubmitting = false
|
||||
})
|
||||
},
|
||||
clearCandidateSelection() {
|
||||
this.selectedCandidates = []
|
||||
if (this.$refs.candidateTable) {
|
||||
@@ -779,7 +969,8 @@ layout("/layouts/platform.html"){
|
||||
const assignItems = this.selectedCandidates.map(item => {
|
||||
return {
|
||||
userId: item.userId,
|
||||
matterId: item.assignmentMatterId || this.assignForm.matterId || ""
|
||||
matterId: item.assignmentMatterId || this.assignForm.matterId || "",
|
||||
photoFiles: item.photoFiles || ""
|
||||
}
|
||||
})
|
||||
this.assignSubmitting = true
|
||||
@@ -806,7 +997,8 @@ layout("/layouts/platform.html"){
|
||||
id: row.id,
|
||||
settingId: row.settingId,
|
||||
matterId: "",
|
||||
userName: row.userName || ""
|
||||
userName: row.userName || "",
|
||||
photoFiles: ""
|
||||
}
|
||||
this.selectMatterOptions = []
|
||||
this.selectMatterDialogVisible = true
|
||||
@@ -834,7 +1026,8 @@ layout("/layouts/platform.html"){
|
||||
this.selectMatterSubmitting = true
|
||||
this.$axios.post(loc() + "/selectMatter", {
|
||||
id: this.selectMatterForm.id,
|
||||
matterId: this.selectMatterForm.matterId
|
||||
matterId: this.selectMatterForm.matterId,
|
||||
photoFiles: this.selectMatterForm.photoFiles
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
const data = res.data || {}
|
||||
@@ -895,6 +1088,12 @@ layout("/layouts/platform.html"){
|
||||
isCancelled(row) {
|
||||
return row && (row.cancelled === true || row.cancelled === 1)
|
||||
},
|
||||
photoFileCount(row) {
|
||||
if (!row || !row.photoFiles) {
|
||||
return 0
|
||||
}
|
||||
return String(row.photoFiles).split(",").map(item => item.trim()).filter(item => item).length
|
||||
},
|
||||
restoreCancel(row) {
|
||||
this.$confirm("确定将【" + row.userName + "】恢复为未退出状态吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
|
||||
+567
-9
@@ -347,7 +347,7 @@ layout("/layouts/platform.html"){
|
||||
<strong>{{ branchTotalQuota }}</strong>
|
||||
</div>
|
||||
<div class="tour-quota-stat">
|
||||
<span>分工会总会员数</span>
|
||||
<span>分工会可参加总人数</span>
|
||||
<strong>{{ branchTotalMemberCount }}</strong>
|
||||
</div>
|
||||
<el-input-number
|
||||
@@ -371,7 +371,7 @@ layout("/layouts/platform.html"){
|
||||
style="width: 100%">
|
||||
<el-table-column label="序号" type="index" width="70" align="center" header-align="center"></el-table-column>
|
||||
<el-table-column label="分工会" prop="unionName" min-width="220" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="当前会员数" prop="memberCount" width="120" align="center" header-align="center"></el-table-column>
|
||||
<el-table-column label="可参加人数" prop="memberCount" width="120" align="center" header-align="center"></el-table-column>
|
||||
<el-table-column label="正式人员数量" width="170" align="center" header-align="center">
|
||||
<template slot-scope="{row}">
|
||||
<el-input-number v-model="row.formalQuota" :controls="false" :min="0" :precision="0" style="width: 100%"></el-input-number>
|
||||
@@ -385,6 +385,120 @@ layout("/layouts/platform.html"){
|
||||
</el-table>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="时间段管理" name="travelPeriod">
|
||||
<div class="tour-tab-fill">
|
||||
<el-alert
|
||||
title="时间段用于划分报名的时间范围,保存后将应用于时间段名额分配。"
|
||||
type="info"
|
||||
show-icon
|
||||
:closable="false"
|
||||
class="tour-tab-alert">
|
||||
</el-alert>
|
||||
<div class="tour-tab-toolbar tour-tab-toolbar-left">
|
||||
<el-button type="primary" size="medium" icon="el-icon-plus" @click="openPeriodDialog()">新增时间段</el-button>
|
||||
</div>
|
||||
<el-table
|
||||
:data="formData.travelPeriods"
|
||||
border
|
||||
class="vi-table tour-tab-table"
|
||||
empty-text="暂无时间段"
|
||||
height="100%"
|
||||
style="width: 100%">
|
||||
<el-table-column label="序号" type="index" width="70" align="center" header-align="center"></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="startDate" width="150" align="center" header-align="center"></el-table-column>
|
||||
<el-table-column label="结束日期" prop="endDate" width="150" align="center" header-align="center"></el-table-column>
|
||||
<el-table-column label="状态" prop="enabled" width="100" align="center" header-align="center">
|
||||
<template slot-scope="{row}">
|
||||
<el-tag size="mini" type="success" v-if="row.enabled">启用</el-tag>
|
||||
<el-tag size="mini" type="info" v-else>禁用</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" prop="createdAt" width="170" align="center" header-align="center">
|
||||
<template slot-scope="{row}">
|
||||
{{ formatPeriodCreatedAt(row.createdAt) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="130" align="center" header-align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" size="mini" @click="openPeriodDialog(scope.row, scope.$index)">编辑</el-button>
|
||||
<el-button type="text" size="mini" class="text-danger" @click="deleteTravelPeriod(scope.$index, scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="时间段名额分配" name="periodQuota">
|
||||
<div class="tour-tab-fill">
|
||||
<el-alert
|
||||
title="按时间段分配各分工会的可用名额,所有时间段名额之和不能超过分工会的正式名额。"
|
||||
type="info"
|
||||
show-icon
|
||||
:closable="false"
|
||||
class="tour-tab-alert">
|
||||
</el-alert>
|
||||
<div class="tour-period-quota-toolbar">
|
||||
<el-select v-model="periodQuotaUnionId" clearable filterable placeholder="全部分工会" size="medium" class="tour-period-quota-union">
|
||||
<el-option
|
||||
v-for="item in formData.unionQuotas"
|
||||
:key="item.unionId"
|
||||
:label="item.unionName"
|
||||
:value="item.unionId">
|
||||
</el-option>
|
||||
</el-select>
|
||||
<el-date-picker
|
||||
v-model="periodQuotaDateRange"
|
||||
type="daterange"
|
||||
value-format="yyyy-MM-dd"
|
||||
range-separator="至"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
size="medium">
|
||||
</el-date-picker>
|
||||
<el-button type="primary" size="medium" @click="checkPeriodQuotaMessage">批量检查</el-button>
|
||||
</div>
|
||||
<el-table
|
||||
:data="periodQuotaMatrixRows"
|
||||
border
|
||||
class="vi-table tour-tab-table tour-period-quota-table"
|
||||
empty-text="暂无分工会名额"
|
||||
height="100%"
|
||||
show-summary
|
||||
:summary-method="periodQuotaSummary"
|
||||
style="width: 100%">
|
||||
<el-table-column label="序号" type="index" width="70" align="center" header-align="center"></el-table-column>
|
||||
<el-table-column label="分工会" prop="unionName" min-width="200" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="总名额" prop="formalQuota" width="110" align="center" header-align="center"></el-table-column>
|
||||
<el-table-column label="时间段名额分配(可用名额)" align="center" header-align="center">
|
||||
<el-table-column
|
||||
v-for="period in periodQuotaDisplayPeriods"
|
||||
:key="period.id"
|
||||
:label="period.startDate + ' ~ ' + period.endDate"
|
||||
min-width="230"
|
||||
align="center"
|
||||
header-align="center">
|
||||
<template slot-scope="{row}">
|
||||
<div class="tour-period-quota-cell">
|
||||
<el-input
|
||||
:value="periodQuotaValue(row.unionId, period.id)"
|
||||
maxlength="10"
|
||||
@input="setPeriodQuotaValue(row, period, $event)">
|
||||
</el-input>
|
||||
<span>可用:{{ periodQuotaRemaining(row.unionId) }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table-column>
|
||||
<el-table-column label="已分配合计" width="150" align="center" header-align="center">
|
||||
<template slot-scope="{row}">
|
||||
<span :class="periodQuotaRowTotal(row.unionId) > toNonNegativeInteger(row.formalQuota) ? 'text-danger' : 'text-success'">
|
||||
{{ periodQuotaRowTotal(row.unionId) }} / {{ toNonNegativeInteger(row.formalQuota) }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="服务须知" name="notice">
|
||||
<el-form-item prop="serviceNotice" label-width="0">
|
||||
<text-editor v-model="formData.serviceNotice"></text-editor>
|
||||
@@ -397,6 +511,46 @@ layout("/layouts/platform.html"){
|
||||
<el-button type="primary" :loading="submitLoading" @click="doSubmit">确认</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
<el-dialog
|
||||
:title="periodDialogTitle"
|
||||
:visible.sync="periodDialogVisible"
|
||||
:close-on-click-modal="false"
|
||||
width="520px"
|
||||
append-to-body>
|
||||
<el-form :model="periodForm" :rules="periodRules" label-width="110px" ref="periodForm">
|
||||
<el-form-item label="时间段名称" prop="periodName">
|
||||
<el-input v-model="periodForm.periodName" maxlength="20" show-word-limit placeholder="请输入时间段名称"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="开始日期" prop="startDate">
|
||||
<el-date-picker
|
||||
v-model="periodForm.startDate"
|
||||
type="date"
|
||||
value-format="yyyy-MM-dd"
|
||||
placeholder="请选择开始日期"
|
||||
style="width: 100%">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="结束日期" prop="endDate">
|
||||
<el-date-picker
|
||||
v-model="periodForm.endDate"
|
||||
type="date"
|
||||
value-format="yyyy-MM-dd"
|
||||
placeholder="请选择结束日期"
|
||||
style="width: 100%">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="enabled">
|
||||
<el-radio-group v-model="periodForm.enabled">
|
||||
<el-radio :label="true">启用</el-radio>
|
||||
<el-radio :label="false">禁用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="periodDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="savePeriodDialog">确定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
<drawer-user-scope
|
||||
ref="drawerUserScope"
|
||||
:group_id.sync="formData.activityGroupId"
|
||||
@@ -456,6 +610,18 @@ layout("/layouts/platform.html"){
|
||||
quotaAllocate: {
|
||||
ratio: null
|
||||
},
|
||||
periodQuotaUnionId: "",
|
||||
periodQuotaDateRange: [],
|
||||
periodDialogVisible: false,
|
||||
periodDialogTitle: "新增时间段",
|
||||
periodEditIndex: -1,
|
||||
periodForm: {
|
||||
id: "",
|
||||
periodName: "",
|
||||
startDate: "",
|
||||
endDate: "",
|
||||
enabled: true
|
||||
},
|
||||
inheritLoading: false,
|
||||
formRules: {
|
||||
year: [{required: true, message: "必填", trigger: ["blur", "change"]}],
|
||||
@@ -466,6 +632,11 @@ layout("/layouts/platform.html"){
|
||||
outProvinceFixedPeople: [{validator: checkOutProvinceFixedPeople, trigger: ["blur", "change"]}],
|
||||
cycleStartYear: [{validator: checkCycleYear, trigger: ["blur", "change"]}],
|
||||
cycleEndYear: [{validator: checkCycleYear, trigger: ["blur", "change"]}]
|
||||
},
|
||||
periodRules: {
|
||||
periodName: [{required: true, message: "必填", trigger: ["blur", "change"]}],
|
||||
startDate: [{required: true, message: "必填", trigger: ["blur", "change"]}],
|
||||
endDate: [{required: true, message: "必填", trigger: ["blur", "change"]}]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -483,11 +654,48 @@ layout("/layouts/platform.html"){
|
||||
branchTotalQuota() {
|
||||
const quota = this.travelPeopleQuota - this.toNonNegativeInteger(this.quotaOverview.schoolFormalAssignedCount)
|
||||
return quota > 0 ? quota : 0
|
||||
},
|
||||
enabledTravelPeriods() {
|
||||
const rows = this.formData.travelPeriods || []
|
||||
return rows.filter((row) => {
|
||||
return row && row.id && row.enabled
|
||||
}).sort((a, b) => {
|
||||
return String(a.startDate || "").localeCompare(String(b.startDate || ""))
|
||||
})
|
||||
},
|
||||
periodQuotaDisplayPeriods() {
|
||||
const range = this.periodQuotaDateRange || []
|
||||
return this.enabledTravelPeriods.filter((period) => {
|
||||
if (!range.length || !range[0] || !range[1]) {
|
||||
return true
|
||||
}
|
||||
return String(period.endDate || "") >= range[0] && String(period.startDate || "") <= range[1]
|
||||
})
|
||||
},
|
||||
periodQuotaMatrixRows() {
|
||||
const rows = this.formData.unionQuotas || []
|
||||
return rows.filter((row) => {
|
||||
return !this.periodQuotaUnionId || row.unionId === this.periodQuotaUnionId
|
||||
}).map((row) => {
|
||||
return Object.assign({}, row, {
|
||||
formalQuota: this.toNonNegativeInteger(row.formalQuota)
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
"formData.travelPeopleQuota": function() {
|
||||
this.refreshQuotaRatio()
|
||||
},
|
||||
"formData.year": function() {
|
||||
if (this.dialogVisible) {
|
||||
this.reloadUnionQuotaRows()
|
||||
}
|
||||
},
|
||||
"formData.activityGroupId": function() {
|
||||
if (this.dialogVisible) {
|
||||
this.reloadUnionQuotaRows()
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -502,15 +710,22 @@ layout("/layouts/platform.html"){
|
||||
})
|
||||
},
|
||||
loadUnionQuotaRows(settingId) {
|
||||
this.$axios.post(loc() + "/unionQuotaRows", {settingId: settingId || ""}).then((res) => {
|
||||
this.$axios.post(loc() + "/unionQuotaRows", {
|
||||
settingId: settingId || "",
|
||||
year: this.formData.year || "",
|
||||
activityGroupId: this.formData.activityGroupId || ""
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$set(this.formData, "unionQuotas", this.normalizeUnionQuotaRows(res.data || []))
|
||||
this.$set(this.formData, "unionQuotas", this.mergeUnionQuotaRows(res.data || []))
|
||||
this.refreshQuotaRatio()
|
||||
} else {
|
||||
this.$message.warning(res.msg || "查询分工会名额失败")
|
||||
}
|
||||
})
|
||||
},
|
||||
reloadUnionQuotaRows() {
|
||||
this.loadUnionQuotaRows(this.formData.id || "")
|
||||
},
|
||||
loadUnionQuotaOverview(settingId) {
|
||||
this.$axios.post(loc() + "/unionQuotaOverview", {settingId: settingId || ""}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
@@ -531,6 +746,50 @@ layout("/layouts/platform.html"){
|
||||
})
|
||||
})
|
||||
},
|
||||
normalizeTravelPeriods(rows) {
|
||||
return (rows || []).map((item) => {
|
||||
return Object.assign({}, item, {
|
||||
id: item.id || this.newClientId(),
|
||||
periodName: item.periodName || "",
|
||||
startDate: item.startDate || "",
|
||||
endDate: item.endDate || "",
|
||||
enabled: item.enabled === undefined || item.enabled === null ? true : !!item.enabled
|
||||
})
|
||||
})
|
||||
},
|
||||
normalizePeriodQuotas(rows) {
|
||||
return (rows || []).map((item) => {
|
||||
return Object.assign({}, item, {
|
||||
formalQuota: this.toNonNegativeInteger(item.formalQuota)
|
||||
})
|
||||
}).filter((item) => {
|
||||
return item.unionId && item.periodId
|
||||
})
|
||||
},
|
||||
mergeUnionQuotaRows(rows) {
|
||||
const currentRows = this.formData.unionQuotas || []
|
||||
const currentQuotaMap = {}
|
||||
currentRows.forEach((row) => {
|
||||
if (row && row.unionId) {
|
||||
currentQuotaMap[row.unionId] = row
|
||||
}
|
||||
})
|
||||
// 刷新可参加人数时只更新统计口径,保留页面上已继承或已录入的正式/替补名额。
|
||||
return this.normalizeUnionQuotaRows(rows).map((row) => {
|
||||
const current = currentQuotaMap[row.unionId]
|
||||
if (!current) {
|
||||
return row
|
||||
}
|
||||
return Object.assign({}, row, {
|
||||
formalQuota: this.toNonNegativeInteger(current.formalQuota),
|
||||
backupQuota: this.toNonNegativeInteger(current.backupQuota)
|
||||
})
|
||||
})
|
||||
},
|
||||
newClientId() {
|
||||
const source = String(Date.now()) + String(Math.floor(Math.random() * 100000000000000000))
|
||||
return (source + "00000000000000000000000000000000").substring(0, 32)
|
||||
},
|
||||
toNonNegativeInteger(value) {
|
||||
const numberValue = parseInt(value, 10)
|
||||
if (isNaN(numberValue) || numberValue < 0) {
|
||||
@@ -538,6 +797,148 @@ layout("/layouts/platform.html"){
|
||||
}
|
||||
return numberValue
|
||||
},
|
||||
formatPeriodCreatedAt(value) {
|
||||
if (!value) {
|
||||
return ""
|
||||
}
|
||||
if (String(value).length === 13) {
|
||||
return moment(Number(value)).format("YYYY-MM-DD HH:mm:ss")
|
||||
}
|
||||
if (String(value).length === 10) {
|
||||
return moment(Number(value) * 1000).format("YYYY-MM-DD HH:mm:ss")
|
||||
}
|
||||
return value
|
||||
},
|
||||
openPeriodDialog(row, index) {
|
||||
this.periodDialogTitle = row ? "编辑时间段" : "新增时间段"
|
||||
this.periodEditIndex = row ? index : -1
|
||||
this.periodForm = Object.assign({
|
||||
id: this.newClientId(),
|
||||
periodName: "",
|
||||
startDate: "",
|
||||
endDate: "",
|
||||
enabled: true
|
||||
}, row || {})
|
||||
this.periodDialogVisible = true
|
||||
this.$nextTick(() => this.$refs.periodForm && this.$refs.periodForm.clearValidate())
|
||||
},
|
||||
savePeriodDialog() {
|
||||
this.$refs.periodForm.validate((valid) => {
|
||||
if (!valid) return
|
||||
if (this.periodForm.startDate > this.periodForm.endDate) {
|
||||
this.$message.warning("开始日期不能大于结束日期")
|
||||
return
|
||||
}
|
||||
if (!this.formData.travelPeriods) {
|
||||
this.$set(this.formData, "travelPeriods", [])
|
||||
}
|
||||
const row = Object.assign({}, this.periodForm, {
|
||||
id: this.periodForm.id || this.newClientId(),
|
||||
enabled: !!this.periodForm.enabled
|
||||
})
|
||||
if (this.periodEditIndex >= 0) {
|
||||
this.$set(this.formData.travelPeriods, this.periodEditIndex, row)
|
||||
} else {
|
||||
this.formData.travelPeriods.push(row)
|
||||
}
|
||||
this.periodDialogVisible = false
|
||||
})
|
||||
},
|
||||
deleteTravelPeriod(index, row) {
|
||||
this.$confirm("确定删除该时间段吗?删除后对应的时间段名额也会移除。", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
this.formData.travelPeriods.splice(index, 1)
|
||||
this.formData.periodQuotas = (this.formData.periodQuotas || []).filter((item) => {
|
||||
return item.periodId !== row.id
|
||||
})
|
||||
})
|
||||
},
|
||||
periodQuotaKey(unionId, periodId) {
|
||||
return String(unionId || "") + "_" + String(periodId || "")
|
||||
},
|
||||
findPeriodQuota(unionId, periodId) {
|
||||
const key = this.periodQuotaKey(unionId, periodId)
|
||||
const rows = this.formData.periodQuotas || []
|
||||
return rows.find((item) => {
|
||||
return this.periodQuotaKey(item.unionId, item.periodId) === key
|
||||
})
|
||||
},
|
||||
periodQuotaValue(unionId, periodId) {
|
||||
const row = this.findPeriodQuota(unionId, periodId)
|
||||
return row ? this.toNonNegativeInteger(row.formalQuota) : 0
|
||||
},
|
||||
periodEnabledMap() {
|
||||
const result = {}
|
||||
;(this.formData.travelPeriods || []).forEach((period) => {
|
||||
if (period && period.id && period.enabled) {
|
||||
result[period.id] = true
|
||||
}
|
||||
})
|
||||
return result
|
||||
},
|
||||
setPeriodQuotaValue(row, period, value) {
|
||||
const nextValue = this.toNonNegativeInteger(String(value || "").replace(/[^\d]/g, ""))
|
||||
if (!this.formData.periodQuotas) {
|
||||
this.$set(this.formData, "periodQuotas", [])
|
||||
}
|
||||
const existing = this.findPeriodQuota(row.unionId, period.id)
|
||||
if (existing) {
|
||||
this.$set(existing, "formalQuota", nextValue)
|
||||
this.$set(existing, "periodName", period.periodName || "")
|
||||
this.$set(existing, "unionName", row.unionName || "")
|
||||
} else {
|
||||
this.formData.periodQuotas.push({
|
||||
unionId: row.unionId,
|
||||
unionName: row.unionName,
|
||||
periodId: period.id,
|
||||
periodName: period.periodName,
|
||||
formalQuota: nextValue
|
||||
})
|
||||
}
|
||||
},
|
||||
periodQuotaRowTotal(unionId) {
|
||||
const rows = this.formData.periodQuotas || []
|
||||
const periodMap = this.periodEnabledMap()
|
||||
return rows.filter((item) => {
|
||||
return item.unionId === unionId && periodMap[item.periodId]
|
||||
}).reduce((sum, item) => {
|
||||
return sum + this.toNonNegativeInteger(item.formalQuota)
|
||||
}, 0)
|
||||
},
|
||||
periodQuotaRemaining(unionId) {
|
||||
const unionRow = (this.formData.unionQuotas || []).find((item) => item.unionId === unionId)
|
||||
const formalQuota = unionRow ? this.toNonNegativeInteger(unionRow.formalQuota) : 0
|
||||
const remaining = formalQuota - this.periodQuotaRowTotal(unionId)
|
||||
return remaining > 0 ? remaining : 0
|
||||
},
|
||||
buildPeriodQuotasForSubmit() {
|
||||
const periodMap = {}
|
||||
;(this.formData.travelPeriods || []).forEach((period) => {
|
||||
if (period && period.id && period.enabled) {
|
||||
periodMap[period.id] = period
|
||||
}
|
||||
})
|
||||
const unionMap = {}
|
||||
;(this.formData.unionQuotas || []).forEach((row) => {
|
||||
if (row && row.unionId) {
|
||||
unionMap[row.unionId] = row
|
||||
}
|
||||
})
|
||||
return (this.formData.periodQuotas || []).filter((item) => {
|
||||
return item && item.unionId && item.periodId && periodMap[item.periodId] && this.toNonNegativeInteger(item.formalQuota) > 0
|
||||
}).map((item) => {
|
||||
const period = periodMap[item.periodId] || {}
|
||||
const unionRow = unionMap[item.unionId] || {}
|
||||
return Object.assign({}, item, {
|
||||
unionName: unionRow.unionName || item.unionName || "",
|
||||
periodName: period.periodName || item.periodName || "",
|
||||
formalQuota: this.toNonNegativeInteger(item.formalQuota)
|
||||
})
|
||||
})
|
||||
},
|
||||
toNumber(value) {
|
||||
const numberValue = parseFloat(value)
|
||||
return isNaN(numberValue) ? null : numberValue
|
||||
@@ -572,6 +973,33 @@ layout("/layouts/platform.html"){
|
||||
return ""
|
||||
})
|
||||
},
|
||||
periodQuotaSummary(param) {
|
||||
const columns = param.columns || []
|
||||
const rows = this.periodQuotaMatrixRows || []
|
||||
return columns.map((column, index) => {
|
||||
if (index === 0) {
|
||||
return "合计"
|
||||
}
|
||||
if (column.property === "formalQuota") {
|
||||
return rows.reduce((sum, row) => sum + this.toNonNegativeInteger(row.formalQuota), 0)
|
||||
}
|
||||
if (index >= 3 && index < 3 + this.periodQuotaDisplayPeriods.length) {
|
||||
const period = this.periodQuotaDisplayPeriods[index - 3]
|
||||
return rows.reduce((sum, row) => sum + this.periodQuotaValue(row.unionId, period.id), 0)
|
||||
}
|
||||
if (column.label === "已分配合计") {
|
||||
const total = rows.reduce((sum, row) => sum + this.periodQuotaRowTotal(row.unionId), 0)
|
||||
const quota = rows.reduce((sum, row) => sum + this.toNonNegativeInteger(row.formalQuota), 0)
|
||||
return total + " / " + quota
|
||||
}
|
||||
return ""
|
||||
})
|
||||
},
|
||||
checkPeriodQuotaMessage() {
|
||||
if (this.validatePeriodQuotas()) {
|
||||
this.$message.success("时间段名额校验通过")
|
||||
}
|
||||
},
|
||||
allocateQuotaByRatio() {
|
||||
const rows = this.formData.unionQuotas || []
|
||||
const ratio = this.toNumber(this.quotaAllocate.ratio)
|
||||
@@ -587,10 +1015,11 @@ layout("/layouts/platform.html"){
|
||||
this.$message.warning("比例不能为负数")
|
||||
return
|
||||
}
|
||||
// 按比例分配:比例 * 分工会会员数,小数直接舍去,不补余数。
|
||||
// 按比例分配:比例 * 分工会可参加人数,小数直接舍去;单个分工会结果不能超过本分工会可参加人数。
|
||||
const nextFormalQuotas = rows.map(row => {
|
||||
const memberCount = this.toNonNegativeInteger(row.memberCount)
|
||||
return Math.floor(ratio * memberCount)
|
||||
const formalQuota = Math.floor(ratio * memberCount)
|
||||
return Math.min(formalQuota, memberCount)
|
||||
})
|
||||
const formalTotal = nextFormalQuotas.reduce((sum, value) => sum + value, 0)
|
||||
if (formalTotal > this.branchTotalQuota) {
|
||||
@@ -607,11 +1036,11 @@ layout("/layouts/platform.html"){
|
||||
this.$refs.drawerUserScope.userScopeDialog = true
|
||||
}
|
||||
},
|
||||
async handleActivityGroupChange() {
|
||||
handleActivityGroupChange() {
|
||||
if (this.formData.activityGroupId !== null && this.formData.activityGroupId !== undefined) {
|
||||
this.formData.activityGroupId = String(this.formData.activityGroupId)
|
||||
}
|
||||
await this.getActivityGroup()
|
||||
this.getActivityGroup()
|
||||
},
|
||||
resetSearch() {
|
||||
this.pageForm.year = ""
|
||||
@@ -645,6 +1074,8 @@ layout("/layouts/platform.html"){
|
||||
homeSignupEntryImage: "",
|
||||
lots: [],
|
||||
unionQuotas: [],
|
||||
travelPeriods: [],
|
||||
periodQuotas: [],
|
||||
serviceNotice: ""
|
||||
}
|
||||
},
|
||||
@@ -655,6 +1086,8 @@ layout("/layouts/platform.html"){
|
||||
// 新建时给出默认年度和开关值,减少校工会管理员录入成本。
|
||||
this.formData = this.defaultFormData()
|
||||
this.boardingPlaceRows = []
|
||||
this.periodQuotaUnionId = ""
|
||||
this.periodQuotaDateRange = []
|
||||
this.quotaOverview.schoolFormalAssignedCount = 0
|
||||
this.dialogVisible = true
|
||||
this.loadUnionQuotaRows("")
|
||||
@@ -689,12 +1122,36 @@ layout("/layouts/platform.html"){
|
||||
settingId: ""
|
||||
})
|
||||
})
|
||||
const periodIdMap = {}
|
||||
const inheritedTravelPeriods = this.normalizeTravelPeriods(previous.travelPeriods || []).map((item) => {
|
||||
const newId = this.newClientId()
|
||||
periodIdMap[item.id] = newId
|
||||
return Object.assign({}, item, {
|
||||
id: newId,
|
||||
settingId: "",
|
||||
createdAt: "",
|
||||
createdBy: "",
|
||||
updatedAt: "",
|
||||
updatedBy: ""
|
||||
})
|
||||
})
|
||||
const inheritedPeriodQuotas = this.normalizePeriodQuotas(previous.periodQuotas || []).filter((item) => {
|
||||
return !!periodIdMap[item.periodId]
|
||||
}).map((item) => {
|
||||
return Object.assign({}, item, {
|
||||
id: "",
|
||||
settingId: "",
|
||||
periodId: periodIdMap[item.periodId]
|
||||
})
|
||||
})
|
||||
this.formData = Object.assign(this.defaultFormData(), previous, {
|
||||
year: String(currentYear),
|
||||
cycleStartYear: previous.cycleStartYear ? String(previous.cycleStartYear) : "",
|
||||
cycleEndYear: previous.cycleEndYear ? String(previous.cycleEndYear) : "",
|
||||
lots: inheritedLots,
|
||||
unionQuotas: inheritedUnionQuotas
|
||||
unionQuotas: inheritedUnionQuotas,
|
||||
travelPeriods: inheritedTravelPeriods,
|
||||
periodQuotas: inheritedPeriodQuotas
|
||||
})
|
||||
this.boardingPlaceRows = this.parseBoardingPlaceRows(this.formData.boardingPlace)
|
||||
delete this.formData.id
|
||||
@@ -719,6 +1176,8 @@ layout("/layouts/platform.html"){
|
||||
this.$set(this.formData, "homeSignupEntryEnabled", false)
|
||||
this.$set(this.formData, "homeSignupEntryImage", "")
|
||||
this.lotDeleteList = []
|
||||
this.periodQuotaUnionId = ""
|
||||
this.periodQuotaDateRange = []
|
||||
this.$message.success("已延用上一年配置信息")
|
||||
this.$nextTick(() => this.$refs.form && this.$refs.form.clearValidate())
|
||||
}).catch(() => {
|
||||
@@ -749,6 +1208,8 @@ layout("/layouts/platform.html"){
|
||||
homeSignupEntryImage: "",
|
||||
lots: [],
|
||||
unionQuotas: [],
|
||||
travelPeriods: [],
|
||||
periodQuotas: [],
|
||||
serviceNotice: ""
|
||||
}, res.data || {})
|
||||
this.boardingPlaceRows = this.parseBoardingPlaceRows(this.formData.boardingPlace)
|
||||
@@ -779,6 +1240,10 @@ layout("/layouts/platform.html"){
|
||||
allowOverReimbursement: !!item.allowOverReimbursement
|
||||
}))
|
||||
this.formData.unionQuotas = this.normalizeUnionQuotaRows(this.formData.unionQuotas || [])
|
||||
this.formData.travelPeriods = this.normalizeTravelPeriods(this.formData.travelPeriods || [])
|
||||
this.formData.periodQuotas = this.normalizePeriodQuotas(this.formData.periodQuotas || [])
|
||||
this.periodQuotaUnionId = ""
|
||||
this.periodQuotaDateRange = []
|
||||
this.loadUnionQuotaOverview(row.id)
|
||||
this.dialogVisible = true
|
||||
this.$nextTick(() => this.$refs.form && this.$refs.form.clearValidate())
|
||||
@@ -792,6 +1257,8 @@ layout("/layouts/platform.html"){
|
||||
if (!valid) return
|
||||
if (!this.validateLots()) return
|
||||
if (!this.validateUnionQuotas()) return
|
||||
if (!this.validateTravelPeriods()) return
|
||||
if (!this.validatePeriodQuotas()) return
|
||||
this.submitLoading = true
|
||||
// Nutz 对子表集合按字符串化 JSON 绑定更稳定,和体检项目维护的提交方式保持一致。
|
||||
const submitData = JSON.parse(JSON.stringify(this.formData))
|
||||
@@ -808,6 +1275,8 @@ layout("/layouts/platform.html"){
|
||||
}
|
||||
submitData.lots = JSON.stringify(this.formData.lots || [])
|
||||
submitData.unionQuotas = JSON.stringify(this.formData.unionQuotas || [])
|
||||
submitData.travelPeriods = JSON.stringify(this.formData.travelPeriods || [])
|
||||
submitData.periodQuotas = JSON.stringify(this.buildPeriodQuotasForSubmit())
|
||||
submitData.lotDeleteList = JSON.stringify(this.lotDeleteList)
|
||||
this.$axios.post(loc() + "/doSubmit", submitData).then((res) => {
|
||||
this.submitLoading = false
|
||||
@@ -955,6 +1424,39 @@ layout("/layouts/platform.html"){
|
||||
}
|
||||
return true
|
||||
},
|
||||
validateTravelPeriods() {
|
||||
const rows = this.formData.travelPeriods || []
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
const row = rows[i]
|
||||
const rowNo = "第" + (i + 1) + "行"
|
||||
if (!row.periodName) {
|
||||
this.$message.warning(rowNo + "时间段名称不能为空")
|
||||
return false
|
||||
}
|
||||
if (!row.startDate || !row.endDate) {
|
||||
this.$message.warning(rowNo + "开始日期和结束日期不能为空")
|
||||
return false
|
||||
}
|
||||
if (row.startDate > row.endDate) {
|
||||
this.$message.warning(rowNo + "开始日期不能大于结束日期")
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
},
|
||||
validatePeriodQuotas() {
|
||||
const rows = this.formData.unionQuotas || []
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
const row = rows[i]
|
||||
const formalQuota = this.toNonNegativeInteger(row.formalQuota)
|
||||
const usedQuota = this.periodQuotaRowTotal(row.unionId)
|
||||
if (usedQuota > formalQuota) {
|
||||
this.$message.warning("分工会【" + row.unionName + "】时间段名额合计 " + usedQuota + ",不能大于正式名额 " + formalQuota)
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
},
|
||||
deleteLot(index, row) {
|
||||
this.$confirm("确定删除该标段吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
@@ -993,6 +1495,17 @@ layout("/layouts/platform.html"){
|
||||
this.quotaOverview = {
|
||||
schoolFormalAssignedCount: 0
|
||||
}
|
||||
this.periodQuotaUnionId = ""
|
||||
this.periodQuotaDateRange = []
|
||||
this.periodDialogVisible = false
|
||||
this.periodEditIndex = -1
|
||||
this.periodForm = {
|
||||
id: "",
|
||||
periodName: "",
|
||||
startDate: "",
|
||||
endDate: "",
|
||||
enabled: true
|
||||
}
|
||||
this.inheritLoading = false
|
||||
this.activeTab = "basic"
|
||||
}
|
||||
@@ -1094,11 +1607,56 @@ layout("/layouts/platform.html"){
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.tour-tab-toolbar-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.tour-tab-alert {
|
||||
flex-shrink: 0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tour-tab-table {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.tour-period-quota-toolbar {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
gap: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tour-period-quota-union {
|
||||
width: 180px;
|
||||
}
|
||||
|
||||
.tour-period-quota-cell {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.tour-period-quota-cell .el-input {
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
.tour-period-quota-cell span {
|
||||
color: #606266;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.text-danger {
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
.text-success {
|
||||
color: #67c23a;
|
||||
}
|
||||
|
||||
.tour-setting-dialog {
|
||||
height: 85vh;
|
||||
max-height: 85vh;
|
||||
|
||||
+16
@@ -183,6 +183,20 @@ layout("/layouts/platform.html"){
|
||||
<el-input v-model="signupForm.travelAgencyName" :disabled="isDirectFamilyLine(signupForm)" readonly></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="图片材料">
|
||||
<file-upload
|
||||
style="--upload-width: 96px;--upload-height:96px"
|
||||
:upload_number="9"
|
||||
:upload_size="20971520"
|
||||
:value.sync="signupForm.photoFiles"
|
||||
accept=".jpg,.jpeg,.png"
|
||||
complete_result
|
||||
upload_mode="image"
|
||||
upload_result_category="interval">
|
||||
</file-upload>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
@@ -867,6 +881,7 @@ layout("/layouts/platform.html"){
|
||||
travelAgencyId: matter.travelAgencyId || "",
|
||||
travelAgencyName: matter.travelAgencyName || "",
|
||||
boardingPlace: matter.defaultBoardingPlace || "",
|
||||
photoFiles: "",
|
||||
travelPeriod: matter.travelPeriod || "",
|
||||
travelStartTime: matter.travelStartTime || "",
|
||||
travelEndTime: matter.travelEndTime || "",
|
||||
@@ -886,6 +901,7 @@ layout("/layouts/platform.html"){
|
||||
travelAgencyId: matter.travelAgencyId || ledger.travelAgencyId || "",
|
||||
travelAgencyName: matter.travelAgencyName || ledger.travelAgencyName || "",
|
||||
boardingPlace: ledger.boardingPlace || matter.defaultBoardingPlace || "",
|
||||
photoFiles: ledger.photoFiles || "",
|
||||
travelPeriod: matter.travelPeriod || "",
|
||||
travelStartTime: matter.travelStartTime || "",
|
||||
travelEndTime: matter.travelEndTime || "",
|
||||
|
||||
+1
-1
@@ -129,7 +129,7 @@ layout("/layouts/platform.html"){
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="人员类型" prop="personType">
|
||||
<el-radio-group v-model="assignForm.personType" @change="assignPersonTypeChange">
|
||||
<el-radio-button label="FORMAL">正式人员</el-radio-button>
|
||||
|
||||
+58
@@ -115,6 +115,17 @@ layout("/layouts/platform_h5.html"){
|
||||
font-size: 13px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tour-upload-field {
|
||||
padding: 12px 16px 14px;
|
||||
}
|
||||
|
||||
.tour-upload-label {
|
||||
margin-bottom: 8px;
|
||||
color: #323233;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app">
|
||||
@@ -162,6 +173,18 @@ layout("/layouts/platform_h5.html"){
|
||||
</van-field>
|
||||
<van-field v-if="fillBedInfo" label="床位信息" v-model="signupForm.bedInfo" maxlength="100" placeholder="请输入床位数量"></van-field>
|
||||
<van-field v-if="fillBedInfo" label="意向拼床人" v-model="signupForm.intendedRoommate" maxlength="100" placeholder="请输入意向拼床人"></van-field>
|
||||
<div class="tour-upload-field">
|
||||
<div class="tour-upload-label">图片材料</div>
|
||||
<h5-file-upload
|
||||
:upload_number="9"
|
||||
:upload_size="20971520"
|
||||
:value.sync="signupForm.photoFiles"
|
||||
accept=".jpg,.jpeg,.png"
|
||||
complete_result
|
||||
upload_mode="image"
|
||||
upload_result_category="array">
|
||||
</h5-file-upload>
|
||||
</div>
|
||||
<van-field v-if="canApplyOverReimbursement(signupForm)" class="tour-over-cost" label="报销超出费用">
|
||||
<template #input>
|
||||
<van-radio-group v-model="signupForm.overCostReimbursed" direction="horizontal">
|
||||
@@ -411,6 +434,7 @@ layout("/layouts/platform_h5.html"){
|
||||
travelAgencyId: matter.travelAgencyId || "",
|
||||
travelAgencyName: matter.travelAgencyName || "",
|
||||
boardingPlace: "",
|
||||
photoFiles: "",
|
||||
travelPeriod: matter.travelPeriod || "",
|
||||
travelStartTime: matter.travelStartTime || "",
|
||||
travelEndTime: matter.travelEndTime || "",
|
||||
@@ -430,6 +454,7 @@ layout("/layouts/platform_h5.html"){
|
||||
travelAgencyId: matter.travelAgencyId || ledger.travelAgencyId || "",
|
||||
travelAgencyName: matter.travelAgencyName || ledger.travelAgencyName || "",
|
||||
boardingPlace: ledger.boardingPlace || assignment.boardingPlace || "",
|
||||
photoFiles: this.toUploadFileList(ledger.photoFiles || assignment.photoFiles || ""),
|
||||
travelPeriod: matter.travelPeriod || "",
|
||||
travelStartTime: matter.travelStartTime || "",
|
||||
travelEndTime: matter.travelEndTime || "",
|
||||
@@ -466,6 +491,38 @@ layout("/layouts/platform_h5.html"){
|
||||
return String(value).split(",").map((item) => item.trim()).filter((item) => item)
|
||||
}
|
||||
},
|
||||
toUploadFileList(value) {
|
||||
if (!value) return []
|
||||
|
||||
return String(value)
|
||||
.split(",")
|
||||
.map((url, index) => {
|
||||
url = url.trim()
|
||||
|
||||
return {
|
||||
url: url,
|
||||
name: "photo_" + index + ".jpg",
|
||||
status: null,
|
||||
isImage: true
|
||||
}
|
||||
})
|
||||
},
|
||||
fileNameFromUrl(url) {
|
||||
if (!url) return "图片材料"
|
||||
const pureUrl = String(url).split("?")[0]
|
||||
const name = pureUrl.substring(pureUrl.lastIndexOf("/") + 1)
|
||||
return name || "图片材料"
|
||||
},
|
||||
serializePhotoFiles(value) {
|
||||
if (!value) return ""
|
||||
if (Array.isArray(value)) {
|
||||
return value.map((item) => {
|
||||
if (typeof item === "string") return item.trim()
|
||||
return item && item.url ? String(item.url).trim() : ""
|
||||
}).filter((item) => item).join(",")
|
||||
}
|
||||
return String(value).split(",").map((item) => item.trim()).filter((item) => item).join(",")
|
||||
},
|
||||
emptyFamily() {
|
||||
return {
|
||||
familyName: "",
|
||||
@@ -667,6 +724,7 @@ layout("/layouts/platform_h5.html"){
|
||||
if (!this.validateDirectRelative()) return
|
||||
const families = this.allowFamily && this.signupForm.hasFamily ? this.familyData : []
|
||||
const form = Object.assign({}, this.signupForm, {
|
||||
photoFiles: this.serializePhotoFiles(this.signupForm.photoFiles),
|
||||
families: JSON.stringify(families),
|
||||
directRelative: this.isDirectFamilyLine(this.signupForm) ? JSON.stringify(this.directRelativeForm) : ""
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user