移植v3疗休养到这个版本上

This commit is contained in:
2026-09-03 08:42:56 +08:00
parent 7b1e83d33b
commit 15991eeb9c
43 changed files with 2436 additions and 579 deletions
@@ -8,10 +8,15 @@ import com.budwk.app.base.result.Result;
import com.budwk.app.zhgh.staffbenefit.recuperation.service.RecuperationAuditService;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.ioc.aop.Aop;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* 旧版通用 audit 审核入口,不接入 wf。stage 支持 branch、lineUnion、school、travel
@@ -48,8 +53,9 @@ public class RecuperationAuditController {
/** pageForm 为分页信息;stage 为审核环节;year 为年度;audited 表示已审/待审;keyword 为姓名或工号。 */
@At
@SaCheckPermission("recuperation.audit")
public Result pageData(PageForm pageForm, String stage, Integer year, Boolean audited, String keyword) {
return Result.success(auditService.auditPage(pageForm, stage, year, audited, keyword));
public Result pageData(PageForm pageForm, String stage, Integer year, Boolean audited, String keyword,
String unionId, String lineId, String agencyId) {
return Result.success(auditService.auditPage(pageForm, stage, year, audited, keyword, unionId, lineId, agencyId));
}
@At
@@ -60,6 +66,7 @@ public class RecuperationAuditController {
/** id 为报名记录,pass 为是否通过,auditOpinion 为意见;返回 success=true 表示 audit 和报名状态已在同一事务更新。 */
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("recuperation.audit")
public Result audit(String id, Boolean pass, String auditOpinion) {
if (StrUtil.isBlank(id) || pass == null) return Result.error("审核参数不完整");
@@ -68,10 +75,23 @@ public class RecuperationAuditController {
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("recuperation.audit")
public Result recall(String id) {
if (StrUtil.isBlank(id)) return Result.error("参数错误");
auditService.recallAudit(id);
return Result.success();
}
/** ids 为逗号分隔的报名记录 IDpass 为审核结果;auditOpinion 为统一意见。 */
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("recuperation.audit")
public Result oneKeyAudit(String ids, Boolean pass, String auditOpinion) {
if (StrUtil.isBlank(ids) || pass == null) return Result.error("请选择需要审核的报名记录");
List<String> idList = Arrays.stream(ids.split(",")).map(String::trim)
.filter(StrUtil::isNotBlank).collect(Collectors.toList());
auditService.auditEnrolls(idList, pass, auditOpinion);
return Result.success("审核完成");
}
}
@@ -7,16 +7,19 @@ import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.date.DateUtil;
import com.budwk.app.base.annotation.SLog;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.utils.CommonDownloadUtil;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.staffbenefit.recuperation.constant.RecuperationState;
import com.budwk.app.zhgh.staffbenefit.recuperation.model.RecuperationConfig;
import com.budwk.app.zhgh.staffbenefit.recuperation.model.RecuperationEnroll;
import com.budwk.app.zhgh.staffbenefit.recuperation.model.RecuperationEnrollBed;
import com.budwk.app.zhgh.staffbenefit.recuperation.model.RecuperationEnrollCompanion;
import com.budwk.app.zhgh.staffbenefit.recuperation.param.RecuperationUserQueryPageForm;
import com.budwk.app.zhgh.staffbenefit.recuperation.service.RecuperationEnrollService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@@ -40,9 +43,12 @@ import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* @ClassName RecuperationBranchUnionUserQueryController
@@ -64,9 +70,12 @@ public class RecuperationBranchUnionUserQueryController {
private RecuperationEnrollService enrollService;
@At("")
@Ok("beetl:/platform/zhgh/staffbenefit/recuperation/branchUnionUserQuery/index.html")
@Ok("beetl:/platform/zhgh/staffbenefit/recuperation/schoolUnionUserQuery/index.html")
@SaCheckLogin
public void index() {}
public void index(HttpServletRequest request) {
request.setAttribute("queryBase", "/platform/recuperation/branchUnionUserQuery");
request.setAttribute("schoolMode", false);
}
/**
* 分工会人员信息查询
@@ -85,7 +94,7 @@ public class RecuperationBranchUnionUserQueryController {
@At
@ApiOperation("分页查询")
@SaCheckPermission("recuperation.branchUnionUserQuery")
public Result pageData(PageForm pageForm,
public Result legacyPageData(PageForm pageForm,
Integer year,
String userName,
String loginName,
@@ -180,6 +189,54 @@ public class RecuperationBranchUnionUserQueryController {
return Result.success(pagination);
}
/**
* 分工会 PC 人员综合查询。
*
* @param query 年度范围、报名状态、报名类型、线路及分页参数;工会固定取当前登录人
* @return Result.data 为 Paginationlist 是人员列表,totalCount 是总人数
*/
@At
@ApiOperation("分工会人员综合查询")
@SaCheckPermission("recuperation.branchUnionUserQuery")
public Result pageData(@Param("..") RecuperationUserQueryPageForm query) {
return Result.success(enrollService.managementPageData(query, true));
}
/**
* 查询当前分工会三种报名类型的人数。
*
* @param query 年度、报名日期和参加状态等查询参数
* @return xlCount、lxsCount、jdCount 分别为线路、灵活组团、定点人数
*/
@At
@ApiOperation("查询报名类型人数")
@SaCheckPermission("recuperation.branchUnionUserQuery")
public Result getCategoryCount(@Param("..") RecuperationUserQueryPageForm query) {
return Result.success(enrollService.managementCategoryCount(query, true));
}
/** 按当前分工会筛选条件提醒人员,loginNames 为空时提醒全部筛选结果。 */
@At
@ApiOperation("提醒疗休养人员")
@SaCheckPermission("recuperation.branchUnionUserQuery")
public Result remindUsers(@Param("..") RecuperationUserQueryPageForm query, String loginNames, String content) {
List<String> selected = StrUtil.isBlank(loginNames) ? new ArrayList<>() : Arrays.stream(loginNames.split(","))
.map(String::trim).filter(StrUtil::isNotBlank).collect(Collectors.toList());
NutMap result = enrollService.remindManagementUsers(query, true, selected, content);
return result.getInt("sendCount", 0) > 0 ? Result.success(result.getString("msg"), result) : Result.error(result.getString("msg"));
}
/** 保留报名记录,将作废记录恢复为正常记录。 */
@At
@Aop(TransAop.READ_COMMITTED)
@ApiOperation("保留报名记录")
@SaCheckPermission("recuperation.branchUnionUserQuery")
@SLog(tag = "疗休养管理-分工会查询", msg = "保留报名记录")
public Result restoreEnroll(String id) {
enrollService.restoreEnroll(id);
return Result.success();
}
/**
* 设置出行人员
*
@@ -244,6 +301,46 @@ public class RecuperationBranchUnionUserQueryController {
return Result.success(data);
}
/**
* 修改报名线路。enroll 包含报名记录 ID 和新的线路选择记录 ID,返回统一 Result。
*/
@At
@Aop(TransAop.READ_COMMITTED)
@ApiOperation("修改报名信息")
@SaCheckPermission("recuperation.branchUnionUserQuery")
@SLog(tag = "疗休养管理-分工会查询", msg = "修改报名信息")
public Result doEdit(RecuperationEnroll enroll) {
enroll.setNormal(true);
dao.updateIgnoreNull(enroll);
return Result.success();
}
/**
* 查询当前年度允许分工会调整的线路,返回线路选择记录、日期、标段和报名方式。
*/
@At
@ApiOperation("获取分工会可选线路")
@SaCheckPermission("recuperation.branchUnionUserQuery")
public Result getUnionSelectLine() {
Sql sql = Sqls.create("""
SELECT rlus.id, rlus.unionId, rlus.lineId,
DATE_FORMAT(rlus.playStartTime, '%Y-%m-%d') AS playStartTime,
DATE_FORMAT(rlus.playEndTime, '%Y-%m-%d') AS playEndTime,
rl.lineName, rl.regionalNature,
IF(rlus.signUpMode=1,'分工会','校工会') AS signUpMode, lot.lotName
FROM the_rapy_recuperation_line_union_select rlus
LEFT JOIN the_rapy_recuperation_line rl ON rlus.lineId=rl.id
LEFT JOIN the_rapy_recuperation_lot lot ON rl.lotId=lot.id
$condition
""");
Cnd cnd = Cnd.where("rlus.isOpen", "=", 1)
.and("rlus.unionId", "=", SecurityUtil.getUnionId())
.and("YEAR(rlus.selectTime)", "=", DateUtil.thisYear());
cnd.desc("lot.lotValue").desc("rl.lineName");
sql.setCondition(cnd);
return Result.success(enrollService.listMap(sql));
}
/**
* 获取线路
*
@@ -290,6 +387,28 @@ public class RecuperationBranchUnionUserQueryController {
return Result.success(list);
}
/** 按分工会页面当前筛选条件导出全部人员,Excel 使用 XSSF 格式。 */
@At
@Ok("void")
@ApiOperation("导出人员综合查询")
@SaCheckPermission("recuperation.branchUnionUserQuery")
public void exportManagement(@Param("..") RecuperationUserQueryPageForm query, HttpServletResponse response) {
List<ExcelExportEntity> columns = new ArrayList<>();
columns.add(new ExcelExportEntity("工号", "loginName", 18));
columns.add(new ExcelExportEntity("姓名", "userName", 14));
columns.add(new ExcelExportEntity("所属单位", "unitName", 24));
columns.add(new ExcelExportEntity("所属工会", "unionName", 20));
columns.add(new ExcelExportEntity("报名状态", "signUpStatus", 14));
columns.add(new ExcelExportEntity("报名类型", "signUpModeName", 14));
columns.add(new ExcelExportEntity("线路/旅行社/定点", "targetName", 30));
columns.add(new ExcelExportEntity("是否参加", "takePartInName", 14));
columns.add(new ExcelExportEntity("报名时间", "signingUptime", 22));
ExportParams exportParams = new ExportParams();
exportParams.setType(ExcelType.XSSF);
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, columns, enrollService.managementList(query, true));
CommonDownloadUtil.download("疗休养人员查询.xlsx", workbook, response);
}
@At
@Ok("void")
@ApiOperation("导出")
@@ -134,6 +134,46 @@ public class RecuperationEvaluateStatisticsController {
return Result.success(enrollService.listMap(sql));
}
/** year 为统计年度;data 返回旅行社综合评分和线路、灵活组团、定点方案评分。 */
@At
@SaCheckPermission("recuperation.statistics")
public Result getSatisfactionRatingStatistics(Integer year) {
return Result.success(enrollService.satisfactionRatingStatistics(year));
}
/** year 为统计年度,typeName 可传全部、线路、灵活组团或定点;data 返回评价明细。 */
@At
@SaCheckPermission("recuperation.statistics")
public Result getSatisfactionFeedbackDetails(Integer year, String typeName) {
return Result.success(enrollService.satisfactionFeedbackDetails(year, typeName));
}
@At
@Ok("void")
@SaCheckPermission("recuperation.statistics")
public void exportSatisfactionFeedbackDetails(Integer year, String typeName, HttpServletResponse response) {
List<NutMap> rows = enrollService.satisfactionFeedbackDetails(year, typeName);
for (int i = 0; i < rows.size(); i++) rows.get(i).setv("no", i + 1);
List<ExcelExportEntity> columns = new ArrayList<>();
columns.add(new ExcelExportEntity("序号", "no", 10));
columns.add(new ExcelExportEntity("报名方式", "typeName", 12));
columns.add(new ExcelExportEntity("方案名称", "schemeName", 24));
columns.add(new ExcelExportEntity("旅行社", "travelAgencyName", 22));
columns.add(new ExcelExportEntity("评价人", "userName", 14));
columns.add(new ExcelExportEntity("所属单位", "unitName", 24));
columns.add(new ExcelExportEntity("旅行社评分", "evaluationForTravelAgency", 14));
columns.add(new ExcelExportEntity("住宿/酒店评分", "evaluationForAccommodation", 16));
columns.add(new ExcelExportEntity("行程评分", "evaluationForJourney", 14));
columns.add(new ExcelExportEntity("餐饮评分", "evaluationForDining", 14));
columns.add(new ExcelExportEntity("交通评分", "evaluationForTransportation", 14));
columns.add(new ExcelExportEntity("综合评分", "compositeScore", 14));
columns.add(new ExcelExportEntity("评价建议", "feedbackContent", 45));
ExportParams exportParams = new ExportParams();
exportParams.setType(ExcelType.XSSF);
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, columns, rows);
CommonDownloadUtil.download("满意度评价明细.xlsx", workbook, response);
}
@At
@Ok("void")
@ApiOperation("导出评价统计")
@@ -0,0 +1,117 @@
package com.budwk.app.zhgh.staffbenefit.recuperation.controller;
import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.utils.CommonDownloadUtil;
import com.budwk.app.zhgh.staffbenefit.recuperation.service.RecuperationProvinceFlexibleGroupService;
import org.apache.poi.ss.usermodel.Workbook;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
/** PC 端省内灵活组团及报名人员查询。 */
@IocBean
@At("/platform/recuperation/flexibleGroupQuery")
@Ok("json:full")
public class RecuperationFlexibleGroupQueryController {
@Inject
private RecuperationProvinceFlexibleGroupService flexibleGroupService;
@At("")
@Ok("beetl:/platform/zhgh/staffbenefit/recuperation/flexibleGroupQuery/index.html")
@SaCheckPermission("recuperation.flexibleGroupQuery")
public void index() {
}
/** 查询年度区间、组团、旅行社和人员关键字;data 返回组团分页数据。 */
@At
@SaCheckPermission("recuperation.flexibleGroupQuery")
public Result pageData(PageForm pageForm, Integer startYear, Integer endYear, String groupName,
String travelAgencyId, String searchKeyword) {
return Result.success(flexibleGroupService.queryPage(pageForm, startYear, endYear, groupName, travelAgencyId, searchKeyword));
}
/** groupId 为灵活组团 ID;data 返回按团长聚合的团列表。 */
@At
@SaCheckLogin
public Result getGroupInfo(String groupId) {
return Result.success(flexibleGroupService.queryGroupInfo(groupId));
}
/** 团长参数指定某个团,noGroupLeader=true 查询未分团人员;data 返回人员分页数据。 */
@At
@SaCheckLogin
public Result getSignUser(PageForm pageForm, String groupId, String groupLeaderUserId,
String groupLeaderLoginName, Boolean noGroupLeader) {
return Result.success(flexibleGroupService.querySignUsers(pageForm, groupId, groupLeaderUserId, groupLeaderLoginName, noGroupLeader));
}
@At
@SaCheckLogin
public Result getAllSignUser(PageForm pageForm, String groupId) {
return Result.success(flexibleGroupService.querySignUsers(pageForm, groupId, null, null, false));
}
@At
@SaCheckLogin
public Result selectTravelAgencyList(Integer startYear, Integer endYear) {
return Result.success(flexibleGroupService.queryTravelAgencyOptions(startYear, endYear));
}
@At
@SaCheckLogin
public Result selectFlexibleGroupList(Integer startYear, Integer endYear) {
return Result.success(flexibleGroupService.queryFlexibleGroupOptions(startYear, endYear));
}
/** id 为报名记录 ID;删除报名时同步删除家属明细。 */
@At
@SaCheckPermission("recuperation.flexibleGroupQuery")
@Aop(TransAop.READ_COMMITTED)
public Result deleteJoinUser(String id) {
if (StrUtil.isBlank(id)) return Result.error("报名记录不能为空");
flexibleGroupService.deleteJoinUser(id);
return Result.success();
}
/** 按当前页面筛选条件导出全部报名人员。 */
@At
@Ok("void")
@SaCheckPermission("recuperation.flexibleGroupQuery")
public void doExport(Integer startYear, Integer endYear, String groupName, String travelAgencyId,
String searchKeyword, HttpServletResponse response) {
List<ExcelExportEntity> columns = new ArrayList<>();
columns.add(new ExcelExportEntity("序号", "no", 10));
columns.add(new ExcelExportEntity("工号", "loginName", 18));
columns.add(new ExcelExportEntity("姓名", "userName", 14));
columns.add(new ExcelExportEntity("联系电话", "mobile", 18));
columns.add(new ExcelExportEntity("身份证号", "idCard", 24));
columns.add(new ExcelExportEntity("单位", "unitName", 24));
columns.add(new ExcelExportEntity("工会", "unionName", 20));
columns.add(new ExcelExportEntity("旅行社", "travelAgencyName", 22));
columns.add(new ExcelExportEntity("报名时间", "signingUptime", 20));
columns.add(new ExcelExportEntity("团长", "leaderName", 20));
columns.add(new ExcelExportEntity("是否成团", "formedTeamState", 14));
ExportParams exportParams = new ExportParams();
exportParams.setType(ExcelType.XSSF);
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, columns,
flexibleGroupService.queryExportUsers(startYear, endYear, groupName, travelAgencyId, searchKeyword));
CommonDownloadUtil.download("灵活组团报名人员名单.xlsx", workbook, response);
}
}
@@ -22,6 +22,8 @@ import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
import org.nutz.mvc.upload.TempFile;
import org.nutz.mvc.upload.UploadAdaptor;
import org.nutz.ioc.aop.Aop;
import org.nutz.aop.interceptor.ioc.TransAop;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
@@ -44,7 +46,7 @@ public class RecuperationJoinUserImportController {
@At
@SaCheckPermission("recuperation.joinUserImport")
public Result options(Integer year, String keyword) { return Result.success(importService.selectLineAndTravelAgencyList(year, keyword)); }
public Result options(Integer year, String keyword) { return Result.success(importService.selectLineOrTravelAgency(year)); }
/** file 为 xls/xlsxlineId 与 travelAgencyId 二选一;data 返回 excelRows 和匹配到的 matchList。 */
@At
@@ -64,6 +66,7 @@ public class RecuperationJoinUserImportController {
/** enrolls 为已确认匹配的报名记录数组;返回 success=true 表示实际参加状态和日期已更新。 */
@At
@SaCheckPermission("recuperation.joinUserImport")
@Aop(TransAop.READ_COMMITTED)
public Result doImport(@Param("enrolls") RecuperationEnroll[] enrolls) {
importService.markParticipated(enrolls == null ? List.of() : List.of(enrolls));
return Result.success();
@@ -11,6 +11,8 @@ import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
import org.nutz.ioc.aop.Aop;
import org.nutz.aop.interceptor.ioc.TransAop;
/** 旧版报名人员调整接口。 */
@IocBean
@@ -47,14 +49,26 @@ public class RecuperationLineAdjustmentController {
return Result.success(adjustmentService.findUnionSignUpModeUserList(lineId, unionId));
}
/** lineId 为线路选择记录,loginNames 为需调入或调出的登录名数组;返回统一 Result。 */
/** lineId 为线路选择记录,loginNames 为人员工号数组,normal 表示恢复正常或调出;返回统一 Result。 */
@At
@SaCheckPermission("recuperation.lineAdjustment")
public Result adjustmentUsers(String lineId, @Param("loginNames") String[] loginNames) {
@Aop(TransAop.READ_COMMITTED)
public Result adjustmentUsers(String lineId, @Param("loginNames") String[] loginNames, boolean normal) {
if (StrUtil.isBlank(lineId) || loginNames == null || loginNames.length == 0) {
return Result.error("请选择需要调整的人员");
}
adjustmentService.adjustmentUsers(lineId, loginNames);
adjustmentService.adjustmentUsers(lineId, loginNames, normal);
return Result.success();
}
/** lineId 为线路选择记录,loginNames 为接收提醒的工号数组;返回消息中心发送结果。 */
@At
@SaCheckPermission("recuperation.lineAdjustment")
public Result smsAlerts(String lineId, @Param("loginNames") String[] loginNames) {
if (StrUtil.isBlank(lineId) || loginNames == null || loginNames.length == 0) {
return Result.error("请选择需要提醒的人员");
}
return adjustmentService.smsAlerts(lineId, loginNames)
? Result.success("提醒发送成功") : Result.error("提醒发送失败,请检查消息中心配置");
}
}
@@ -9,6 +9,8 @@ import com.budwk.app.zhgh.staffbenefit.recuperation.model.RecuperationCluster;
import com.budwk.app.zhgh.staffbenefit.recuperation.service.RecuperationLineClusterService;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.ioc.aop.Aop;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.json.Json;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
@@ -46,6 +48,7 @@ public class RecuperationLineClusterController {
/** clusters 为组团及 members 的 JSON 数组,lineId 为线路选择记录;返回统一 Result。 */
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("recuperation.lineCluster")
public Result setClusterMembers(@Param("clusters") String clusters, String lineId) {
if (StrUtil.isBlank(lineId) || StrUtil.isBlank(clusters)) {
@@ -162,6 +162,7 @@ public class RecuperationLineSelectController {
*/
@At("/selectLine")
@ApiOperation("选择线路")
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission(value = {"recuperation.branchUnionSelect", "recuperation.schoolUnionSelect"}, mode = SaMode.OR)
@SLog(tag = "疗休养管理-选择线路", msg = "选择线路")
public Result selectLine(RecuperationLineSelect us) {
@@ -0,0 +1,72 @@
package com.budwk.app.zhgh.staffbenefit.recuperation.controller;
import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.zhgh.staffbenefit.recuperation.service.RecuperationEnrollService;
import com.budwk.app.zhgh.staffbenefit.recuperation.service.RecuperationLineStatisticsService;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
/** PC 端线路报名情况及成团通知。 */
@IocBean
@At("/platform/recuperation/lineStatistics")
@Ok("json:full")
public class RecuperationLineStatisticsController {
@Inject private RecuperationLineStatisticsService statisticsService;
@Inject private RecuperationEnrollService enrollService;
@At("")
@Ok("beetl:/platform/zhgh/staffbenefit/recuperation/lineStatistics/index.html")
@SaCheckPermission("recuperation.lineStatistics")
public void index() { }
/** 按年度、工会、线路、标段、报名模式及出行批次查询线路报名统计。 */
@At
@SaCheckPermission("recuperation.lineStatistics")
public Result pageData(PageForm pageForm, Integer startYear, Integer endYear, String unionId, String takePartInLineId,
String lotId, String signUpMode, String selectId, String regionalNature) {
return Result.success(statisticsService.pageData(pageForm, startYear, endYear, unionId, takePartInLineId,
lotId, signUpMode, selectId, regionalNature));
}
/** takePartLineId 为工会选线记录 ID;data 返回审核通过的正常报名人员分页数据。 */
@At
@SaCheckPermission("recuperation.lineStatistics")
public Result getUserDateByLine(PageForm pageForm, String takePartLineId, String searchKeyword, String unionId, String unitId) {
return Result.success(statisticsService.userPage(pageForm, takePartLineId, searchKeyword, unionId, unitId));
}
@At
@SaCheckLogin
public Result getLineOptions(Integer startYear, Integer endYear, String signUpMode, String regionalNature) {
return Result.success(statisticsService.lineOptions(startYear, endYear, signUpMode, regionalNature));
}
@At
@SaCheckPermission("recuperation.lineStatistics")
public Result countSuccessNotice(String id) { return Result.success(enrollService.countLineGroupNoticeUsers(id, true)); }
@At
@SaCheckPermission("recuperation.lineStatistics")
public Result countFailNotice(String id) { return Result.success(enrollService.countLineGroupNoticeUsers(id, false)); }
/** id 为工会选线记录,content 为管理员确认后的通知正文。 */
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("recuperation.lineStatistics")
public Result sendSuccess(String id, String content) { return Result.success(enrollService.sendLineGroupSuccessNotice(id, content)); }
/** type=true 保留并调出报名记录,false 同步删除报名、家属及床位记录。 */
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("recuperation.lineStatistics")
public Result sendFail(String id, Boolean type, String content) {
return Result.success(enrollService.sendLineGroupFailureNotice(id, Boolean.TRUE.equals(type), content));
}
}
@@ -15,6 +15,8 @@ import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
import org.nutz.ioc.aop.Aop;
import org.nutz.aop.interceptor.ioc.TransAop;
/**
* 旧版省内灵活组团入口。接口接收年度、组团名称、工会及旅行社筛选条件,
@@ -55,6 +57,7 @@ public class RecuperationProvinceFlexibleGroupController {
/** 保存新增或编辑数据;返回 Result,success=true 表示旧表写入成功。 */
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("recuperation.provinceFlexibleGroup")
public Result onSubmit(RecuperationProvinceFlexibleGroup flexibleGroup) {
if (flexibleGroup == null) {
@@ -65,6 +68,7 @@ public class RecuperationProvinceFlexibleGroupController {
}
@At("/toggle/?")
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("recuperation.provinceFlexibleGroup")
public Result toggle(String id) {
if (StrUtil.isBlank(id)) {
@@ -75,6 +79,7 @@ public class RecuperationProvinceFlexibleGroupController {
}
@At("/delete/?")
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("recuperation.provinceFlexibleGroup")
public Result delete(String id) {
if (StrUtil.isBlank(id)) {
@@ -85,6 +90,7 @@ public class RecuperationProvinceFlexibleGroupController {
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("recuperation.provinceFlexibleGroup")
public Result batchAssignSignUpTimes(@Param("ids") String[] ids, RecuperationProvinceFlexibleGroup form) {
flexibleGroupService.batchAssignSignUpTimes(ids, form.getSignUpStartTime(), form.getSignUpEndTime(), form.getChangeEndTime());
@@ -1,12 +1,15 @@
package com.budwk.app.zhgh.staffbenefit.recuperation.controller;
import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.annotation.SLog;
import com.budwk.app.base.page.Pagination;
@@ -20,6 +23,8 @@ import com.budwk.app.zhgh.staffbenefit.recuperation.model.RecuperationConfig;
import com.budwk.app.zhgh.staffbenefit.recuperation.model.RecuperationEnroll;
import com.budwk.app.zhgh.staffbenefit.recuperation.model.RecuperationEnrollBed;
import com.budwk.app.zhgh.staffbenefit.recuperation.model.RecuperationEnrollCompanion;
import com.budwk.app.zhgh.staffbenefit.recuperation.mode.RecuperationEnrollExcelMode;
import com.budwk.app.zhgh.staffbenefit.recuperation.param.RecuperationUserQueryPageForm;
import com.budwk.app.zhgh.staffbenefit.recuperation.service.RecuperationEnrollService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@@ -42,11 +47,26 @@ import org.nutz.lang.util.NutMap;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
import org.nutz.mvc.annotation.AdaptBy;
import org.nutz.mvc.upload.TempFile;
import org.nutz.mvc.upload.UploadAdaptor;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.io.ByteArrayOutputStream;
import java.io.BufferedOutputStream;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* @ClassName RecuperationSchoolUnionUserQueryController
@@ -72,7 +92,10 @@ public class RecuperationSchoolUnionUserQueryController {
@At("")
@Ok("beetl:/platform/zhgh/staffbenefit/recuperation/schoolUnionUserQuery/index.html")
@SaCheckLogin
public void index() {}
public void index(HttpServletRequest request) {
request.setAttribute("queryBase", "/platform/recuperation/schoolUnionUserQuery");
request.setAttribute("schoolMode", true);
}
/**
* 校工会人员信息查询
@@ -91,7 +114,7 @@ public class RecuperationSchoolUnionUserQueryController {
@At
@ApiOperation("分页查询")
@SaCheckPermission("recuperation.schoolUnionUserQuery")
public Result pageData(PageForm pageForm,
public Result legacyPageData(PageForm pageForm,
Integer year,
String userName,
String loginName,
@@ -188,6 +211,66 @@ public class RecuperationSchoolUnionUserQueryController {
return Result.success(pagination);
}
/**
* 校工会 PC 人员综合查询。
*
* @param query 年度范围、报名状态、报名类型、工会、线路及分页参数
* @return Result.data 为 Paginationlist 是人员列表,totalCount 是总人数
*/
@At
@ApiOperation("校工会人员综合查询")
@SaCheckPermission("recuperation.schoolUnionUserQuery")
public Result pageData(@Param("..") RecuperationUserQueryPageForm query) {
return Result.success(enrollService.managementPageData(query, false));
}
/**
* 查询当前条件下三种报名类型的人数。
*
* @param query 年度、分工会、报名日期和参加状态等查询参数
* @return xlCount、lxsCount、jdCount 分别为线路、灵活组团、定点人数
*/
@At
@ApiOperation("查询报名类型人数")
@SaCheckPermission("recuperation.schoolUnionUserQuery")
public Result getCategoryCount(@Param("..") RecuperationUserQueryPageForm query) {
return Result.success(enrollService.managementCategoryCount(query, false));
}
/**
* 按当前筛选条件提醒人员。
*
* @param query 查询参数
* @param loginNames 页面勾选工号,逗号分隔;为空时提醒筛选结果全部人员
* @param content 提醒正文
* @return sendCount 为实际接收人数,msg 为发送结果
*/
@At
@ApiOperation("提醒疗休养人员")
@SaCheckPermission("recuperation.schoolUnionUserQuery")
public Result remindUsers(@Param("..") RecuperationUserQueryPageForm query, String loginNames, String content) {
List<String> selected = StrUtil.isBlank(loginNames) ? new ArrayList<>() : Arrays.stream(loginNames.split(","))
.map(String::trim).filter(StrUtil::isNotBlank).collect(Collectors.toList());
NutMap result = enrollService.remindManagementUsers(query, false, selected, content);
return result.getInt("sendCount", 0) > 0 ? Result.success(result.getString("msg"), result) : Result.error(result.getString("msg"));
}
/**
* 将作废报名恢复为正常报名。
*
* @param id 报名记录 ID
* @return 操作结果
*/
@At
@Aop(TransAop.READ_COMMITTED)
@ApiOperation("保留报名记录")
@SaCheckPermission("recuperation.schoolUnionUserQuery")
@SLog(tag = "疗休养管理-校工会查询", msg = "保留报名记录")
public Result restoreEnroll(String id) {
enrollService.restoreEnroll(id);
return Result.success();
}
/**
* 设置出行人员
*
@@ -331,6 +414,111 @@ public class RecuperationSchoolUnionUserQueryController {
return Result.success(listMap);
}
/**
* 按页面当前条件导出全部人员。
*
* @param query 查询参数,含报名、未报名、未成团状态及全部筛选项
* @param response Excel 下载响应
*/
@At
@Ok("void")
@ApiOperation("导出人员综合查询")
@SaCheckPermission("recuperation.schoolUnionUserQuery")
public void exportManagement(@Param("..") RecuperationUserQueryPageForm query, HttpServletResponse response) {
List<ExcelExportEntity> columns = new ArrayList<>();
columns.add(new ExcelExportEntity("工号", "loginName", 18));
columns.add(new ExcelExportEntity("姓名", "userName", 14));
columns.add(new ExcelExportEntity("所属单位", "unitName", 24));
columns.add(new ExcelExportEntity("所属工会", "unionName", 20));
columns.add(new ExcelExportEntity("报名状态", "signUpStatus", 14));
columns.add(new ExcelExportEntity("报名类型", "signUpModeName", 14));
columns.add(new ExcelExportEntity("线路/旅行社/定点", "targetName", 30));
columns.add(new ExcelExportEntity("是否参加", "takePartInName", 14));
columns.add(new ExcelExportEntity("报名时间", "signingUptime", 22));
ExportParams exportParams = new ExportParams();
exportParams.setType(ExcelType.XSSF);
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, columns, enrollService.managementList(query, false));
CommonDownloadUtil.download("疗休养人员查询.xlsx", workbook, response);
}
/** 下载实际参加人员导入模板,模板字段为工号、姓名、参加时间和标段时间。 */
@At
@Ok("void")
@SaCheckPermission("recuperation.schoolUnionUserQuery")
public void downloadImportTemplate(HttpServletResponse response) {
ExportParams exportParams = new ExportParams();
exportParams.setType(ExcelType.XSSF);
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, RecuperationEnrollExcelMode.class, new ArrayList<>());
CommonDownloadUtil.download("参加人员导入模板.xlsx", workbook, response);
}
/** file 为 xls/xlsx;按参加时间年度匹配省外线路报名并返回成功、失败及错误明细。 */
@At
@AdaptBy(type = UploadAdaptor.class, args = {"ioc:fileUpload"})
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("recuperation.schoolUnionUserQuery")
public Result enrollImport(@Param("file") TempFile file) {
if (file == null || !List.of("xls", "xlsx").contains(FileUtil.extName(file.getFile()).toLowerCase())) {
return Result.error("请上传xls、xlsx文件");
}
try {
List<RecuperationEnrollExcelMode> rows = ExcelImportUtil.importExcel(file.getFile(), RecuperationEnrollExcelMode.class, new ImportParams());
if (rows == null || rows.isEmpty()) return Result.error("读取不到数据,请检查Excel文件格式");
NutMap result = enrollService.importParticipants(rows);
return Result.success("导入完成,成功" + result.getInt("successCount") + "人,失败" + result.getInt("errorCount") + "", result);
} catch (Exception e) {
log.error("导入参加人员失败", e);
return Result.error("读取不到数据,请检查Excel文件格式");
}
}
/** 将当前筛选结果按线路、旅行社或定点分别生成 Excel 并打包下载。 */
@At
@Ok("void")
@SaCheckPermission("recuperation.schoolUnionUserQuery")
public void exportZip(@Param("..") RecuperationUserQueryPageForm query, HttpServletResponse response) throws Exception {
List<NutMap> rows = enrollService.managementList(query, false);
Map<String, List<NutMap>> groups = rows.stream().collect(Collectors.groupingBy(
row -> StrUtil.blankToDefault(row.getString("signUpModeName"), "其他") + "/" + StrUtil.blankToDefault(row.getString("targetName"), "未命名"),
LinkedHashMap::new, Collectors.toList()));
response.setContentType("application/zip");
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("疗休养报名人员名单.zip", StandardCharsets.UTF_8));
Set<String> entryNames = new HashSet<>();
try (ZipOutputStream zip = new ZipOutputStream(new BufferedOutputStream(response.getOutputStream()))) {
for (Map.Entry<String, List<NutMap>> entry : groups.entrySet()) {
String entryName = cleanZipEntry(entry.getKey()) + ".xlsx";
int suffix = 2;
while (!entryNames.add(entryName)) entryName = cleanZipEntry(entry.getKey()) + "_" + suffix++ + ".xlsx";
ExportParams exportParams = new ExportParams();
exportParams.setType(ExcelType.XSSF);
try (Workbook workbook = ExcelExportUtil.exportExcel(exportParams, managementExportColumns(), entry.getValue());
ByteArrayOutputStream output = new ByteArrayOutputStream()) {
workbook.write(output);
zip.putNextEntry(new ZipEntry(entryName));
zip.write(output.toByteArray());
zip.closeEntry();
}
}
}
}
private List<ExcelExportEntity> managementExportColumns() {
List<ExcelExportEntity> columns = new ArrayList<>();
columns.add(new ExcelExportEntity("工号", "loginName", 18));
columns.add(new ExcelExportEntity("姓名", "userName", 14));
columns.add(new ExcelExportEntity("单位", "unitName", 24));
columns.add(new ExcelExportEntity("工会", "unionName", 20));
columns.add(new ExcelExportEntity("报名类型", "signUpModeName", 14));
columns.add(new ExcelExportEntity("线路/旅行社/定点", "targetName", 28));
columns.add(new ExcelExportEntity("报名时间", "signingUptime", 22));
columns.add(new ExcelExportEntity("联系电话", "mobile", 18));
return columns;
}
private String cleanZipEntry(String name) {
return name.replaceAll("[\\\\:*?\"<>|]", "_").replace("..", "_");
}
@At
@Ok("void")
@ApiOperation("导出")
@@ -0,0 +1,34 @@
package com.budwk.app.zhgh.staffbenefit.recuperation.param;
import com.budwk.app.base.param.PageForm;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* PC 端疗休养人员查询参数。
*
* <p>startYear、endYear 为报名年度范围;signUpStatus 为 0 未报名、1 已报名、2 未成团;
* state 为 1 线路、2 灵活组团、3 定点;其余字段分别用于工会、单位、线路、旅行社、
* 定点、标段、区域、报名方式、参加状态和报名日期筛选。分页结果返回 Pagination
* list 为人员数据,totalCount 为符合条件的总人数。</p>
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class RecuperationUserQueryPageForm extends PageForm {
private Integer startYear;
private Integer endYear;
private Integer signUpStatus;
private Integer state;
private Integer isTakePartIn;
private String unFormedType;
private String unionId;
private String unitId;
private String takePartInLineId;
private String agencyId;
private String lotId;
private String takePartInBaseManagementId;
private String signUpMode;
private String regionalNature;
private String signStartTime;
private String signEndTime;
}
@@ -41,11 +41,15 @@ public interface RecuperationAuditService extends BaseService<Audit> {
void schoolAudit(Integer stateId, String loginName,Boolean adjustment, String takePartInLineId);
/** 按审核环节分页查询旧版报名记录。 */
Pagination auditPage(PageForm pageForm, String stage, Integer year, Boolean audited, String keyword);
Pagination auditPage(PageForm pageForm, String stage, Integer year, Boolean audited, String keyword,
String unionId, String lineId, String agencyId);
/** 根据报名当前状态完成分工会、线路工会或校工会审核。 */
void auditEnroll(String id, boolean pass, String auditOpinion);
/** 撤回最近一次审核,将报名恢复到对应待审核状态。 */
void recallAudit(String id);
/** 批量审核报名记录,ids 为报名 IDpass 为结果,auditOpinion 为统一审核意见。 */
void auditEnrolls(List<String> ids, boolean pass, String auditOpinion);
}
@@ -5,6 +5,8 @@ import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.sys.models.Sys_union;
import com.budwk.app.zhgh.staffbenefit.recuperation.model.RecuperationEnroll;
import com.budwk.app.zhgh.staffbenefit.recuperation.param.RecuperationUserQueryPageForm;
import com.budwk.app.zhgh.staffbenefit.recuperation.mode.RecuperationEnrollExcelMode;
import org.nutz.dao.Cnd;
import org.nutz.lang.util.NutMap;
@@ -119,4 +121,62 @@ public interface RecuperationEnrollService extends BaseService<RecuperationEnrol
NutMap selectLineAllInfo(String usId, String usUnionId);
List<Sys_union> getUnions(Integer year);
/**
* 查询 PC 管理端疗休养人员。
*
* @param query 查询参数,包含年度范围、报名状态、报名类型、工会及线路等筛选项
* @param currentUnionOnly true 时强制限定为当前登录人的分工会,false 时允许校工会选择分工会
* @return 分页结果;list 为报名、未报名或未成团人员,totalCount 为总人数
*/
Pagination managementPageData(RecuperationUserQueryPageForm query, boolean currentUnionOnly);
/** 按 PC 查询条件返回全部人员,用于导出;字段与分页列表一致。 */
List<NutMap> managementList(RecuperationUserQueryPageForm query, boolean currentUnionOnly);
/**
* 统计当前筛选范围内线路、灵活组团和定点报名人数。
*
* @param query 查询参数,年度、工会、报名日期和参加状态与列表口径一致
* @param currentUnionOnly 是否强制限定当前分工会
* @return xlCount、lxsCount、jdCount 分别表示线路、灵活组团和定点人数
*/
NutMap managementCategoryCount(RecuperationUserQueryPageForm query, boolean currentUnionOnly);
/**
* 按 PC 查询条件发送报名提醒。
*
* @param query 查询条件
* @param currentUnionOnly 是否限定当前分工会
* @param selectedLoginNames 页面勾选工号;为空时提醒当前筛选结果全部人员
* @param content 消息正文,为空时使用疗休养配置中的默认提醒内容
* @return sendCount 为实际接收人数,msg 为发送结果
*/
NutMap remindManagementUsers(RecuperationUserQueryPageForm query, boolean currentUnionOnly,
List<String> selectedLoginNames, String content);
/**
* 保留报名记录:将已作废记录恢复为正常记录。
*
* @param id 报名记录 ID
*/
void restoreEnroll(String id);
/** 预览线路成团或未成团通知人数及默认内容。 */
NutMap countLineGroupNoticeUsers(String lineUnionSelectId, boolean success);
/** 发送线路成团通知。 */
NutMap sendLineGroupSuccessNotice(String lineUnionSelectId, String content);
/** 发送未成团通知,并按 keepEnrollRecords 决定保留为调出记录或彻底删除。 */
NutMap sendLineGroupFailureNotice(String lineUnionSelectId, boolean keepEnrollRecords, String content);
/** 批量导入省外线路实际参加时间和标段,返回成功、失败及错误明细。 */
NutMap importParticipants(List<RecuperationEnrollExcelMode> rows);
/** 按年度统计旅行社及具体方案的满意度评分。 */
NutMap satisfactionRatingStatistics(Integer year);
/** 查询满意度评价明细,typeName 可传线路、灵活组团、定点或全部。 */
List<NutMap> satisfactionFeedbackDetails(Integer year, String typeName);
}
@@ -17,7 +17,10 @@ public interface RecuperationLineAdjustmentService extends BaseService<Recuperat
/** 查询指定年度可进行人员调整的线路。 */
List<?> findLineOptions(Integer year);
/** 切换指定线路报名人员正常/调出状态。 */
void adjustmentUsers(String lineId, String[] loginNames);
/** 指定线路报名人员设置为正常调出状态。 */
void adjustmentUsers(String lineId, String[] loginNames, boolean normal);
/** 向线路调整人员发送未成团提醒,返回是否全部发送成功。 */
boolean smsAlerts(String lineId, String[] loginNames);
}
@@ -0,0 +1,17 @@
package com.budwk.app.zhgh.staffbenefit.recuperation.service;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm;
import org.nutz.lang.util.NutMap;
import java.util.List;
/** PC 端线路成团统计查询。 */
public interface RecuperationLineStatisticsService {
Pagination pageData(PageForm pageForm, Integer startYear, Integer endYear, String unionId,
String lineId, String lotId, String signUpMode, String selectId, String regionalNature);
Pagination userPage(PageForm pageForm, String lineSelectId, String searchKeyword, String unionId, String unitId);
List<NutMap> lineOptions(Integer startYear, Integer endYear, String signUpMode, String regionalNature);
}
@@ -93,4 +93,28 @@ public interface RecuperationProvinceFlexibleGroupService extends BaseService<Re
* @param changeEndTime 变更截至时间
*/
void batchAssignSignUpTimes(String[] ids, Date signUpStartTime, Date signUpEndTime, Date changeEndTime);
/** PC 端灵活组团查询列表,返回组团数和报名人数。 */
Pagination queryPage(PageForm pageForm, Integer startYear, Integer endYear, String groupName,
String travelAgencyId, String searchKeyword);
/** 查询一个灵活组团下按团长聚合的团列表。 */
List<NutMap> queryGroupInfo(String groupId);
/** 查询灵活组团报名人员;团长参数为空时返回全部人员。 */
Pagination querySignUsers(PageForm pageForm, String groupId, String groupLeaderUserId,
String groupLeaderLoginName, Boolean noGroupLeader);
/** 查询年度区间内可用旅行社。 */
List<NutMap> queryTravelAgencyOptions(Integer startYear, Integer endYear);
/** 查询年度区间内的灵活组团选项。 */
List<NutMap> queryFlexibleGroupOptions(Integer startYear, Integer endYear);
/** 按查询条件导出灵活组团报名人员。 */
List<NutMap> queryExportUsers(Integer startYear, Integer endYear, String groupName,
String travelAgencyId, String searchKeyword);
/** 删除灵活组团中的一条报名及其家属信息。 */
void deleteJoinUser(String id);
}
@@ -161,7 +161,8 @@ public class RecuperationAuditServiceImpl extends BaseServiceImpl<Audit> impleme
}
@Override
public Pagination auditPage(PageForm pageForm, String stage, Integer year, Boolean audited, String keyword) {
public Pagination auditPage(PageForm pageForm, String stage, Integer year, Boolean audited, String keyword,
String unionId, String lineId, String agencyId) {
Sql sql = Sqls.create("""
SELECT enroll.*, line.lineName, ta.travelAgencyName,
self_union.name AS selfUnionName,
@@ -181,10 +182,17 @@ public class RecuperationAuditServiceImpl extends BaseServiceImpl<Audit> impleme
cnd.and(Cnd.exps("enroll.userName", "like", "%" + keyword + "%")
.or("enroll.loginName", "like", "%" + keyword + "%"));
}
if ("school".equals(stage) || "travel".equals(stage)) {
cnd.andEX("enroll.selfUnionId", "=", unionId);
cnd.andEX("line.id", "=", lineId);
cnd.andEX("ta.id", "=", agencyId);
if ("travel".equals(stage)) {
// 旅行社报名沿用分工会审核状态:待审为 UNIT,审核后为 UNITFAIL 或 PASS。
cnd.and("enroll.takePartInTravelAgencyId", "is not", null);
cnd.and("enroll.stateId", audited == null || !audited ? "=" : "in",
audited == null || !audited ? RecuperationState.UNIT : new Integer[]{RecuperationState.UNITFAIL, RecuperationState.PASS});
} else if ("school".equals(stage)) {
cnd.and("enroll.stateId", audited == null || !audited ? "=" : "in",
audited == null || !audited ? RecuperationState.SCHOOL : new Integer[]{RecuperationState.SCHOOLFAIL, RecuperationState.PASS});
if ("travel".equals(stage)) cnd.and("enroll.takePartInTravelAgencyId", "is not", null);
} else if ("lineUnion".equals(stage)) {
cnd.and("enroll.takePartInUnionId", "=", SecurityUtil.getUnionId());
cnd.and("enroll.stateId", audited == null || !audited ? "=" : "in",
@@ -246,4 +254,13 @@ public class RecuperationAuditServiceImpl extends BaseServiceImpl<Audit> impleme
}
dao().update(enroll, "^stateId|selfUnionAuditId|joinLineUnionAuditId|schoolUnionAuditId$");
}
@Override
@Aop(TransAop.READ_COMMITTED)
public void auditEnrolls(List<String> ids, boolean pass, String auditOpinion) {
if (ids == null) return;
for (String id : ids) {
if (StrUtil.isNotBlank(id)) auditEnroll(id, pass, auditOpinion);
}
}
}
@@ -4,6 +4,7 @@ import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.sms.SmsService;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.sys.models.Sys_union;
@@ -13,6 +14,8 @@ import com.budwk.app.zhgh.activity.basic.models.ActivityUserScope;
import com.budwk.app.zhgh.activity.basic.service.ActivityBasicScopeService;
import com.budwk.app.zhgh.staffbenefit.recuperation.constant.*;
import com.budwk.app.zhgh.staffbenefit.recuperation.model.*;
import com.budwk.app.zhgh.staffbenefit.recuperation.param.RecuperationUserQueryPageForm;
import com.budwk.app.zhgh.staffbenefit.recuperation.mode.RecuperationEnrollExcelMode;
import com.budwk.app.zhgh.staffbenefit.recuperation.service.RecuperationEnrollService;
import com.budwk.app.zhgh.staffbenefit.recuperation.service.RecuperationBaseManagerService;
import com.budwk.app.zhgh.staffbenefit.recuperation.service.RecuperationLineService;
@@ -21,6 +24,7 @@ import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.util.cri.Static;
import org.nutz.dao.entity.Record;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.Daos;
@@ -58,6 +62,8 @@ public class RecuperationEnrollServiceImpl extends BaseServiceImpl<RecuperationE
private RecuperationBaseManagerService baseManagerService;
@Inject
private ActivityBasicScopeService activityBasicScopeService;
@Inject
private SmsService smsService;
public RecuperationEnrollServiceImpl(Dao dao) {
super(dao);
@@ -1157,4 +1163,520 @@ public class RecuperationEnrollServiceImpl extends BaseServiceImpl<RecuperationE
}
return new ArrayList<>();
}
/**
* PC 人员查询统一入口。报名、未报名和未成团共用同一参数对象,避免校工会与分工会页面
* 各自维护不同的查询口径。
*/
@Override
public Pagination managementPageData(RecuperationUserQueryPageForm query, boolean currentUnionOnly) {
int startYear = query.getStartYear() == null ? DateUtil.thisYear() : query.getStartYear();
int endYear = query.getEndYear() == null ? startYear : query.getEndYear();
if (startYear > endYear) {
int temp = startYear;
startYear = endYear;
endYear = temp;
}
String unionId = currentUnionOnly ? SecurityUtil.getUnionId() : query.getUnionId();
if (Integer.valueOf(0).equals(query.getSignUpStatus())) {
return unSignManagementPage(query, startYear, endYear, unionId);
}
if (Integer.valueOf(2).equals(query.getSignUpStatus())) {
return unFormedManagementPage(query, startYear, endYear, unionId);
}
Sql sql = Sqls.create("""
SELECT
enroll.*,
'已报名' AS signUpStatus,
CASE WHEN enroll.takePartInBaseManagementId IS NOT NULL AND enroll.takePartInBaseManagementId!='' THEN '定点'
WHEN enroll.takePartInTravelAgencyId IS NOT NULL AND enroll.takePartInTravelAgencyId!='' THEN '灵活组团'
ELSE '线路' END AS signUpModeName,
IF(enroll.isTakePartIn=true,'已参加','未参加') AS takePartInName,
line.regionalNature,
line.lineName,
agency.travelAgencyName,
ma.baseName,
COALESCE(ma.baseName, line.lineName, agency.travelAgencyName) AS targetName,
lineu.lineId,
lineu.signUpMode,
lineu.playStartTime,
lineu.playEndTime,
CASE WHEN enroll.lotId IN ('两天','三天','五天') THEN enroll.lotId
ELSE lot.lotName END AS lotName,
(SELECT COUNT(1) FROM the_rapy_recuperation_enroll_companion companion
WHERE companion.trreId = enroll.id) AS isFamily
FROM the_rapy_recuperation_enroll enroll
LEFT JOIN the_rapy_recuperation_line_union_select lineu ON lineu.id = enroll.takePartInLineId
LEFT JOIN the_rapy_recuperation_line line ON line.id = lineu.lineId
LEFT JOIN the_rapy_recuperation_lot lot ON lot.id = IFNULL(NULLIF(enroll.lotId, ''), line.lotId)
LEFT JOIN the_rapy_recuperation_travel_agency agency ON agency.id = enroll.takePartInTravelAgencyId
LEFT JOIN the_rapy_recuperation_base_management ma ON ma.id = enroll.takePartInBaseManagementId
$condition
""");
Cnd cnd = Cnd.where("YEAR(enroll.signingUptime)", ">=", startYear)
.and("YEAR(enroll.signingUptime)", "<=", endYear)
.and("enroll.stateId", "=", RecuperationState.PASS)
.and("enroll.isNormal", "=", true);
appendManagementConditions(cnd, query, unionId);
if (Integer.valueOf(1).equals(query.getState())) {
cnd.and("enroll.takePartInLineId", "is not", null).and("enroll.takePartInLineId", "!=", "");
} else if (Integer.valueOf(2).equals(query.getState())) {
cnd.and("enroll.takePartInTravelAgencyId", "is not", null).and("enroll.takePartInTravelAgencyId", "!=", "");
} else if (Integer.valueOf(3).equals(query.getState())) {
cnd.and("enroll.takePartInBaseManagementId", "is not", null).and("enroll.takePartInBaseManagementId", "!=", "");
}
cnd.desc("enroll.signingUptime").asc("enroll.unionName");
sql.setCondition(cnd);
return listPageMap(query.getPageNumber(), query.getPageSize(), sql);
}
@Override
public List<NutMap> managementList(RecuperationUserQueryPageForm query, boolean currentUnionOnly) {
int originalPage = query.getPageNumber() == null ? 1 : query.getPageNumber();
int originalSize = query.getPageSize() == null ? 10 : query.getPageSize();
query.setPageNumber(1);
query.setPageSize(200);
Pagination firstPage = managementPageData(query, currentUnionOnly);
List<NutMap> result = new ArrayList<>();
appendManagementRows(firstPage, result);
for (int page = 2; page <= firstPage.getTotalPage(); page++) {
query.setPageNumber(page);
appendManagementRows(managementPageData(query, currentUnionOnly), result);
}
query.setPageNumber(originalPage);
query.setPageSize(originalSize);
return result;
}
/** 将分页中的 Map 或实体统一转换为导出使用的 NutMap。 */
private void appendManagementRows(Pagination page, List<NutMap> target) {
if (page == null || page.getList() == null) return;
for (Object item : page.getList()) {
target.add(item instanceof NutMap ? (NutMap) item : Lang.obj2nutmap(item));
}
}
/** 查询参加范围内没有有效报名记录的教职工。 */
private Pagination unSignManagementPage(RecuperationUserQueryPageForm query, int startYear, int endYear, String unionId) {
RecuperationConfig config = dao().fetch(RecuperationConfig.class);
if (config == null || config.getActivityGroupId() == null) {
return new Pagination(query.getPageNumber(), query.getPageSize(), 0, new ArrayList<>());
}
Sql sql = Sqls.create("""
SELECT DISTINCT u.id, u.loginname AS loginName, u.username AS userName, u.sex, u.mobile,
u.unitname AS unitName, u.unionname AS unionName, u.unitid AS selfUnitId,
u.unionid AS selfUnionId, '未报名' AS signUpStatus
FROM vw_user u
$condition
""");
Cnd cnd = Cnd.where("u.id", "in", activityBasicScopeService.buildGroupUserIdSubSql(config.getActivityGroupId()))
.and(new Static("NOT EXISTS (SELECT 1 FROM the_rapy_recuperation_enroll enroll WHERE enroll.loginName=u.loginname " +
"AND YEAR(enroll.signingUptime)>=" + startYear + " AND YEAR(enroll.signingUptime)<=" + endYear +
" AND enroll.isNormal=true AND enroll.stateId NOT IN (2715,2725,2735))"));
if (StrUtil.isNotBlank(unionId)) {
cnd.and("u.unionid", "=", unionId);
}
appendSearchCondition(cnd, query, "u.loginname", "u.username");
cnd.asc("u.unionname").asc("u.unitname").asc("u.loginname");
sql.setCondition(cnd);
return listPageMap(query.getPageNumber(), query.getPageSize(), sql);
}
/** 查询报名成功但没有达到旧版成团人数规则的线路或灵活组团人员。 */
private Pagination unFormedManagementPage(RecuperationUserQueryPageForm query, int startYear, int endYear, String unionId) {
boolean flexible = "flexible".equals(query.getUnFormedType());
String sqlText = flexible ? """
SELECT * FROM (
SELECT enroll.*, ta.travelAgencyName AS targetName, ta.id AS agencyId, '灵活组团' AS signUpModeName,
3 AS groupThreshold,
(SELECT COUNT(DISTINCT sameLeader.loginName) FROM the_rapy_recuperation_enroll sameLeader
WHERE sameLeader.takePartInTravelAgencyId=enroll.takePartInTravelAgencyId
AND YEAR(sameLeader.signingUptime)=YEAR(enroll.signingUptime) AND sameLeader.isNormal=true
AND sameLeader.stateId=2750 AND IFNULL(sameLeader.groupLeaderLoginName,sameLeader.loginName)=
IFNULL(enroll.groupLeaderLoginName,enroll.loginName)) AS currentCount
FROM the_rapy_recuperation_enroll enroll
LEFT JOIN the_rapy_recuperation_travel_agency ta ON ta.id=enroll.takePartInTravelAgencyId
WHERE enroll.takePartInTravelAgencyId IS NOT NULL AND enroll.takePartInTravelAgencyId!=''
AND enroll.isNormal=true AND enroll.stateId=2750
AND YEAR(enroll.signingUptime) BETWEEN @startYear AND @endYear
) unformed $condition
""" : """
SELECT * FROM (
SELECT enroll.*, line.lineName AS targetName, line.regionalNature, lineu.playStartTime,
IFNULL((SELECT outsideQuota FROM the_rapy_recuperation_config LIMIT 1),0) AS groupThreshold,
((SELECT COUNT(DISTINCT sameLine.loginName) FROM the_rapy_recuperation_enroll sameLine
WHERE sameLine.takePartInLineId=enroll.takePartInLineId AND sameLine.isNormal=true AND sameLine.stateId=2750)
+ IFNULL((SELECT SUM(IFNULL(sameLine.familyNumber,0)) FROM the_rapy_recuperation_enroll sameLine
WHERE sameLine.takePartInLineId=enroll.takePartInLineId AND sameLine.isNormal=true AND sameLine.stateId=2750),0)) AS currentCount
FROM the_rapy_recuperation_enroll enroll
LEFT JOIN the_rapy_recuperation_line_union_select lineu ON lineu.id=enroll.takePartInLineId
LEFT JOIN the_rapy_recuperation_line line ON line.id=lineu.lineId
WHERE enroll.takePartInLineId IS NOT NULL AND enroll.takePartInLineId!=''
AND enroll.isNormal=true AND enroll.stateId=2750
AND YEAR(enroll.signingUptime) BETWEEN @startYear AND @endYear
) unformed $condition
""";
Sql sql = Sqls.create(sqlText).setParam("startYear", startYear).setParam("endYear", endYear);
Cnd cnd = Cnd.where("unformed.groupThreshold", ">", 0)
.and(new Static("unformed.currentCount < unformed.groupThreshold"));
if (StrUtil.isNotBlank(unionId)) {
cnd.and("unformed.selfUnionId", "=", unionId);
}
if (StrUtil.isNotBlank(query.getAgencyId()) && flexible) {
cnd.and("unformed.agencyId", "=", query.getAgencyId());
}
appendSearchCondition(cnd, query, "unformed.loginName", "unformed.userName");
cnd.asc("unformed.unionName").asc("unformed.targetName").asc("unformed.signingUptime");
sql.setCondition(cnd);
return listPageMap(query.getPageNumber(), query.getPageSize(), sql);
}
/** 追加报名列表的公共筛选条件。 */
private void appendManagementConditions(Cnd cnd, RecuperationUserQueryPageForm query, String unionId) {
cnd.andEX("enroll.selfUnionId", "=", unionId);
cnd.andEX("enroll.selfUnitId", "=", query.getUnitId());
cnd.andEX("line.id", "=", query.getTakePartInLineId());
cnd.andEX("enroll.takePartInTravelAgencyId", "=", query.getAgencyId());
cnd.andEX("enroll.takePartInBaseManagementId", "=", query.getTakePartInBaseManagementId());
cnd.andEX("line.regionalNature", "=", query.getRegionalNature());
cnd.andEX("lineu.signUpMode", "=", query.getSignUpMode());
cnd.andEX("enroll.isTakePartIn", "=", query.getIsTakePartIn());
cnd.andEX("enroll.lotId", "=", query.getLotId());
cnd.andEX("enroll.signingUptime", ">=", query.getSignStartTime());
cnd.andEX("enroll.signingUptime", "<=", query.getSignEndTime());
appendSearchCondition(cnd, query, "enroll.loginName", "enroll.userName");
}
/** 姓名和工号查询只能使用固定字段,禁止将页面字段名直接拼入 SQL。 */
private void appendSearchCondition(Cnd cnd, RecuperationUserQueryPageForm query, String loginColumn, String userColumn) {
if (StrUtil.isBlank(query.getSearchKeyword())) {
return;
}
if ("loginName".equals(query.getSearchName())) {
cnd.and(Cnd.likeEX(loginColumn, query.getSearchKeyword()));
} else {
cnd.and(Cnd.likeEX(userColumn, query.getSearchKeyword()));
}
}
@Override
public NutMap managementCategoryCount(RecuperationUserQueryPageForm query, boolean currentUnionOnly) {
String unionId = currentUnionOnly ? SecurityUtil.getUnionId() : query.getUnionId();
int startYear = query.getStartYear() == null ? DateUtil.thisYear() : query.getStartYear();
int endYear = query.getEndYear() == null ? startYear : query.getEndYear();
int minYear = Math.min(startYear, endYear);
int maxYear = Math.max(startYear, endYear);
int lineCount = dao().count(RecuperationEnroll.class, buildManagementCountCnd(query, unionId, minYear, maxYear)
.and("takePartInLineId", "is not", null).and("takePartInLineId", "!=", ""));
int agencyCount = dao().count(RecuperationEnroll.class, buildManagementCountCnd(query, unionId, minYear, maxYear)
.and("takePartInTravelAgencyId", "is not", null).and("takePartInTravelAgencyId", "!=", ""));
int baseCount = dao().count(RecuperationEnroll.class, buildManagementCountCnd(query, unionId, minYear, maxYear)
.and("takePartInBaseManagementId", "is not", null).and("takePartInBaseManagementId", "!=", ""));
return NutMap.NEW().setv("xlCount", lineCount).setv("lxsCount", agencyCount).setv("jdCount", baseCount);
}
@Override
public NutMap remindManagementUsers(RecuperationUserQueryPageForm query, boolean currentUnionOnly,
List<String> selectedLoginNames, String content) {
RecuperationConfig config = dao().fetch(RecuperationConfig.class);
String sendContent = StrUtil.trim(content);
if (StrUtil.isBlank(sendContent) && config != null) {
sendContent = StrUtil.trim(config.getRemindContent());
}
if (StrUtil.isBlank(sendContent)) {
return NutMap.NEW().setv("sendCount", 0).setv("msg", "提醒内容不能为空");
}
Set<String> selected = selectedLoginNames == null ? Collections.emptySet() : selectedLoginNames.stream()
.filter(StrUtil::isNotBlank).collect(Collectors.toSet());
int originalPage = query.getPageNumber() == null ? 1 : query.getPageNumber();
int originalSize = query.getPageSize() == null ? 10 : query.getPageSize();
query.setPageNumber(1);
query.setPageSize(200);
Pagination firstPage = managementPageData(query, currentUnionOnly);
int totalPages = Math.max(1, (firstPage.getTotalCount() + 199) / 200);
LinkedHashSet<String> loginNames = new LinkedHashSet<>();
collectManagementLoginNames(firstPage, selected, loginNames);
for (int page = 2; page <= totalPages; page++) {
query.setPageNumber(page);
collectManagementLoginNames(managementPageData(query, currentUnionOnly), selected, loginNames);
}
query.setPageNumber(originalPage);
query.setPageSize(originalSize);
if (loginNames.isEmpty()) {
return NutMap.NEW().setv("sendCount", 0).setv("msg", "当前筛选条件下没有可提醒人员");
}
boolean success = smsService.sendMsg("56", new ArrayList<>(loginNames), null,
"疗休养报名提醒", sendContent, null, "/platform/h5/recuperation");
return NutMap.NEW().setv("sendCount", loginNames.size())
.setv("msg", success ? "已发送提醒" + loginNames.size() + "" : "消息发送失败,请检查消息中心配置");
}
/** 从分页结果中提取工号;页面勾选不为空时只保留勾选人员。 */
private void collectManagementLoginNames(Pagination page, Set<String> selected, Set<String> result) {
if (page == null || page.getList() == null) {
return;
}
for (Object item : page.getList()) {
NutMap row = item instanceof NutMap ? (NutMap) item : Lang.obj2nutmap(item);
String loginName = row.getString("loginName");
if (StrUtil.isNotBlank(loginName) && (selected.isEmpty() || selected.contains(loginName))) {
result.add(loginName);
}
}
}
/** 构造分类数量统计的独立条件对象,避免三个统计相互污染。 */
private Cnd buildManagementCountCnd(RecuperationUserQueryPageForm query, String unionId, int startYear, int endYear) {
return Cnd.where("YEAR(signingUptime)", ">=", startYear)
.and("YEAR(signingUptime)", "<=", endYear)
.and("stateId", "=", RecuperationState.PASS)
.and("isNormal", "=", true)
.andEX("selfUnionId", "=", unionId)
.andEX("isTakePartIn", "=", query.getIsTakePartIn())
.andEX("signingUptime", ">=", query.getSignStartTime())
.andEX("signingUptime", "<=", query.getSignEndTime());
}
@Override
@Aop(TransAop.READ_COMMITTED)
public void restoreEnroll(String id) {
dao().update(RecuperationEnroll.class, org.nutz.dao.Chain.make("isNormal", true), Cnd.where("id", "=", id));
}
@Override
public NutMap countLineGroupNoticeUsers(String lineUnionSelectId, boolean success) {
NutMap notice = lineGroupNoticeInfo(lineUnionSelectId);
if (!notice.getBoolean("valid")) return lineNoticeResult(0, notice.getString("msg"), notice, success, null);
List<RecuperationEnroll> enrolls = lineNoticeEnrolls(lineUnionSelectId);
long count = enrolls.stream().map(RecuperationEnroll::getLoginName).filter(StrUtil::isNotBlank).distinct().count();
return lineNoticeResult((int) count, count == 0 ? "当前暂无需要发送通知的报名人员" : "当前将发送通知" + count + "", notice, success, null);
}
@Override
public NutMap sendLineGroupSuccessNotice(String lineUnionSelectId, String content) {
return sendLineGroupNotice(lineUnionSelectId, true, true, content);
}
@Override
@Aop(TransAop.READ_COMMITTED)
public NutMap sendLineGroupFailureNotice(String lineUnionSelectId, boolean keepEnrollRecords, String content) {
return sendLineGroupNotice(lineUnionSelectId, false, keepEnrollRecords, content);
}
/** 统一校验线路、发送消息,并在未成团时处理报名数据。 */
private NutMap sendLineGroupNotice(String lineUnionSelectId, boolean success, boolean keepEnrollRecords, String content) {
NutMap notice = lineGroupNoticeInfo(lineUnionSelectId);
if (!notice.getBoolean("valid")) return lineNoticeResult(0, notice.getString("msg"), notice, success, content);
List<RecuperationEnroll> enrolls = lineNoticeEnrolls(lineUnionSelectId);
List<String> loginNames = enrolls.stream().map(RecuperationEnroll::getLoginName)
.filter(StrUtil::isNotBlank).distinct().collect(Collectors.toList());
if (loginNames.isEmpty()) return lineNoticeResult(0, "当前暂无需要发送通知的报名人员", notice, success, content);
String sendContent = StrUtil.blankToDefault(StrUtil.trim(content), defaultLineNoticeContent(notice, success));
if (StrUtil.isBlank(sendContent)) return lineNoticeResult(0, "消息内容不能为空", notice, success, content);
String title = success ? "疗休养成团通知" : "疗休养未成团通知";
if (!smsService.sendMsg("56", loginNames, null, title, sendContent, null, null)) {
return lineNoticeResult(0, "消息发送失败,请检查消息中心配置", notice, success, sendContent);
}
if (!success) handleLineGroupFailure(enrolls, keepEnrollRecords);
return lineNoticeResult(loginNames.size(), "已发送通知" + loginNames.size() + "", notice, success, sendContent);
}
private List<RecuperationEnroll> lineNoticeEnrolls(String lineUnionSelectId) {
return dao().query(RecuperationEnroll.class, Cnd.where("takePartInLineId", "=", lineUnionSelectId)
.and("isNormal", "=", true).and("stateId", "not in",
Lang.array(RecuperationState.UNITFAIL, RecuperationState.LINEUNITFAIL, RecuperationState.SCHOOLFAIL)));
}
private NutMap lineGroupNoticeInfo(String lineUnionSelectId) {
RecuperationLineSelect lineSelect = dao().fetch(RecuperationLineSelect.class, lineUnionSelectId);
if (lineSelect == null) return NutMap.NEW().setv("valid", false).setv("msg", "线路报名信息不存在");
if (lineSelect.getSignUpEndTime() != null && new Date().before(lineSelect.getSignUpEndTime())) {
return NutMap.NEW().setv("valid", false).setv("msg", "报名还未结束,不能发送");
}
RecuperationLine line = dao().fetch(RecuperationLine.class, lineSelect.getLineId());
if (line == null) return NutMap.NEW().setv("valid", false).setv("msg", "线路信息不存在");
return NutMap.NEW().setv("valid", true).setv("lineName", line.getLineName())
.setv("playStartTime", lineSelect.getPlayStartTime() == null ? "" : DateUtil.formatDate(lineSelect.getPlayStartTime()));
}
/** 未成团记录保留时转为调出;删除时同步清理家属和床位。 */
private void handleLineGroupFailure(List<RecuperationEnroll> enrolls, boolean keepEnrollRecords) {
List<String> ids = enrolls.stream().map(RecuperationEnroll::getId).collect(Collectors.toList());
if (keepEnrollRecords) {
dao().update(RecuperationEnroll.class, org.nutz.dao.Chain.make("isNormal", false), Cnd.where("id", "in", ids));
return;
}
List<String> bedIds = enrolls.stream().map(RecuperationEnroll::getBedInfoId).filter(StrUtil::isNotBlank).collect(Collectors.toList());
dao().clear(RecuperationEnrollCompanion.class, Cnd.where("trreId", "in", ids));
if (!bedIds.isEmpty()) dao().clear(RecuperationEnrollBed.class, Cnd.where("id", "in", bedIds));
dao().clear(RecuperationEnroll.class, Cnd.where("id", "in", ids));
}
private String defaultLineNoticeContent(NutMap notice, boolean success) {
return success
? "您好!您报名的" + notice.getString("lineName") + "线路,出行时间为" + notice.getString("playStartTime") + ",已达到成团条件,请准时参加疗休养活动。"
: "您好!您报名的" + notice.getString("lineName") + "线路,出行时间为" + notice.getString("playStartTime") + ",因报名人数不足未达到成团条件,请尽快进入疗休养管理系统重新选择线路。";
}
private NutMap lineNoticeResult(int sendCount, String msg, NutMap notice, boolean success, String content) {
return NutMap.NEW().setv("sendCount", sendCount).setv("msg", msg)
.setv("title", success ? "疗休养成团通知" : "疗休养未成团通知")
.setv("content", StrUtil.blankToDefault(StrUtil.trim(content), notice == null ? "" : defaultLineNoticeContent(notice, success)))
.setv("sendType", "56").setv("sendTypeName", "微信、钉钉");
}
@Override
@Aop(TransAop.READ_COMMITTED)
public NutMap importParticipants(List<RecuperationEnrollExcelMode> rows) {
List<NutMap> successList = new ArrayList<>();
List<NutMap> errorList = new ArrayList<>();
List<RecuperationLot> lots = dao().query(RecuperationLot.class, Cnd.NEW());
for (int i = 0; i < rows.size(); i++) {
RecuperationEnrollExcelMode row = rows.get(i);
NutMap result = NutMap.NEW().setv("no", i + 2).setv("loginName", StrUtil.trim(row.getLoginName()))
.setv("userName", row.getUserName()).setv("takePartInTime", row.getTakePartInTime() == null ? "" : DateUtil.formatDate(row.getTakePartInTime()))
.setv("lotName", row.getLotId());
if (StrUtil.isBlank(row.getLoginName()) || StrUtil.isBlank(row.getUserName()) || row.getTakePartInTime() == null || StrUtil.isBlank(row.getLotId())) {
errorList.add(result.setv("errors", "工号、姓名、参加时间和标段时间均不能为空"));
continue;
}
int year = DateUtil.year(row.getTakePartInTime());
RecuperationEnroll enroll = dao().fetch(RecuperationEnroll.class, Cnd.where("loginName", "=", StrUtil.trim(row.getLoginName()))
.and("YEAR(signingUptime)", "=", year).desc("signingUptime"));
if (enroll == null) {
errorList.add(result.setv("errors", "未找到参加时间所属年度报名记录(" + year + ""));
continue;
}
String existsError = participantImportExistsError(enroll);
if (StrUtil.isNotBlank(existsError)) {
errorList.add(result.setv("errors", existsError));
continue;
}
RecuperationLot lot = lots.stream().filter(item -> StrUtil.equals(item.getLotName(), row.getLotId())).findFirst().orElse(null);
if (lot == null) {
errorList.add(result.setv("errors", "标段时间不存在或名称不匹配"));
continue;
}
enroll.setTakePartInTime(row.getTakePartInTime());
enroll.setLotId(lot.getId());
enroll.setTakePartIn(true);
enroll.setNormal(true);
dao().updateIgnoreNull(enroll);
successList.add(result.setv("result", "导入成功").setv("errors", ""));
}
return NutMap.NEW().setv("totalCount", rows.size()).setv("successCount", successList.size())
.setv("errorCount", errorList.size()).setv("successList", successList).setv("errorList", errorList);
}
/** 导入参加信息只允许覆盖已有省外线路报名,避免误覆盖定点、灵活组团和省内线路。 */
private String participantImportExistsError(RecuperationEnroll enroll) {
if (StrUtil.isNotBlank(enroll.getTakePartInBaseManagementId())) return "该年度已有定点报名记录,导入不会覆盖原报名";
if (StrUtil.isNotBlank(enroll.getTakePartInTravelAgencyId())) return "该年度已有灵活组团报名记录,导入不会覆盖原报名";
if (StrUtil.isBlank(enroll.getTakePartInLineId())) return "该年度报名记录未选择线路";
Sql sql = Sqls.create("SELECT line.regionalNature FROM the_rapy_recuperation_line_union_select us LEFT JOIN the_rapy_recuperation_line line ON line.id=us.lineId WHERE us.id=@id");
sql.setParam("id", enroll.getTakePartInLineId()).setCallback(Sqls.callback.map());
dao().execute(sql);
NutMap line = sql.getObject(NutMap.class);
String regionalNature = line == null ? "" : line.getString("regionalNature");
return "省外".equals(regionalNature) ? "" : "该年度已有" + StrUtil.blankToDefault(regionalNature, "") + "线路报名记录,导入不会覆盖原报名";
}
@Override
public NutMap satisfactionRatingStatistics(Integer year) {
int statisticYear = year == null ? DateUtil.thisYear() : year;
Sql agencySql = Sqls.create("""
SELECT agency.travelAgencyName,
ROUND(AVG(CASE WHEN rating.evaluationForTravelAgency IS NOT NULL AND rating.evaluationForJourney IS NOT NULL
AND rating.evaluationForAccommodation IS NOT NULL AND rating.evaluationForDining IS NOT NULL
AND rating.evaluationForTransportation IS NOT NULL THEN
(rating.evaluationForTravelAgency+rating.evaluationForJourney+rating.evaluationForAccommodation+rating.evaluationForDining+rating.evaluationForTransportation)/5 END),1) AS avg,
COUNT(DISTINCT CASE WHEN rating.evaluationForTravelAgency IS NOT NULL AND rating.evaluationForJourney IS NOT NULL
AND rating.evaluationForAccommodation IS NOT NULL AND rating.evaluationForDining IS NOT NULL
AND rating.evaluationForTransportation IS NOT NULL THEN rating.loginName END) AS ratingCount
FROM the_rapy_recuperation_travel_agency agency
LEFT JOIN ($ratingSql) rating ON rating.travelAgencyId=agency.id
WHERE agency.year=@year
GROUP BY agency.id, agency.travelAgencyName
ORDER BY CASE WHEN avg IS NULL THEN 1 ELSE 0 END, avg DESC, ratingCount DESC, agency.travelAgencyName
""");
agencySql.setVar("ratingSql", satisfactionBaseSql()).setParam("year", statisticYear);
Sql schemeSql = Sqls.create("""
SELECT rating.typeName, rating.schemeId, rating.schemeName, rating.travelAgencyName,
ROUND(AVG(rating.evaluationForTravelAgency),1) AS travelAgencyAvg,
COUNT(DISTINCT CASE WHEN rating.evaluationForTravelAgency IS NOT NULL THEN rating.loginName END) AS travelAgencyCount,
ROUND(AVG(rating.evaluationForJourney),1) AS journeyAvg,
COUNT(DISTINCT CASE WHEN rating.evaluationForJourney IS NOT NULL THEN rating.loginName END) AS journeyCount,
ROUND(AVG(rating.evaluationForAccommodation),1) AS accommodationAvg,
COUNT(DISTINCT CASE WHEN rating.evaluationForAccommodation IS NOT NULL THEN rating.loginName END) AS accommodationCount,
ROUND(AVG(rating.evaluationForDining),1) AS diningAvg,
COUNT(DISTINCT CASE WHEN rating.evaluationForDining IS NOT NULL THEN rating.loginName END) AS diningCount,
ROUND(AVG(rating.evaluationForTransportation),1) AS transportationAvg,
COUNT(DISTINCT CASE WHEN rating.evaluationForTransportation IS NOT NULL THEN rating.loginName END) AS transportationCount,
ROUND(AVG(CASE
WHEN rating.typeName='线路' AND rating.evaluationForTravelAgency IS NOT NULL AND rating.evaluationForJourney IS NOT NULL
AND rating.evaluationForAccommodation IS NOT NULL AND rating.evaluationForDining IS NOT NULL AND rating.evaluationForTransportation IS NOT NULL
THEN (rating.evaluationForTravelAgency+rating.evaluationForJourney+rating.evaluationForAccommodation+rating.evaluationForDining+rating.evaluationForTransportation)/5
WHEN rating.typeName IN ('灵活组团','定点') AND rating.evaluationForTravelAgency IS NOT NULL AND rating.evaluationForAccommodation IS NOT NULL
THEN (rating.evaluationForTravelAgency+rating.evaluationForAccommodation)/2 END),1) AS compositeAvg,
COUNT(DISTINCT CASE
WHEN rating.typeName='线路' AND rating.evaluationForTravelAgency IS NOT NULL AND rating.evaluationForJourney IS NOT NULL
AND rating.evaluationForAccommodation IS NOT NULL AND rating.evaluationForDining IS NOT NULL AND rating.evaluationForTransportation IS NOT NULL THEN rating.loginName
WHEN rating.typeName IN ('灵活组团','定点') AND rating.evaluationForTravelAgency IS NOT NULL AND rating.evaluationForAccommodation IS NOT NULL THEN rating.loginName END) AS ratingCount
FROM ($ratingSql) rating
WHERE IFNULL(rating.schemeId,'')<>''
GROUP BY rating.typeName, rating.schemeId, rating.schemeName, rating.travelAgencyName
ORDER BY FIELD(rating.typeName,'线路','灵活组团','定点'), rating.schemeName
""");
schemeSql.setVar("ratingSql", satisfactionBaseSql()).setParam("year", statisticYear);
return NutMap.NEW().setv("travelAgencyCompositeRatings", listMap(agencySql))
.setv("schemeServiceRatings", listMap(schemeSql));
}
@Override
public List<NutMap> satisfactionFeedbackDetails(Integer year, String typeName) {
Sql sql = Sqls.create("""
SELECT rating.*,
ROUND(CASE WHEN rating.typeName='线路' THEN
(rating.evaluationForTravelAgency+rating.evaluationForJourney+rating.evaluationForAccommodation+rating.evaluationForDining+rating.evaluationForTransportation)/5
ELSE (rating.evaluationForTravelAgency+rating.evaluationForAccommodation)/2 END,1) AS compositeScore
FROM ($ratingSql) rating
WHERE rating.feedbackContent IS NOT NULL AND rating.feedbackContent<>''
ORDER BY rating.signingUptime DESC
""");
sql.setVar("ratingSql", satisfactionBaseSql()).setParam("year", year == null ? DateUtil.thisYear() : year);
List<NutMap> list = listMap(sql);
if (StrUtil.isBlank(typeName) || "全部".equals(typeName)) return list;
return list.stream().filter(item -> typeName.equals(item.getString("typeName"))).collect(Collectors.toList());
}
/** 满意度统计与明细共用同一报名方式、方案和旅行社映射。 */
private String satisfactionBaseSql() {
return """
SELECT enroll.id, enroll.loginName, enroll.userName, enroll.unitName, enroll.signingUptime, enroll.feedbackContent,
enroll.evaluationForTravelAgency, enroll.evaluationForJourney, enroll.evaluationForAccommodation,
enroll.evaluationForDining, enroll.evaluationForTransportation,
CASE WHEN enroll.takePartInLineId IS NOT NULL THEN '线路' WHEN enroll.takePartInBaseManagementId IS NOT NULL THEN '定点' ELSE '灵活组团' END AS typeName,
CASE WHEN enroll.takePartInLineId IS NOT NULL THEN COALESCE(line.id,directLine.id) WHEN enroll.takePartInBaseManagementId IS NOT NULL THEN base.id ELSE COALESCE(flexible.id,enroll.takePartInTravelAgencyId) END AS schemeId,
CASE WHEN enroll.takePartInLineId IS NOT NULL THEN COALESCE(line.lineName,directLine.lineName) WHEN enroll.takePartInBaseManagementId IS NOT NULL THEN base.baseName ELSE COALESCE(flexible.groupName, directAgency.travelAgencyName) END AS schemeName,
COALESCE(selectAgency.id,lineAgency.id,directLineAgency.id,baseAgency.id,directAgency.id) AS travelAgencyId,
COALESCE(selectAgency.travelAgencyName,lineAgency.travelAgencyName,directLineAgency.travelAgencyName,baseAgency.travelAgencyName,directAgency.travelAgencyName) AS travelAgencyName
FROM the_rapy_recuperation_enroll enroll
LEFT JOIN the_rapy_recuperation_line_union_select lineSelect ON lineSelect.id=enroll.takePartInLineId
LEFT JOIN the_rapy_recuperation_line line ON line.id=lineSelect.lineId
LEFT JOIN the_rapy_recuperation_line directLine ON directLine.id=enroll.takePartInLineId
LEFT JOIN the_rapy_recuperation_base_management base ON base.id=enroll.takePartInBaseManagementId
LEFT JOIN the_rapy_recuperation_travel_agency selectAgency ON selectAgency.id=lineSelect.travelAgencyId
LEFT JOIN the_rapy_recuperation_travel_agency lineAgency ON lineAgency.id=line.travelAgencyId
LEFT JOIN the_rapy_recuperation_travel_agency directLineAgency ON directLineAgency.id=directLine.travelAgencyId
LEFT JOIN the_rapy_recuperation_travel_agency baseAgency ON baseAgency.id=base.travelAgencyId
LEFT JOIN the_rapy_recuperation_travel_agency directAgency ON directAgency.id=enroll.takePartInTravelAgencyId
LEFT JOIN the_rapy_recuperation_province_flexible_group flexible ON flexible.id=(
SELECT firstFlexible.id FROM the_rapy_recuperation_province_flexible_group firstFlexible
WHERE firstFlexible.travelAgencyId=enroll.takePartInTravelAgencyId
AND firstFlexible.year=YEAR(enroll.signingUptime) AND firstFlexible.isDisabled=false
ORDER BY firstFlexible.sortNumber, firstFlexible.id LIMIT 1
)
WHERE enroll.isNormal=true AND enroll.isTakePartIn=true AND YEAR(enroll.signingUptime)=@year
""";
}
}
@@ -2,6 +2,7 @@ package com.budwk.app.zhgh.staffbenefit.recuperation.service.impl;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.sms.SmsService;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.service.impl.BaseServiceImpl;
@@ -16,12 +17,14 @@ import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.aop.Aop;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.lang.Strings;
import org.nutz.lang.util.NutMap;
import java.util.List;
import java.util.Collections;
/**
* @FileName com.budwk.app.zhgh.staffbenefit.recuperation.service.impl.RecuperationLineAdjustmentServiceImpl
@@ -33,6 +36,9 @@ import java.util.List;
@IocBean(args = {"refer:dao"})
public class RecuperationLineAdjustmentServiceImpl extends BaseServiceImpl<RecuperationEnroll> implements RecuperationLineAdjustmentService {
@Inject
private SmsService smsService;
public RecuperationLineAdjustmentServiceImpl(Dao dao) {
super(dao);
}
@@ -159,8 +165,37 @@ public class RecuperationLineAdjustmentServiceImpl extends BaseServiceImpl<Recup
@Override
@Aop(TransAop.READ_COMMITTED)
public void adjustmentUsers(String lineId, String[] loginNames) {
public void adjustmentUsers(String lineId, String[] loginNames, boolean normal) {
Cnd cnd = Cnd.where("takePartInLineId", "=", lineId).and("loginName", "in", loginNames);
dao().update(RecuperationEnroll.class, Chain.makeSpecial("isNormal", "isNormal ^ 1"), cnd);
dao().update(RecuperationEnroll.class, Chain.make("isNormal", normal), cnd);
}
@Override
public boolean smsAlerts(String lineId, String[] loginNames) {
Sql lineSql = Sqls.create("""
SELECT line.lineName, line.regionalNature, lot.lotName,
DATE_FORMAT(us.playStartTime, '%Y-%m-%d') AS playStartTime,
DATE_FORMAT(us.playEndTime, '%Y-%m-%d') AS playEndTime
FROM the_rapy_recuperation_line_union_select us
LEFT JOIN the_rapy_recuperation_line line ON line.id = us.lineId
LEFT JOIN the_rapy_recuperation_lot lot ON lot.id = line.lotId
WHERE us.id = @lineId
""");
lineSql.setParam("lineId", lineId);
NutMap line = listMap(lineSql).stream().findFirst().orElse(null);
if (line == null) {
return false;
}
List<RecuperationEnroll> users = dao().query(RecuperationEnroll.class,
Cnd.where("takePartInLineId", "=", lineId).and("loginName", "in", loginNames));
boolean success = true;
for (RecuperationEnroll user : users) {
String content = String.format("%s老师您好,您报名的%s【%s-%s】(%s至%s)线路未达到成团标准,现已解散,请您选择其他线路进行报名!",
user.getUserName(), line.getString("lineName"), line.getString("lotName"),
line.getString("regionalNature"), line.getString("playStartTime"), line.getString("playEndTime"));
success = smsService.sendMsg("56", Collections.singletonList(user.getLoginName()), null,
"疗休养线路调整提醒", content, null, null) && success;
}
return success;
}
}
@@ -149,7 +149,8 @@ public class RecuperationLineClusterServiceImpl extends BaseServiceImpl<Recupera
// sql.setVar("usCnd", usCnd);
Sql sql = Sqls.create("""
SELECT
line.id,
us.id,
line.id AS sourceLineId,
line.serialNumber,
line.lineName,
line.regionalNature,
@@ -168,8 +169,22 @@ public class RecuperationLineClusterServiceImpl extends BaseServiceImpl<Recupera
gh.id AS ascriptionUnionId,
u.username AS createUserName,
ta.travelAgencyName,
( SELECT count( 1 ) FROM the_rapy_recuperation_enroll $summaryCnd ) AS signUpUserNum,
( SELECT count( 1 ) FROM the_rapy_recuperation_enroll_companion WHERE trreId IN ( SELECT id FROM the_rapy_recuperation_enroll $summaryCnd )) AS signUpUserFamilyNum
(SELECT count(1)
FROM the_rapy_recuperation_enroll enroll
WHERE enroll.takePartInLineId = us.id
AND enroll.isNormal = true
AND enroll.stateId = 2750
AND (us.signUpMode <> 1 OR enroll.takePartInUnionId = us.unionId)) AS signUpUserNum,
(SELECT count(1)
FROM the_rapy_recuperation_enroll_companion companion
WHERE companion.trreId IN (
SELECT enroll.id
FROM the_rapy_recuperation_enroll enroll
WHERE enroll.takePartInLineId = us.id
AND enroll.isNormal = true
AND enroll.stateId = 2750
AND (us.signUpMode <> 1 OR enroll.takePartInUnionId = us.unionId)
)) AS signUpUserFamilyNum
FROM
the_rapy_recuperation_line_union_select us
LEFT JOIN the_rapy_recuperation_line line ON line.id = us.lineId
@@ -179,22 +194,16 @@ public class RecuperationLineClusterServiceImpl extends BaseServiceImpl<Recupera
$condition
""");
Cnd cnd = Cnd.NEW();
Cnd summaryCnd = Cnd.NEW();
summaryCnd.and("isNormal", "=", true);
summaryCnd.and("takePartInLineId", "=", "us.id");
summaryCnd.and("stateId", "=", RecuperationState.PASS);
//如果是超级管理和校工会管理员,查看校工会线路
if (AuthUtil.hasRoleOr("sysadmin,A06")) {
cnd.andEX("us.unionId", "=", unionId);
cnd.andEX("us.signUpMode", "=", Strings.isNotBlank(unionId) ? 1 : 2);
}else{
summaryCnd.and("takePartInUnionId", "=", "us.unionId");
cnd.andEX("us.signUpMode", "=", 1);
cnd.andEX("us.unionId", "=", SecurityUtil.getUnionId());
}
cnd.andEX("line.year", "=", year);
cnd.and(Cnd.likeEX("line.lineName", keywords));
sql.setVar("summaryCnd", summaryCnd);
cnd.asc("line.serialNumber");
sql.setCondition(cnd);
return listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
@@ -257,6 +266,11 @@ public class RecuperationLineClusterServiceImpl extends BaseServiceImpl<Recupera
dao().clear(RecuperationCluster.class, Cnd.where("lineId", "=", lineId));
dao().clear(RecuperationClusterMember.class, Cnd.where("lineId", "=", lineId));
for (RecuperationCluster cluster : clusters) {
// 线路组团以“工会选线记录”作为业务线路 ID,保证与报名记录 takePartInLineId 一致。
cluster.setLineId(lineId);
if (Lang.isNotEmpty(cluster.getMembers())) {
cluster.getMembers().forEach(member -> member.setLineId(lineId));
}
insertWith(cluster, "members");
}
}
@@ -0,0 +1,97 @@
package com.budwk.app.zhgh.staffbenefit.recuperation.service.impl;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.web.commons.auth.utils.AuthUtil;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.staffbenefit.recuperation.model.RecuperationEnroll;
import com.budwk.app.zhgh.staffbenefit.recuperation.service.RecuperationLineStatisticsService;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.util.NutMap;
import java.util.List;
/** 线路统计服务,统一限定审核通过且未调出的报名记录。 */
@IocBean(args = {"refer:dao"})
public class RecuperationLineStatisticsServiceImpl extends BaseServiceImpl<RecuperationEnroll> implements RecuperationLineStatisticsService {
public RecuperationLineStatisticsServiceImpl(Dao dao) { super(dao); }
@Override
public Pagination pageData(PageForm pageForm, Integer startYear, Integer endYear, String unionId,
String lineId, String lotId, String signUpMode, String selectId, String regionalNature) {
Sql sql = Sqls.create("""
SELECT line.id, line.lineName, line.regionalNature, lineu.id AS lineUId, lineu.signUpMode,
YEAR(lineu.selectTime) AS year,
CONCAT(DATE_FORMAT(lineu.playStartTime,'%m月%d日'),'-',DATE_FORMAT(lineu.playEndTime,'%m月%d日')) AS linePlayTime,
agency.travelAgencyName, agency.contact, agency.contactMobileNumber,
IF(lineu.signUpMode=2,'校工会',un.name) AS unionName, lot.lotName,
COALESCE((SELECT outsideQuota FROM the_rapy_recuperation_config LIMIT 1),0) AS minimumGroupSize,
COUNT(DISTINCT enroll.loginName) AS lineNum,
IF(COALESCE((SELECT familyInfo FROM the_rapy_recuperation_config LIMIT 1),1)=2,
(SELECT COUNT(1) FROM the_rapy_recuperation_enroll_companion c WHERE c.trreId IN
(SELECT e.id FROM the_rapy_recuperation_enroll e WHERE e.takePartInLineId=lineu.id AND e.stateId=2750 AND e.isNormal=true)),
COALESCE(SUM(enroll.familyNumber),0)) AS signUpUserFamilyNum
FROM the_rapy_recuperation_line_union_select lineu
LEFT JOIN the_rapy_recuperation_line line ON line.id=lineu.lineId
LEFT JOIN the_rapy_recuperation_lot lot ON lot.id=line.lotId
LEFT JOIN the_rapy_recuperation_travel_agency agency ON agency.id=lineu.travelAgencyId
LEFT JOIN sys_union un ON un.id=lineu.unionId
LEFT JOIN the_rapy_recuperation_enroll enroll ON enroll.takePartInLineId=lineu.id AND enroll.stateId=2750 AND enroll.isNormal=true
$condition
""");
Cnd cnd = Cnd.where("YEAR(lineu.selectTime)", ">=", startYear).and("YEAR(lineu.selectTime)", "<=", endYear);
cnd.andEX("lineu.signUpMode", "=", signUpMode).andEX("line.lotId", "=", lotId)
.andEX("line.id", "=", lineId).andEX("lineu.id", "=", selectId).andEX("line.regionalNature", "=", regionalNature);
if (AuthUtil.hasRoleOr("sysadmin", "H06", "A06")) cnd.andEX("lineu.unionId", "=", unionId);
else cnd.and("lineu.unionId", "=", SecurityUtil.getUnionId());
cnd.groupBy("lineu.id", "line.id", "line.lineName", "line.regionalNature", "lineu.signUpMode",
"lineu.selectTime", "lineu.playStartTime", "lineu.playEndTime", "agency.travelAgencyName",
"agency.contact", "agency.contactMobileNumber", "un.name", "lot.lotName");
cnd.asc("line.serialNumber").asc("lineu.playStartTime");
sql.setCondition(cnd);
return listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
}
@Override
public Pagination userPage(PageForm pageForm, String lineSelectId, String searchKeyword, String unionId, String unitId) {
Sql sql = Sqls.create("""
SELECT enroll.*, line.lineName,
CONCAT(DATE_FORMAT(lineu.playStartTime,'%m月%d日'),'-',DATE_FORMAT(lineu.playEndTime,'%m月%d日')) AS linePlayTime,
(SELECT COUNT(1) FROM the_rapy_recuperation_enroll_companion WHERE trreId=enroll.id) AS familyCount
FROM the_rapy_recuperation_enroll enroll
LEFT JOIN the_rapy_recuperation_line_union_select lineu ON lineu.id=enroll.takePartInLineId
LEFT JOIN the_rapy_recuperation_line line ON line.id=lineu.lineId
$condition
""");
Cnd cnd = Cnd.where("enroll.takePartInLineId", "=", lineSelectId).and("enroll.stateId", "=", 2750).and("enroll.isNormal", "=", true);
cnd.andEX("enroll.selfUnionId", "=", unionId).andEX("enroll.selfUnitId", "=", unitId);
if (StrUtil.isNotBlank(searchKeyword)) cnd.and(Cnd.exps("enroll.loginName", "like", "%" + searchKeyword + "%").or("enroll.userName", "like", "%" + searchKeyword + "%"));
cnd.desc("enroll.signingUptime").desc("enroll.unitName");
sql.setCondition(cnd);
return listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
}
@Override
public List<NutMap> lineOptions(Integer startYear, Integer endYear, String signUpMode, String regionalNature) {
Sql sql = Sqls.create("""
SELECT DISTINCT line.id, line.lineName, line.regionalNature, lot.lotName,
IF(lineu.signUpMode=1,'分工会组织','校工会组织') AS signUpMode
FROM the_rapy_recuperation_line_union_select lineu
LEFT JOIN the_rapy_recuperation_line line ON line.id=lineu.lineId
LEFT JOIN the_rapy_recuperation_lot lot ON lot.id=line.lotId
$condition
""");
Cnd cnd = Cnd.where("YEAR(lineu.selectTime)", ">=", startYear).and("YEAR(lineu.selectTime)", "<=", endYear)
.andEX("lineu.signUpMode", "=", signUpMode).andEX("line.regionalNature", "=", regionalNature);
if (!AuthUtil.hasRoleOr("sysadmin", "H06", "A06")) cnd.and("lineu.unionId", "=", SecurityUtil.getUnionId());
cnd.asc("line.serialNumber");
sql.setCondition(cnd);
return listMap(sql);
}
}
@@ -8,6 +8,7 @@ import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.staffbenefit.recuperation.constant.RecuperationState;
import com.budwk.app.zhgh.staffbenefit.recuperation.model.RecuperationEnroll;
import com.budwk.app.zhgh.staffbenefit.recuperation.model.RecuperationEnrollCompanion;
import com.budwk.app.zhgh.staffbenefit.recuperation.model.RecuperationProvinceFlexibleGroup;
import com.budwk.app.zhgh.staffbenefit.recuperation.model.RecuperationTravelAgency;
import com.budwk.app.zhgh.staffbenefit.recuperation.service.RecuperationProvinceFlexibleGroupService;
@@ -376,6 +377,158 @@ public class RecuperationProvinceFlexibleGroupServiceImpl extends BaseServiceImp
delete(id);
}
@Override
public Pagination queryPage(PageForm pageForm, Integer startYear, Integer endYear, String groupName,
String travelAgencyId, String searchKeyword) {
Sql sql = Sqls.create("""
SELECT fg.id, fg.groupName, fg.year, ta.travelAgencyName,
COUNT(DISTINCT CASE WHEN enroll.groupLeaderUserId IS NOT NULL AND enroll.groupLeaderUserId <> ''
THEN enroll.groupLeaderUserId WHEN enroll.groupLeaderLoginName IS NOT NULL
AND enroll.groupLeaderLoginName <> '' THEN enroll.groupLeaderLoginName END) AS groupCount,
COUNT(DISTINCT enroll.loginName) AS signCount
FROM the_rapy_recuperation_province_flexible_group fg
LEFT JOIN the_rapy_recuperation_travel_agency ta ON ta.id = fg.travelAgencyId
LEFT JOIN the_rapy_recuperation_enroll enroll
ON enroll.takePartInTravelAgencyId = fg.travelAgencyId
AND YEAR(enroll.signingUptime) = fg.year
AND enroll.isNormal = true
AND (enroll.stateId IS NULL OR enroll.stateId NOT IN (2715,2725,2735))
$condition
""");
Cnd cnd = Cnd.where("ta.signUpTravelAgency", "=", true);
cnd.andEX("fg.year", ">=", startYear).andEX("fg.year", "<=", endYear);
cnd.andEX("fg.travelAgencyId", "=", travelAgencyId);
cnd.and(Cnd.likeEX("fg.groupName", groupName));
if (StrUtil.isNotBlank(searchKeyword)) {
cnd.and(Cnd.exps("enroll.userName", "like", "%" + searchKeyword + "%")
.or("enroll.loginName", "like", "%" + searchKeyword + "%"));
}
if (!AuthUtil.hasRoleOr("sysadmin", "A06") && AuthUtil.hasRoleOr("H04")) {
cnd.and("fg.createUnionId", "=", SecurityUtil.getUnionId());
}
cnd.groupBy("fg.id", "fg.groupName", "fg.year", "ta.travelAgencyName");
cnd.desc("fg.year").asc("fg.groupName");
sql.setCondition(cnd);
return listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
}
@Override
public List<NutMap> queryGroupInfo(String groupId) {
Sql sql = Sqls.create("""
SELECT enroll.groupLeaderUserId, enroll.groupLeaderUserName, enroll.groupLeaderLoginName,
MAX(CASE WHEN enroll.loginName = enroll.groupLeaderLoginName THEN enroll.groupLeaderPassword ELSE '' END) AS groupLeaderPassword,
COUNT(DISTINCT enroll.loginName) AS userCount
FROM the_rapy_recuperation_enroll enroll
WHERE enroll.takePartInTravelAgencyId = (SELECT travelAgencyId FROM the_rapy_recuperation_province_flexible_group WHERE id=@groupId)
AND YEAR(enroll.signingUptime) = (SELECT year FROM the_rapy_recuperation_province_flexible_group WHERE id=@groupId)
AND enroll.isNormal = true AND (enroll.stateId IS NULL OR enroll.stateId NOT IN (2715,2725,2735))
GROUP BY enroll.groupLeaderUserId, enroll.groupLeaderUserName, enroll.groupLeaderLoginName
ORDER BY enroll.groupLeaderUserName, enroll.groupLeaderLoginName
""");
sql.setParam("groupId", groupId);
return listMap(sql);
}
@Override
public Pagination querySignUsers(PageForm pageForm, String groupId, String groupLeaderUserId,
String groupLeaderLoginName, Boolean noGroupLeader) {
StringBuilder condition = new StringBuilder();
if (isValidGroupLeaderParam(groupLeaderUserId)) {
condition.append(" AND enroll.groupLeaderUserId=@groupLeaderUserId");
} else if (isValidGroupLeaderParam(groupLeaderLoginName)) {
condition.append(" AND enroll.groupLeaderLoginName=@groupLeaderLoginName");
} else if (Boolean.TRUE.equals(noGroupLeader)) {
condition.append(" AND (enroll.groupLeaderUserId IS NULL OR enroll.groupLeaderUserId='')");
}
String sqlText = """
SELECT enroll.id, enroll.loginName, enroll.userName,
CASE WHEN enroll.loginName = enroll.groupLeaderLoginName THEN '团长' ELSE '队员' END AS identity,
enroll.unionName, enroll.unitName, enroll.sex, enroll.mobile,
(SELECT COUNT(1) FROM the_rapy_recuperation_enroll_companion WHERE trreId=enroll.id) AS isFamily
FROM the_rapy_recuperation_enroll enroll
WHERE enroll.takePartInTravelAgencyId=(SELECT travelAgencyId FROM the_rapy_recuperation_province_flexible_group WHERE id=@groupId)
AND YEAR(enroll.signingUptime)=(SELECT year FROM the_rapy_recuperation_province_flexible_group WHERE id=@groupId)
AND enroll.isNormal=true
AND (enroll.stateId IS NULL OR enroll.stateId NOT IN ($auditFailStates))
$condition
ORDER BY CASE WHEN enroll.loginName=enroll.groupLeaderLoginName THEN 0 ELSE 1 END, enroll.signingUptime
""";
// 动态团长条件必须在创建 Sql 前拼入,确保 Nutz 能识别对应参数占位符。
Sql sql = Sqls.create(sqlText.replace("$condition", condition.toString()))
.setParam("groupId", groupId)
.setParam("groupLeaderUserId", groupLeaderUserId)
.setParam("groupLeaderLoginName", groupLeaderLoginName)
.setVar("auditFailStates", RecuperationState.UNITFAIL + "," + RecuperationState.LINEUNITFAIL + "," + RecuperationState.SCHOOLFAIL);
return listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
}
/** 过滤浏览器可能传入的 undefined/null 文本,避免错误命中团长条件。 */
private boolean isValidGroupLeaderParam(String value) {
return StrUtil.isNotBlank(value) && !"undefined".equalsIgnoreCase(value) && !"null".equalsIgnoreCase(value);
}
@Override
public List<NutMap> queryTravelAgencyOptions(Integer startYear, Integer endYear) {
Sql sql = Sqls.create("""
SELECT DISTINCT ta.id, ta.travelAgencyName, ta.serialNumber
FROM the_rapy_recuperation_province_flexible_group fg
LEFT JOIN the_rapy_recuperation_travel_agency ta ON ta.id=fg.travelAgencyId
WHERE ta.id IS NOT NULL AND ta.isDisabled=false AND ta.signUpTravelAgency=true
AND fg.year>=@startYear AND fg.year<=@endYear
ORDER BY ta.serialNumber * 1, ta.travelAgencyName
""");
sql.setParam("startYear", startYear).setParam("endYear", endYear);
return listMap(sql);
}
@Override
public List<NutMap> queryFlexibleGroupOptions(Integer startYear, Integer endYear) {
Sql sql = Sqls.create("""
SELECT fg.id, fg.groupName FROM the_rapy_recuperation_province_flexible_group fg
LEFT JOIN the_rapy_recuperation_travel_agency ta ON ta.id=fg.travelAgencyId
WHERE ta.signUpTravelAgency=true AND fg.year>=@startYear AND fg.year<=@endYear ORDER BY fg.groupName
""");
sql.setParam("startYear", startYear).setParam("endYear", endYear);
return listMap(sql);
}
@Override
public List<NutMap> queryExportUsers(Integer startYear, Integer endYear, String groupName,
String travelAgencyId, String searchKeyword) {
Sql sql = Sqls.create("""
SELECT enroll.loginName, enroll.userName, enroll.mobile, enroll.idCard, enroll.unitName, enroll.unionName,
ta.travelAgencyName, DATE_FORMAT(enroll.signingUptime,'%Y-%m-%d %H:%i:%s') AS signingUptime,
CONCAT(IFNULL(enroll.groupLeaderUserName,''), IF(enroll.groupLeaderLoginName IS NULL OR enroll.groupLeaderLoginName='', '', CONCAT('',enroll.groupLeaderLoginName,''))) AS leaderName,
CASE WHEN (SELECT COUNT(DISTINCT sameLeader.loginName) FROM the_rapy_recuperation_enroll sameLeader
WHERE sameLeader.takePartInTravelAgencyId=enroll.takePartInTravelAgencyId
AND sameLeader.groupLeaderLoginName=enroll.groupLeaderLoginName AND sameLeader.isNormal=true
AND YEAR(sameLeader.signingUptime)=YEAR(enroll.signingUptime))>=3 THEN '已成团' ELSE '未成团' END AS formedTeamState
FROM the_rapy_recuperation_enroll enroll
LEFT JOIN the_rapy_recuperation_travel_agency ta ON ta.id=enroll.takePartInTravelAgencyId
LEFT JOIN the_rapy_recuperation_province_flexible_group fg ON fg.travelAgencyId=enroll.takePartInTravelAgencyId AND fg.year=YEAR(enroll.signingUptime)
$condition
""");
Cnd cnd = Cnd.where("fg.year", ">=", startYear).and("fg.year", "<=", endYear)
.and("enroll.isNormal", "=", true);
cnd.andEX("fg.travelAgencyId", "=", travelAgencyId).and(Cnd.likeEX("fg.groupName", groupName));
if (StrUtil.isNotBlank(searchKeyword)) {
cnd.and(Cnd.exps("enroll.userName", "like", "%" + searchKeyword + "%").or("enroll.loginName", "like", "%" + searchKeyword + "%"));
}
if (!AuthUtil.hasRoleOr("sysadmin", "A06") && AuthUtil.hasRoleOr("H04")) cnd.and("fg.createUnionId", "=", SecurityUtil.getUnionId());
cnd.asc("fg.year").asc("ta.serialNumber * 1").asc("enroll.groupLeaderUserName").asc("enroll.signingUptime");
sql.setCondition(cnd);
List<NutMap> list = listMap(sql);
for (int i = 0; i < list.size(); i++) list.get(i).setv("no", i + 1);
return list;
}
@Override
@Aop(TransAop.READ_COMMITTED)
public void deleteJoinUser(String id) {
dao().clear(RecuperationEnrollCompanion.class, Cnd.where("trreId", "=", id));
dao().clear(RecuperationEnroll.class, Cnd.where("id", "=", id));
}
/**
* 查询旅行社编辑页中“是否灵活组团”为是且未禁用的旅行社。
*
@@ -167,15 +167,16 @@ layout("/layouts/platform.html"){
}
},
methods: {
async getNumData(){
const resp = await this.$axios.post('/platform/recuperation/annualAnalysis/getNumData', this.pageForm)
getNumData(){
return this.$axios.post('/platform/recuperation/annualAnalysis/getNumData', this.pageForm).then((resp) => {
if (resp.code === 0) {
this.numData = resp.data
this.$set(this, 'numData', resp.data)
}
})
},
async getLotNumData(){
this.lotLoading = true
const resp = await this.$axios.post('/platform/recuperation/annualAnalysis/getLotNum', this.pageForm)
getLotNumData(){
this.$set(this, 'lotLoading', true)
return this.$axios.post('/platform/recuperation/annualAnalysis/getLotNum', this.pageForm).then((resp) => {
if (resp.code === 0) {
let {data} = resp
document.getElementById("lotChart").innerHTML = ''
@@ -231,12 +232,12 @@ layout("/layouts/platform.html"){
...config,
});
plot.render();
this.lotLoading = false
}
}).finally(() => { this.$set(this, 'lotLoading', false) })
},
async getAgeData(){
this.ageLoading = true
const resp = await this.$axios.post('/platform/recuperation/annualAnalysis/getAgeNum', this.pageForm)
getAgeData(){
this.$set(this, 'ageLoading', true)
return this.$axios.post('/platform/recuperation/annualAnalysis/getAgeNum', this.pageForm).then((resp) => {
if (resp.code === 0) {
let {data} = resp
document.getElementById("ageChart").innerHTML = ''
@@ -268,14 +269,14 @@ layout("/layouts/platform.html"){
...config,
});
plot.render();
this.ageLoading = false
}
}).finally(() => { this.$set(this, 'ageLoading', false) })
},
async getLineTravelData(){
getLineTravelData(){
if (this.dualAxisChartInstance) {
this.dualAxisChartInstance.destroy(); // 如果已有实例,先销毁
}
const resp = await this.$axios.post('/platform/recuperation/annualAnalysis/getLineTravelAndAgeData', this.pageForm)
return this.$axios.post('/platform/recuperation/annualAnalysis/getLineTravelAndAgeData', this.pageForm).then((resp) => {
if (resp.code === 0) {
let {data} = resp
document.getElementById("lineTravelChart").innerHTML = ''
@@ -326,24 +327,24 @@ layout("/layouts/platform.html"){
});
dualAxes.render();
}
})
},
doLineTravelOrAgeSwitch(){
this.isLineTravel = !this.isLineTravel
this.lineTravelOrAgeTip = this.isLineTravel ? '出行线路年龄统计' : '出行线路人数统计'
this.$set(this, 'isLineTravel', !this.isLineTravel)
this.$set(this, 'lineTravelOrAgeTip', this.isLineTravel ? '出行线路年龄统计' : '出行线路人数统计')
},
async initData(){
await this.getNumData()
await this.getLotNumData()
await this.getAgeData()
await this.getLineTravelData()
initData(){
this.getNumData()
this.getLotNumData()
this.getAgeData()
this.getLineTravelData()
},
async getEnumOptions(enumName) {
const resp = await this.$axios.post("/open/common/dictEnumOptions", { name: enumName })
return resp.data
getEnumOptions(enumName) {
return this.$axios.post("/open/common/dictEnumOptions", { name: enumName }).then((resp) => resp.data || [])
},
},
async created() {
this.regionalNatureList.push(...await this.getEnumOptions('RecuperationProvinceType'))
created() {
this.getEnumOptions('RecuperationProvinceType').then((res) => this.$set(this, 'regionalNatureList', this.regionalNatureList.concat(res)))
this.initData()
}
})
@@ -1,11 +1,73 @@
<!--# layout("/layouts/platform.html"){ #-->
<div id="app" v-cloak>
<el-card shadow="never"><search @search="doSearch"><search-item label="年度"><el-date-picker v-model="pageForm.year" type="year" value-format="yyyy" @change="doSearch"></el-date-picker></search-item><search-item label="审核状态"><el-select v-model="pageForm.audited" @change="doSearch"><el-option label="待审核" :value="false"></el-option><el-option label="已审核" :value="true"></el-option></el-select></search-item><search-item label="姓名/工号"><el-input v-model="pageForm.keyword" clearable @keyup.enter.native="doSearch"></el-input></search-item></search></el-card>
<el-card shadow="never" class="mt20"><table-tool label="疗休养审核"></table-tool><el-table v-loading="tableLoading" :data="tableData" :size="tableSize"><el-table-column type="index" :index="indexMethod" label="序号" width="60"></el-table-column><el-table-column prop="loginName" label="工号"></el-table-column><el-table-column prop="userName" label="姓名"></el-table-column><el-table-column prop="unitName" label="单位"></el-table-column><el-table-column prop="lineName" label="线路" min-width="160"></el-table-column><el-table-column prop="travelAgencyName" label="旅行社"></el-table-column><el-table-column prop="stateId" label="状态"><template v-slot="{row}">{{stateText(row.stateId)}}</template></el-table-column><el-table-column label="操作" width="230"><template v-slot="{row}"><el-button size="mini" @click="view(row)">查看</el-button><el-button v-if="!pageForm.audited" size="mini" type="success" @click="openAudit(row,true)">通过</el-button><el-button v-if="!pageForm.audited" size="mini" type="danger" @click="openAudit(row,false)">驳回</el-button><el-button v-if="pageForm.audited" size="mini" @click="recall(row)">撤回</el-button></template></el-table-column></el-table><!--#include("/layouts/pagination.html"){}#--></el-card>
<el-dialog title="报名详情" :visible.sync="viewVisible" width="760px" append-to-body><el-descriptions :column="2" border><el-descriptions-item label="工号">{{viewData.loginName}}</el-descriptions-item><el-descriptions-item label="姓名">{{viewData.userName}}</el-descriptions-item><el-descriptions-item label="身份证">{{viewData.idCard}}</el-descriptions-item><el-descriptions-item label="手机">{{viewData.mobile}}</el-descriptions-item><el-descriptions-item label="线路">{{viewData.lineName}}</el-descriptions-item><el-descriptions-item label="旅行社">{{viewData.travelAgencyName}}</el-descriptions-item></el-descriptions></el-dialog>
<el-dialog title="审核意见" :visible.sync="auditVisible" width="520px" append-to-body><el-input type="textarea" :rows="4" v-model="auditForm.auditOpinion"></el-input><template slot="footer"><el-button @click="auditVisible=false">取消</el-button><el-button type="primary" :loading="formLoading" @click="submitAudit">确认</el-button></template></el-dialog>
<el-card shadow="never">
<search @search="doSearch">
<search-item label="年度"><el-date-picker v-model="pageForm.year" type="year" value-format="yyyy" @change="doSearch"></el-date-picker></search-item>
<search-item label="审核状态"><el-select v-model="pageForm.audited" @change="doSearch"><el-option label="待审核" :value="false"></el-option><el-option label="已审核" :value="true"></el-option></el-select></search-item>
<search-item v-if="pageForm.stage==='school'||pageForm.stage==='travel'" label="所属工会"><el-select v-model="pageForm.unionId" filterable clearable @change="doSearch"><el-option v-for="item in unionOptions" :key="item.id" :label="item.name" :value="item.id"></el-option></el-select></search-item>
<search-item label="线路"><el-select v-model="pageForm.lineId" filterable clearable @change="doSearch"><el-option v-for="item in lineOptions" :key="item.id" :label="item.lineName" :value="item.id"></el-option></el-select></search-item>
<search-item v-if="pageForm.stage==='travel'" label="旅行社"><el-select v-model="pageForm.agencyId" filterable clearable @change="doSearch"><el-option v-for="item in agencyOptions" :key="item.id" :label="item.travelAgencyName" :value="item.id"></el-option></el-select></search-item>
<search-item label="姓名/工号"><el-input v-model="pageForm.keyword" clearable @keyup.enter.native="doSearch"></el-input></search-item>
</search>
</el-card>
<el-card shadow="never" class="mt20">
<table-tool :label="stageTitle">
<el-button v-if="!pageForm.audited" type="success" size="small" @click="openBatchAudit(true)">一键通过</el-button>
<el-button v-if="!pageForm.audited" type="danger" size="small" @click="openBatchAudit(false)">一键拒绝</el-button>
</table-tool>
<el-table ref="tableRef" v-loading="tableLoading" :data="tableData" :size="tableSize" row-key="id" @selection-change="selectionChange">
<el-table-column v-if="!pageForm.audited" type="selection" reserve-selection width="50"></el-table-column>
<el-table-column type="index" :index="indexMethod" label="序号" width="60"></el-table-column>
<el-table-column prop="loginName" label="工号" width="110"></el-table-column>
<el-table-column prop="userName" label="姓名" width="100"></el-table-column>
<el-table-column prop="unitName" label="单位" min-width="150" show-overflow-tooltip></el-table-column>
<el-table-column prop="selfUnionName" label="所属工会" min-width="130"></el-table-column>
<el-table-column prop="lineName" label="线路" min-width="180" show-overflow-tooltip></el-table-column>
<el-table-column prop="travelAgencyName" label="旅行社" min-width="150"></el-table-column>
<el-table-column prop="signingUptime" label="报名时间" width="165"></el-table-column>
<el-table-column prop="stateId" label="状态" width="120"><template v-slot="{row}">{{stateText(row.stateId)}}</template></el-table-column>
<el-table-column label="操作" width="230" fixed="right"><template v-slot="{row}">
<el-button size="mini" @click="view(row)">查看</el-button>
<el-button v-if="!pageForm.audited" size="mini" type="success" @click="openAudit(row,true)">通过</el-button>
<el-button v-if="!pageForm.audited" size="mini" type="danger" @click="openAudit(row,false)">拒绝</el-button>
<el-button v-if="pageForm.audited&&canRecall(row)" size="mini" @click="recall(row)">撤回</el-button>
</template></el-table-column>
</el-table>
<!--#include("/layouts/pagination.html"){}#-->
</el-card>
<el-dialog title="报名详情" :visible.sync="viewVisible" width="820px" append-to-body>
<el-descriptions :column="2" border>
<el-descriptions-item label="工号">{{viewData.loginName}}</el-descriptions-item><el-descriptions-item label="姓名">{{viewData.userName}}</el-descriptions-item>
<el-descriptions-item label="身份证">{{viewData.idCard}}</el-descriptions-item><el-descriptions-item label="手机">{{viewData.mobile}}</el-descriptions-item>
<el-descriptions-item label="单位">{{viewData.unitName}}</el-descriptions-item><el-descriptions-item label="工会">{{viewData.unionName}}</el-descriptions-item>
<el-descriptions-item label="线路">{{viewData.lineName}}</el-descriptions-item><el-descriptions-item label="旅行社">{{viewData.travelAgencyName}}</el-descriptions-item>
</el-descriptions>
<el-table v-if="viewData.companionList&&viewData.companionList.length" :data="viewData.companionList" class="mt20">
<el-table-column prop="userName" label="家属姓名"></el-table-column><el-table-column prop="relation" label="关系"></el-table-column><el-table-column prop="idCard" label="身份证"></el-table-column>
</el-table>
</el-dialog>
<el-dialog :title="batchMode?'一键审核':'审核意见'" :visible.sync="auditVisible" width="520px" append-to-body>
<div class="mb10">本次将{{auditForm.pass?'通过':'拒绝'}} {{auditCount}} 条报名记录。</div>
<el-input type="textarea" :rows="4" v-model="auditForm.auditOpinion" placeholder="请输入审核意见"></el-input>
<template slot="footer"><el-button @click="auditVisible=false">取消</el-button><el-button type="primary" :loading="formLoading" @click="submitAudit">确认</el-button></template>
</el-dialog>
</div>
<script nonce="${cspNonce!}">
new Vue({el:"#app",mixins:[initTableMixins],data(){return {pageForm:{pageNumber:1,pageSize:10,totalCount:0,year:new Date().getFullYear(),audited:false,stage:"${stage!}"},viewVisible:false,auditVisible:false,formLoading:false,viewData:{},auditForm:{}}},methods:{pageData(){this.tableLoading=true;this.$axios.post("/platform/recuperation/audit/pageData",this.pageForm).then((res)=>{if(res.code===0){this.$set(this,"tableData",res.data.list||[]);this.$set(this.pageForm,"totalCount",res.data.totalCount||0)}}).finally(()=>{this.tableLoading=false})},stateText(state){const map={2710:"分工会待审",2715:"分工会驳回",2720:"线路工会待审",2725:"线路工会驳回",2730:"校工会待审",2735:"校工会驳回",2750:"审核通过"};return map[state]||state},view(row){this.$axios.post("/platform/recuperation/audit/findOne",{id:row.id}).then((res)=>{if(res.code===0){this.$set(this,"viewData",res.data.viewData||{});this.$set(this,"viewVisible",true)}})},openAudit(row,pass){this.$set(this,"auditForm",{id:row.id,pass:pass,auditOpinion:""});this.$set(this,"auditVisible",true)},submitAudit(){this.formLoading=true;this.$axios.post("/platform/recuperation/audit/audit",this.auditForm).then((res)=>{if(res.code===0){this.$message.success(res.msg);this.$set(this,"auditVisible",false);this.pageData()}}).finally(()=>{this.formLoading=false})},recall(row){this.$confirm("确定撤回该审核吗?","提示").then(()=>{this.$axios.post("/platform/recuperation/audit/recall",{id:row.id}).then((res)=>{if(res.code===0)this.pageData()})}).catch(()=>{})}},created(){this.pageData()}})
new Vue({
el:"#app",mixins:[initTableMixins],
data(){return{pageForm:{pageNumber:1,pageSize:10,totalCount:0,year:new Date().getFullYear().toString(),audited:false,stage:"${stage!}",keyword:'',unionId:'',lineId:'',agencyId:''},viewVisible:false,auditVisible:false,formLoading:false,viewData:{},auditForm:{pass:true,auditOpinion:''},selectedRows:[],batchMode:false,unionOptions:[],lineOptions:[],agencyOptions:[]}},
computed:{stageTitle(){const titles={branch:'分工会审核',lineUnion:'线路工会审核',school:'校工会审核',travel:'旅行社审核'};return titles[this.pageForm.stage]||'疗休养审核'},auditCount(){return this.batchMode?this.selectedRows.length:1}},
methods:{
pageData(){this.$set(this,'tableLoading',true);this.$axios.post('/platform/recuperation/audit/pageData',this.pageForm).then((res)=>{if(res.code===0){this.$set(this,'tableData',res.data.list||[]);this.$set(this.pageForm,'totalCount',res.data.totalCount||0)}}).finally(()=>{this.$set(this,'tableLoading',false)})},
stateText(state){const map={2710:'分工会待审',2715:'分工会驳回',2720:'线路工会待审',2725:'线路工会驳回',2730:'校工会待审',2735:'校工会驳回',2750:'审核通过'};return map[state]||state},
canRecall(row){return[2715,2725,2735].indexOf(row.stateId)!==-1},selectionChange(rows){this.$set(this,'selectedRows',rows)},
view(row){this.$axios.post('/platform/recuperation/audit/findOne',{id:row.id}).then((res)=>{if(res.code===0){this.$set(this,'viewData',(res.data&&res.data.viewData)||{});this.$set(this,'viewVisible',true)}})},
openAudit(row,pass){this.$set(this,'batchMode',false);this.$set(this,'auditForm',{id:row.id,pass:pass,auditOpinion:''});this.$set(this,'auditVisible',true)},
openBatchAudit(pass){if(!this.selectedRows.length){this.$message.warning('请选择需要审核的报名记录');return}this.$set(this,'batchMode',true);this.$set(this,'auditForm',{pass:pass,auditOpinion:''});this.$set(this,'auditVisible',true)},
submitAudit(){this.$set(this,'formLoading',true);const url=this.batchMode?'/platform/recuperation/audit/oneKeyAudit':'/platform/recuperation/audit/audit';const params=Object.assign({},this.auditForm,this.batchMode?{ids:this.selectedRows.map((item)=>item.id).join(',')}:{});this.$axios.post(url,params).then((res)=>{if(res.code===0){this.$message.success(res.msg);this.$set(this,'auditVisible',false);this.pageData()}else this.$message.warning(res.msg)}).finally(()=>{this.$set(this,'formLoading',false)})},
recall(row){this.$confirm('确定撤回该审核吗?','提示').then(()=>{this.$axios.post('/platform/recuperation/audit/recall',{id:row.id}).then((res)=>{if(res.code===0)this.pageData()})}).catch(()=>{})},
loadOptions(){this.$businessTool.listUnion().then((res)=>{this.$set(this,'unionOptions',res||[])});this.$axios.post('/platform/recuperation/line/pageData',{pageNumber:1,pageSize:200,year:this.pageForm.year}).then((res)=>{if(res.code===0)this.$set(this,'lineOptions',res.data.list||[])});this.$axios.post('/platform/recuperation/travelAgency/selectTravelAgency',{year:this.pageForm.year}).then((res)=>{if(res.code===0)this.$set(this,'agencyOptions',res.data||[])})}
},created(){this.loadOptions();this.pageData()}
})
</script>
<!--# } #-->
@@ -121,48 +121,51 @@ const editForm = {
}
},
methods: {
async onOpen(id, year) {
this.year = year
this.visible = true
await this.getUnionSelectLine()
await this.getModifyConfig()
const {code, msg, data} = await this.$axios.get("/platform/recuperation/branchUnionUserQuery/findOne", {id})
onOpen(id, year) {
this.$set(this, 'year', year)
this.$set(this, 'visible', true)
Promise.all([this.getUnionSelectLine(),this.getModifyConfig()]).then(() => this.$axios.get("/platform/recuperation/branchUnionUserQuery/findOne", {id})).then((res) => {
const {code, msg, data} = res
if (code === 0) {
this.formData = data
this.$set(this, 'formData', data)
if (this.config.familyInfo === 2) {
this.labelName = "家属信息"
} else {
this.formData.bedType = data.familyNumber
this.labelName = "家属数量"
this.$set(this.formData, 'bedType', data.familyNumber)
this.$set(this, 'labelName', "家属数量")
}
} else {
this.$message.error(msg)
}
})
},
async getModifyConfig() {
const {code, data, msg} = await this.$axios.post('/platform/recuperation/config/fetchOne')
getModifyConfig() {
return this.$axios.post('/platform/recuperation/config/fetchOne').then((res) => {
const {code, data, msg} = res
if (code === 0) {
this.config = data
this.$set(this, 'config', data)
} else {
this.$message.error(msg)
}
})
},
async getUnionSelectLine() {
const resp = await this.$axios.post('/platform/recuperation/schoolUnionUserQuery/getUnionSelectLine', {
getUnionSelectLine() {
return this.$axios.post('/platform/recuperation/schoolUnionUserQuery/getUnionSelectLine', {
year: this.year,
signUpMode: 1,
})
}).then((resp) => {
if (resp.code === 0) {
this.unionSelectLines = resp.data
this.$set(this, 'unionSelectLines', resp.data)
}
})
},
async validateLine() {
const resp = await this.$axios.post('/platform/recuperation/line/enroll/validSignUpInfo'
, {enroll: JSON.stringify(this.formData)})
validateLine() {
this.$axios.post('/platform/recuperation/line/enroll/validSignUpInfo', {enroll: JSON.stringify(this.formData)}).then((resp) => {
if (resp.code !== 0) {
this.$message.warning(resp.msg)
this.formData.takePartInLineId = ''
this.$set(this.formData, 'takePartInLineId', '')
}
})
},
onSubmit() {
this.$refs.formRef.validate((valid) => {
@@ -171,19 +174,21 @@ const editForm = {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
}).then(() => {
if (!this.formData.takePartInLineId && !this.formData.takePartInBaseManagementId) {
this.$message.warning('请选择线路')
return
}
const {code, msg} = await this.$axios.post('/platform/recuperation/schoolUnionUserQuery/doEdit', this.formData)
return this.$axios.post('/platform/recuperation/schoolUnionUserQuery/doEdit', this.formData).then((res) => {
const {code, msg} = res
if (code === 0) {
this.$message.success(msg)
this.visible = false
this.$set(this, 'visible', false)
this.$emit('refresh')
} else {
this.$message.error(msg)
}
})
}).catch()
}
})
@@ -313,10 +313,10 @@ layout("/layouts/platform.html"){
cancelButtonText: '取消',
dangerouslyUseHTMLString: true,
type: 'warning'
}).then(async () => {
const {code, msg} = await this.$axios.post('/platform/recuperation/branchUnionUserQuery/deleteMyEnrollInfoById', {
}).then(() => this.$axios.post('/platform/recuperation/branchUnionUserQuery/deleteMyEnrollInfoById', {
id: row.id
})
})).then((res) => {
const {code, msg} = res
if (code === 0) {
this.doSearch()
this.$message.success(msg)
@@ -61,11 +61,12 @@ const setUpPart = {
this.tableData = JSON.parse(JSON.stringify(selection))
this.getModifyConfig()
},
async getModifyConfig() {
const res = await this.$axios.post('/platform/recuperation/config/fetchOne')
getModifyConfig() {
this.$axios.post('/platform/recuperation/config/fetchOne').then((res) => {
if (res.code === 0) {
this.config = res.data
this.$set(this, 'config', res.data)
}
})
},
lotChange(val) {
const selection = this.$refs.tableRef.selection
@@ -119,12 +120,12 @@ const setUpPart = {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async res => {
const {code, msg} = await this.$axios.post("/platform/recuperation/branchUnionUserQuery/setUpParticipants", {data: JSON.stringify(data)})
}).then(() => this.$axios.post("/platform/recuperation/branchUnionUserQuery/setUpParticipants", {data: JSON.stringify(data)})).then((res) => {
const {code, msg} = res
if (code === 0) {
this.$message.success(msg)
this.$refs.tableRef.clearSelection()
this.visible = false
this.$set(this, 'visible', false)
} else {
this.$message.error(msg)
}
@@ -88,29 +88,24 @@ const enrollInfo = {
}
},
methods: {
async onOpen(id) {
await this.findOne(id)
onOpen(id) {
return this.findOne(id)
},
async findOne(id) {
this.loading = true
const resp = await this.$axios.post("/platform/recuperation/branchUnionAudit/findOne", {id})
this.loading = false
if (resp.code === 0) {
this.viewData = resp.data.viewData
} else {
this.viewData = {}
this.$message.error(resp.msg)
}
findOne(id) {
this.$set(this, 'loading', true)
return this.$axios.post("/platform/recuperation/branchUnionAudit/findOne", {id}).then((resp) => {
if (resp.code === 0) this.$set(this, 'viewData', resp.data.viewData)
else { this.$set(this, 'viewData', {}); this.$message.error(resp.msg) }
}).finally(() => { this.$set(this, 'loading', false) })
},
async getModifyConfig() {
const res = await this.$axios.post('/platform/recuperation/config/fetchOne')
if (res.code === 0) {
this.modifyConfig = res.data
}
getModifyConfig() {
return this.$axios.post('/platform/recuperation/config/fetchOne').then((res) => {
if (res.code === 0) this.$set(this, 'modifyConfig', res.data)
})
},
},
async created() {
await this.getModifyConfig()
created() {
this.getModifyConfig()
},
style: /*language=CSS*/ `
.panel-heading {
@@ -49,12 +49,12 @@ layout("/layouts/platform.html"){
</div>
</el-form-item>
<el-form-item label="省外最多成团人数" prop="outsideQuota">
<el-form-item label="最少成团人数(含家属)" prop="outsideQuota">
<el-input v-model.number="formData.outsideQuota"
placeholder="请输入每条省外线路最多成团人数"></el-input>
placeholder="请输入最少成团人数"></el-input>
</el-form-item>
<el-form-item label="省外名额分配比例" prop="outsideQuotaProportion">
<el-form-item v-if="formData.outsideQuotaMode==='fixedRatio'" label="省外名额分配比例" prop="outsideQuotaProportion">
<el-input placeholder="请输入省外名额分配比例" v-model="formData.outsideQuotaProportion">
<template slot="append">%</template>
</el-input>
@@ -90,6 +90,34 @@ layout("/layouts/platform.html"){
v-model.number="formData.modifyNumber"></el-input>
</el-form-item>
<el-form-item label="统一报名时间" required>
<el-date-picker
v-model="formData.signUpStartTime"
type="datetime"
value-format="timestamp"
placeholder="请选择报名开始时间"
style="width: 32%">
</el-date-picker>
<span style="margin: 0 10px"></span>
<el-date-picker
v-model="formData.signUpEndTime"
type="datetime"
value-format="timestamp"
placeholder="请选择报名结束时间"
style="width: 32%">
</el-date-picker>
</el-form-item>
<el-form-item label="统一变更截止时间" prop="changeEndTime">
<el-date-picker
v-model="formData.changeEndTime"
type="datetime"
value-format="timestamp"
placeholder="请选择报名变更截止时间"
style="width: 66%">
</el-date-picker>
</el-form-item>
<el-form-item label="省内线路是否需要审核" prop="isSnLine">
<el-radio-group v-model="formData.isSnLine">
<el-radio-button :label="true">需要</el-radio-button>
@@ -104,15 +132,49 @@ layout("/layouts/platform.html"){
</el-radio-group>
</el-form-item>
<el-form-item label="旅行社报名是否需要审核" prop="travelAudit">
<el-radio-group v-model="formData.travelAudit">
<el-radio-button :label="true">需要</el-radio-button>
<el-radio-button :label="false">不需要</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item label="省外名额分配方式" prop="outsideQuotaMode">
<el-radio-group v-model="formData.outsideQuotaMode">
<el-radio-button label="fixedRatio">固定比例分配</el-radio-button>
<el-radio-button label="unionRatio">分工会单独分配</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item v-if="formData.outsideQuotaMode==='unionRatio'" label="分工会名额分配" prop="unionLimit">
<el-button type="primary" size="small" @click="openUnionLimit">设置分工会比例</el-button>
<span v-if="formData.unionLimit&&formData.unionLimit.length" class="text-success ml10">已设置 {{formData.unionLimit.length}} 个分工会</span>
</el-form-item>
<el-form-item label="提醒可见人员范围" prop="remindVisibleGroupId">
<el-select v-model="formData.remindVisibleGroupId" placeholder="请选择提醒可见人员范围"
filterable clearable style="width: 100%">
<el-option v-for="item in activityGroupList"
:value="item.groupId"
:key="item.groupId"
:label="item.groupName"></el-option>
</el-select>
</el-form-item>
<el-form-item label="默认提醒内容" prop="remindContent">
<el-input v-model="formData.remindContent" type="textarea" :rows="4"
placeholder="请输入未报名、已报名或未成团人员提醒时默认带出的内容"></el-input>
</el-form-item>
<el-form-item label="全批次最多报名人数" prop="allLineSignUpNumber">
<el-input max="100"
placeholder="请输入全批次最多报名人数"
v-model.number="formData.allLineSignUpNumber"></el-input>
</el-form-item>
<el-form-item label="省起始年份" prop="provinceStartYear">
<el-form-item label="省起始年份" prop="provinceStartYear">
<el-input max="100"
placeholder="请输入省起始年份"
placeholder="请输入省起始年份"
v-model.number="formData.provinceStartYear"></el-input>
</el-form-item>
@@ -220,6 +282,11 @@ layout("/layouts/platform.html"){
ref="drawerUserScope"
:group_id.sync="formData.activityGroupId"
></drawer-user-scope>
<el-dialog title="分工会省外名额分配" :visible.sync="unionLimitVisible" width="760px" append-to-body>
<div class="mb10"><el-input-number v-model="unionLimitQuickValue" :min="0" :max="100" :precision="2"></el-input-number><span class="ml10">%</span><el-button type="primary" size="small" class="ml10" @click="quickSetUnionLimit">统一设置</el-button><span class="ml20">当前合计:{{unionLimitTotal}}%</span></div>
<el-table :data="formData.unionLimit" max-height="480"><el-table-column prop="unionName" label="分工会"></el-table-column><el-table-column label="名额比例" width="230"><template v-slot="{row}"><el-input-number v-model="row.limitCount" :min="0" :max="100" :precision="2"></el-input-number><span class="ml10">%</span></template></el-table-column></el-table>
<template slot="footer"><el-button @click="unionLimitVisible=false">取消</el-button><el-button type="primary" @click="saveUnionLimit">确定</el-button></template>
</el-dialog>
</div>
<script nonce="${cspNonce!}">
@@ -247,6 +314,14 @@ layout("/layouts/platform.html"){
provinceStartYear: null,
bedInfo: true,
familyInfo: 1,
signUpStartTime: null,
signUpEndTime: null,
changeEndTime: null,
travelAudit: false,
outsideQuotaMode: 'fixedRatio',
remindVisibleGroupId: null,
remindContent: '',
unionLimit: [],
},
formRules: {
configName: [{required: true, message: '请输入配置名称', trigger: ['blur', 'change']}],
@@ -272,6 +347,10 @@ layout("/layouts/platform.html"){
provinceStartYear: [{required: true, message: '请输入省内起始年份', trigger: ['change', 'blur']}],
bedInfo: [{required: true, message: '请选择床位信息', trigger: ['change', 'blur']}],
familyInfo: [{required: true, message: '请选择家属信息', trigger: ['change', 'blur']}],
signUpStartTime: [{required: true, message: '请选择报名开始时间', trigger: ['change', 'blur']}],
signUpEndTime: [{required: true, message: '请选择报名结束时间', trigger: ['change', 'blur']}],
changeEndTime: [{required: true, message: '请选择变更截止时间', trigger: ['change', 'blur']}],
travelAudit: [{required: true, message: '请选择旅行社报名是否审核', trigger: ['change', 'blur']}],
},
marks: [],
lotDeleteList: [],
@@ -280,43 +359,48 @@ layout("/layouts/platform.html"){
tableScopeId: '',
tableScopeIndex: '',
userScopeDialog: false,
unionLimitVisible: false,
unionLimitQuickValue: 0,
unionOptions: [],
}
},
computed: {
unionLimitTotal() {
return (this.formData.unionLimit || []).reduce((total, item) => total + Number(item.limitCount || 0), 0).toFixed(2)
}
},
components: {
"drawer-user-scope": httpVueLoader("/components/module/activity/DrawerUserScope.vue"),
},
methods: {
async deleteLotsRow(scope) {
const res = await this.$axios.get(loc() + '/getLotsById?lotId=' + scope.row.id)
if (res.code === 0) {
this.LineNameList = res.data
}
if (!$.isEmptyObject(this.LineNameList)) {
this.centerDialogVisible = true;
this.tableScopeId = scope.row.id
this.tableScopeIndex = scope.$index;
} else {
const confirm = await this.$confirm('您确定要删除吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
if (confirm !== 'confirm') return
this.tableScopeId = scope.row.id
this.tableScopeIndex = scope.$index;
await this.deleteLotById();
}
deleteLotsRow(scope) {
this.$axios.post(loc() + '/getLotsById', {lotId: scope.row.id}).then((res) => {
if (res.code === 0) this.$set(this, 'LineNameList', res.data || {})
if (!$.isEmptyObject(this.LineNameList)) {
this.$set(this, 'centerDialogVisible', true)
this.$set(this, 'tableScopeId', scope.row.id)
this.$set(this, 'tableScopeIndex', scope.$index)
} else {
this.$confirm('您确定要删除吗?', '提示', {
confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning'
}).then(() => {
this.$set(this, 'tableScopeId', scope.row.id)
this.$set(this, 'tableScopeIndex', scope.$index)
this.deleteLotById()
}).catch(() => {})
}
})
},
resetForm() {
this.formData.lots = []
this.$set(this.formData, 'lots', [])
if (this.$refs['addForm']) {
this.$refs['addForm'].resetFields()
}
},
async operation() {
const valid = await this.$refs["addForm"].validate()
if (valid) {
this.subLoading = true
operation() {
this.$refs["addForm"].validate((valid) => {
if (!valid) return
this.$set(this, 'subLoading', true)
const loading = this.$loading({
lock: true,
text: '正在提交...',
@@ -330,64 +414,81 @@ layout("/layouts/platform.html"){
if (this.formData.lots && this.formData.lots.length > 0) {
cloneData.lots = JSON.stringify(this.formData.lots)
}
cloneData.outsideQuotaProportion = cloneData.outsideQuotaProportion / 100
const resp = await this.$axios.post(loc() + "/onSubmit", cloneData)
if (resp.code === 0) {
this.$message.success(resp.msg)
loading.close()
this.subLoading = false
} else {
this.$message.warning(resp.msg)
if (this.formData.unionLimit && this.formData.unionLimit.length > 0) {
cloneData.unionLimit = JSON.stringify(this.formData.unionLimit)
}
cloneData.outsideQuotaProportion = cloneData.outsideQuotaProportion / 100
this.$axios.post(loc() + "/onSubmit", cloneData).then((resp) => {
if (resp.code === 0) this.$message.success(resp.msg)
else this.$message.warning(resp.msg)
}).finally(() => { loading.close(); this.$set(this, 'subLoading', false) })
})
},
findOne() {
this.getActivityGroup().then(() => this.$axios.post(loc() + "/fetchOne")).then((res) => {
const data = res.data || {}
if (data.outsideQuotaProportion) data.outsideQuotaProportion = data.outsideQuotaProportion * 1000 / 10
if (!data.lots) data.lots = []
if (!data.unionLimit) data.unionLimit = []
this.$set(this, 'formData', data)
})
},
openUnionLimit() {
const existing = this.formData.unionLimit || []
const limits = this.unionOptions.map((item) => {
const old = existing.find((limit) => limit.unionId === item.id) || {}
return {unionId: item.id, unionName: item.name, limitCount: Number(old.limitCount || 0)}
})
this.$set(this.formData, 'unionLimit', limits)
this.$set(this, 'unionLimitVisible', true)
},
quickSetUnionLimit() {
this.formData.unionLimit.forEach((item) => this.$set(item, 'limitCount', Number(this.unionLimitQuickValue || 0)))
},
saveUnionLimit() {
if (Number(this.unionLimitTotal) > 100) {
this.$message.warning('分工会名额比例合计不能超过100%')
return
}
this.$set(this, 'unionLimitVisible', false)
},
async findOne() {
await this.getActivityGroup()
const {data} = await this.$axios.get(loc() + "/fetchOne")
if (data.outsideQuotaProportion) {
data.outsideQuotaProportion = data.outsideQuotaProportion * 1000 / 10
}
if (!data.lots) {
data.lots = []
}
this.formData = data
getActivityGroup() {
return this.$axios.post('/platform/activity/basic/scope/getActivityUserScopeGroup').then((res) => {
this.$set(this, 'activityGroupList', res.data || [])
return this.activityGroupList
})
},
async getActivityGroup() {
const {data} = await this.$axios.get('/platform/activity/basic/scope/getActivityUserScopeGroup')
this.activityGroupList = data
closeDialog() {
this.$set(this, 'tableScopeIndex', '');
this.$set(this, 'tableScopeId', '');
this.$set(this, 'centerDialogVisible', false);
this.$set(this, 'LineNameList', {});
},
async closeDialog() {
this.tableScopeIndex = '';
this.tableScopeId = '';
this.centerDialogVisible = false;
this.LineNameList = {};
},
async deleteLotById() {
deleteLotById() {
if (this.tableScopeId == null || this.tableScopeId === '') {
this.formData.lots.splice(this.tableScopeIndex, 1);
this.$message.success('操作成功')
return;
}
const resp = await this.$axios.get(loc() + '/deleteLotById?lotId=' + this.tableScopeId)
if (resp.code === 0) {
this.$message.success(resp.msg)
this.tableScopeId = '';
this.centerDialogVisible = false;
this.formData.lots.splice(this.tableScopeIndex, 1)
await this.findOne();
} else {
this.$message.warning(resp.msg)
}
this.$axios.post(loc() + '/deleteLotById', {lotId: this.tableScopeId}).then((resp) => {
if (resp.code === 0) {
this.$message.success(resp.msg)
this.$set(this, 'tableScopeId', '')
this.$set(this, 'centerDialogVisible', false)
this.formData.lots.splice(this.tableScopeIndex, 1)
this.findOne()
} else this.$message.warning(resp.msg)
})
},
},
async created() {
created() {
this.$businessTool.listUnion().then((res) => this.$set(this, 'unionOptions', res || []))
this.findOne();
},
watch: {
'formData.activityGroupId': {
async handler(newVal, oldVal) {
this.activityGroupList = await this.getActivityGroup()
this.userScopeDialog = false
handler(newVal, oldVal) {
this.getActivityGroup().then(() => { this.$set(this, 'userScopeDialog', false) })
},
deep: true
},
@@ -0,0 +1,43 @@
<!--# layout("/layouts/platform.html"){ #-->
<div id="app" v-cloak>
<el-card shadow="never">
<search @search="doSearch">
<search-item label="年度范围"><el-date-picker v-model="yearRange" type="yearrange" value-format="yyyy" range-separator="至" start-placeholder="开始年度" end-placeholder="结束年度" @change="yearChange"></el-date-picker></search-item>
<search-item label="灵活组团"><el-select v-model="pageForm.groupName" clearable filterable @change="doSearch"><el-option v-for="item in groupOptions" :key="item.id" :label="item.groupName" :value="item.groupName"></el-option></el-select></search-item>
<search-item label="旅行社"><el-select v-model="pageForm.travelAgencyId" clearable filterable @change="doSearch"><el-option v-for="item in agencyOptions" :key="item.id" :label="item.travelAgencyName" :value="item.id"></el-option></el-select></search-item>
<search-item label="姓名/工号"><el-input v-model="pageForm.searchKeyword" clearable @keyup.enter.native="doSearch"></el-input></search-item>
</search>
</el-card>
<el-card shadow="never" class="mt20">
<table-tool label="省内灵活组团查询"><template slot="right"><el-button type="success" size="small" @click="doExport">导出Excel</el-button></template></table-tool>
<el-table v-loading="tableLoading" :data="tableData" :size="tableSize">
<el-table-column type="index" :index="indexMethod" label="序号" width="60"></el-table-column><el-table-column prop="year" label="年度" width="90"></el-table-column><el-table-column prop="groupName" label="组团名称" min-width="180"></el-table-column><el-table-column prop="travelAgencyName" label="旅行社" min-width="180"></el-table-column><el-table-column prop="groupCount" label="团队数" width="90"></el-table-column><el-table-column prop="signCount" label="报名人数" width="100"></el-table-column><el-table-column label="操作" width="180"><template v-slot="{row}"><el-button size="mini" @click="openGroups(row)">查看团队</el-button><el-button type="primary" size="mini" @click="openAllUsers(row)">全部人员</el-button></template></el-table-column>
</el-table>
<!--#include("/layouts/pagination.html"){}#-->
</el-card>
<el-dialog title="团队列表" :visible.sync="groupVisible" width="900px" append-to-body>
<el-table v-loading="dialogLoading" :data="groups"><el-table-column prop="groupLeaderUserName" label="团长姓名"></el-table-column><el-table-column prop="groupLeaderLoginName" label="团长工号"></el-table-column><el-table-column prop="groupLeaderPassword" label="团口令"></el-table-column><el-table-column prop="userCount" label="人数" width="90"></el-table-column><el-table-column label="操作" width="100"><template v-slot="{row}"><el-button type="primary" size="mini" @click="openGroupUsers(row)">人员</el-button></template></el-table-column></el-table>
</el-dialog>
<el-dialog title="报名人员" :visible.sync="userVisible" width="1050px" append-to-body>
<el-table v-loading="userLoading" :data="users"><el-table-column type="index" label="序号" width="60"></el-table-column><el-table-column prop="loginName" label="工号" width="110"></el-table-column><el-table-column prop="userName" label="姓名" width="100"></el-table-column><el-table-column prop="identity" label="身份" width="80"></el-table-column><el-table-column prop="sex" label="性别" width="60"></el-table-column><el-table-column prop="unitName" label="单位" show-overflow-tooltip></el-table-column><el-table-column prop="unionName" label="工会" show-overflow-tooltip></el-table-column><el-table-column prop="mobile" label="联系电话" width="130"></el-table-column><el-table-column prop="isFamily" label="家属人数" width="90"></el-table-column><el-table-column label="操作" width="80"><template v-slot="{row}"><el-button type="danger" size="mini" @click="deleteUser(row)">删除</el-button></template></el-table-column></el-table>
<el-pagination class="mt20" background layout="total, sizes, prev, pager, next" :current-page="userPage.pageNumber" :page-size="userPage.pageSize" :total="userPage.totalCount" @current-change="userPageChange" @size-change="userSizeChange"></el-pagination>
</el-dialog>
</div>
<script nonce="${cspNonce!}">
new Vue({
el:'#app',mixins:[initTableMixins],data(){const year=new Date().getFullYear().toString();return{yearRange:[year,year],pageForm:{pageNumber:1,pageSize:10,totalCount:0,startYear:year,endYear:year,groupName:'',travelAgencyId:'',searchKeyword:''},agencyOptions:[],groupOptions:[],currentGroup:{},groups:[],users:[],groupVisible:false,userVisible:false,dialogLoading:false,userLoading:false,userPage:{pageNumber:1,pageSize:10,totalCount:0},userQuery:{}}},
methods:{
pageData(){this.$set(this,'tableLoading',true);this.$axios.post(loc()+'/pageData',this.pageForm).then((res)=>{if(res.code===0){this.$set(this,'tableData',res.data.list||[]);this.$set(this.pageForm,'totalCount',res.data.totalCount||0)}}).finally(()=>{this.$set(this,'tableLoading',false)})},
yearChange(value){this.$set(this.pageForm,'startYear',value&&value.length?value[0]:'');this.$set(this.pageForm,'endYear',value&&value.length?value[1]:'');this.loadOptions();this.doSearch()},
loadOptions(){const params={startYear:this.pageForm.startYear,endYear:this.pageForm.endYear};this.$axios.post(loc()+'/selectTravelAgencyList',params).then((res)=>{if(res.code===0)this.$set(this,'agencyOptions',res.data||[])});this.$axios.post(loc()+'/selectFlexibleGroupList',params).then((res)=>{if(res.code===0)this.$set(this,'groupOptions',res.data||[])})},
doExport(){window.open(loc()+'/doExport?'+$.param(this.pageForm))},
openGroups(row){this.$set(this,'currentGroup',row);this.$set(this,'groupVisible',true);this.$set(this,'dialogLoading',true);this.$axios.post(loc()+'/getGroupInfo',{groupId:row.id}).then((res)=>{if(res.code===0)this.$set(this,'groups',res.data||[])}).finally(()=>{this.$set(this,'dialogLoading',false)})},
openAllUsers(row){this.$set(this,'currentGroup',row);this.$set(this,'userQuery',{});this.$set(this.userPage,'pageNumber',1);this.$set(this,'userVisible',true);this.loadUsers()},
openGroupUsers(row){this.$set(this,'userQuery',{groupLeaderUserId:row.groupLeaderUserId,groupLeaderLoginName:row.groupLeaderLoginName,noGroupLeader:!row.groupLeaderUserId&&!row.groupLeaderLoginName});this.$set(this.userPage,'pageNumber',1);this.$set(this,'userVisible',true);this.loadUsers()},
loadUsers(){this.$set(this,'userLoading',true);const params=Object.assign({groupId:this.currentGroup.id,pageNumber:this.userPage.pageNumber,pageSize:this.userPage.pageSize},this.userQuery);this.$axios.post(loc()+'/getSignUser',params).then((res)=>{if(res.code===0){this.$set(this,'users',res.data.list||[]);this.$set(this.userPage,'totalCount',res.data.totalCount||0)}}).finally(()=>{this.$set(this,'userLoading',false)})},
userPageChange(value){this.$set(this.userPage,'pageNumber',value);this.loadUsers()},userSizeChange(value){this.$set(this.userPage,'pageSize',value);this.$set(this.userPage,'pageNumber',1);this.loadUsers()},
deleteUser(row){this.$confirm('确定删除该报名人员吗?','提示',{type:'warning'}).then(()=>{this.$axios.post(loc()+'/deleteJoinUser',{id:row.id}).then((res)=>{if(res.code===0){this.$message.success(res.msg);this.loadUsers();this.pageData()}else this.$message.warning(res.msg)})}).catch(()=>{})}
},created(){this.loadOptions();this.pageData()}
})
</script>
<!--# } #-->
@@ -1,9 +1,23 @@
<!--# layout("/layouts/platform.html"){ #-->
<div id="app" v-cloak>
<el-card shadow="never"><el-form inline><el-form-item label="年度"><el-date-picker v-model="year" type="year" value-format="yyyy" @change="loadOptions"></el-date-picker></el-form-item><el-form-item label="线路/旅行社"><el-select v-model="targetId" filterable placeholder="请选择"><el-option v-for="item in options" :key="item.id" :label="item.lineName||item.travelAgencyName" :value="item.id"></el-option></el-select></el-form-item><el-form-item><el-upload action="/platform/recuperation/joinUserImport/readExcel" name="file" :data="uploadData" :show-file-list="false" :on-success="onUploadSuccess" accept=".xls,.xlsx"><el-button type="primary">上传参加人员 Excel</el-button></el-upload></el-form-item><el-form-item><el-button @click="downloadTemplate">下载模板</el-button></el-form-item></el-form></el-card>
<el-card shadow="never" class="mt20"><table-tool label="匹配到的报名人员"><el-button type="primary" :loading="formLoading" @click="doImport">确认标记参加</el-button></table-tool><el-table :data="matchList"><el-table-column prop="loginName" label="工号"></el-table-column><el-table-column prop="userName" label="姓名"></el-table-column><el-table-column prop="takePartInTime" label="参加时间"></el-table-column><el-table-column prop="lineName" label="线路"></el-table-column></el-table></el-card>
<el-card shadow="never">
<div slot="header"><span>导入实际参加人员</span></div>
<el-steps :active="stepActive" finish-status="success"><el-step title="选择线路或旅行社"></el-step><el-step title="读取人员名单"></el-step><el-step title="确认导入数据"></el-step><el-step title="导入结果"></el-step></el-steps>
<div class="mt30" style="min-height:430px">
<template v-if="stepActive===0"><el-form label-width="100px"><el-form-item label="年度"><el-date-picker v-model="year" type="year" value-format="yyyy" @change="loadOptions"></el-date-picker></el-form-item><el-row :gutter="20"><el-col :span="12"><el-card shadow="never"><div slot="header">线路</div><el-radio-group v-model="lineId" @change="selectLine" style="display:block"><el-radio v-for="item in lines" :key="item.id" :label="item.id" border style="display:block;margin:8px 0">{{item.label}}</el-radio></el-radio-group></el-card></el-col><el-col :span="12"><el-card shadow="never"><div slot="header">旅行社</div><el-radio-group v-model="travelAgencyId" @change="selectAgency" style="display:block"><el-radio v-for="item in travels" :key="item.id" :label="item.id" border style="display:block;margin:8px 0">{{item.label}}</el-radio></el-radio-group></el-card></el-col></el-row></el-form></template>
<template v-if="stepActive===1"><el-button type="primary" class="mb20" @click="downloadTemplate">下载模板</el-button><el-upload ref="upload" drag :action="loc()+'/readExcel'" name="file" :auto-upload="false" :limit="1" :data="{lineId:lineId,travelAgencyId:travelAgencyId}" :on-success="uploadSuccess" :on-error="uploadError" :on-exceed="uploadExceed" accept=".xls,.xlsx"><i class="el-icon-upload"></i><div class="el-upload__text">将 Excel 文件拖到此处,或<em>点击上传</em></div><div slot="tip" class="el-upload__tip">只允许上传一个 xls/xlsx 文件</div></el-upload></template>
<template v-if="stepActive===2"><el-alert :title="'Excel共 '+excelRows.length+' 行,合法匹配 '+matchList.length+' 行'" type="warning" :closable="false" class="mb20"></el-alert><el-table :data="matchList"><el-table-column type="index" label="序号" width="60"></el-table-column><el-table-column prop="loginName" label="工号"></el-table-column><el-table-column prop="userName" label="姓名"></el-table-column><el-table-column prop="takePartInTime" label="参加时间"></el-table-column><el-table-column prop="lineName" label="线路"></el-table-column><el-table-column prop="travelAgencyName" label="旅行社"></el-table-column></el-table></template>
<template v-if="stepActive===3"><el-result icon="success" title="导入成功" :sub-title="'已标记实际参加 '+matchList.length+' 人'"></el-result></template>
</div>
<div style="text-align:center"><el-button :disabled="stepActive===0||loading" @click="prev">上一步</el-button><el-button type="primary" :loading="loading" @click="next">{{stepActive===3?'完成':'下一步'}}</el-button></div>
</el-card>
</div>
<script nonce="${cspNonce!}">
new Vue({el:"#app",data(){return {year:new Date().getFullYear(),targetId:null,options:[],matchList:[],formLoading:false}},computed:{uploadData(){return {lineId:this.targetId}}},methods:{loadOptions(){this.$axios.post(loc()+"/options",{year:this.year}).then((res)=>{if(res.code===0)this.$set(this,"options",res.data||[])})},onUploadSuccess(res){if(res.code===0){this.$set(this,"matchList",res.data.matchList||[]);this.$message.success("已匹配 "+this.matchList.length+" 人")}else this.$message.warning(res.msg)},doImport(){if(!this.matchList.length){this.$message.warning("暂无匹配人员");return}this.formLoading=true;this.$axios.post(loc()+"/doImport",{enrolls:JSON.stringify(this.matchList)}).then((res)=>{if(res.code===0){this.$message.success(res.msg);this.$set(this,"matchList",[])}}).finally(()=>{this.formLoading=false})},downloadTemplate(){window.open(loc()+"/downloadTemplate")}},created(){this.loadOptions()}})
new Vue({el:'#app',data(){return{loading:false,stepActive:0,year:new Date().getFullYear().toString(),lines:[],travels:[],lineId:'',travelAgencyId:'',excelRows:[],matchList:[]}},methods:{
loadOptions(){this.$axios.post(loc()+'/options',{year:this.year}).then((res)=>{if(res.code===0){this.$set(this,'lines',(res.data&&res.data.lines)||[]);this.$set(this,'travels',(res.data&&res.data.travels)||[])}})},selectLine(){this.$set(this,'travelAgencyId','')},selectAgency(){this.$set(this,'lineId','')},
uploadSuccess(res){this.$set(this,'loading',false);if(res.code===0){this.$set(this,'excelRows',res.data.excelRows||[]);this.$set(this,'matchList',res.data.matchList||[]);this.$set(this,'stepActive',2)}else{this.$message.warning(res.msg);this.$refs.upload.clearFiles()}},uploadError(){this.$set(this,'loading',false);this.$message.warning('文件上传失败')},uploadExceed(){this.$message.warning('最多只能选择一个文件')},
prev(){this.$set(this,'stepActive',this.stepActive-1)},next(){if(this.stepActive===0){if(!this.lineId&&!this.travelAgencyId){this.$message.warning('请先选择线路或者旅行社');return}this.$set(this,'stepActive',1);return}if(this.stepActive===1){if(!this.$refs.upload.uploadFiles.length){this.$message.warning('请先选择文件');return}this.$set(this,'loading',true);this.$refs.upload.submit();return}if(this.stepActive===2){if(!this.matchList.length){this.$message.warning('没有合法数据,无法导入');return}this.$set(this,'loading',true);this.$axios.post(loc()+'/doImport',{enrolls:JSON.stringify(this.matchList)}).then((res)=>{if(res.code===0){this.$message.success(res.msg);this.$set(this,'stepActive',3)}else this.$message.warning(res.msg)}).finally(()=>{this.$set(this,'loading',false)});return}this.reset()},
reset(){this.$set(this,'stepActive',0);this.$set(this,'lineId','');this.$set(this,'travelAgencyId','');this.$set(this,'excelRows',[]);this.$set(this,'matchList',[]);if(this.$refs.upload)this.$refs.upload.clearFiles()},downloadTemplate(){window.open(loc()+'/downloadTemplate')}
},created(){this.loadOptions()}})
</script>
<!--# } #-->
@@ -1,10 +1,54 @@
<!--# layout("/layouts/platform.html"){ #-->
<div id="app" v-cloak>
<el-card shadow="never"><search @search="doSearch"><search-item label="年度"><el-date-picker v-model="pageForm.year" type="year" value-format="yyyy" @change="doSearch"></el-date-picker></search-item><search-item label="关键字"><el-input v-model="pageForm.keywords" clearable></el-input></search-item></search></el-card>
<el-card shadow="never" class="mt20"><table-tool label="线路人员调整"></table-tool><el-table v-loading="tableLoading" :data="tableData" :size="tableSize"><el-table-column type="index" :index="indexMethod" label="序号" width="60"></el-table-column><el-table-column prop="serialNumber" label="线路编号"></el-table-column><el-table-column prop="lineName" label="线路名称" min-width="180"></el-table-column><el-table-column prop="selectUnionName" label="报名工会"></el-table-column><el-table-column prop="signUpUserNum" label="报名人数"></el-table-column><el-table-column label="操作" width="100"><template v-slot="{row}"><el-button type="primary" size="mini" @click="openUsers(row)">调整</el-button></template></el-table-column></el-table><!--#include("/layouts/pagination.html"){}#--></el-card>
<el-dialog title="报名人员调整" :visible.sync="usersVisible" width="900px" append-to-body><el-table :data="users" @selection-change="(rows)=>{selectedUsers=rows}"><el-table-column type="selection" width="50"></el-table-column><el-table-column prop="loginName" label="工号"></el-table-column><el-table-column prop="userName" label="姓名"></el-table-column><el-table-column prop="unitName" label="单位"></el-table-column><el-table-column prop="isNormal" label="状态"><template v-slot="{row}">{{row.isNormal?'正常':'已调出'}}</template></el-table-column></el-table><template slot="footer"><el-button @click="usersVisible=false">取消</el-button><el-button type="primary" :loading="formLoading" @click="toggleUsers">切换选中人员状态</el-button></template></el-dialog>
<el-card shadow="never">
<search @search="doSearch">
<search-item label="年度"><el-date-picker v-model="pageForm.year" type="year" value-format="yyyy" @change="yearChange"></el-date-picker></search-item>
<search-item label="线路"><el-select v-model="pageForm.lineId" filterable clearable @change="doSearch"><el-option v-for="item in lineOptions" :key="item.id" :label="item.lineName" :value="item.id"></el-option></el-select></search-item>
<search-item label="所属工会"><el-select v-model="pageForm.unionId" filterable clearable @change="doSearch"><el-option v-for="item in unionOptions" :key="item.id" :label="item.name" :value="item.id"></el-option></el-select></search-item>
<search-item label="关键字"><el-input v-model="pageForm.keywords" clearable @keyup.enter.native="doSearch"></el-input></search-item>
</search>
</el-card>
<el-card shadow="never" class="mt20">
<table-tool label="线路人员调整"></table-tool>
<el-table v-loading="tableLoading" :data="tableData" :size="tableSize">
<el-table-column type="index" :index="indexMethod" label="序号" width="60"></el-table-column>
<el-table-column prop="serialNumber" label="线路编号" width="110"></el-table-column>
<el-table-column prop="lineName" label="线路名称" min-width="180"></el-table-column>
<el-table-column prop="selectUnionName" label="报名工会"></el-table-column>
<el-table-column prop="playStartTime" label="出发日期" width="110"></el-table-column>
<el-table-column prop="signUpUserNum" label="报名人数" width="90"></el-table-column>
<el-table-column prop="signUpUserFamilyNum" label="家属人数" width="90"></el-table-column>
<el-table-column label="操作" width="100"><template v-slot="{row}"><el-button type="primary" size="mini" @click="openUsers(row)">调整</el-button></template></el-table-column>
</el-table>
<!--#include("/layouts/pagination.html"){}#-->
</el-card>
<el-dialog title="报名人员调整" :visible.sync="usersVisible" width="1050px" append-to-body>
<el-tabs v-model="userTab"><el-tab-pane label="正常报名人员" name="normal"></el-tab-pane><el-tab-pane label="已调出人员" name="removed"></el-tab-pane></el-tabs>
<div class="mb10">
<el-button v-if="userTab==='normal'" type="danger" size="small" :loading="formLoading" @click="setUsers(false)">调出所选人员</el-button>
<el-button v-else type="success" size="small" :loading="formLoading" @click="setUsers(true)">恢复所选人员</el-button>
<el-button v-if="userTab==='normal'" type="warning" size="small" :loading="smsLoading" @click="sendAlerts">发送未成团提醒</el-button>
</div>
<el-table :data="visibleUsers" @selection-change="selectionChange">
<el-table-column type="selection" width="50"></el-table-column><el-table-column prop="loginName" label="工号" width="110"></el-table-column><el-table-column prop="userName" label="姓名" width="100"></el-table-column><el-table-column prop="sex" label="性别" width="60"></el-table-column><el-table-column prop="unitName" label="单位" show-overflow-tooltip></el-table-column><el-table-column prop="unionName" label="工会" show-overflow-tooltip></el-table-column><el-table-column prop="companionCount" label="家属人数" width="90"></el-table-column><el-table-column prop="signingUptime" label="报名时间" width="170"></el-table-column>
</el-table>
<template slot="footer"><el-button @click="usersVisible=false">关闭</el-button></template>
</el-dialog>
</div>
<script nonce="${cspNonce!}">
new Vue({el:"#app",mixins:[initTableMixins],data(){return {pageForm:{pageNumber:1,pageSize:10,totalCount:0,year:new Date().getFullYear(),keywords:null},usersVisible:false,formLoading:false,currentRow:{},users:[],selectedUsers:[]}},methods:{pageData(){this.tableLoading=true;this.$axios.post(loc()+"/pageData",this.pageForm).then((res)=>{if(res.code===0){this.$set(this,"tableData",res.data.list||[]);this.$set(this.pageForm,"totalCount",res.data.totalCount||0)}}).finally(()=>{this.tableLoading=false})},openUsers(row){this.$set(this,"currentRow",row);this.$axios.post(loc()+"/findUsers",{lineId:row.usId,unionId:row.unionId}).then((res)=>{if(res.code===0){this.$set(this,"users",res.data||[]);this.$set(this,"usersVisible",true)}})},toggleUsers(){const names=this.selectedUsers.map((item)=>item.loginName);if(!names.length){this.$message.warning("请选择人员");return}this.formLoading=true;this.$axios.post(loc()+"/adjustmentUsers",{lineId:this.currentRow.usId,loginNames:names}).then((res)=>{if(res.code===0){this.$message.success(res.msg);this.openUsers(this.currentRow);this.pageData()}}).finally(()=>{this.formLoading=false})}},created(){this.pageData()}})
new Vue({
el:'#app',mixins:[initTableMixins],
data(){return{pageForm:{pageNumber:1,pageSize:10,totalCount:0,year:new Date().getFullYear().toString(),lineId:'',unionId:'',keywords:''},lineOptions:[],unionOptions:[],usersVisible:false,formLoading:false,smsLoading:false,currentRow:{},users:[],selectedUsers:[],userTab:'normal'}},
computed:{visibleUsers(){return this.users.filter((item)=>this.userTab==='normal'?item.isNormal:!item.isNormal)}},
methods:{
pageData(){this.$set(this,'tableLoading',true);this.$axios.post(loc()+'/pageData',this.pageForm).then((res)=>{if(res.code===0){this.$set(this,'tableData',res.data.list||[]);this.$set(this.pageForm,'totalCount',res.data.totalCount||0)}}).finally(()=>{this.$set(this,'tableLoading',false)})},
loadOptions(){this.$axios.post(loc()+'/findLineOptions',{year:this.pageForm.year}).then((res)=>{if(res.code===0)this.$set(this,'lineOptions',res.data||[])})},
yearChange(){this.$set(this.pageForm,'lineId','');this.loadOptions();this.doSearch()},
openUsers(row){this.$set(this,'currentRow',row);this.$axios.post(loc()+'/findUsers',{lineId:row.usId,unionId:row.unionId}).then((res)=>{if(res.code===0){this.$set(this,'users',res.data||[]);this.$set(this,'selectedUsers',[]);this.$set(this,'userTab','normal');this.$set(this,'usersVisible',true)}})},
selectionChange(rows){this.$set(this,'selectedUsers',rows)},selectedNames(){return this.selectedUsers.map((item)=>item.loginName)},
setUsers(normal){const names=this.selectedNames();if(!names.length){this.$message.warning('请选择人员');return}this.$set(this,'formLoading',true);this.$axios.post(loc()+'/adjustmentUsers',{lineId:this.currentRow.usId,loginNames:names,normal:normal}).then((res)=>{if(res.code===0){this.$message.success(normal?'人员已恢复':'人员已调出');this.openUsers(this.currentRow);this.pageData()}else this.$message.warning(res.msg)}).finally(()=>{this.$set(this,'formLoading',false)})},
sendAlerts(){const names=this.selectedNames();if(!names.length){this.$message.warning('请选择需要提醒的人员');return}this.$set(this,'smsLoading',true);this.$axios.post(loc()+'/smsAlerts',{lineId:this.currentRow.usId,loginNames:names}).then((res)=>{if(res.code===0)this.$message.success(res.msg);else this.$message.warning(res.msg)}).finally(()=>{this.$set(this,'smsLoading',false)})}
},created(){this.$businessTool.listUnion().then((res)=>{this.$set(this,'unionOptions',res||[])});this.loadOptions();this.pageData()}
})
</script>
<!--# } #-->
@@ -1,15 +1,35 @@
<!--# layout("/layouts/platform.html"){ #-->
<div id="app" v-cloak>
<el-card shadow="never"><search @search="doSearch"><search-item label="年度"><el-date-picker v-model="pageForm.year" type="year" value-format="yyyy" @change="doSearch"></el-date-picker></search-item><search-item label="线路"><el-input v-model="pageForm.keywords" clearable></el-input></search-item></search></el-card>
<el-card shadow="never"><search @search="doSearch"><search-item label="年度"><el-date-picker v-model="pageForm.year" type="year" value-format="yyyy" @change="doSearch"></el-date-picker></search-item><search-item label="所属工会"><el-select v-model="pageForm.unionId" filterable clearable @change="doSearch"><el-option v-for="item in unionOptions" :key="item.id" :label="item.name" :value="item.id"></el-option></el-select></search-item><search-item label="线路"><el-input v-model="pageForm.keywords" clearable @keyup.enter.native="doSearch"></el-input></search-item></search></el-card>
<el-card shadow="never" class="mt20"><table-tool label="线路组团"></table-tool>
<el-table v-loading="tableLoading" :data="tableData" :size="tableSize">
<el-table-column type="index" :index="indexMethod" label="序号" width="60"></el-table-column><el-table-column prop="serialNumber" label="线路编号"></el-table-column><el-table-column prop="lineName" label="线路名称" min-width="180"></el-table-column><el-table-column prop="ascriptionUnionName" label="工会"></el-table-column><el-table-column prop="signUpUserNum" label="报名人数"></el-table-column>
<el-table-column label="操作" width="100"><template v-slot="{row}"><el-button size="mini" type="primary" @click="openCluster(row)">组团</el-button></template></el-table-column>
<el-table-column type="index" :index="indexMethod" label="序号" width="60"></el-table-column><el-table-column prop="serialNumber" label="线路编号" width="110"></el-table-column><el-table-column prop="lineName" label="线路名称" min-width="180"></el-table-column><el-table-column prop="ascriptionUnionName" label="线路归属"></el-table-column><el-table-column prop="travelAgencyName" label="旅行社"></el-table-column><el-table-column prop="signUpUserNum" label="报名人数" width="90"></el-table-column><el-table-column prop="signUpUserFamilyNum" label="家属人数" width="90"></el-table-column>
<el-table-column label="操作" width="150"><template v-slot="{row}"><el-button size="mini" @click="openView(row)">查看</el-button><el-button size="mini" type="primary" @click="openCluster(row)">组团</el-button></template></el-table-column>
</el-table><!--#include("/layouts/pagination.html"){}#-->
</el-card>
<el-dialog title="组团人员" :visible.sync="clusterVisible" width="900px" append-to-body><el-input type="textarea" :rows="18" v-model="clustersJson" placeholder="按旧版格式编辑组团 JSON"></el-input><template slot="footer"><el-button @click="clusterVisible=false">取消</el-button><el-button type="primary" :loading="formLoading" @click="saveCluster">保存</el-button></template></el-dialog>
<el-dialog title="线路组团" :visible.sync="clusterVisible" width="95%" top="30px" append-to-body>
<div class="mb10"><el-button type="primary" size="small" @click="addCluster">添加团</el-button><span class="ml20 text-danger">人员可通过“移动到”分配到团;每团只能设置一名团长。</span></div>
<el-row :gutter="16">
<el-col :span="8"><el-card shadow="never"><div slot="header">未分配人员({{unSelectedUsers.length}}<el-button class="float-right" size="mini" type="primary" @click="batchVisible=true">批量调整</el-button></div><el-table ref="unSelectedRef" :data="unSelectedUsers" height="430" @selection-change="unSelectedChange"><el-table-column type="selection" width="45"></el-table-column><el-table-column prop="loginName" label="工号" width="95"></el-table-column><el-table-column prop="userName" label="姓名"></el-table-column><el-table-column prop="unionName" label="工会" show-overflow-tooltip></el-table-column><el-table-column label="移动到" width="105"><template v-slot="{row}"><el-select size="mini" placeholder="选择团" @change="moveMember(row,$event,'unSelectedUsers')"><el-option v-for="item in clusters" :key="item.key" :label="item.clusterName" :value="item.key"></el-option></el-select></template></el-table-column></el-table></el-card></el-col>
<el-col :span="8" v-for="cluster in clusters" :key="cluster.key"><el-card shadow="never"><div slot="header"><el-input v-model="cluster.clusterName" size="mini" maxlength="10" style="width:150px"></el-input><el-button class="float-right" size="mini" type="danger" @click="removeCluster(cluster)">删除团</el-button></div><el-table :data="cluster.members" height="430"><el-table-column prop="loginName" label="工号" width="95"></el-table-column><el-table-column prop="userName" label="姓名"></el-table-column><el-table-column prop="companionCount" label="家属" width="55"></el-table-column><el-table-column label="角色" width="85"><template v-slot="{row}"><el-select v-model="row.isLeader" size="mini" @change="leaderChange(cluster,row)"><el-option label="成员" :value="false"></el-option><el-option label="团长" :value="true"></el-option></el-select></template></el-table-column><el-table-column label="移动" width="105"><template v-slot="{row}"><el-select size="mini" @change="moveMember(row,$event,cluster.key)"><el-option label="未分配" value="unSelectedUsers"></el-option><el-option v-for="item in clusters" v-if="item.key!==cluster.key" :key="item.key" :label="item.clusterName" :value="item.key"></el-option></el-select></template></el-table-column></el-table></el-card></el-col>
</el-row>
<template slot="footer"><el-button @click="clusterVisible=false">取消</el-button><el-button type="primary" :loading="formLoading" @click="saveCluster">提交</el-button></template>
</el-dialog>
<el-dialog title="批量调整" :visible.sync="batchVisible" width="460px" append-to-body><el-form label-width="90px"><el-form-item label="已选人数">{{unSelectedSelection.length}}</el-form-item><el-form-item label="目标团"><el-select v-model="batchTarget" style="width:100%"><el-option v-for="item in clusters" :key="item.key" :label="item.clusterName" :value="item.key"></el-option></el-select></el-form-item></el-form><template slot="footer"><el-button @click="batchVisible=false">取消</el-button><el-button type="primary" @click="batchMove">确定</el-button></template></el-dialog>
<el-dialog title="团列表" :visible.sync="viewVisible" width="800px" append-to-body><el-tabs v-model="viewClusterId"><el-tab-pane v-for="item in viewClusters" :key="item.key" :name="item.key" :label="item.clusterName+''+item.members.length+''"><el-table :data="item.members"><el-table-column prop="loginName" label="工号"></el-table-column><el-table-column prop="userName" label="姓名"></el-table-column><el-table-column prop="unionName" label="工会"></el-table-column><el-table-column label="角色"><template v-slot="{row}">{{row.isLeader?'团长':'成员'}}</template></el-table-column></el-table></el-tab-pane></el-tabs></el-dialog>
</div>
<script nonce="${cspNonce!}">
new Vue({el:"#app",mixins:[initTableMixins],data(){return {pageForm:{pageNumber:1,pageSize:10,totalCount:0,year:new Date().getFullYear(),keywords:null},clusterVisible:false,formLoading:false,currentRow:{},clustersJson:"[]"}},methods:{pageData(){this.tableLoading=true;this.$axios.post(loc()+"/pageData",this.pageForm).then((res)=>{if(res.code===0){this.$set(this,"tableData",res.data.list||[]);this.$set(this.pageForm,"totalCount",res.data.totalCount||0)}}).finally(()=>{this.tableLoading=false})},openCluster(row){this.$set(this,"currentRow",row);this.$axios.post(loc()+"/findClusterInfo",{lineId:row.id,usUnionId:row.ascriptionUnionId}).then((res)=>{if(res.code===0){this.$set(this,"clustersJson",JSON.stringify(res.data,null,2));this.$set(this,"clusterVisible",true)}})},saveCluster(){this.formLoading=true;this.$axios.post(loc()+"/setClusterMembers",{lineId:this.currentRow.id,clusters:this.clustersJson}).then((res)=>{if(res.code===0){this.$message.success(res.msg);this.$set(this,"clusterVisible",false);this.pageData()}}).finally(()=>{this.formLoading=false})}},created(){this.pageData()}})
new Vue({el:'#app',mixins:[initTableMixins],data(){return{pageForm:{pageNumber:1,pageSize:10,totalCount:0,year:new Date().getFullYear().toString(),unionId:'',keywords:''},unionOptions:[],clusterVisible:false,viewVisible:false,batchVisible:false,formLoading:false,currentRow:{},clusters:[],unSelectedUsers:[],unSelectedSelection:[],batchTarget:'',viewClusters:[],viewClusterId:''}},methods:{
pageData(){this.$set(this,'tableLoading',true);this.$axios.post(loc()+'/pageData',this.pageForm).then((res)=>{if(res.code===0){this.$set(this,'tableData',res.data.list||[]);this.$set(this.pageForm,'totalCount',res.data.totalCount||0)}}).finally(()=>{this.$set(this,'tableLoading',false)})},
loadCluster(row,viewOnly){return this.$axios.post(loc()+'/findClusterInfo',{lineId:row.id,usUnionId:row.ascriptionUnionId}).then((res)=>{if(res.code!==0){this.$message.warning(res.msg);return}const data=res.data||{};const list=[];Object.keys(data).forEach((key)=>{if(key!=='unSelectedUsers'){const item=data[key];this.$set(item,'key',key);if(!item.members)item.members=[];list.push(item)}});if(viewOnly){this.$set(this,'viewClusters',list);this.$set(this,'viewClusterId',list.length?list[0].key:'');this.$set(this,'viewVisible',true)}else{this.$set(this,'unSelectedUsers',(data.unSelectedUsers&&data.unSelectedUsers.members)||[]);this.$set(this,'clusters',list);this.$set(this,'clusterVisible',true)}})},
openCluster(row){this.$set(this,'currentRow',row);this.loadCluster(row,false)},openView(row){this.loadCluster(row,true)},
addCluster(){const key='cluster'+new Date().getTime();this.clusters.push({key:key,lineId:this.currentRow.id,clusterName:'团'+(this.clusters.length+1),members:[]})},
removeCluster(cluster){this.$set(this,'unSelectedUsers',this.unSelectedUsers.concat(cluster.members||[]));this.$set(this,'clusters',this.clusters.filter((item)=>item.key!==cluster.key))},
findGroup(key){return key==='unSelectedUsers'?this.unSelectedUsers:(this.clusters.find((item)=>item.key===key)||{members:[]}).members},
moveMember(row,target,source){const sourceMembers=this.findGroup(source);const index=sourceMembers.findIndex((item)=>item.loginName===row.loginName);if(index!==-1)sourceMembers.splice(index,1);this.$set(row,'isLeader',false);this.findGroup(target).push(row)},
leaderChange(cluster,row){if(row.isLeader)cluster.members.forEach((item)=>{if(item.loginName!==row.loginName)this.$set(item,'isLeader',false)})},
unSelectedChange(rows){this.$set(this,'unSelectedSelection',rows)},batchMove(){if(!this.batchTarget||!this.unSelectedSelection.length){this.$message.warning('请选择人员和目标团');return}this.unSelectedSelection.slice().forEach((row)=>this.moveMember(row,this.batchTarget,'unSelectedUsers'));this.$set(this,'batchVisible',false);this.$set(this,'batchTarget','')},
saveCluster(){const empty=this.clusters.find((item)=>!item.members.length);if(empty){this.$message.warning(empty.clusterName+'未分配成员');return}this.$set(this,'formLoading',true);const data=this.clusters.map((item)=>({id:item.id,lineId:this.currentRow.id,clusterName:item.clusterName,members:item.members}));this.$axios.post(loc()+'/setClusterMembers',{lineId:this.currentRow.id,clusters:JSON.stringify(data)}).then((res)=>{if(res.code===0){this.$message.success(res.msg);this.$set(this,'clusterVisible',false);this.pageData()}else this.$message.warning(res.msg)}).finally(()=>{this.$set(this,'formLoading',false)})}
},created(){this.$businessTool.listUnion().then((res)=>{this.$set(this,'unionOptions',res||[])});this.pageData()}})
</script>
<!--# } #-->
@@ -183,34 +183,15 @@ const batchSelect = {
}
this.setLineTimeDialog = true
},
async onSelect() {
const valid = await this.$refs['form'].validate()
if (!valid) return
const confirm = await this.$confirm('请再次确认,是否为选择线路统赋时间?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
onSelect() {
this.$refs['form'].validate((valid) => {
if (!valid) return
this.$confirm('请再次确认,是否为选择线路统赋时间?', '提示', {confirmButtonText:'确定',cancelButtonText:'取消',type:'warning'}).then(() => {
const lineIds=this.multipleSelection.map((item)=>item.id)
const fmtData=this.formData.times.map((item)=>Object.assign({},item,{signUpMode:this.formData.signUpMode,mode:GetQueryString('mode')}))
return this.$axios.post(loc() + '/setGiveLineTimes', {lineIds:JSON.stringify(lineIds),lineUnionSelects:JSON.stringify(fmtData),year:this.year})
}).then((resp)=>{if(resp.code===0){this.$set(this,'setLineTimeDialog',false);this.$message.success(resp.msg)}else this.$message.warning(resp.msg)}).catch(()=>{})
})
if (confirm !== 'confirm') return
const lineIds = this.multipleSelection.map(v => v.id)
const fmtData = this.formData.times.map(v => {
return {
...v,
signUpMode: this.formData.signUpMode,
mode: GetQueryString('mode')
}
})
const resp = await this.$axios.post(loc() + '/setGiveLineTimes', {
lineIds: JSON.stringify(lineIds),
lineUnionSelects: JSON.stringify(fmtData),
year: this.year
})
if (resp.code === 0) {
this.setLineTimeDialog = false
this.$message.success(resp.msg)
} else {
this.$message.warning(resp.msg)
}
},
},
async created() {
@@ -236,21 +236,15 @@ layout("/layouts/platform.html"){
}
},
methods: {
async cancelSelect({applyCount, id: lineId, usUnionId: unionId}) {
cancelSelect({applyCount, id: lineId, usUnionId: unionId}) {
const msg = applyCount > 0 ? '已经有教工选择本线路,请确认是否取消选择本线路,一旦取消将清空报名人员!!!' : '您确定要取消吗?'
const confirm = await this.$confirm(msg, '提示', {
this.$confirm(msg, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
if (confirm !== 'confirm') return
const resp = await this.$axios.post('/platform/recuperation/lineSelect/deSelect', {lineId, unionId})
if (resp.code === 0) {
this.doSearch()
this.$message.success(resp.msg)
} else {
this.$message.warning(resp.msg)
}
}).then(() => this.$axios.post('/platform/recuperation/lineSelect/deSelect', {lineId, unionId}))
.then((resp) => { if (resp.code === 0) { this.doSearch(); this.$message.success(resp.msg) } else this.$message.warning(resp.msg) })
.catch(() => {})
},
refresh() {
this.doSearch()
@@ -284,25 +278,23 @@ layout("/layouts/platform.html"){
}
})
},
async getTravelAgencyOptions() {
const {data} = await this.$axios.post('/platform/recuperation/lineSelect/getTravelAgencyOptions')
this.travelAgencyOptions = data
getTravelAgencyOptions() {
return this.$axios.post('/platform/recuperation/lineSelect/getTravelAgencyOptions').then((res) => this.$set(this, 'travelAgencyOptions', res.data || []))
},
async getEnumOptions(enumName) {
const resp = await this.$axios.post("/open/common/dictEnumOptions", { name: enumName })
return resp.data
getEnumOptions(enumName) {
return this.$axios.post("/open/common/dictEnumOptions", { name: enumName }).then((resp) => resp.data || [])
},
},
async created() {
created() {
this.$set(this.pageForm, 'mode', Number(`${mode}`))
if(this.pageForm.mode === 2) {
this.$set(this.pageForm, 'regionalNature', '全部')
}
this.unionList = await this.$businessTool.listUnion()
this.createModeList = await this.getEnumOptions('RecuperationLineCreateMode')
this.signUpModeList = await this.getEnumOptions('RecuperationSignUpMode')
this.regionalNatureList.push(...await this.getEnumOptions('RecuperationProvinceType'))
this.travelAgencyOptions = this.getTravelAgencyOptions()
this.$businessTool.listUnion().then((res) => this.$set(this, 'unionList', res || []))
this.getEnumOptions('RecuperationLineCreateMode').then((res) => this.$set(this, 'createModeList', res))
this.getEnumOptions('RecuperationSignUpMode').then((res) => this.$set(this, 'signUpModeList', res))
this.getEnumOptions('RecuperationProvinceType').then((res) => this.$set(this, 'regionalNatureList', this.regionalNatureList.concat(res)))
this.getTravelAgencyOptions()
this.pageData()
}
})
@@ -253,22 +253,20 @@ const select = {
doSetChangeTime(index, value){
this.$set(this.formData.times[index], "changeEndTime", value)
},
async getLineConfig(lineId) {
const {data} = await this.$axios.post(loc() + '/getLineConfig/' + lineId)
this.lineConfig = data
getLineConfig(lineId) {
return this.$axios.post(loc() + '/getLineConfig/' + lineId).then((res) => this.$set(this, 'lineConfig', res.data || {}))
},
async onOpen(id, lineName, usUnionId, signUpMode, year) {
await this.getLineConfig(id)
const resp = await this.$axios.post(loc() + '/selectLineInfo', {
onOpen(id, lineName, usUnionId, signUpMode, year) {
this.getLineConfig(id).then(() => this.$axios.post(loc() + '/selectLineInfo', {
lineId: id,
unionId: usUnionId,
mode: GetQueryString('mode'),
year: year
})
if (resp.code === 0 && resp.data) {
this.formData = {
})).then((resp) => {
if (resp.code === 0 && resp.data) {
this.$set(this, 'formData', {
times: []
}
})
if (resp.data && resp.data.length > 0) {
this.$set(this.formData, 'times', resp.data)
} else {
@@ -278,42 +276,26 @@ const select = {
estimatedCost: this.lineConfig.cost
})
}
this.formData.lineId = id
this.formData.signUpMode = signUpMode
this.$set(this.formData, 'lineId', id)
this.$set(this.formData, 'signUpMode', signUpMode)
this.$set(this.formData, 'lineName', lineName)
this.setLineTimeDialog = true
this.$set(this, 'setLineTimeDialog', true)
} else {
this.$message.warning(resp.msg)
}
})
},
async onSelect() {
const valid = await this.$refs['form'].validate()
if (!valid) return
const confirm = await this.$confirm('请再次确认,是否选择此线路为疗休养线路?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
onSelect() {
this.$refs['form'].validate((valid) => {
if (!valid) return
this.$confirm('请再次确认,是否选择此线路为疗休养线路?', '提示', {confirmButtonText:'确定',cancelButtonText:'取消',type:'warning'}).then(() => {
const fmtData=this.formData.times.map((item)=>Object.assign({},item,{lineId:this.formData.lineId,signUpMode:this.formData.signUpMode,mode:GetQueryString('mode')}))
return this.$axios.post(loc() + '/selectLineTimes', {lineSelects: JSON.stringify(fmtData)})
}).then((resp) => { if (resp.code === 0) { this.$set(this,'setLineTimeDialog',false);this.$emit('refresh');this.$message.success(resp.msg) } else this.$message.warning(resp.msg) }).catch(()=>{})
})
if (confirm !== 'confirm') return
const fmtData = this.formData.times.map(v => {
return {
...v,
lineId: this.formData.lineId,
signUpMode: this.formData.signUpMode,
mode: GetQueryString('mode')
}
})
const resp = await this.$axios.post(loc() + '/selectLineTimes', {lineSelects: JSON.stringify(fmtData)})
if (resp.code === 0) {
this.setLineTimeDialog = false
this.$emit('refresh')
this.$message.success(resp.msg)
} else {
this.$message.warning(resp.msg)
}
},
},
async created() {
created() {
},
style: /*language=CSS*/ `
@@ -76,19 +76,16 @@ const timeInfo = {
}
},
methods: {
async onOpen(id, usUnionId, year) {
const resp = await this.$axios.post(loc() + '/selectLineInfo', {
onOpen(id, usUnionId, year) {
this.$axios.post(loc() + '/selectLineInfo', {
lineId: id,
unionId: usUnionId,
mode: GetQueryString('mode'),
year: year
}).then((resp) => {
if (resp.code === 0) { this.$set(this, 'timeLots', resp.data); this.$set(this, 'timeLotsDialogVisible', true) }
else this.$message.warning(resp.msg)
})
if (resp.code === 0) {
this.timeLots = resp.data
this.timeLotsDialogVisible = true
} else {
this.$message.warning(resp.msg)
}
},
},
style: /*language=CSS*/ `
@@ -0,0 +1,26 @@
<!--# layout("/layouts/platform.html"){ #-->
<div id="app" v-cloak>
<el-card shadow="never"><search @search="doSearch">
<search-item label="年度范围"><el-date-picker v-model="yearRange" type="yearrange" value-format="yyyy" range-separator="至" @change="yearChange"></el-date-picker></search-item>
<search-item label="所属工会"><el-select v-model="pageForm.unionId" filterable clearable @change="doSearch"><el-option v-for="item in unionOptions" :key="item.id" :label="item.name" :value="item.id"></el-option></el-select></search-item>
<search-item label="线路类型"><el-select v-model="pageForm.regionalNature" clearable @change="filterChange"><el-option label="省内" value="省内"></el-option><el-option label="省外" value="省外"></el-option></el-select></search-item>
<search-item label="组织方式"><el-select v-model="pageForm.signUpMode" clearable @change="filterChange"><el-option label="分工会组织" value="1"></el-option><el-option label="校工会组织" value="2"></el-option></el-select></search-item>
<search-item label="线路"><el-select v-model="pageForm.takePartInLineId" filterable clearable @change="doSearch"><el-option v-for="item in lineOptions" :key="item.id" :label="item.lineName+'-'+item.regionalNature+'【'+(item.lotName||'')+'】('+item.signUpMode+''" :value="item.id"></el-option></el-select></search-item>
<search-item label="标段"><el-select v-model="pageForm.lotId" filterable clearable @change="doSearch"><el-option v-for="item in lots" :key="item.id" :label="item.lotName" :value="item.id"></el-option></el-select></search-item>
</search></el-card>
<el-card shadow="never" class="mt20"><table-tool label="线路报名统计"></table-tool>
<el-table v-loading="tableLoading" :data="tableData" :size="tableSize"><el-table-column type="index" :index="indexMethod" label="序号" width="60"></el-table-column><el-table-column prop="year" label="年度" width="70"></el-table-column><el-table-column prop="lineName" label="线路名称" min-width="180"></el-table-column><el-table-column prop="linePlayTime" label="出行时间" width="150"></el-table-column><el-table-column prop="travelAgencyName" label="承担旅行社" min-width="150"></el-table-column><el-table-column prop="unionName" label="选择线路工会"></el-table-column><el-table-column prop="regionalNature" label="线路类型" width="90"></el-table-column><el-table-column prop="minimumGroupSize" label="最少成团人数" width="110"></el-table-column><el-table-column label="报名人数(家属)" width="130"><template v-slot="{row}"><el-link type="primary" @click="openUsers(row)">{{Number(row.lineNum||0)+Number(row.signUpUserFamilyNum||0)}}{{row.signUpUserFamilyNum||0}}</el-link></template></el-table-column><el-table-column label="操作" width="300"><template v-slot="{row}"><el-button size="mini" @click="openUsers(row)">查看人员</el-button><el-button type="success" size="mini" @click="notice(row,true,true)">成团通知</el-button><el-dropdown class="ml10"><el-button type="warning" size="mini">未成团通知<i class="el-icon-arrow-down el-icon--right"></i></el-button><el-dropdown-menu slot="dropdown"><el-dropdown-item @click.native="notice(row,false,true)">保留报名记录</el-dropdown-item><el-dropdown-item @click.native="notice(row,false,false)">删除报名记录</el-dropdown-item></el-dropdown-menu></el-dropdown></template></el-table-column></el-table>
<!--#include("/layouts/pagination.html"){}#-->
</el-card>
<el-dialog title="线路报名人员" :visible.sync="userVisible" width="1050px" append-to-body><div class="mb10"><el-input v-model="userPage.searchKeyword" placeholder="姓名或工号" clearable style="width:220px" @keyup.enter.native="loadUsers"></el-input><el-button type="primary" class="ml10" @click="loadUsers">查询</el-button></div><el-table v-loading="userLoading" :data="users"><el-table-column type="index" label="序号" width="60"></el-table-column><el-table-column prop="loginName" label="工号"></el-table-column><el-table-column prop="userName" label="姓名"></el-table-column><el-table-column prop="unionName" label="所属工会"></el-table-column><el-table-column prop="unitName" label="所属单位"></el-table-column><el-table-column prop="familyCount" label="家属人数" width="90"></el-table-column><el-table-column prop="linePlayTime" label="出行时间"></el-table-column></el-table><el-pagination class="mt20" background layout="total, sizes, prev, pager, next" :current-page="userPage.pageNumber" :page-size="userPage.pageSize" :total="userPage.totalCount" @current-change="userPageChange" @size-change="userSizeChange"></el-pagination></el-dialog>
</div>
<script nonce="${cspNonce!}">
new Vue({el:'#app',mixins:[initTableMixins],data(){const year=new Date().getFullYear().toString();return{yearRange:[year,year],pageForm:{pageNumber:1,pageSize:10,totalCount:0,startYear:year,endYear:year,unionId:'',regionalNature:'',signUpMode:'',takePartInLineId:'',lotId:'',selectId:''},unionOptions:[],lineOptions:[],lots:[],userVisible:false,userLoading:false,currentRow:{},users:[],userPage:{pageNumber:1,pageSize:10,totalCount:0,searchKeyword:'',unionId:'',unitId:''}}},methods:{
pageData(){this.$set(this,'tableLoading',true);this.$axios.post(loc()+'/pageData',this.pageForm).then((res)=>{if(res.code===0){this.$set(this,'tableData',res.data.list||[]);this.$set(this.pageForm,'totalCount',res.data.totalCount||0)}}).finally(()=>{this.$set(this,'tableLoading',false)})},
yearChange(value){this.$set(this.pageForm,'startYear',value[0]);this.$set(this.pageForm,'endYear',value[1]);this.filterChange()},filterChange(){this.$set(this.pageForm,'takePartInLineId','');this.loadLines();this.doSearch()},
loadLines(){this.$axios.post(loc()+'/getLineOptions',{startYear:this.pageForm.startYear,endYear:this.pageForm.endYear,signUpMode:this.pageForm.signUpMode,regionalNature:this.pageForm.regionalNature}).then((res)=>{if(res.code===0)this.$set(this,'lineOptions',res.data||[])})},
openUsers(row){this.$set(this,'currentRow',row);this.$set(this.userPage,'pageNumber',1);this.$set(this,'userVisible',true);this.loadUsers()},loadUsers(){this.$set(this,'userLoading',true);const params=Object.assign({},this.userPage,{takePartLineId:this.currentRow.lineUId});this.$axios.post(loc()+'/getUserDateByLine',params).then((res)=>{if(res.code===0){this.$set(this,'users',res.data.list||[]);this.$set(this.userPage,'totalCount',res.data.totalCount||0)}}).finally(()=>{this.$set(this,'userLoading',false)})},userPageChange(value){this.$set(this.userPage,'pageNumber',value);this.loadUsers()},userSizeChange(value){this.$set(this.userPage,'pageSize',value);this.$set(this.userPage,'pageNumber',1);this.loadUsers()},
notice(row,success,keep){const countUrl=success?'/countSuccessNotice':'/countFailNotice';this.$axios.post(loc()+countUrl,{id:row.lineUId}).then((res)=>{if(res.code!==0){this.$message.warning(res.msg);return}const data=res.data||{};if(!data.sendCount){this.$message.warning(data.msg);return}this.$prompt('当前将通过'+data.sendTypeName+'发送'+data.sendCount+'人,请确认消息内容。','提示',{inputType:'textarea',inputValue:data.content||'',inputValidator:(value)=>!!(value&&value.trim()),inputErrorMessage:'消息内容不能为空'}).then((prompt)=>{const url=success?'/sendSuccess':'/sendFail';this.$axios.post(loc()+url,{id:row.lineUId,type:keep,content:prompt.value}).then((sendRes)=>{if(sendRes.code===0){const result=sendRes.data||{};this.$message.success(result.msg||'通知发送完成');this.pageData()}else this.$message.warning(sendRes.msg)})}).catch(()=>{})})}
},created(){this.$businessTool.listUnion().then((res)=>{this.$set(this,'unionOptions',res||[])});this.$axios.post('/platform/recuperation/config/fetchOne').then((res)=>{if(res.code===0)this.$set(this,'lots',(res.data&&res.data.lots)||[])});this.loadLines();this.pageData()}})
</script>
<!--# } #-->
@@ -121,48 +121,36 @@ const editForm = {
}
},
methods: {
async onOpen(id, year) {
onOpen(id, year) {
this.year = year
this.visible = true
await this.getUnionSelectLine()
await this.getModifyConfig()
const {code, msg, data} = await this.$axios.post("/platform/recuperation/schoolUnionUserQuery/findOne", {id})
if (code === 0) {
this.formData = data
if (this.config.familyInfo === 2) {
this.labelName = "家属信息"
} else {
this.formData.bedType = data.familyNumber
this.labelName = "家属数量"
}
} else {
this.$message.error(msg)
}
Promise.all([this.getUnionSelectLine(), this.getModifyConfig()]).then(() => {
this.$axios.post(queryBase + "/findOne", {id}).then((res) => {
if (res.code === 0) {
this.formData = res.data
if (this.config.familyInfo === 2) this.labelName = "家属信息"
else { this.$set(this.formData, "bedType", res.data.familyNumber); this.labelName = "家属数量" }
} else this.$message.error(res.msg)
})
})
},
async getModifyConfig() {
const {code, data, msg} = await this.$axios.post('/platform/recuperation/config/fetchOne')
if (code === 0) {
this.config = data
} else {
this.$message.error(msg)
}
getModifyConfig() {
return this.$axios.post('/platform/recuperation/config/fetchOne').then((res) => {
if (res.code === 0) this.config = res.data
else this.$message.error(res.msg)
})
},
async getUnionSelectLine() {
const resp = await this.$axios.post('/platform/recuperation/schoolUnionUserQuery/getUnionSelectLine', {
getUnionSelectLine() {
return this.$axios.post(queryBase + '/getUnionSelectLine', {
year: this.year,
signUpMode: 1,
})
if (resp.code === 0) {
this.unionSelectLines = resp.data
}
}).then((resp) => { if (resp.code === 0) this.unionSelectLines = resp.data })
},
async validateLine() {
const resp = await this.$axios.post('/platform/recuperation/line/enroll/validSignUpInfo'
, {enroll: JSON.stringify(this.formData)})
if (resp.code !== 0) {
this.$message.warning(resp.msg)
this.formData.takePartInLineId = ''
}
validateLine() {
this.$axios.post('/platform/recuperation/line/enroll/validSignUpInfo',
{enroll: JSON.stringify(this.formData)}).then((resp) => {
if (resp.code !== 0) { this.$message.warning(resp.msg); this.$set(this.formData, 'takePartInLineId', '') }
})
},
onSubmit() {
this.$refs.formRef.validate((valid) => {
@@ -171,19 +159,15 @@ const editForm = {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
}).then(() => {
if (!this.formData.takePartInLineId && !this.formData.takePartInBaseManagementId) {
this.$message.warning('请选择线路')
return
}
const {code,msg} = await this.$axios.post('/platform/recuperation/schoolUnionUserQuery/doEdit', this.formData)
if (code === 0) {
this.$message.success(msg)
this.visible = false
this.$emit('refresh')
} else {
this.$message.error(msg)
}
this.$axios.post(queryBase + '/doEdit', this.formData).then((res) => {
if (res.code === 0) { this.$message.success(res.msg); this.visible = false; this.$emit('refresh') }
else this.$message.error(res.msg)
})
}).catch()
}
})
@@ -71,26 +71,51 @@ layout("/layouts/platform.html"){
<el-card shadow="never" class="filter-container">
<el-form :model="pageForm" ref="pageFormRef" label-width="90px" size="medium">
<div class="form-row">
<el-form-item label="年度">
<el-form-item label="开始年度">
<el-date-picker
v-model="pageForm.year"
v-model="pageForm.startYear"
type="year"
placeholder="选择年度"
placeholder="选择开始年度"
value-format="yyyy"
style="width: 100%">
</el-date-picker>
</el-form-item>
<el-form-item label="姓名">
<el-input v-model="pageForm.userName" placeholder="请输入姓名" clearable
prefix-icon="el-icon-user"></el-input>
<el-form-item label="结束年度">
<el-date-picker v-model="pageForm.endYear" type="year" placeholder="选择结束年度"
value-format="yyyy" style="width: 100%"></el-date-picker>
</el-form-item>
<el-form-item label="工号">
<el-input v-model="pageForm.loginName" placeholder="请输入工号" clearable
prefix-icon="el-icon-postcard"></el-input>
<el-form-item label="人员查询">
<el-input v-model="pageForm.searchKeyword" placeholder="请输入姓名或工号" clearable>
<el-select v-model="pageForm.searchName" slot="prepend" style="width: 90px">
<el-option label="姓名" value="userName"></el-option>
<el-option label="工号" value="loginName"></el-option>
</el-select>
</el-input>
</el-form-item>
</div>
<div class="route-line">
<div class="route-line-title">报名状态</div>
<div class="route-line-content">
<el-radio-group v-model="pageForm.signUpStatus" size="small" @change="changeSignUpStatus">
<el-radio-button :label="1">已报名</el-radio-button>
<el-radio-button :label="0">未报名</el-radio-button>
<el-radio-button :label="2">未成团</el-radio-button>
</el-radio-group>
<el-radio-group v-if="pageForm.signUpStatus===1" v-model="pageForm.isTakePartIn"
size="small" class="ml20" @change="doSearch">
<el-radio-button label="">全部</el-radio-button>
<el-radio-button :label="1">已参加</el-radio-button>
<el-radio-button :label="0">未参加</el-radio-button>
</el-radio-group>
<el-radio-group v-if="pageForm.signUpStatus===2" v-model="pageForm.unFormedType"
size="small" class="ml20" @change="doSearch">
<el-radio-button label="line">线路</el-radio-button>
<el-radio-button label="flexible">灵活组团</el-radio-button>
</el-radio-group>
</div>
</div>
<div class="form-row">
<el-form-item label="分工会">
<el-form-item v-if="schoolMode" label="分工会">
<el-select v-model="pageForm.unionId"
@change="doSearch"
placeholder="请选择所属工会"
@@ -106,6 +131,33 @@ layout("/layouts/platform.html"){
</el-select>
</el-form-item>
</div>
<div class="route-line" v-if="pageForm.signUpStatus===1">
<div class="route-line-title">报名类型</div>
<div class="route-line-content">
<el-radio-group v-model="pageForm.state" size="small" @change="changeState">
<el-radio-button :label="1">线路({{categoryCount.xlCount||0}}</el-radio-button>
<el-radio-button :label="2">灵活组团({{categoryCount.lxsCount||0}}</el-radio-button>
<el-radio-button :label="3">定点({{categoryCount.jdCount||0}}</el-radio-button>
</el-radio-group>
</div>
</div>
<div class="form-row" v-if="pageForm.signUpStatus===1">
<el-form-item label="报名时间">
<el-date-picker v-model="signTime" type="daterange" range-separator="至"
start-placeholder="开始日期" end-placeholder="结束日期"
value-format="yyyy-MM-dd" style="width:100%" @change="changeSignTime"></el-date-picker>
</el-form-item>
<el-form-item v-if="pageForm.state===2" label="旅行社">
<el-select v-model="pageForm.agencyId" filterable clearable style="width:100%" @change="doSearch">
<el-option v-for="item in agencyOptions" :key="item.id" :label="item.travelAgencyName" :value="item.id"></el-option>
</el-select>
</el-form-item>
<el-form-item v-if="pageForm.state===3" label="定点">
<el-select v-model="pageForm.takePartInBaseManagementId" filterable clearable style="width:100%" @change="doSearch">
<el-option v-for="item in baseOptions" :key="item.id" :label="item.baseName" :value="item.id"></el-option>
</el-select>
</el-form-item>
</div>
<div class="route-line">
<div class="route-line-title">区域</div>
<div class="route-line-content">
@@ -150,13 +202,17 @@ layout("/layouts/platform.html"){
</el-card>
<el-card shadow="never" class="mt10">
<table-tool label="人员列表" ref="table_tool">
<el-button icon="el-icon-s-promotion" size="small" type="primary" @click="noSignExport">
<el-button icon="el-icon-message" size="small" type="primary" @click="openRemind">一键提醒</el-button>
<el-button v-if="schoolMode&&pageForm.signUpStatus===1" icon="el-icon-upload2" size="small" type="primary"
@click="openImport">参加人员导入</el-button>
<el-button v-if="schoolMode&&pageForm.signUpStatus===1" icon="el-icon-folder-opened" size="small" type="primary" @click="exportZip">导出Zip</el-button>
<el-button v-if="schoolMode" icon="el-icon-s-promotion" size="small" type="primary" @click="noSignExport">
导出未报名人员
</el-button>
<el-button icon="el-icon-s-promotion" size="small" type="primary" @click="doExport">
导出
</el-button>
<el-button icon="el-icon-s-promotion" size="small" type="primary"
<el-button v-if="pageForm.signUpStatus===1" icon="el-icon-s-promotion" size="small" type="primary"
@click="openSetUpPart"
>设置参加人员
</el-button>
@@ -210,11 +266,17 @@ layout("/layouts/platform.html"){
{{row.lotName}}
</template>
</el-table-column>
<el-table-column label="操作" width="250px">
<el-table-column v-if="pageForm.signUpStatus===1" label="操作" width="250px">
<template v-slot="{row}">
<el-button @click="openView(row)" size="mini" type="primary">查看</el-button>
<el-button @click="openEdit(row)" size="mini" type="primary">编辑</el-button>
<el-button @click="onDelete(row)" size="mini" type="danger">删除</el-button>
<el-dropdown class="ml10" trigger="click">
<el-button size="mini" type="primary">调整<i class="el-icon-arrow-down el-icon--right"></i></el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="restoreEnroll(row)">保留报名记录</el-dropdown-item>
<el-dropdown-item @click.native="onDelete(row)">删除报名记录</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
</el-table-column>
</el-table>
@@ -227,11 +289,32 @@ layout("/layouts/platform.html"){
<enroll-info ref="enrollInfoRef"></enroll-info>
</el-dialog>
<set-up-part ref="setUpRef"></set-up-part>
<set-up-part ref="setUpRef" @refresh="doSearch"></set-up-part>
<edit-form ref="editRef" @refresh="doSearch"></edit-form>
<el-dialog title="发送提醒" :visible.sync="remindVisible" width="560px">
<div class="mb10">将按当前筛选条件提醒人员,请确认提醒内容。</div>
<el-input v-model="remindContent" type="textarea" :rows="5"></el-input>
<template slot="footer">
<el-button @click="remindVisible=false">取消</el-button>
<el-button type="primary" :loading="remindLoading" @click="sendRemind">发送</el-button>
</template>
</el-dialog>
<el-dialog title="参加人员导入" :visible.sync="importVisible" width="700px" append-to-body>
<el-button type="primary" size="small" class="mb20" @click="downloadImportTemplate">下载导入模板</el-button>
<el-upload ref="importUpload" drag :action="queryBase+'/enrollImport'" name="file" :auto-upload="false" :limit="1" :on-success="importSuccess" :on-error="importError" accept=".xls,.xlsx">
<i class="el-icon-upload"></i><div class="el-upload__text">将 Excel 文件拖到此处,或<em>点击选择</em></div><div slot="tip" class="el-upload__tip">按参加时间所属年度匹配省外线路报名</div>
</el-upload>
<el-alert v-if="importResult.totalCount" class="mt20" :closable="false" type="info" :title="'总记录 '+importResult.totalCount+',成功 '+importResult.successCount+',失败 '+importResult.errorCount"></el-alert>
<el-button v-if="importResult.errorCount" type="text" @click="importErrorVisible=true">查看错误记录</el-button>
<template slot="footer"><el-button @click="importVisible=false">关闭</el-button><el-button type="primary" :loading="importLoading" @click="submitImport">开始导入</el-button></template>
</el-dialog>
<el-dialog title="导入错误记录" :visible.sync="importErrorVisible" width="900px" append-to-body><el-table :data="importResult.errorList||[]"><el-table-column prop="no" label="Excel行" width="80"></el-table-column><el-table-column prop="loginName" label="工号"></el-table-column><el-table-column prop="userName" label="姓名"></el-table-column><el-table-column prop="takePartInTime" label="参加时间"></el-table-column><el-table-column prop="lotName" label="标段时间"></el-table-column><el-table-column prop="errors" label="失败原因" min-width="260"></el-table-column></el-table></el-dialog>
</div>
<script nonce="${cspNonce!}">
const queryBase = "${queryBase!'/platform/recuperation/schoolUnionUserQuery'}"
const schoolMode = ${schoolMode!true}
<!--#include('setUpPart.js'){}#-->
<!--#include('editForm.js'){}#-->
<!--#include('../line/info.js'){}#-->
@@ -248,12 +331,20 @@ layout("/layouts/platform.html"){
data() {
return {
pageForm: {
year: new Date().getFullYear().toString(),
loginName: '',
userName: '',
startYear: new Date().getFullYear().toString(),
endYear: new Date().getFullYear().toString(),
searchName: 'userName',
searchKeyword: '',
signUpStatus: 1,
state: 1,
isTakePartIn: '',
unFormedType: 'line',
takePartInLineId: '',
unionId: '',
signUpMode: 1,
agencyId: '',
takePartInBaseManagementId: '',
// 校工会旧版默认查询全部组织形式;分工会仍默认查询分工会组织线路。
signUpMode: schoolMode ? '' : 1,
lotId: '',
regionalNature: ''
},
@@ -270,28 +361,49 @@ layout("/layouts/platform.html"){
],
viewVisible: false,
unionOptions: [],
config: {}
config: {},
categoryCount: {},
agencyOptions: [],
baseOptions: [],
signTime: [],
remindVisible: false,
remindLoading: false,
remindContent: '',
schoolMode: schoolMode,
queryBase: queryBase,
importVisible: false,
importLoading: false,
importErrorVisible: false,
importResult: {totalCount:0,successCount:0,errorCount:0,errorList:[]}
}
},
methods: {
resetQuery() {
this.pageForm = {
year: new Date().getFullYear().toString(),
loginName: '',
userName: '',
this.$set(this, 'pageForm', {
startYear: new Date().getFullYear().toString(),
endYear: new Date().getFullYear().toString(),
searchName: 'userName',
searchKeyword: '',
signUpStatus: 1,
state: 1,
isTakePartIn: '',
unFormedType: 'line',
takePartInLineId: '',
unionId: '',
signUpMode: 1,
agencyId: '',
takePartInBaseManagementId: '',
// 重置后保持与页面初始查询口径一致,避免校工会被隐藏条件 signUpMode=1 限制。
signUpMode: schoolMode ? '' : 1,
lotId: '',
regionalNature: '',
pageNumber: 1,
pageSize: 10,
totalCount: 0,
}
})
this.doSearch()
},
openView(row) {
this.viewVisible = true
this.$set(this, 'viewVisible', true)
this.$nextTick(() => {
this.$refs.enrollInfoRef.onOpen(row.id)
})
@@ -305,23 +417,27 @@ layout("/layouts/platform.html"){
cancelButtonText: '取消',
dangerouslyUseHTMLString: true,
type: 'warning'
}).then(async () => {
const {code, msg} = await this.$axios.post('/platform/recuperation/schoolUnionUserQuery/deleteMyEnrollInfoById', {
}).then(() => {
this.$axios.post(queryBase + '/deleteMyEnrollInfoById', {
id: row.id
}).then((res) => {
if (res.code === 0) {
this.doSearch()
this.$message.success(res.msg)
} else {
this.$message.warning(res.msg)
}
})
if (code === 0) {
this.doSearch()
this.$message.success(msg)
} else {
this.$message.warning(msg)
}
})
},
noSignExport() {
this.$downLoad('/platform/recuperation/schoolUnionUserQuery/noSignExport')
this.$downLoad(queryBase + '/noSignExport')
},
doExport() {
this.$downLoad('/platform/recuperation/schoolUnionUserQuery/exportXlsx', this.pageForm)
this.$downLoad(queryBase + '/exportManagement', this.pageForm)
},
exportZip() {
this.$downLoad(queryBase + '/exportZip', this.pageForm)
},
openSetUpPart() {
const selection = this.$refs.tableRef.selection
@@ -335,33 +451,135 @@ layout("/layouts/platform.html"){
getConfig() {
this.$axios.post('/platform/recuperation/config/fetchOne').then((res) => {
if (res.code === 0) {
this.config = res.data
const config = res.data || {}
const lots = (config.lots || []).filter((item, index, list) => {
// 标段 ID 是业务主键,同一 ID 只保留首次出现的数据,避免 Vue 生成重复 key 和重复选项。
return item && item.id && list.findIndex((lot) => lot && lot.id === item.id) === index
})
this.$set(config, 'lots', lots)
this.$set(this, 'config', config)
}
})
}).catch(() => { this.$message.error('疗休养配置接口请求失败') })
},
getLines() {
this.$axios.post('/platform/recuperation/schoolUnionUserQuery/listLine', {
year: this.pageForm.year,
if (this.pageForm.state !== 1) {
return
}
this.$axios.post(queryBase + '/listLine', {
year: this.pageForm.startYear,
unionId: this.pageForm.unionId,
signUpMode: this.pageForm.signUpMode,
regionalNature: this.pageForm.regionalNature
}).then(res => {
if (res.code === 0) {
this.takePartInLines = res.data
this.$set(this, 'takePartInLines', res.data)
} else {
this.$message.warning(res.msg)
}
}).catch(() => { this.$message.error('线路选项接口请求失败') })
},
changeSignUpStatus() {
this.$set(this.pageForm, 'pageNumber', 1)
this.doSearch()
},
changeState() {
this.$set(this.pageForm, 'takePartInLineId', '')
this.$set(this.pageForm, 'agencyId', '')
this.$set(this.pageForm, 'takePartInBaseManagementId', '')
this.doSearch()
},
changeSignTime(value) {
this.$set(this.pageForm, 'signStartTime', value && value.length ? value[0] : '')
this.$set(this.pageForm, 'signEndTime', value && value.length ? value[1] + ' 23:59:59' : '')
this.doSearch()
},
getCategoryCount() {
this.$axios.post(queryBase + '/getCategoryCount', this.pageForm).then((res) => {
if (res.code === 0) this.$set(this, 'categoryCount', res.data || {})
}).catch(() => { this.$message.error('报名分类统计接口请求失败') })
},
getManagementOptions() {
if (this.pageForm.state === 2) {
this.$axios.post('/platform/recuperation/travelAgency/selectTravelAgencyByYears', {
startYear: this.pageForm.startYear,
endYear: this.pageForm.endYear
}).then((res) => { if (res.code === 0) this.$set(this, 'agencyOptions', res.data || []) })
}
if (this.pageForm.state === 3) {
this.$axios.post('/platform/recuperation/baseManagement/listAllBase', this.pageForm).then((res) => {
if (res.code === 0) this.$set(this, 'baseOptions', res.data || [])
})
}
},
restoreEnroll(row) {
this.$axios.post(queryBase + '/restoreEnroll', {id: row.id}).then((res) => {
if (res.code === 0) { this.$message.success(res.msg); this.doSearch() }
})
},
openImport() {
this.$set(this, 'importResult', {totalCount:0,successCount:0,errorCount:0,errorList:[]})
this.$set(this, 'importVisible', true)
this.$nextTick(() => { if (this.$refs.importUpload) this.$refs.importUpload.clearFiles() })
},
downloadImportTemplate() {
this.$downLoad(queryBase + '/downloadImportTemplate')
},
submitImport() {
if (!this.$refs.importUpload || !this.$refs.importUpload.uploadFiles.length) { this.$message.warning('请选择Excel文件'); return }
this.$set(this, 'importLoading', true)
this.$refs.importUpload.submit()
},
importSuccess(res) {
this.$set(this, 'importLoading', false)
if (res.code === 0) { this.$set(this, 'importResult', res.data || {}); this.$message.success(res.msg); this.doSearch() }
else { this.$message.warning(res.msg); this.$refs.importUpload.clearFiles() }
},
importError() {
this.$set(this, 'importLoading', false)
this.$message.warning('文件上传失败')
},
openRemind() {
this.$set(this, 'remindContent', this.config.remindContent || '')
this.$set(this, 'remindVisible', true)
},
sendRemind() {
this.$set(this, 'remindLoading', true)
this.$axios.post(queryBase + '/remindUsers', Object.assign({}, this.pageForm, {
content: this.remindContent,
loginNames: this.$refs.tableRef.selection.map((item) => item.loginName).join(',')
})).then((res) => {
if (res.code === 0) { this.$message.success(res.msg); this.$set(this, 'remindVisible', false) }
else this.$message.warning(res.msg)
}).finally(() => { this.$set(this, 'remindLoading', false) })
},
doSearch(){
this.getLines()
this.tableKey = new Date().getTime()
this.pageForm.pageNumber = 1
this.getManagementOptions()
this.getCategoryCount()
this.$set(this, 'tableKey', new Date().getTime())
this.$set(this.pageForm, 'pageNumber', 1)
this.pageData()
},
// 共用模板必须使用 Controller 下发的接口前缀,避免 Pjax 场景下 loc() 取到外层页面地址。
pageData() {
this.$set(this, 'tableLoading', true)
this.$axios.post(queryBase + '/pageData', this.pageForm).then((res) => {
if (res.code === 0) {
this.$set(this, 'tableData', res.data.list || [])
this.$set(this.pageForm, 'totalCount', res.data.totalCount || 0)
} else {
this.$message.warning(res.msg)
}
}).catch(() => {
this.$message.error('人员分页接口请求失败')
}).finally(() => {
this.$set(this, 'tableLoading', false)
})
}
},
async created() {
this.$businessTool.listUnion().then((res) => (this.unionOptions = res))
created() {
this.$businessTool.listUnion().then((res) => this.$set(this, 'unionOptions', res || []))
.catch(() => { this.$message.error('工会选项接口请求失败') })
this.doSearch()
this.getConfig()
}
@@ -61,11 +61,10 @@ const setUpPart = {
this.tableData = JSON.parse(JSON.stringify(selection))
this.getModifyConfig()
},
async getModifyConfig() {
const res = await this.$axios.post('/platform/recuperation/config/fetchOne')
if (res.code === 0) {
this.config = res.data
}
getModifyConfig() {
this.$axios.post('/platform/recuperation/config/fetchOne').then((res) => {
if (res.code === 0) this.config = res.data
})
},
// 标段改变
lotChange(val) {
@@ -122,18 +121,15 @@ const setUpPart = {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async res => {
const {
code,
msg
} = await this.$axios.post("/platform/recuperation/schoolUnionUserQuery/setUpParticipants", {data: JSON.stringify(data)})
if (code === 0) {
this.$message.success(msg)
this.$refs.tableRef.clearSelection()
this.visible = false
} else {
this.$message.error(msg)
}
}).then(() => {
this.$axios.post(queryBase + "/setUpParticipants", {data: JSON.stringify(data)}).then((res) => {
if (res.code === 0) {
this.$message.success(res.msg)
this.$refs.tableRef.clearSelection()
this.visible = false
this.$emit('refresh')
} else this.$message.error(res.msg)
})
})
}
}
@@ -1,187 +1,19 @@
<!--#
layout("/layouts/platform.html"){
#-->
<style>
</style>
<!--# layout("/layouts/platform.html"){ #-->
<div id="app" v-cloak>
<guava ref="guava">
<template>
<el-card shadow="never">
<search @search="doSearch">
<search-item label="年度">
<el-date-picker
placeholder="请选择年度"
type="year"
@change="yearChange"
style="width: 100%"
v-model="pageForm.year"
value-format="yyyy"
></el-date-picker>
</search-item>
<search-item label="线路">
<el-select @change="doSearch" filterable
placeholder="请选择线路"
style="width: 100%;" clearable
v-model="pageForm.lineId">
<el-option :label="item.lineName + '' + item.unionName + ''" :value="item.id"
:key="value.id"
v-for="item in lineList"></el-option>
</el-select>
</search-item>
<search-item label="姓名/工号">
<el-input placeholder="请输入姓名或工号查询" clearable v-model="pageForm.searchKeyword"
@keyup.enter.native="doSearch"></el-input>
</search-item>
<search-item label="所属工会">
<el-select v-model="pageForm.unionId" placeholder="请选择所属工会" filterable clearable @change="doSearch">
<el-option v-for="item in unionList" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
</search-item>
<search-item label="所属单位">
<el-select v-model="pageForm.unitId" placeholder="请选择所属单位" filterable clearable @change="doSearch">
<el-option v-for="item in unitList" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
</search-item>
<search-item label="评分:">
<el-select clearable filterable placeholder="请选择评分"
style="width: 100%" @change="doSearch"
v-model="pageForm.evaluateScore">
<el-option
:key="item.code"
:label="item.name"
:value="item.code"
v-for="item in evaluateScores">
</el-option>
</el-select>
</search-item>
</search>
</el-card>
<el-card shadow="never" class="mt20">
<table-tool label="评价名单">
<el-button type="primary" size="small" @click="exportEvaluate">
<i class="el-icon-download"></i>
导出名单
</el-button>
</table-tool>
<el-table :data="tableData" @sort-change="pageOrder" :size="tableSize">
<el-table-column label="序号" type="index" :index="indexMethod" width="60"></el-table-column>
<el-table-column
:label="column.label"
:prop="column.prop"
:key="column.prop"
:width="column.width"
:sortable="column.sortable"
align="center"
header-align="center"
show-overflow-tooltip
v-for="column in tableColumns"
>
</el-table-column>
<el-table-column label="操作" width="100">
<template v-slot="{ row }">
<el-button @click="onView(row)" size="mini" type="primary">查看</el-button>
</template>
</el-table-column>
</el-table>
<!--#include("/layouts/pagination.html"){}#-->
</el-card>
</template>
</guava>
<el-dialog
:close-on-click-modal="false"
:visible.sync="evaluateDialogVisible"
title="评分内容"
width="30%">
<el-form :model="formData" label-width="60px" ref="form">
<el-form-item :rules="[{ required: true, message: ''}]" label="评分">
<span>{{viewData.evaluateScore}}</span>
</el-form-item>
<el-form-item label="评价">
<span>{{viewData.evaluateText}}</span>
</el-form-item>
</el-form>
<span class="dialog-footer" slot="footer">
<el-button @click="evaluateDialogVisible = false" type="primary">关 闭</el-button>
</span>
</el-dialog>
<el-card shadow="never"><el-row type="flex" align="middle"><el-col :span="8"><span>统计年度:</span><el-date-picker v-model="pageForm.year" type="year" value-format="yyyy" :clearable="false" @change="yearChange"></el-date-picker></el-col><el-col :span="16"><el-tabs v-model="activeTab" @tab-click="tabChange"><el-tab-pane label="满意度统计" name="summary"></el-tab-pane><el-tab-pane label="评价建议明细" name="feedback"></el-tab-pane><el-tab-pane label="评价人员名单" name="users"></el-tab-pane></el-tabs></el-col></el-row></el-card>
<template v-if="activeTab==='summary'">
<el-row :gutter="20" class="mt20"><el-col :span="8"><el-card shadow="never"><div slot="header">旅行社综合满意度</div><el-table v-loading="summaryLoading" :data="agencyRatings" height="530"><el-table-column type="index" label="排名" width="65"></el-table-column><el-table-column prop="travelAgencyName" label="旅行社" show-overflow-tooltip></el-table-column><el-table-column prop="avg" label="综合评分" width="90"><template v-slot="{row}">{{score(row.avg)}}</template></el-table-column><el-table-column prop="ratingCount" label="评价人数" width="85"></el-table-column></el-table></el-card></el-col><el-col :span="16"><el-card shadow="never"><div slot="header">方案服务评分</div><el-table v-loading="summaryLoading" :data="schemeRatings" height="530"><el-table-column prop="typeName" label="类型" width="85"></el-table-column><el-table-column prop="schemeName" label="方案名称" min-width="150" show-overflow-tooltip></el-table-column><el-table-column prop="travelAgencyName" label="旅行社" min-width="130" show-overflow-tooltip></el-table-column><el-table-column label="旅行社" width="75"><template v-slot="{row}">{{score(row.travelAgencyAvg)}}</template></el-table-column><el-table-column label="行程" width="65"><template v-slot="{row}">{{score(row.journeyAvg)}}</template></el-table-column><el-table-column label="住宿/酒店" width="90"><template v-slot="{row}">{{score(row.accommodationAvg)}}</template></el-table-column><el-table-column label="餐饮" width="65"><template v-slot="{row}">{{score(row.diningAvg)}}</template></el-table-column><el-table-column label="交通" width="65"><template v-slot="{row}">{{score(row.transportationAvg)}}</template></el-table-column><el-table-column label="综合" width="65"><template v-slot="{row}"><strong>{{score(row.compositeAvg)}}</strong></template></el-table-column><el-table-column prop="ratingCount" label="人数" width="65"></el-table-column></el-table></el-card></el-col></el-row>
</template>
<template v-if="activeTab==='feedback'"><el-card shadow="never" class="mt20"><table-tool label="评价建议"><el-select v-model="feedbackType" size="small" style="width:140px" @change="loadFeedback"><el-option v-for="item in ['全部','线路','灵活组团','定点']" :key="item" :label="item" :value="item"></el-option></el-select><el-button type="primary" size="small" class="ml10" @click="exportFeedback">导出评价明细</el-button></table-tool><el-table v-loading="feedbackLoading" :data="feedbackDetails"><el-table-column type="index" label="序号" width="60"></el-table-column><el-table-column prop="typeName" label="报名方式" width="100"></el-table-column><el-table-column prop="schemeName" label="方案名称" min-width="160"></el-table-column><el-table-column prop="travelAgencyName" label="旅行社" min-width="140"></el-table-column><el-table-column prop="userName" label="评价人" width="100"></el-table-column><el-table-column prop="unitName" label="所属单位" min-width="140"></el-table-column><el-table-column prop="compositeScore" label="综合评分" width="100"></el-table-column><el-table-column prop="feedbackContent" label="评价建议" min-width="240" show-overflow-tooltip></el-table-column><el-table-column label="操作" width="80"><template v-slot="{row}"><el-button type="primary" size="mini" @click="viewFeedback(row)">查看</el-button></template></el-table-column></el-table></el-card></template>
<template v-if="activeTab==='users'"><el-card shadow="never" class="mt20"><search @search="doSearch"><search-item label="线路"><el-select v-model="pageForm.lineId" filterable clearable @change="doSearch"><el-option v-for="item in lineList" :key="item.id" :label="item.lineName+''+item.unionName+''" :value="item.id"></el-option></el-select></search-item><search-item label="姓名/工号"><el-input v-model="pageForm.searchKeyword" clearable @keyup.enter.native="doSearch"></el-input></search-item><search-item label="所属工会"><el-select v-model="pageForm.unionId" filterable clearable @change="doSearch"><el-option v-for="item in unionList" :key="item.id" :label="item.name" :value="item.id"></el-option></el-select></search-item><search-item label="所属单位"><el-select v-model="pageForm.unitId" filterable clearable @change="doSearch"><el-option v-for="item in unitList" :key="item.id" :label="item.name" :value="item.id"></el-option></el-select></search-item><search-item label="评分"><el-select v-model="pageForm.evaluateScore" clearable @change="doSearch"><el-option v-for="item in ['满意','一般','不满意']" :key="item" :label="item" :value="item"></el-option></el-select></search-item></search><table-tool label="评价人员名单"><el-button type="primary" size="small" @click="exportEvaluate">导出名单</el-button></table-tool><el-table v-loading="tableLoading" :data="tableData"><el-table-column type="index" :index="indexMethod" label="序号" width="60"></el-table-column><el-table-column prop="lineName" label="线路"></el-table-column><el-table-column prop="playStartTime" label="出行时间"></el-table-column><el-table-column prop="loginName" label="工号"></el-table-column><el-table-column prop="userName" label="姓名"></el-table-column><el-table-column prop="unitName" label="所属单位"></el-table-column><el-table-column prop="unionName" label="所属工会"></el-table-column><el-table-column prop="evaluateScore" label="评分"></el-table-column><el-table-column prop="evaluateText" label="评价" show-overflow-tooltip></el-table-column></el-table><!--#include("/layouts/pagination.html"){}#--></el-card></template>
<el-dialog title="评价详情" :visible.sync="feedbackVisible" width="650px" append-to-body><el-descriptions :column="2" border><el-descriptions-item label="报名方式">{{feedbackDetail.typeName}}</el-descriptions-item><el-descriptions-item label="综合评分">{{feedbackDetail.compositeScore}}</el-descriptions-item><el-descriptions-item label="旅行社评分">{{feedbackDetail.evaluationForTravelAgency}}</el-descriptions-item><el-descriptions-item label="住宿/酒店评分">{{feedbackDetail.evaluationForAccommodation}}</el-descriptions-item><el-descriptions-item label="行程评分">{{feedbackDetail.evaluationForJourney}}</el-descriptions-item><el-descriptions-item label="餐饮评分">{{feedbackDetail.evaluationForDining}}</el-descriptions-item><el-descriptions-item label="交通评分">{{feedbackDetail.evaluationForTransportation}}</el-descriptions-item><el-descriptions-item label="方案">{{feedbackDetail.schemeName}}</el-descriptions-item></el-descriptions><el-alert class="mt20" :closable="false" :title="feedbackDetail.feedbackContent||'暂无评价建议'" type="info"></el-alert></el-dialog>
</div>
<script nonce="${cspNonce!}">
new Vue({
el: "#app",
mixins: [initTableMixins],
components: {},
data() {
return {
unionList: [],
unitList: [],
lineList: [],
evaluateDialogVisible: false,
tableColumns: [
{prop: 'lineName', label: '线路'},
{prop: 'playStartTime', label: '出行时间'},
{prop: 'loginName', label: '工号'},
{prop: 'userName', label: '姓名'},
{prop: 'unitName', label: '所属单位'},
{prop: 'unionName', label: '所属工会'},
{prop: 'evaluateScore', label: '评分', sortable: true},
{prop: 'evaluateText', label: '评价'},
],
viewData: {},
evaluateScores: [
{name: "满意", code: "满意"},
{name: "一般", code: "一般"},
{name: "不满意", code: "不满意"},
],
pageForm: {
year: new Date().getFullYear().toString(),
},
}
},
methods: {
exportEvaluate() {
this.$downLoad(loc() + '/exportEvaluate', this.pageForm)
},
onView(row) {
this.viewData = {
lineId: row.id,
evaluateText: row.evaluateText,
evaluateScore: row.evaluateScore,
}
this.evaluateDialogVisible = true
},
async getLineList() {
const resp = await this.$axios.post(loc() + '/lineList', {year: this.pageForm.year})
this.lineList = resp.data
},
async yearChange() {
this.$set(this.pageForm, "lineId", null)
await this.getLineList()
this.doSearch()
},
pageData() {
this.$axios.post(loc() + "/pageData", this.pageForm).then((res) => {
if (res.code === 0) {
this.tableData = res.data.list
this.pageForm.totalCount = res.data.totalCount
}
})
},
},
async created() {
this.unionList = await this.$businessTool.listUnion()
this.unitList = await this.$businessTool.listUnit()
await this.getLineList()
this.pageData()
}
})
new Vue({el:'#app',mixins:[initTableMixins],data(){return{activeTab:'summary',pageForm:{pageNumber:1,pageSize:10,totalCount:0,year:new Date().getFullYear().toString(),lineId:'',searchKeyword:'',unionId:'',unitId:'',evaluateScore:''},unionList:[],unitList:[],lineList:[],summaryLoading:false,feedbackLoading:false,agencyRatings:[],schemeRatings:[],feedbackDetails:[],feedbackType:'全部',feedbackVisible:false,feedbackDetail:{}}},methods:{
score(value){return value===null||value===undefined||value===''?'—':Number(value).toFixed(1)},yearChange(){this.$set(this.pageForm,'lineId','');this.loadLines();this.loadSummary();if(this.activeTab==='feedback')this.loadFeedback();if(this.activeTab==='users')this.doSearch()},tabChange(){if(this.activeTab==='summary')this.loadSummary();if(this.activeTab==='feedback')this.loadFeedback();if(this.activeTab==='users')this.pageData()},
loadSummary(){this.$set(this,'summaryLoading',true);this.$axios.post(loc()+'/getSatisfactionRatingStatistics',{year:this.pageForm.year}).then((res)=>{if(res.code===0){this.$set(this,'agencyRatings',(res.data&&res.data.travelAgencyCompositeRatings)||[]);this.$set(this,'schemeRatings',(res.data&&res.data.schemeServiceRatings)||[])}}).finally(()=>{this.$set(this,'summaryLoading',false)})},
loadFeedback(){this.$set(this,'feedbackLoading',true);this.$axios.post(loc()+'/getSatisfactionFeedbackDetails',{year:this.pageForm.year,typeName:this.feedbackType}).then((res)=>{if(res.code===0)this.$set(this,'feedbackDetails',res.data||[])}).finally(()=>{this.$set(this,'feedbackLoading',false)})},exportFeedback(){this.$downLoad(loc()+'/exportSatisfactionFeedbackDetails',{year:this.pageForm.year,typeName:this.feedbackType})},viewFeedback(row){this.$set(this,'feedbackDetail',row);this.$set(this,'feedbackVisible',true)},
loadLines(){this.$axios.post(loc()+'/lineList',{year:this.pageForm.year}).then((res)=>{if(res.code===0)this.$set(this,'lineList',res.data||[])})},pageData(){this.$set(this,'tableLoading',true);this.$axios.post(loc()+'/pageData',this.pageForm).then((res)=>{if(res.code===0){this.$set(this,'tableData',res.data.list||[]);this.$set(this.pageForm,'totalCount',res.data.totalCount||0)}}).finally(()=>{this.$set(this,'tableLoading',false)})},exportEvaluate(){this.$downLoad(loc()+'/exportEvaluate',this.pageForm)}
},created(){this.$businessTool.listUnion().then((res)=>this.$set(this,'unionList',res||[]));this.$businessTool.listUnit().then((res)=>this.$set(this,'unitList',res||[]));this.loadLines();this.loadSummary()}})
</script>
<!--#
}
#-->
<!--# } #-->