Compare commits
13
Commits
bcb59899d3
...
b4c8ee6a84
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b4c8ee6a84 | ||
|
|
50937b936d | ||
|
|
d605391fa5 | ||
|
|
b87567230f | ||
|
|
c0d9636e1f | ||
|
|
a1225d4d76 | ||
|
|
bc9afdc078 | ||
|
|
7ee9fd0cb0 | ||
|
|
248b661320 | ||
|
|
ffe16d42df | ||
|
|
9bc129e83d | ||
|
|
d158e2f00c | ||
|
|
0a9d355913 |
Generated
+15
@@ -1,6 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RemoteRepositoriesConfiguration">
|
||||
<remote-repository>
|
||||
<option name="id" value="central" />
|
||||
<option name="name" value="Central Repository" />
|
||||
<option name="url" value="https://repo.maven.apache.org/maven2" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="nutz" />
|
||||
<option name="name" value="nutz" />
|
||||
<option name="url" value="https://jfrog.nutz.cn/artifactory/libs-release" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="nutz-snapshots" />
|
||||
<option name="name" value="nutz-snapshots" />
|
||||
<option name="url" value="https://jfrog.nutz.cn/artifactory/snapshots" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="central" />
|
||||
<option name="name" value="Maven Central repository" />
|
||||
|
||||
Generated
-1
@@ -1,7 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="KubernetesApiProvider"><![CDATA[{}]]></component>
|
||||
<component name="MavenProjectsManager">
|
||||
<option name="originalFiles">
|
||||
<list>
|
||||
|
||||
@@ -38,6 +38,7 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.Base64;
|
||||
@@ -270,10 +271,12 @@ public class SysFileServiceImpl extends BaseServiceImpl<Sys_file> implements Sys
|
||||
}
|
||||
}
|
||||
HtmlSaveOptions saveOptions = new HtmlSaveOptions();
|
||||
// Aspose输出和字节流读取必须固定使用UTF-8,避免不同服务器的JVM默认编码导致中文乱码。
|
||||
saveOptions.setEncoding(StandardCharsets.UTF_8);
|
||||
saveOptions.setExportImagesAsBase64(true);
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
doc.save(bos, saveOptions);
|
||||
String htmlContent = Jsoup.parse(bos.toString()).body().html();
|
||||
String htmlContent = Jsoup.parse(bos.toString(StandardCharsets.UTF_8)).body().html();
|
||||
|
||||
Pattern pattern = Pattern.compile(IMG_BASE64_PATTERN, Pattern.CASE_INSENSITIVE);
|
||||
Matcher matcher = pattern.matcher(htmlContent);
|
||||
|
||||
+4
-2
@@ -157,12 +157,14 @@ public class RecuperationEvaluateStatisticsController {
|
||||
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("方案名称", "projectName", 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));
|
||||
String accommodationLabel = "线路".equals(typeName) ? "住宿评分"
|
||||
: ("灵活组团".equals(typeName) || "定点".equals(typeName) ? "酒店评分" : "住宿/酒店评分");
|
||||
columns.add(new ExcelExportEntity(accommodationLabel, "evaluationForAccommodation", 16));
|
||||
columns.add(new ExcelExportEntity("行程评分", "evaluationForJourney", 14));
|
||||
columns.add(new ExcelExportEntity("餐饮评分", "evaluationForDining", 14));
|
||||
columns.add(new ExcelExportEntity("交通评分", "evaluationForTransportation", 14));
|
||||
|
||||
+11
-10
@@ -62,10 +62,11 @@ public class RecuperationFlexibleGroupQueryController {
|
||||
return Result.success(flexibleGroupService.querySignUsers(pageForm, groupId, groupLeaderUserId, groupLeaderLoginName, noGroupLeader));
|
||||
}
|
||||
|
||||
/** pageForm 传分页信息,groupId 传灵活组团 ID;data 返回全部报名人员的分页结构。 */
|
||||
@At
|
||||
@SaCheckLogin
|
||||
public Result getAllSignUser(PageForm pageForm, String groupId) {
|
||||
return Result.success(flexibleGroupService.querySignUsers(pageForm, groupId, null, null, false));
|
||||
return Result.success(flexibleGroupService.queryAllSignUsers(pageForm, groupId));
|
||||
}
|
||||
|
||||
@At
|
||||
@@ -98,16 +99,16 @@ public class RecuperationFlexibleGroupQueryController {
|
||||
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("工号", "loginName", 20));
|
||||
columns.add(new ExcelExportEntity("姓名", "userName", 20));
|
||||
columns.add(new ExcelExportEntity("联系电话", "mobile", 20));
|
||||
columns.add(new ExcelExportEntity("身份证号", "idCard", 30));
|
||||
columns.add(new ExcelExportEntity("单位", "unitName", 30));
|
||||
columns.add(new ExcelExportEntity("工会", "unionName", 30));
|
||||
columns.add(new ExcelExportEntity("旅行社", "travelAgencyName", 30));
|
||||
columns.add(new ExcelExportEntity("报名时间", "signingUptime", 20));
|
||||
columns.add(new ExcelExportEntity("团长", "leaderName", 20));
|
||||
columns.add(new ExcelExportEntity("是否成团", "formedTeamState", 14));
|
||||
columns.add(new ExcelExportEntity("团长", "leaderName", 25));
|
||||
columns.add(new ExcelExportEntity("是否成团", "formedTeamState", 15));
|
||||
ExportParams exportParams = new ExportParams();
|
||||
exportParams.setType(ExcelType.XSSF);
|
||||
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, columns,
|
||||
|
||||
+21
@@ -13,6 +13,9 @@ import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.mvc.annotation.At;
|
||||
import org.nutz.mvc.annotation.Ok;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/** PC 端线路报名情况及成团通知。 */
|
||||
@IocBean
|
||||
@At("/platform/recuperation/lineStatistics")
|
||||
@@ -48,6 +51,24 @@ public class RecuperationLineStatisticsController {
|
||||
return Result.success(statisticsService.lineOptions(startYear, endYear, signUpMode, regionalNature));
|
||||
}
|
||||
|
||||
/** lineId 为线路 ID;data 中 selectId 表示具体出行批次,times 为时间及报名人数说明。 */
|
||||
@At
|
||||
@SaCheckPermission("recuperation.lineStatistics")
|
||||
public Result getLinePlayTimes(String lineId, Integer startYear, Integer endYear) {
|
||||
return Result.success(statisticsService.linePlayTimeOptions(lineId, startYear, endYear));
|
||||
}
|
||||
|
||||
/** 按当前筛选条件导出达到最低成团人数的线路人员,并按线路生成 Excel 压缩包。 */
|
||||
@At
|
||||
@Ok("void")
|
||||
@SaCheckPermission("recuperation.lineStatistics")
|
||||
public void doExport(Integer startYear, Integer endYear, String unionId, String takePartInLineId,
|
||||
String lotId, String selectId, String regionalNature,
|
||||
HttpServletResponse response) throws IOException {
|
||||
statisticsService.exportFormedLineUsers(startYear, endYear, unionId, takePartInLineId,
|
||||
lotId, selectId, regionalNature, response);
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("recuperation.lineStatistics")
|
||||
public Result countSuccessNotice(String id) { return Result.success(enrollService.countLineGroupNoticeUsers(id, true)); }
|
||||
|
||||
+28
@@ -4,6 +4,8 @@ import com.budwk.app.base.page.Pagination;
|
||||
import com.budwk.app.base.param.PageForm;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/** PC 端线路成团统计查询。 */
|
||||
@@ -14,4 +16,30 @@ public interface RecuperationLineStatisticsService {
|
||||
Pagination userPage(PageForm pageForm, String lineSelectId, String searchKeyword, String unionId, String unitId);
|
||||
|
||||
List<NutMap> lineOptions(Integer startYear, Integer endYear, String signUpMode, String regionalNature);
|
||||
|
||||
/**
|
||||
* 查询指定线路在年度范围内的出行批次选项。
|
||||
*
|
||||
* @param lineId 线路 ID
|
||||
* @param startYear 开始年度
|
||||
* @param endYear 结束年度
|
||||
* @return selectId 为工会选线记录 ID,times 为出行时间及报名人数说明
|
||||
*/
|
||||
List<NutMap> linePlayTimeOptions(String lineId, Integer startYear, Integer endYear);
|
||||
|
||||
/**
|
||||
* 导出达到最低成团人数的线路报名人员。
|
||||
*
|
||||
* @param startYear 开始年度
|
||||
* @param endYear 结束年度
|
||||
* @param unionId 报名人员所属工会 ID
|
||||
* @param lineId 线路 ID
|
||||
* @param lotId 标段 ID
|
||||
* @param selectId 工会选线记录 ID,用于限定具体出行批次
|
||||
* @param regionalNature 线路类型,可传省内、省外或空值
|
||||
* @param response 返回 ZIP 文件的 HTTP 响应
|
||||
*/
|
||||
void exportFormedLineUsers(Integer startYear, Integer endYear, String unionId, String lineId,
|
||||
String lotId, String selectId, String regionalNature,
|
||||
HttpServletResponse response) throws IOException;
|
||||
}
|
||||
|
||||
+9
@@ -105,6 +105,15 @@ public interface RecuperationProvinceFlexibleGroupService extends BaseService<Re
|
||||
Pagination querySignUsers(PageForm pageForm, String groupId, String groupLeaderUserId,
|
||||
String groupLeaderLoginName, Boolean noGroupLeader);
|
||||
|
||||
/**
|
||||
* 查询一个灵活组团的全部报名人员,并按团长、团内身份和报名时间排序。
|
||||
*
|
||||
* @param pageForm 分页信息
|
||||
* @param groupId 灵活组团 ID
|
||||
* @return 报名人员分页数据
|
||||
*/
|
||||
Pagination queryAllSignUsers(PageForm pageForm, String groupId);
|
||||
|
||||
/** 查询年度区间内可用旅行社。 */
|
||||
List<NutMap> queryTravelAgencyOptions(Integer startYear, Integer endYear);
|
||||
|
||||
|
||||
+16
-7
@@ -1594,7 +1594,7 @@ public class RecuperationEnrollServiceImpl extends BaseServiceImpl<RecuperationE
|
||||
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
|
||||
LEFT JOIN ($ratingSql) rating ON rating.travelAgencyId=agency.id AND rating.statisticYear=@year
|
||||
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
|
||||
@@ -1623,7 +1623,7 @@ public class RecuperationEnrollServiceImpl extends BaseServiceImpl<RecuperationE
|
||||
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,'')<>''
|
||||
WHERE rating.statisticYear=@year AND IFNULL(rating.schemeId,'')<>''
|
||||
GROUP BY rating.typeName, rating.schemeId, rating.schemeName, rating.travelAgencyName
|
||||
ORDER BY FIELD(rating.typeName,'线路','灵活组团','定点'), rating.schemeName
|
||||
""");
|
||||
@@ -1635,12 +1635,20 @@ public class RecuperationEnrollServiceImpl extends BaseServiceImpl<RecuperationE
|
||||
@Override
|
||||
public List<NutMap> satisfactionFeedbackDetails(Integer year, String typeName) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT rating.*,
|
||||
SELECT rating.*, rating.schemeName AS projectName,
|
||||
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<>''
|
||||
WHERE rating.statisticYear=@year AND (
|
||||
rating.evaluationForTravelAgency IS NOT NULL
|
||||
OR rating.evaluationForJourney IS NOT NULL
|
||||
OR rating.evaluationForAccommodation IS NOT NULL
|
||||
OR rating.evaluationForDining IS NOT NULL
|
||||
OR rating.evaluationForTransportation IS NOT NULL
|
||||
OR rating.evaluationForLine IS NOT NULL
|
||||
OR IFNULL(rating.feedbackContent,'')<>''
|
||||
)
|
||||
ORDER BY rating.signingUptime DESC
|
||||
""");
|
||||
sql.setVar("ratingSql", satisfactionBaseSql()).setParam("year", year == null ? DateUtil.thisYear() : year);
|
||||
@@ -1652,9 +1660,10 @@ public class RecuperationEnrollServiceImpl extends BaseServiceImpl<RecuperationE
|
||||
/** 满意度统计与明细共用同一报名方式、方案和旅行社映射。 */
|
||||
private String satisfactionBaseSql() {
|
||||
return """
|
||||
SELECT enroll.id, enroll.loginName, enroll.userName, enroll.unitName, enroll.signingUptime, enroll.feedbackContent,
|
||||
SELECT enroll.id, enroll.loginName, enroll.userName, enroll.unitName, enroll.signingUptime,
|
||||
YEAR(enroll.signingUptime) AS statisticYear, enroll.feedbackContent,
|
||||
enroll.evaluationForTravelAgency, enroll.evaluationForJourney, enroll.evaluationForAccommodation,
|
||||
enroll.evaluationForDining, enroll.evaluationForTransportation,
|
||||
enroll.evaluationForDining, enroll.evaluationForTransportation, enroll.evaluationForLine,
|
||||
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,
|
||||
@@ -1676,7 +1685,7 @@ public class RecuperationEnrollServiceImpl extends BaseServiceImpl<RecuperationE
|
||||
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
|
||||
WHERE enroll.isNormal=true AND enroll.isTakePartIn=true
|
||||
""";
|
||||
}
|
||||
}
|
||||
|
||||
+212
-1
@@ -1,13 +1,20 @@
|
||||
package com.budwk.app.zhgh.staffbenefit.recuperation.service.impl;
|
||||
|
||||
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.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.common.RecuperationCommon;
|
||||
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.service.RecuperationLineStatisticsService;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.dao.Sqls;
|
||||
@@ -15,7 +22,21 @@ import org.nutz.dao.sql.Sql;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
/** 线路统计服务,统一限定审核通过且未调出的报名记录。 */
|
||||
@IocBean(args = {"refer:dao"})
|
||||
@@ -26,11 +47,12 @@ public class RecuperationLineStatisticsServiceImpl extends BaseServiceImpl<Recup
|
||||
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,
|
||||
SELECT line.id, line.id AS lineId, 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,
|
||||
MAX(enroll.takePartInUnionId) AS usUnionId,
|
||||
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,
|
||||
@@ -94,4 +116,193 @@ public class RecuperationLineStatisticsServiceImpl extends BaseServiceImpl<Recup
|
||||
sql.setCondition(cnd);
|
||||
return listMap(sql);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NutMap> linePlayTimeOptions(String lineId, Integer startYear, Integer endYear) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT lineu.id AS selectId,
|
||||
CONCAT(
|
||||
DATE_FORMAT(lineu.playStartTime,'%m月%d日'),'-',DATE_FORMAT(lineu.playEndTime,'%m月%d日'),
|
||||
'(报名人数:',
|
||||
COUNT(DISTINCT enroll.loginName) +
|
||||
IF(COALESCE((SELECT familyInfo FROM the_rapy_recuperation_config LIMIT 1),1)=2,
|
||||
(SELECT COUNT(1)
|
||||
FROM the_rapy_recuperation_enroll_companion companion
|
||||
WHERE companion.trreId IN (
|
||||
SELECT signed.id
|
||||
FROM the_rapy_recuperation_enroll signed
|
||||
WHERE signed.takePartInLineId=lineu.id
|
||||
AND signed.isNormal=true
|
||||
AND signed.stateId NOT IN (2715,2725,2735)
|
||||
)),
|
||||
COALESCE(SUM(enroll.familyNumber),0)),
|
||||
',其中家属:',
|
||||
IF(COALESCE((SELECT familyInfo FROM the_rapy_recuperation_config LIMIT 1),1)=2,
|
||||
(SELECT COUNT(1)
|
||||
FROM the_rapy_recuperation_enroll_companion companion
|
||||
WHERE companion.trreId IN (
|
||||
SELECT signed.id
|
||||
FROM the_rapy_recuperation_enroll signed
|
||||
WHERE signed.takePartInLineId=lineu.id
|
||||
AND signed.isNormal=true
|
||||
AND signed.stateId NOT IN (2715,2725,2735)
|
||||
)),
|
||||
COALESCE(SUM(enroll.familyNumber),0)),
|
||||
'人)'
|
||||
) AS times
|
||||
FROM the_rapy_recuperation_line_union_select lineu
|
||||
LEFT JOIN the_rapy_recuperation_enroll enroll
|
||||
ON enroll.takePartInLineId=lineu.id
|
||||
AND enroll.isNormal=true
|
||||
AND enroll.stateId NOT IN (2715,2725,2735)
|
||||
$condition
|
||||
""");
|
||||
Cnd cnd = Cnd.where("lineu.lineId", "=", lineId)
|
||||
.andEX("YEAR(lineu.selectTime)", ">=", startYear)
|
||||
.andEX("YEAR(lineu.selectTime)", "<=", endYear);
|
||||
if (!AuthUtil.hasRoleOr("sysadmin", "H06", "A06")) {
|
||||
cnd.and("lineu.unionId", "=", SecurityUtil.getUnionId());
|
||||
}
|
||||
cnd.groupBy("lineu.id", "lineu.playStartTime", "lineu.playEndTime");
|
||||
cnd.asc("lineu.playStartTime");
|
||||
sql.setCondition(cnd);
|
||||
return listMap(sql);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exportFormedLineUsers(Integer startYear, Integer endYear, String unionId, String lineId,
|
||||
String lotId, String selectId, String regionalNature,
|
||||
HttpServletResponse response) throws IOException {
|
||||
List<NutMap> exportRows = queryLineExportRows(startYear, endYear, unionId, lineId, lotId, selectId, regionalNature);
|
||||
RecuperationConfig config = dao().fetch(RecuperationConfig.class, Cnd.NEW());
|
||||
Integer minimumGroupSize = config == null ? null : config.getOutsideQuota();
|
||||
|
||||
Map<String, List<NutMap>> batchGroups = exportRows.stream().collect(Collectors.groupingBy(
|
||||
row -> StrUtil.blankToDefault(row.getString("takePartInLineId"), ""),
|
||||
LinkedHashMap::new,
|
||||
Collectors.toList()));
|
||||
List<NutMap> formedRows = batchGroups.values().stream()
|
||||
.filter(rows -> isFormedGroup(rows, minimumGroupSize))
|
||||
.flatMap(List::stream)
|
||||
.collect(Collectors.toList());
|
||||
Map<String, List<NutMap>> lineGroups = formedRows.stream().collect(Collectors.groupingBy(
|
||||
row -> StrUtil.blankToDefault(row.getString("lineId"), ""),
|
||||
LinkedHashMap::new,
|
||||
Collectors.toList()));
|
||||
|
||||
response.setContentType("application/zip");
|
||||
response.setHeader("Content-Disposition", "attachment;filename="
|
||||
+ URLEncoder.encode("疗休养集体线路报名人员名单.zip", StandardCharsets.UTF_8));
|
||||
Set<String> zipEntryNames = new HashSet<>();
|
||||
try (ZipOutputStream zipOutputStream = new ZipOutputStream(new BufferedOutputStream(response.getOutputStream()))) {
|
||||
for (List<NutMap> rows : lineGroups.values()) {
|
||||
String lineName = rows.isEmpty() ? "未命名" : StrUtil.blankToDefault(rows.get(0).getString("lineName"), "未命名");
|
||||
writeExcelToZip(zipOutputStream, zipEntryNames, lineName, rows);
|
||||
}
|
||||
zipOutputStream.flush();
|
||||
}
|
||||
response.flushBuffer();
|
||||
}
|
||||
|
||||
/** 查询导出所需的审核通过线路报名人员,并按当前登录角色限制可见工会。 */
|
||||
private List<NutMap> queryLineExportRows(Integer startYear, Integer endYear, String unionId, String lineId,
|
||||
String lotId, String selectId, String regionalNature) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT line.id AS lineId, line.lineName, line.regionalNature, agency.travelAgencyName,
|
||||
enroll.*, DATE_FORMAT(lineu.playStartTime,'%Y-%m-%d') AS playStartTime,
|
||||
IF(COALESCE((SELECT familyInfo FROM the_rapy_recuperation_config LIMIT 1),1)=2,
|
||||
(SELECT COUNT(1) FROM the_rapy_recuperation_enroll_companion companion WHERE companion.trreId=enroll.id),
|
||||
COALESCE(enroll.familyNumber,0)) AS familyCount,
|
||||
(SELECT GROUP_CONCAT(
|
||||
companion.userName,'-',companion.sex,'(',companion.idcard,'、',companion.mobile,')-',
|
||||
IFNULL(bed.bedType,''),',家属备注:',IFNULL(companion.remark,'')
|
||||
SEPARATOR ';')
|
||||
FROM the_rapy_recuperation_enroll_companion companion
|
||||
LEFT JOIN the_rapy_recuperation_enroll_bed bed ON bed.id=companion.bedInfoId
|
||||
WHERE companion.trreId=enroll.id) AS companionInfo,
|
||||
user_info.arrivalAtSchoolDate AS schoolTime
|
||||
FROM the_rapy_recuperation_enroll enroll
|
||||
LEFT JOIN sys_user user_info ON user_info.loginname=enroll.loginName
|
||||
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_travel_agency agency ON agency.id=lineu.travelAgencyId
|
||||
$condition
|
||||
""");
|
||||
Cnd cnd = Cnd.where("enroll.stateId", "=", 2750)
|
||||
.and("enroll.isNormal", "=", true)
|
||||
.and("enroll.takePartInLineId", "is not", null)
|
||||
.and("enroll.takePartInLineId", "!=", "")
|
||||
.andEX("YEAR(enroll.signingUptime)", ">=", startYear)
|
||||
.andEX("YEAR(enroll.signingUptime)", "<=", endYear)
|
||||
.andEX("line.id", "=", lineId)
|
||||
.andEX("line.lotId", "=", lotId)
|
||||
.andEX("lineu.id", "=", selectId)
|
||||
.andEX("line.regionalNature", "=", regionalNature);
|
||||
if (AuthUtil.hasRoleOr("sysadmin", "H06", "A06")) {
|
||||
cnd.andEX("enroll.selfUnionId", "=", unionId);
|
||||
} else {
|
||||
cnd.and(Cnd.exps("enroll.takePartInUnionId", "=", SecurityUtil.getUnionId())
|
||||
.or("enroll.selfUnionId", "=", SecurityUtil.getUnionId()));
|
||||
}
|
||||
cnd.asc("line.lineName").asc("lineu.playStartTime").asc("enroll.unionName");
|
||||
sql.setCondition(cnd);
|
||||
List<NutMap> rows = listMap(sql);
|
||||
rows.forEach(row -> row.setv("remark", RecuperationCommon.getRemarkBySchoolTime(row.getString("schoolTime"))));
|
||||
return rows;
|
||||
}
|
||||
|
||||
/** 最低成团人数同时计算去重后的教职工人数和家属人数。 */
|
||||
private boolean isFormedGroup(List<NutMap> rows, Integer minimumGroupSize) {
|
||||
if (minimumGroupSize == null || minimumGroupSize <= 0) {
|
||||
return false;
|
||||
}
|
||||
long teacherCount = rows.stream().map(row -> row.getString("loginName"))
|
||||
.filter(StrUtil::isNotBlank).distinct().count();
|
||||
int familyCount = rows.stream().mapToInt(row -> row.getInt("familyCount", 0)).sum();
|
||||
return teacherCount + familyCount >= minimumGroupSize;
|
||||
}
|
||||
|
||||
/** 每条线路生成一个 XSSF 工作簿后写入 ZIP,避免工作簿内部流与外层 ZIP 冲突。 */
|
||||
private void writeExcelToZip(ZipOutputStream zipOutputStream, Set<String> zipEntryNames,
|
||||
String rawLineName, List<NutMap> rows) throws IOException {
|
||||
String safeLineName = rawLineName.replaceAll("[\\\\/:*?\"<>|]", "_").replaceAll("[\\r\\n]", "").trim();
|
||||
String entryName = "集体线路报名人员/" + StrUtil.blankToDefault(safeLineName, "未命名") + ".xlsx";
|
||||
int index = 1;
|
||||
while (!zipEntryNames.add(entryName)) {
|
||||
entryName = "集体线路报名人员/" + StrUtil.blankToDefault(safeLineName, "未命名") + "-" + index++ + ".xlsx";
|
||||
}
|
||||
ExportParams exportParams = new ExportParams();
|
||||
exportParams.setType(ExcelType.XSSF);
|
||||
try (Workbook workbook = ExcelExportUtil.exportExcel(exportParams, lineExportColumns(), rows);
|
||||
ByteArrayOutputStream excelOutputStream = new ByteArrayOutputStream()) {
|
||||
workbook.write(excelOutputStream);
|
||||
zipOutputStream.putNextEntry(new ZipEntry(entryName));
|
||||
zipOutputStream.write(excelOutputStream.toByteArray());
|
||||
zipOutputStream.closeEntry();
|
||||
}
|
||||
}
|
||||
|
||||
/** 旧版成团线路人员导出的列结构。 */
|
||||
private List<ExcelExportEntity> lineExportColumns() {
|
||||
List<ExcelExportEntity> columns = new ArrayList<>();
|
||||
columns.add(new ExcelExportEntity("姓名", "userName", 20));
|
||||
columns.add(new ExcelExportEntity("工号", "loginName", 20));
|
||||
columns.add(new ExcelExportEntity("性别", "sex", 10));
|
||||
columns.add(new ExcelExportEntity("所属单位", "unitName", 20));
|
||||
columns.add(new ExcelExportEntity("所属工会", "unionName", 20));
|
||||
columns.add(new ExcelExportEntity("身份证号", "idCard", 30));
|
||||
columns.add(new ExcelExportEntity("手机号", "mobile", 15));
|
||||
columns.add(new ExcelExportEntity("备注", "remark", 30));
|
||||
ExcelExportEntity signingTime = new ExcelExportEntity("报名时间", "signingUptime", 20);
|
||||
signingTime.setFormat("yyyy-MM-dd HH:mm:ss");
|
||||
columns.add(signingTime);
|
||||
columns.add(new ExcelExportEntity("出行时间", "playStartTime", 20));
|
||||
columns.add(new ExcelExportEntity("线路名称", "lineName", 20));
|
||||
columns.add(new ExcelExportEntity("旅行社", "travelAgencyName", 20));
|
||||
columns.add(new ExcelExportEntity("家属人数", "familyCount", 12));
|
||||
ExcelExportEntity companionInfo = new ExcelExportEntity("家属信息", "companionInfo", 50);
|
||||
companionInfo.setWrap(true);
|
||||
columns.add(companionInfo);
|
||||
return columns;
|
||||
}
|
||||
}
|
||||
|
||||
+120
-9
@@ -380,6 +380,9 @@ public class RecuperationProvinceFlexibleGroupServiceImpl extends BaseServiceImp
|
||||
@Override
|
||||
public Pagination queryPage(PageForm pageForm, Integer startYear, Integer endYear, String groupName,
|
||||
String travelAgencyId, String searchKeyword) {
|
||||
if (StrUtil.isNotBlank(searchKeyword)) {
|
||||
return queryPersonMatchedGroups(pageForm, startYear, endYear, groupName, travelAgencyId, 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 <> ''
|
||||
@@ -412,6 +415,53 @@ public class RecuperationProvinceFlexibleGroupServiceImpl extends BaseServiceImp
|
||||
return listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
|
||||
}
|
||||
|
||||
/** 姓名或工号查询时按匹配人员展开,并保留组团总团队数和总报名人数。 */
|
||||
private Pagination queryPersonMatchedGroups(PageForm pageForm, Integer startYear, Integer endYear,
|
||||
String groupName, String travelAgencyId, String searchKeyword) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT fg.id, CONCAT(fg.id,'_',enroll.id) AS rowKey, fg.groupName, fg.year,
|
||||
ta.travelAgencyName, enroll.userName AS signUserName, enroll.loginName AS signLoginName,
|
||||
(SELECT COUNT(DISTINCT CASE
|
||||
WHEN grouped.groupLeaderUserId IS NOT NULL AND grouped.groupLeaderUserId<>'' THEN grouped.groupLeaderUserId
|
||||
WHEN grouped.groupLeaderLoginName IS NOT NULL AND grouped.groupLeaderLoginName<>'' THEN grouped.groupLeaderLoginName
|
||||
END)
|
||||
FROM the_rapy_recuperation_enroll grouped
|
||||
WHERE grouped.takePartInTravelAgencyId=fg.travelAgencyId
|
||||
AND YEAR(grouped.signingUptime)=fg.year
|
||||
AND grouped.isNormal=true
|
||||
AND grouped.stateId NOT IN ($auditFailStates)) AS groupCount,
|
||||
(SELECT COUNT(DISTINCT grouped.loginName)
|
||||
FROM the_rapy_recuperation_enroll grouped
|
||||
WHERE grouped.takePartInTravelAgencyId=fg.travelAgencyId
|
||||
AND YEAR(grouped.signingUptime)=fg.year
|
||||
AND grouped.isNormal=true
|
||||
AND grouped.stateId NOT IN ($auditFailStates)) 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 NOT IN ($auditFailStates)
|
||||
$condition
|
||||
""");
|
||||
Cnd cnd = Cnd.where("ta.signUpTravelAgency", "=", true)
|
||||
.andEX("fg.year", ">=", startYear)
|
||||
.andEX("fg.year", "<=", endYear)
|
||||
.andEX("fg.travelAgencyId", "=", travelAgencyId)
|
||||
.and(Cnd.likeEX("fg.groupName", groupName))
|
||||
.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", "enroll.id");
|
||||
cnd.desc("fg.year").asc("fg.groupName").asc("enroll.userName");
|
||||
sql.setVar("auditFailStates", auditFailStates());
|
||||
sql.setCondition(cnd);
|
||||
return listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NutMap> queryGroupInfo(String groupId) {
|
||||
Sql sql = Sqls.create("""
|
||||
@@ -462,11 +512,44 @@ public class RecuperationProvinceFlexibleGroupServiceImpl extends BaseServiceImp
|
||||
return listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pagination queryAllSignUsers(PageForm pageForm, String groupId) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT enroll.id, enroll.loginName, enroll.userName,
|
||||
CASE WHEN enroll.groupLeaderLoginName IS NOT NULL
|
||||
AND enroll.groupLeaderLoginName<>''
|
||||
AND 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 NOT IN ($auditFailStates)
|
||||
ORDER BY enroll.groupLeaderUserName,
|
||||
CASE WHEN enroll.groupLeaderLoginName IS NOT NULL
|
||||
AND enroll.groupLeaderLoginName<>''
|
||||
AND enroll.loginName=enroll.groupLeaderLoginName
|
||||
THEN 0 ELSE 1 END,
|
||||
enroll.signingUptime
|
||||
""");
|
||||
sql.setParam("groupId", groupId).setVar("auditFailStates", auditFailStates());
|
||||
return listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
|
||||
}
|
||||
|
||||
/** 过滤浏览器可能传入的 undefined/null 文本,避免错误命中团长条件。 */
|
||||
private boolean isValidGroupLeaderParam(String value) {
|
||||
return StrUtil.isNotBlank(value) && !"undefined".equalsIgnoreCase(value) && !"null".equalsIgnoreCase(value);
|
||||
}
|
||||
|
||||
/** 灵活组团查询排除分工会、线路工会和校工会审核失败状态。 */
|
||||
private String auditFailStates() {
|
||||
return RecuperationState.UNITFAIL + "," + RecuperationState.LINEUNITFAIL + "," + RecuperationState.SCHOOLFAIL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NutMap> queryTravelAgencyOptions(Integer startYear, Integer endYear) {
|
||||
Sql sql = Sqls.create("""
|
||||
@@ -498,24 +581,52 @@ public class RecuperationProvinceFlexibleGroupServiceImpl extends BaseServiceImp
|
||||
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
|
||||
CASE
|
||||
WHEN (enroll.groupLeaderUserName IS NULL OR enroll.groupLeaderUserName='')
|
||||
AND (enroll.groupLeaderLoginName IS NULL OR enroll.groupLeaderLoginName='') THEN '未选择'
|
||||
WHEN enroll.groupLeaderLoginName IS NULL OR enroll.groupLeaderLoginName='' THEN enroll.groupLeaderUserName
|
||||
WHEN enroll.groupLeaderUserName IS NULL OR enroll.groupLeaderUserName='' THEN enroll.groupLeaderLoginName
|
||||
ELSE CONCAT(enroll.groupLeaderUserName,'(',enroll.groupLeaderLoginName,')')
|
||||
END AS leaderName,
|
||||
CASE WHEN (
|
||||
SELECT COUNT(DISTINCT sameLeader.loginName)
|
||||
FROM the_rapy_recuperation_enroll sameLeader
|
||||
WHERE sameLeader.takePartInTravelAgencyId=enroll.takePartInTravelAgencyId
|
||||
AND ((enroll.groupLeaderUserId IS NOT NULL AND enroll.groupLeaderUserId<>''
|
||||
AND sameLeader.groupLeaderUserId=enroll.groupLeaderUserId)
|
||||
OR (enroll.groupLeaderLoginName IS NOT NULL AND enroll.groupLeaderLoginName<>''
|
||||
AND sameLeader.groupLeaderLoginName=enroll.groupLeaderLoginName))
|
||||
AND YEAR(sameLeader.signingUptime)=YEAR(enroll.signingUptime)
|
||||
AND sameLeader.isNormal=true
|
||||
AND sameLeader.stateId NOT IN ($auditFailStates)
|
||||
)>=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)
|
||||
LEFT JOIN the_rapy_recuperation_province_flexible_group fg ON fg.id=(
|
||||
SELECT configured.id
|
||||
FROM the_rapy_recuperation_province_flexible_group configured
|
||||
WHERE configured.travelAgencyId=enroll.takePartInTravelAgencyId
|
||||
AND configured.year=YEAR(enroll.signingUptime)
|
||||
AND configured.isDisabled=false
|
||||
ORDER BY configured.id
|
||||
LIMIT 1)
|
||||
$condition
|
||||
""");
|
||||
Cnd cnd = Cnd.where("fg.year", ">=", startYear).and("fg.year", "<=", endYear)
|
||||
.and("enroll.isNormal", "=", true);
|
||||
Cnd cnd = Cnd.where("ta.signUpTravelAgency", "=", true)
|
||||
.and("fg.id", "is not", null)
|
||||
.andEX("fg.year", ">=", startYear)
|
||||
.andEX("fg.year", "<=", endYear)
|
||||
.and("enroll.isNormal", "=", true)
|
||||
.and("enroll.stateId", "not in", Lang.array(RecuperationState.UNITFAIL,
|
||||
RecuperationState.LINEUNITFAIL, RecuperationState.SCHOOLFAIL));
|
||||
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");
|
||||
cnd.asc("ta.serialNumber * 1").asc("ta.travelAgencyName").asc("enroll.groupLeaderUserName")
|
||||
.asc("enroll.groupLeaderLoginName").asc("fg.year").asc("enroll.signingUptime");
|
||||
sql.setVar("auditFailStates", auditFailStates());
|
||||
sql.setCondition(cnd);
|
||||
List<NutMap> list = listMap(sql);
|
||||
for (int i = 0; i < list.size(); i++) list.get(i).setv("no", i + 1);
|
||||
|
||||
@@ -165,6 +165,60 @@ public class WelfareListController {
|
||||
welfareListService.exportXlsx(pageForm, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出指定福利项目和套餐的报销表。
|
||||
*
|
||||
* @param projectId 福利项目 ID
|
||||
* @param optionId 福利套餐 ID,必须属于 projectId 对应项目
|
||||
* @param response XLSX 文件下载响应
|
||||
*/
|
||||
@At
|
||||
@Ok("void")
|
||||
@SaCheckPermission("welfare.list.mange")
|
||||
@ApiOperation("导出福利报销表")
|
||||
public void doExcelByOptionId(@Param("projectId") String projectId,
|
||||
@Param("optionId") String optionId,
|
||||
HttpServletResponse response) {
|
||||
welfareListService.doExcelByOptionId(projectId, optionId, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出供货商使用的套餐选择数据,按套餐拆分工作表。
|
||||
*
|
||||
* @param projectId 福利项目 ID
|
||||
* @param unionId 可选的分工会 ID;为空时按当前用户的数据权限导出
|
||||
* @param response XLSX 文件下载响应
|
||||
*/
|
||||
@At
|
||||
@Ok("void")
|
||||
@SaCheckPermission("welfare.list.mange")
|
||||
@ApiOperation("导出供货商选择数据")
|
||||
public void exportSelectData(@Param("projectId") String projectId,
|
||||
@Param("unionId") String unionId,
|
||||
HttpServletResponse response) {
|
||||
welfareListService.exportSelectData(projectId, unionId, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入供应商提供的快递单号文件。
|
||||
*
|
||||
* @param file XLS 或 XLSX 文件,包含工号与最多四个快递单号列
|
||||
* @param projectId 福利项目 ID
|
||||
* @param optionId 福利套餐 ID,必须属于 projectId 对应项目
|
||||
* @return 导入结果;成功时返回成功提示,失败时返回具体原因
|
||||
*/
|
||||
@At
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
@SaCheckPermission("welfare.list.mange")
|
||||
@AdaptBy(type = UploadAdaptor.class, args = {"ioc:fileUpload"})
|
||||
@SLog(tag = "福利名单管理", msg = "导入福利快递单号")
|
||||
@ApiOperation("导入福利快递单号")
|
||||
public Result importCourierNumber(@Param("file") TempFile file,
|
||||
@Param("projectId") String projectId,
|
||||
@Param("optionId") String optionId) {
|
||||
return welfareListService.importCourierNumber(file, projectId, optionId);
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("welfare.list.mange")
|
||||
@ApiOperation("编辑备注")
|
||||
|
||||
@@ -60,7 +60,6 @@ public class WelfareMineController {
|
||||
END AS isChoose,
|
||||
GROUP_CONCAT(DISTINCT wuso.optionName ,'(',wus.selectNum,'份)') AS gist_list,
|
||||
MAX(wus.selectTime) AS selectTime,
|
||||
MAX(wus.deliveryDate) AS deliveryDate,
|
||||
wus.receiveAddress,
|
||||
wl.userId
|
||||
FROM
|
||||
@@ -98,7 +97,6 @@ public class WelfareMineController {
|
||||
wpus.receiveAddress,
|
||||
wpso.optionName,
|
||||
wpus.selectNum,
|
||||
wpus.deliveryDate,
|
||||
wpus.remark,
|
||||
wpus.userSign,
|
||||
wpus.courierNumber,
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.budwk.app.zhgh.welfare.controller;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.base.result.Result;
|
||||
import com.budwk.app.zhgh.welfare.service.WelfareProjectService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
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 javax.validation.Valid;
|
||||
|
||||
/**
|
||||
* 福利通知附件查看入口。
|
||||
*/
|
||||
@IocBean
|
||||
@Ok("json:full")
|
||||
@At("/platform/welfare/notice")
|
||||
@Api(tags = "福利通知")
|
||||
public class WelfareNoticeController {
|
||||
|
||||
@Inject
|
||||
private WelfareProjectService projectService;
|
||||
|
||||
@At
|
||||
@Ok("beetl:/platform/zhghh5/welfare/notice/detail.html")
|
||||
@ApiOperation("福利通知附件详情页")
|
||||
public void index() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询福利通知附件详情。
|
||||
*
|
||||
* @param id 福利项目 ID
|
||||
* @return JSON 结果,data 中包含项目名称、文件名称、下载地址和 PDF 预览地址
|
||||
*/
|
||||
@At
|
||||
@ApiOperation("查询福利通知附件详情")
|
||||
public Result detailData(@Valid String id) {
|
||||
if (StrUtil.isBlank(id)) {
|
||||
return Result.error("福利项目ID不能为空");
|
||||
}
|
||||
return Result.success(projectService.getNoticeAttachmentInfo(id));
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -110,8 +110,8 @@ public class WelfareStatisticsController {
|
||||
@Ok("void")
|
||||
@SaCheckPermission("welfare.statistics")
|
||||
@ApiOperation("按福利选项导出")
|
||||
public void exportByWelfareOptions(String projectId, HttpServletResponse response) {
|
||||
welfareStatisticsService.exportByWelfareOptions(projectId, response);
|
||||
public void exportByWelfareOptions(String projectId, String unionId, HttpServletResponse response) {
|
||||
welfareStatisticsService.exportByWelfareOptions(projectId, unionId, response);
|
||||
}
|
||||
|
||||
@At
|
||||
|
||||
+3
-9
@@ -8,7 +8,6 @@ import com.budwk.app.base.param.PageForm;
|
||||
import com.budwk.app.base.result.Result;
|
||||
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
|
||||
import com.budwk.app.zhgh.welfare.model.WelfareList;
|
||||
import com.budwk.app.zhgh.welfare.model.WelfareProject;
|
||||
import com.budwk.app.zhgh.welfare.model.WelfareUserSelection;
|
||||
import com.budwk.app.zhgh.welfare.service.WelfareProjectService;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
@@ -24,10 +23,8 @@ import org.nutz.mvc.annotation.At;
|
||||
import org.nutz.mvc.annotation.Ok;
|
||||
import org.nutz.mvc.annotation.Param;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@IocBean
|
||||
@Ok("json:full")
|
||||
@@ -57,7 +54,6 @@ public class WelfareUserSelectController {
|
||||
ELSE 0
|
||||
END AS isChoose,
|
||||
wus.selectTime,
|
||||
MAX(wus.deliveryDate) AS deliveryDate,
|
||||
MAX(wus.remark) AS remark,
|
||||
GROUP_CONCAT(DISTINCT wuso.optionName ,'(',wus.selectNum,'份)') AS gist_list,
|
||||
wus.receiveAddress
|
||||
@@ -93,8 +89,6 @@ public class WelfareUserSelectController {
|
||||
if (count == 0) {
|
||||
return Result.error("您没有选择的权限");
|
||||
}
|
||||
WelfareProject project = dao.fetch(WelfareProject.class, projectId);
|
||||
|
||||
Date selectTime = new Date();
|
||||
String additionalInfoValidationMessage = welfareProjectService.validateSelectionAdditionalInfo(selections, selectTime);
|
||||
if (additionalInfoValidationMessage != null) {
|
||||
@@ -112,9 +106,9 @@ public class WelfareUserSelectController {
|
||||
return Result.error(groupQuantityValidationMessage);
|
||||
}
|
||||
|
||||
int sum = Arrays.stream(selections).mapToInt(selection -> Objects.requireNonNullElse(selection.getSelectNum(), 0)).sum();
|
||||
if (sum > project.getMultiSelectNum()) {
|
||||
return Result.error("选择的数量不能超过" + project.getMultiSelectNum());
|
||||
String projectQuantityValidationMessage = welfareProjectService.validateProjectSelectionQuantity(projectId, selections);
|
||||
if (projectQuantityValidationMessage != null) {
|
||||
return Result.error(projectQuantityValidationMessage);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -30,9 +30,19 @@ public class WelfareExportEntityTc {
|
||||
@Excel(name = "已选福利", width = 80d)
|
||||
private String optionName;
|
||||
|
||||
@Excel(name = "收货信息", width = 100d)
|
||||
// V4 原“收货信息”导出列保留字段,现按 V3 规则拆分为收货人、联系方式和收货地址。
|
||||
// @Excel(name = "收货信息", width = 100d)
|
||||
private String receiveAddress;
|
||||
|
||||
@Excel(name = "收货人", width = 15d)
|
||||
private String recipient;
|
||||
|
||||
@Excel(name = "联系方式", width = 20d)
|
||||
private String phone;
|
||||
|
||||
@Excel(name = "收货地址", width = 60d)
|
||||
private String address;
|
||||
|
||||
@Excel(name = "快递单号", width = 40d)
|
||||
private String courierNumber;
|
||||
|
||||
|
||||
@@ -207,6 +207,14 @@ public class WelfareProject extends BaseModel implements SysHomeConvert {
|
||||
@ColDefine(type = ColType.VARCHAR, width = 200)
|
||||
private String cover;
|
||||
|
||||
/**
|
||||
* 福利通知附件下载地址,只允许关联一个 Word 文件。
|
||||
*/
|
||||
@Column
|
||||
@Comment("福利通知附件")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 200)
|
||||
private String noticeAttachment;
|
||||
|
||||
@Column
|
||||
@Comment("是否全年生日蛋糕卷")
|
||||
@ColDefine(type = ColType.BOOLEAN)
|
||||
|
||||
@@ -37,4 +37,10 @@ public class WelfareSelectionSituationPageForm extends PageForm {
|
||||
|
||||
@ApiModelProperty("人员属性")
|
||||
private String userAttribute;
|
||||
|
||||
@ApiModelProperty("人员类型")
|
||||
private String[] personTypes;
|
||||
|
||||
@ApiModelProperty("在职状态")
|
||||
private String[] userStates;
|
||||
}
|
||||
|
||||
@@ -13,10 +13,32 @@ import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
public interface WelfareListService extends BaseService<WelfareList> {
|
||||
|
||||
/**
|
||||
* 导出一个福利项目下指定套餐的报销表。
|
||||
*
|
||||
* @param projectId 福利项目 ID
|
||||
* @param optionId 福利套餐 ID,必须属于指定福利项目
|
||||
* @param response XLSX 文件下载响应
|
||||
*/
|
||||
void doExcelByOptionId(String projectId, String optionId, HttpServletResponse response);
|
||||
|
||||
/**
|
||||
* 导出供应商所需的选择数据,并按套餐创建工作表。
|
||||
*
|
||||
* @param projectId 福利项目 ID
|
||||
* @param unionId 可选的分工会 ID;为空时按当前登录人的数据范围导出
|
||||
* @param response XLSX 文件下载响应
|
||||
*/
|
||||
void exportSelectData(String projectId, String unionId, HttpServletResponse response);
|
||||
|
||||
/**
|
||||
* 导入指定福利项目、套餐下的快递单号。
|
||||
*
|
||||
* @param file XLS 或 XLSX 文件,列为工号、姓名和最多四个快递单号
|
||||
* @param projectId 福利项目 ID
|
||||
* @param optionId 福利套餐 ID,必须属于指定福利项目
|
||||
* @return 成功或失败的导入结果
|
||||
*/
|
||||
Result importCourierNumber(TempFile file, String projectId, String optionId);
|
||||
|
||||
void doEditWelfareData(String editWelfareData, String welfareOptions);
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.budwk.app.zhgh.welfare.service;
|
||||
import com.budwk.app.base.service.BaseService;
|
||||
import com.budwk.app.zhgh.welfare.model.WelfareProject;
|
||||
import com.budwk.app.zhgh.welfare.model.WelfareUserSelection;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
@@ -25,6 +26,14 @@ public interface WelfareProjectService extends BaseService<WelfareProject> {
|
||||
*/
|
||||
List<WelfareProject> listHistoryProject();
|
||||
|
||||
/**
|
||||
* 查询福利通知详情页需要的项目和附件信息。
|
||||
*
|
||||
* @param projectId 福利项目 ID
|
||||
* @return 包含 projectName、fileId、fileName、suffix、attachmentUrl 和 previewUrl 的详情数据;未上传附件时文件字段为空
|
||||
*/
|
||||
NutMap getNoticeAttachmentInfo(String projectId);
|
||||
|
||||
/**
|
||||
* 校验福利选择是否保留系统默认福利。
|
||||
*
|
||||
@@ -44,11 +53,19 @@ public interface WelfareProjectService extends BaseService<WelfareProject> {
|
||||
String validateGroupSelectionQuantity(String projectId, WelfareUserSelection[] selections);
|
||||
|
||||
/**
|
||||
* 校验福利选择附加信息。配送时间为必填项,必须按日期粒度不早于本次选择时间;
|
||||
* 同一次提交的全部福利项必须使用相同配送时间和备注。
|
||||
* 校验福利项目一次提交的总选择份数。
|
||||
*
|
||||
* @param selections 用户提交的福利选择数组,每项需包含 deliveryDate,可选包含 remark
|
||||
* @param selectTime 后端生成的本次选择时间,用于防止客户端伪造日期
|
||||
* @param projectId 福利项目 ID,用于读取单选或多选模式及多选数量上限
|
||||
* @param selections 用户提交的福利选择数组,每项需包含 selectOptionId 和 selectNum
|
||||
* @return 校验通过返回 {@code null};超过项目上限或多选项目未配置上限时返回错误提示
|
||||
*/
|
||||
String validateProjectSelectionQuantity(String projectId, WelfareUserSelection[] selections);
|
||||
|
||||
/**
|
||||
* 校验福利选择附加信息。同一次提交的全部福利项必须使用相同备注,配送时间字段已停用。
|
||||
*
|
||||
* @param selections 用户提交的福利选择数组,每项可包含 remark
|
||||
* @param selectTime 后端生成的本次选择时间,保留该参数以兼容现有调用接口
|
||||
* @return 校验通过返回 {@code null},否则返回可直接展示的错误提示
|
||||
*/
|
||||
String validateSelectionAdditionalInfo(WelfareUserSelection[] selections, Date selectTime);
|
||||
|
||||
+14
-1
@@ -15,12 +15,25 @@ public interface WelfareSelectionSituationService extends BaseService<WelfareLis
|
||||
* 根据选择情况页面的全部查询条件发送钉钉消息。
|
||||
*
|
||||
* @param pageForm 查询条件,包含项目、福利选项、人员信息、组织范围及选择状态
|
||||
* @param messageContent 纯文本消息内容,不应包含HTML标签或富文本样式
|
||||
* @param messageContent 纯文本消息内容,不应包含HTML标签或富文本样式;发送前会补充福利通知详情地址
|
||||
* @return 实际提交到钉钉消息渠道的去重人员数量
|
||||
*/
|
||||
int sendDingTalkMessage(WelfareSelectionSituationPageForm pageForm, String messageContent);
|
||||
|
||||
/**
|
||||
* 按选择情况页面的查询条件导出人员选择明细。
|
||||
*
|
||||
* @param pageForm 页面查询条件,包含项目、福利选项、姓名、工号、组织范围、人员类型和选择状态
|
||||
* @param response HTTP响应,返回包含收货备注、快递单号和签字图片的XLSX文件流
|
||||
*/
|
||||
void exportXlsx(WelfareSelectionSituationPageForm pageForm, HttpServletResponse response);
|
||||
|
||||
/**
|
||||
* 导出福利领取表。
|
||||
*
|
||||
* @param pageForm 页面查询条件,projectId指定福利项目
|
||||
* @param response HTTP响应,返回包含福利选项、收货备注和签字图片的XLSX文件流
|
||||
*/
|
||||
void receiveXlsx(WelfareSelectionSituationPageForm pageForm, HttpServletResponse response);
|
||||
|
||||
/**
|
||||
|
||||
@@ -62,7 +62,7 @@ public interface WelfareStatisticsService extends BaseService<WelfareProject> {
|
||||
* @param projectId
|
||||
* @param response
|
||||
*/
|
||||
void exportByWelfareOptions(String projectId, HttpServletResponse response);
|
||||
void exportByWelfareOptions(String projectId, String unionId, HttpServletResponse response);
|
||||
|
||||
/**
|
||||
* 导出汇总表
|
||||
|
||||
@@ -15,8 +15,8 @@ import javax.servlet.http.HttpServletResponse;
|
||||
public interface WelfareUserEvaluationService extends BaseService<WelfareUserEvaluation> {
|
||||
|
||||
/**
|
||||
* 校验当前用户是否已到福利配送日期。只有存在选择记录、全部记录已设置配送日期,且当前日期
|
||||
* 不早于配送日期时才允许打开或提交评价。
|
||||
* 校验当前用户是否已经选择福利且项目活动已经结束。活动结束时间以福利项目的
|
||||
* choiceTimeEnd 为准,PC、移动端与接口提交使用同一规则。
|
||||
*
|
||||
* @param projectId 福利项目ID
|
||||
* @param userId 当前登录用户ID
|
||||
|
||||
@@ -61,8 +61,17 @@ public class WelfareListServiceImpl extends BaseServiceImpl<WelfareList> impleme
|
||||
|
||||
@Override
|
||||
public void doExcelByOptionId(String projectId, String optionId, HttpServletResponse response) {
|
||||
if (StrUtil.isBlank(projectId) || StrUtil.isBlank(optionId)) {
|
||||
log.warn("导出报销表失败:福利项目或套餐不能为空");
|
||||
return;
|
||||
}
|
||||
WelfareProject project = dao().fetch(WelfareProject.class, projectId);
|
||||
WelfareProjectSubjectOption subjectOption = dao().fetch(WelfareProjectSubjectOption.class, optionId);
|
||||
WelfareProjectSubjectOption subjectOption = dao().fetch(WelfareProjectSubjectOption.class,
|
||||
Cnd.where("id", "=", optionId).and("welfareId", "=", projectId));
|
||||
if (project == null || subjectOption == null) {
|
||||
log.warn("导出报销表失败:福利项目或套餐不存在,projectId={}, optionId={}", projectId, optionId);
|
||||
return;
|
||||
}
|
||||
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
@@ -75,30 +84,35 @@ public class WelfareListServiceImpl extends BaseServiceImpl<WelfareList> impleme
|
||||
wpus.selectNum
|
||||
FROM
|
||||
welfare_project_user_selection wpus
|
||||
LEFT JOIN `welfare_list` wl ON wpus.selectUserId = wl.userId
|
||||
INNER JOIN `welfare_list` wl ON wl.userId = wpus.selectUserId
|
||||
AND wl.projectId = wpus.welfareId
|
||||
LEFT JOIN `sys_user` u ON u.id = wl.userId
|
||||
LEFT JOIN sys_union un ON un.id = wl.welfareUnitId
|
||||
LEFT JOIN sys_unit it ON it.id = wl.welfareSecondLevelUnitId
|
||||
LEFT JOIN sys_union un ON un.id = wl.welfareUnionId
|
||||
LEFT JOIN sys_unit it ON it.id = wl.welfareUnitId
|
||||
WHERE
|
||||
wl.projectId = @welfareId
|
||||
AND wpus.selectOptionId = @selectOptionId and wpus.selectNum!=0
|
||||
|
||||
wpus.welfareId = @welfareId
|
||||
AND wpus.selectOptionId = @selectOptionId
|
||||
AND IFNULL(wpus.selectNum, 0) <> 0
|
||||
ORDER BY wl.welfareUnionName, wl.welfareUnitName, u.loginname
|
||||
""").setParam("welfareId", projectId).setParam("selectOptionId", optionId);
|
||||
|
||||
List<NutMap> mapList = listMap(sql);
|
||||
|
||||
List<String> userId = mapList.stream().map(m -> m.getString("id")).collect(Collectors.toList());
|
||||
|
||||
List<WelfareCourierNumber> courierNumberList = dao().query(WelfareCourierNumber.class,
|
||||
List<WelfareCourierNumber> courierNumberList = userId.isEmpty()
|
||||
? Collections.emptyList()
|
||||
: dao().query(WelfareCourierNumber.class,
|
||||
Cnd.where("selectUserId", "in", userId)
|
||||
.and("welfareId", "=", projectId)
|
||||
.and("selectOptionId", "=", optionId));
|
||||
.and("selectOptionId", "=", optionId)
|
||||
.asc("createdAt"));
|
||||
|
||||
List<String> nameList = List.of("one", "two", "three", "four");
|
||||
for (NutMap map : mapList) {
|
||||
List<WelfareCourierNumber> userCourierNumbers = courierNumberList.stream()
|
||||
.filter(c -> c.getSelectUserId().equals(map.getString("id"))).collect(Collectors.toList());
|
||||
for (int i = 0; i < userCourierNumbers.size(); i++) {
|
||||
for (int i = 0; i < userCourierNumbers.size() && i < nameList.size(); i++) {
|
||||
map.setv(nameList.get(i) + "CourierNumber", userCourierNumbers.get(i).getCourierNumber());
|
||||
}
|
||||
}
|
||||
@@ -138,14 +152,13 @@ public class WelfareListServiceImpl extends BaseServiceImpl<WelfareList> impleme
|
||||
exportParams.setSheetName(un);
|
||||
service.createSheetForMap(workbook, exportParams, exportEntities, v);
|
||||
}*/
|
||||
try {
|
||||
ExportParams exportParams = new ExportParams();
|
||||
exportParams.setType(ExcelType.XSSF);
|
||||
exportParams.setTitle(project.getName() + subjectOption.getOptionName() + "发放名单");
|
||||
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, exportEntities, mapList);
|
||||
ExportParams exportParams = new ExportParams();
|
||||
exportParams.setType(ExcelType.XSSF);
|
||||
exportParams.setTitle(project.getName() + subjectOption.getOptionName() + "报销表");
|
||||
try (Workbook workbook = ExcelExportUtil.exportExcel(exportParams, exportEntities, mapList)) {
|
||||
CommonDownloadUtil.download(project.getName() + subjectOption.getOptionName() + ".xlsx", workbook, response);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
log.error("导出报销表失败,projectId={}, optionId={}", projectId, optionId, e);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -153,6 +166,10 @@ public class WelfareListServiceImpl extends BaseServiceImpl<WelfareList> impleme
|
||||
@Override
|
||||
public void exportSelectData(String projectId, String unionId, HttpServletResponse response) {
|
||||
WelfareProject welfareProject = dao().fetch(WelfareProject.class, Cnd.where("id", "=", projectId));
|
||||
if (welfareProject == null) {
|
||||
log.warn("导出供货商选择数据失败:福利项目不存在,projectId={}", projectId);
|
||||
return;
|
||||
}
|
||||
String projectName = welfareProject.getName();
|
||||
Cnd cnd = Cnd.NEW();
|
||||
Sql sql = Sqls.create("""
|
||||
@@ -170,16 +187,18 @@ public class WelfareListServiceImpl extends BaseServiceImpl<WelfareList> impleme
|
||||
left join `sys_user` u on u.id = wpus.selectUserId
|
||||
left join welfare_project_subject_option wpso on wpso.id = wpus.selectOptionId
|
||||
left join welfare_list wl on wl.userId = wpus.selectUserId and wl.projectId=wpus.welfareId
|
||||
LEFT JOIN sys_union un ON un.id = wl.welfareUnitId
|
||||
LEFT JOIN sys_unit it ON it.id = wl.welfareSecondLevelUnitId
|
||||
LEFT JOIN sys_union un ON un.id = wl.welfareUnionId
|
||||
LEFT JOIN sys_unit it ON it.id = wl.welfareUnitId
|
||||
$condition
|
||||
""");
|
||||
cnd.and("wpus.welfareId", "=", projectId);
|
||||
|
||||
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name(), RoleConstant.SCHOOL_UNION_WELFARE_ADMIN.name())) {
|
||||
cnd.and("wl.welfareUnitId", "=", SecurityUtil.getUnionId());
|
||||
if (AuthUtil.hasRole(RoleConstant.BRANCH_UNION_CHAIRMAN.name())
|
||||
|| AuthUtil.hasRole(RoleConstant.BRANCH_UNION_ADMIN.name())
|
||||
|| AuthUtil.hasRole(RoleConstant.BRANCH_UNION_WENTI_SPORTS.name())) {
|
||||
cnd.and("wl.welfareUnionId", "=", SecurityUtil.getUnionId());
|
||||
}
|
||||
cnd.andEX("wl.welfareUnitId", "=", unionId);
|
||||
cnd.andEX("wl.welfareUnionId", "=", unionId);
|
||||
cnd.groupBy("wpus.selectUserId", "wpus.selectOptionId");
|
||||
cnd.asc("it.unitcode");
|
||||
sql.setCondition(cnd);
|
||||
@@ -207,12 +226,21 @@ public class WelfareListServiceImpl extends BaseServiceImpl<WelfareList> impleme
|
||||
try {
|
||||
CommonDownloadUtil.download(projectName + ".xlsx", workbook, response);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
log.error("导出供货商选择数据失败,projectId={}", projectId, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public Result importCourierNumber(TempFile file, String projectId, String optionId) {
|
||||
if (StrUtil.isBlank(projectId) || StrUtil.isBlank(optionId)) {
|
||||
return Result.error("福利项目和套餐不能为空");
|
||||
}
|
||||
WelfareProjectSubjectOption subjectOption = dao().fetch(WelfareProjectSubjectOption.class,
|
||||
Cnd.where("id", "=", optionId).and("welfareId", "=", projectId));
|
||||
if (subjectOption == null) {
|
||||
return Result.error("福利套餐不存在或不属于当前福利项目");
|
||||
}
|
||||
if (Lang.isEmpty(file)) {
|
||||
return Result.error("上传的文件不能为空");
|
||||
}
|
||||
@@ -226,7 +254,7 @@ public class WelfareListServiceImpl extends BaseServiceImpl<WelfareList> impleme
|
||||
ImportParams importParams = new ImportParams();
|
||||
excelList = ExcelImportUtil.importExcel(file.getFile(), CourierNumberExcelMode.class, importParams);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
log.warn("读取快递单号导入文件失败", e);
|
||||
return Result.error("读取不到数据,请检查excel文件格式");
|
||||
}
|
||||
|
||||
@@ -236,7 +264,7 @@ public class WelfareListServiceImpl extends BaseServiceImpl<WelfareList> impleme
|
||||
|
||||
try {
|
||||
if (excelList.stream().anyMatch(v -> StrUtil.isBlank(v.getLoginName()))) {
|
||||
return Result.error("工号和快递单号不能为空");
|
||||
return Result.error("工号不能为空");
|
||||
}
|
||||
|
||||
List<CourierNumberExcelMode> filterExcelList = excelList.stream().collect(Collectors.collectingAndThen(
|
||||
@@ -249,6 +277,9 @@ public class WelfareListServiceImpl extends BaseServiceImpl<WelfareList> impleme
|
||||
Sql loginNameSql = Sqls.create("select id,loginname from sys_user where loginname in (@loginNameList)");
|
||||
loginNameSql.setParam("loginNameList", loginNameList);
|
||||
List<NutMap> loginNameMaps = listMap(loginNameSql);
|
||||
if (loginNameMaps.isEmpty()) {
|
||||
return Result.error("Excel中的工号均未匹配到系统用户");
|
||||
}
|
||||
|
||||
List<String> userIds = loginNameMaps.stream().map(v -> v.getString("id")).collect(Collectors.toList());
|
||||
|
||||
@@ -291,12 +322,13 @@ public class WelfareListServiceImpl extends BaseServiceImpl<WelfareList> impleme
|
||||
welfareCourierNumbers.add(courierNumber);
|
||||
}
|
||||
}
|
||||
insert(welfareCourierNumbers);
|
||||
if (!welfareCourierNumbers.isEmpty()) {
|
||||
insert(welfareCourierNumbers);
|
||||
}
|
||||
|
||||
return Result.success("导入成功");
|
||||
} catch (Exception e) {
|
||||
|
||||
e.printStackTrace();
|
||||
log.error("导入快递单号失败,projectId={}, optionId={}", projectId, optionId, e);
|
||||
return Result.error("导入快递单号失败");
|
||||
}
|
||||
|
||||
@@ -685,12 +717,10 @@ public class WelfareListServiceImpl extends BaseServiceImpl<WelfareList> impleme
|
||||
t2.loginname,
|
||||
t2.username,
|
||||
t2.sex,
|
||||
DATE_FORMAT(t2.birthday, '%Y-%m-%d') AS birthday,
|
||||
threeUnit.name AS threeUnitName
|
||||
DATE_FORMAT(t2.birthday, '%Y-%m-%d') AS birthday
|
||||
FROM
|
||||
`welfare_list` t1
|
||||
LEFT JOIN sys_user t2 ON t2.id = t1.userId
|
||||
LEFT JOIN sys_unit threeUnit ON threeUnit.id = t2.threeUnitId
|
||||
$condition
|
||||
""");
|
||||
Cnd cnd = Cnd.NEW();
|
||||
@@ -735,7 +765,6 @@ public class WelfareListServiceImpl extends BaseServiceImpl<WelfareList> impleme
|
||||
entities.add(new ExcelExportEntity("在职状态", "userState", 20));
|
||||
entities.add(new ExcelExportEntity("所属工会", "welfareUnionName", 20));
|
||||
entities.add(new ExcelExportEntity("所属单位", "welfareUnitName", 20));
|
||||
entities.add(new ExcelExportEntity("三级单位", "threeUnitName", 20));
|
||||
entities.add(new ExcelExportEntity("备注", "remark", 20));
|
||||
|
||||
// 设置导出参数
|
||||
|
||||
+117
-19
@@ -4,6 +4,7 @@ import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.base.service.impl.BaseServiceImpl;
|
||||
import com.budwk.app.sys.models.Sys_home_activity;
|
||||
import com.budwk.app.sys.models.Sys_file;
|
||||
import com.budwk.app.sys.models.Sys_user;
|
||||
import com.budwk.app.zhgh.welfare.model.WelfareList;
|
||||
import com.budwk.app.zhgh.welfare.model.WelfareProject;
|
||||
@@ -79,6 +80,25 @@ public class WelfareProjectServiceImpl extends BaseServiceImpl<WelfareProject> i
|
||||
return dao().query(WelfareProject.class, Cnd.NEW().desc(WelfareProject::getYear).desc(WelfareProject::getChoiceTimeStart));
|
||||
}
|
||||
|
||||
@Override
|
||||
public NutMap getNoticeAttachmentInfo(String projectId) {
|
||||
WelfareProject project = fetch(projectId);
|
||||
if (project == null) {
|
||||
throw new IllegalArgumentException("福利项目不存在");
|
||||
}
|
||||
NutMap result = NutMap.NEW().addv("projectName", project.getName());
|
||||
if (StrUtil.isBlank(project.getNoticeAttachment())) {
|
||||
return result;
|
||||
}
|
||||
Sys_file noticeFile = resolveNoticeAttachmentFile(project.getNoticeAttachment());
|
||||
return result
|
||||
.addv("fileId", noticeFile.getId())
|
||||
.addv("fileName", noticeFile.getName())
|
||||
.addv("suffix", noticeFile.getSuffix())
|
||||
.addv("attachmentUrl", noticeFile.getDownloadPath())
|
||||
.addv("previewUrl", "/platform/sys/file/convertPDF?id=" + noticeFile.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String validateSystemDefaultOptionSelection(String projectId, WelfareUserSelection[] selections) {
|
||||
List<WelfareProjectSubjectOption> systemDefaultOptions = dao().query(WelfareProjectSubjectOption.class,
|
||||
@@ -182,8 +202,42 @@ public class WelfareProjectServiceImpl extends BaseServiceImpl<WelfareProject> i
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验一次选择提交中的配送日期和备注。日期比较统一截断到当天零点,允许选择当天配送;
|
||||
* 多项福利的附加信息必须一致,避免同一次选择产生互相冲突的评价开放时间。
|
||||
* 校验福利项目总选择份数。单选项目固定只能选择 1 份,不读取仅供多选项目使用的
|
||||
* multiSelectNum;多选项目必须配置有效的数量上限。
|
||||
*
|
||||
* @param projectId 福利项目 ID
|
||||
* @param selections 当前用户提交的福利选择数据
|
||||
* @return 校验通过返回 {@code null},否则返回可直接展示的错误提示
|
||||
*/
|
||||
@Override
|
||||
public String validateProjectSelectionQuantity(String projectId, WelfareUserSelection[] selections) {
|
||||
WelfareProject project = fetch(projectId);
|
||||
if (project == null) {
|
||||
return "福利项目不存在";
|
||||
}
|
||||
int selectedQuantity = Arrays.stream(selections == null ? new WelfareUserSelection[0] : selections)
|
||||
.filter(Objects::nonNull)
|
||||
.mapToInt(selection -> Objects.requireNonNullElse(selection.getSelectNum(), 0))
|
||||
.sum();
|
||||
boolean multipleSelection = "checkBox".equals(project.getIsCheckBox());
|
||||
int maxSelectQuantity;
|
||||
if (multipleSelection) {
|
||||
if (project.getMultiSelectNum() == null || project.getMultiSelectNum() <= 0) {
|
||||
return "福利项目未配置最多选择数量";
|
||||
}
|
||||
maxSelectQuantity = project.getMultiSelectNum();
|
||||
} else {
|
||||
maxSelectQuantity = 1;
|
||||
}
|
||||
if (selectedQuantity > maxSelectQuantity) {
|
||||
return "选择的数量不能超过" + maxSelectQuantity;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验一次选择提交中的备注。配送时间字段已停用,新提交数据统一清空该字段;
|
||||
* 多项福利的备注必须一致,避免同一次选择出现互相冲突的附加信息。
|
||||
*
|
||||
* @param selections 当前提交的福利选择数据
|
||||
* @param selectTime 后端生成的选择时间
|
||||
@@ -194,30 +248,23 @@ public class WelfareProjectServiceImpl extends BaseServiceImpl<WelfareProject> i
|
||||
if (selections == null || selections.length == 0) {
|
||||
return "请至少选择一项福利";
|
||||
}
|
||||
Date selectionDate = DateUtil.beginOfDay(selectTime == null ? new Date() : selectTime);
|
||||
Date submittedDeliveryDate = null;
|
||||
String submittedRemark = null;
|
||||
boolean firstSelection = true;
|
||||
for (WelfareUserSelection selection : selections) {
|
||||
if (selection == null || selection.getDeliveryDate() == null) {
|
||||
return "请选择配送时间";
|
||||
}
|
||||
Date deliveryDate = DateUtil.beginOfDay(selection.getDeliveryDate());
|
||||
if (deliveryDate.before(selectionDate)) {
|
||||
return "配送时间不能早于选择时间";
|
||||
}
|
||||
if (submittedDeliveryDate == null) {
|
||||
submittedDeliveryDate = deliveryDate;
|
||||
submittedRemark = StrUtil.trim(selection.getRemark());
|
||||
} else if (!submittedDeliveryDate.equals(deliveryDate)) {
|
||||
return "同一次选择的配送时间必须一致";
|
||||
} else if (!Objects.equals(submittedRemark, StrUtil.trim(selection.getRemark()))) {
|
||||
return "同一次选择的备注必须一致";
|
||||
if (selection == null) {
|
||||
return "福利选择数据不能为空";
|
||||
}
|
||||
String remark = StrUtil.trim(selection.getRemark());
|
||||
if (firstSelection) {
|
||||
submittedRemark = remark;
|
||||
firstSelection = false;
|
||||
} else if (!Objects.equals(submittedRemark, remark)) {
|
||||
return "同一次选择的备注必须一致";
|
||||
}
|
||||
if (remark != null && remark.length() > 200) {
|
||||
return "备注不能超过200字";
|
||||
}
|
||||
selection.setDeliveryDate(deliveryDate);
|
||||
selection.setDeliveryDate(null);
|
||||
selection.setRemark(remark);
|
||||
}
|
||||
return null;
|
||||
@@ -257,6 +304,7 @@ public class WelfareProjectServiceImpl extends BaseServiceImpl<WelfareProject> i
|
||||
@Override
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public void saveProject(WelfareProject project) {
|
||||
normalizeNoticeAttachment(project);
|
||||
// 保存前根据福利选项分组去重并校验选择数量范围,防止非法 JSON 配置进入数据库。
|
||||
normalizeGroupSelectionConfigs(project);
|
||||
// 插入项目
|
||||
@@ -272,6 +320,7 @@ public class WelfareProjectServiceImpl extends BaseServiceImpl<WelfareProject> i
|
||||
@Override
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public void updateProject(WelfareProject project) {
|
||||
normalizeNoticeAttachment(project);
|
||||
// 修改项目时同样归一化分组配置,确保新增、编辑两条保存链路规则一致。
|
||||
normalizeGroupSelectionConfigs(project);
|
||||
// 更新项目
|
||||
@@ -325,6 +374,55 @@ public class WelfareProjectServiceImpl extends BaseServiceImpl<WelfareProject> i
|
||||
dao().clear(Sys_home_activity.class, Cnd.where("id", "=", id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验福利通知附件数量和真实文件类型,并将提交地址归一化为系统文件下载地址。
|
||||
*
|
||||
* @param project 待保存的福利项目,noticeAttachment 应为单个系统文件下载地址
|
||||
*/
|
||||
private void normalizeNoticeAttachment(WelfareProject project) {
|
||||
String attachment = StrUtil.trim(project.getNoticeAttachment());
|
||||
if (StrUtil.isBlank(attachment)) {
|
||||
project.setNoticeAttachment(null);
|
||||
return;
|
||||
}
|
||||
if (attachment.contains(",")) {
|
||||
throw new IllegalArgumentException("福利通知附件只能上传一个文件");
|
||||
}
|
||||
Sys_file noticeFile = resolveNoticeAttachmentFile(attachment);
|
||||
String suffix = StrUtil.blankToDefault(noticeFile.getSuffix(), "").toLowerCase(Locale.ROOT);
|
||||
if (!"doc".equals(suffix) && !"docx".equals(suffix)) {
|
||||
throw new IllegalArgumentException("福利通知附件只能上传Word文件");
|
||||
}
|
||||
project.setNoticeAttachment(noticeFile.getDownloadPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* 从系统下载地址解析文件 ID,并确认文件记录真实存在。
|
||||
*
|
||||
* @param attachment 系统文件下载地址,格式为 /platform/sys/file/download?id=文件ID
|
||||
* @return 对应的系统文件记录
|
||||
*/
|
||||
private Sys_file resolveNoticeAttachmentFile(String attachment) {
|
||||
int idIndex = attachment.indexOf("id=");
|
||||
if (idIndex < 0) {
|
||||
throw new IllegalArgumentException("福利通知附件地址无效");
|
||||
}
|
||||
String fileId = attachment.substring(idIndex + 3);
|
||||
int parameterIndex = fileId.indexOf('&');
|
||||
if (parameterIndex >= 0) {
|
||||
fileId = fileId.substring(0, parameterIndex);
|
||||
}
|
||||
fileId = fileId.trim();
|
||||
if (StrUtil.isBlank(fileId)) {
|
||||
throw new IllegalArgumentException("福利通知附件地址无效");
|
||||
}
|
||||
Sys_file noticeFile = dao().fetch(Sys_file.class, fileId);
|
||||
if (noticeFile == null) {
|
||||
throw new IllegalArgumentException("福利通知附件文件不存在,请重新上传");
|
||||
}
|
||||
return noticeFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据福利选项中的非空分组生成最终配置数组,并校验最小、最大选择数量。
|
||||
* 最大选择数量为 0 时表示不限,不参与最小值与最大值的大小比较。
|
||||
|
||||
+133
-17
@@ -14,6 +14,7 @@ import com.budwk.app.base.exception.BaseException;
|
||||
import com.budwk.app.base.page.Pagination;
|
||||
import com.budwk.app.base.service.impl.BaseServiceImpl;
|
||||
import com.budwk.app.base.sms.SmsService;
|
||||
import com.budwk.app.web.commons.base.Globals;
|
||||
import com.budwk.app.base.utils.CommonDownloadUtil;
|
||||
import com.budwk.app.base.utils.PageUtil;
|
||||
import com.budwk.app.sys.models.Sys_file;
|
||||
@@ -21,6 +22,7 @@ import com.budwk.app.sys.services.SysFileService;
|
||||
import com.budwk.app.sys.utils.SysFileMinIoUtil;
|
||||
import com.budwk.app.web.commons.auth.utils.AuthUtil;
|
||||
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
|
||||
import com.budwk.app.zhgh.welfare.model.WelfareCourierNumber;
|
||||
import com.budwk.app.zhgh.welfare.model.WelfareList;
|
||||
import com.budwk.app.zhgh.welfare.model.WelfareProject;
|
||||
import com.budwk.app.zhgh.welfare.model.WelfareProjectSubjectOption;
|
||||
@@ -47,6 +49,7 @@ import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@IocBean(args = {"refer:dao"})
|
||||
@@ -75,7 +78,7 @@ public class WelfareSelectionSituationServiceImpl extends BaseServiceImpl<Welfar
|
||||
GROUP_CONCAT(DISTINCT t3.optionName ,'(',t2.selectNum,'份)') AS selectedOptions,
|
||||
GROUP_CONCAT(DISTINCT t2.mobile) AS mobile,
|
||||
GROUP_CONCAT(DISTINCT t2.receiveAddress) AS receiveAddress,
|
||||
MAX(t2.deliveryDate) AS deliveryDate,
|
||||
MAX(t2.remark) AS remark,
|
||||
t4.username AS userName,
|
||||
t4.loginname AS loginName,
|
||||
t4.sex,
|
||||
@@ -143,13 +146,36 @@ public class WelfareSelectionSituationServiceImpl extends BaseServiceImpl<Welfar
|
||||
}
|
||||
|
||||
String title = project.getName() + "-福利通知";
|
||||
boolean success = smsService.sendMsg("6", loginNames, null, title, plainContent, null, null);
|
||||
String detailUrl = StrUtil.removeSuffix(Globals.AppDomain, "/")
|
||||
+ "/platform/welfare/notice/index?id=" + project.getId();
|
||||
String detailLine = "查看详情:" + detailUrl;
|
||||
String messageBody = removeExistingNoticeDetailLinks(plainContent, detailUrl);
|
||||
String sendContent = StrUtil.isBlank(messageBody) ? detailLine : messageBody + "\n" + detailLine;
|
||||
// 详情地址已经包含在消息正文中,跳转参数保持为空,避免消息中心再次追加 PC、移动端详情链接。
|
||||
boolean success = smsService.sendMsg("6", loginNames, null, title, sendContent, null, null);
|
||||
if (!success) {
|
||||
throw new BaseException("钉钉消息发送失败,请检查消息发送配置或稍后重试");
|
||||
}
|
||||
return loginNames.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除正文中已存在的当前福利详情链接,避免管理员在默认链接前继续输入文字后重复追加。
|
||||
*
|
||||
* @param content 管理员在发送弹窗中编辑的纯文本正文,可在任意位置包含“查看详情:URL”
|
||||
* @param detailUrl 当前福利项目的标准详情地址
|
||||
* @return 已移除全部当前项目详情链接并清理空白行的消息正文;没有其他正文时返回空字符串
|
||||
*/
|
||||
private String removeExistingNoticeDetailLinks(String content, String detailUrl) {
|
||||
String detailPattern = "查看详情\\s*[::]\\s*" + Pattern.quote(detailUrl);
|
||||
String contentWithoutDetail = content.replaceAll(detailPattern, "");
|
||||
return Arrays.stream(contentWithoutDetail.split("\\R"))
|
||||
.map(String::trim)
|
||||
.filter(StrUtil::isNotBlank)
|
||||
.collect(Collectors.joining("\n"))
|
||||
.trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建选择情况列表与消息接收人共用的筛选条件。
|
||||
* 除页面条件外会主动叠加当前登录人的分工会数据范围。
|
||||
@@ -169,6 +195,8 @@ public class WelfareSelectionSituationServiceImpl extends BaseServiceImpl<Welfar
|
||||
cnd.andEX("t2.selectOptionId", "=", pageForm.getWelfareOptionId());
|
||||
cnd.andEX("t4.aidFundMemberUserType", "=", pageForm.getAidFundMemberUserType());
|
||||
cnd.andEX("t4.userAttribute", "=", pageForm.getUserAttribute());
|
||||
cnd.andEX("t1.personType", "in", pageForm.getPersonTypes());
|
||||
cnd.andEX("t1.userState", "in", pageForm.getUserStates());
|
||||
if (StrUtil.isNotBlank(pageForm.getUserName())) {
|
||||
cnd.where().andLike("t4.username", pageForm.getUserName());
|
||||
}
|
||||
@@ -188,12 +216,16 @@ public class WelfareSelectionSituationServiceImpl extends BaseServiceImpl<Welfar
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
t1.id,
|
||||
t1.userId,
|
||||
t1.welfareUnionName,
|
||||
t1.welfareUnitName,
|
||||
GROUP_CONCAT(DISTINCT t3.optionName,'(',t2.selectNum,'份)') AS selectedOptions,
|
||||
GROUP_CONCAT(DISTINCT t2.mobile) AS mobile,
|
||||
GROUP_CONCAT(DISTINCT t2.userName) AS userName2,
|
||||
GROUP_CONCAT(DISTINCT t2.receiveAddress) AS receiveAddress,
|
||||
GROUP_CONCAT(DISTINCT t2.courierNumber) AS legacyCourierNumber,
|
||||
MAX(t2.userSign) AS userSign,
|
||||
MAX(t2.remark) AS remark,
|
||||
t4.username AS userName,
|
||||
t4.loginname AS loginName,
|
||||
t4.sex
|
||||
@@ -221,6 +253,8 @@ public class WelfareSelectionSituationServiceImpl extends BaseServiceImpl<Welfar
|
||||
}
|
||||
cnd.andEX("t1.welfareUnitId", "in", pageForm.getUnitIds());
|
||||
cnd.andEX("t1.welfareUnionId", "=", pageForm.getUnionId());
|
||||
cnd.andEX("t1.personType", "in", pageForm.getPersonTypes());
|
||||
cnd.andEX("t1.userState", "in", pageForm.getUserStates());
|
||||
|
||||
if (pageForm.getIsSelect() != null) {
|
||||
if (pageForm.getIsSelect()) {
|
||||
@@ -240,6 +274,8 @@ public class WelfareSelectionSituationServiceImpl extends BaseServiceImpl<Welfar
|
||||
cnd.groupBy("t1.id");
|
||||
sql.setCondition(cnd);
|
||||
List<NutMap> list = listMap(sql);
|
||||
fillExportCourierNumbers(list, pageForm);
|
||||
list.forEach(this::fillExportUserSign);
|
||||
|
||||
// 项目
|
||||
WelfareProject project = dao().fetch(WelfareProject.class, pageForm.getProjectId());
|
||||
@@ -259,6 +295,14 @@ public class WelfareSelectionSituationServiceImpl extends BaseServiceImpl<Welfar
|
||||
if(project.getProvideMode() == 3){
|
||||
entities.add(new ExcelExportEntity("收货地址", "receiveAddress", 40));
|
||||
}
|
||||
entities.add(new ExcelExportEntity("备注", "remark", 30));
|
||||
ExcelExportEntity courierNumberEntity = new ExcelExportEntity("快递单号", "courierNumber", 30);
|
||||
courierNumberEntity.setWrap(true);
|
||||
entities.add(courierNumberEntity);
|
||||
ExcelExportEntity userSignEntity = new ExcelExportEntity("签字信息", "userSignBytes", 20);
|
||||
userSignEntity.setType(2);
|
||||
userSignEntity.setExportImageType(2);
|
||||
entities.add(userSignEntity);
|
||||
|
||||
// 设置导出参数
|
||||
ExportParams exportParams = new ExportParams();
|
||||
@@ -271,6 +315,73 @@ public class WelfareSelectionSituationServiceImpl extends BaseServiceImpl<Welfar
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量补充选择情况导出的快递单号。
|
||||
* 当前数据从快递单号表读取;历史数据仍兼容选择记录中的旧快递单号字段。
|
||||
*
|
||||
* @param rows 选择情况导出行,每行必须包含userId,处理后增加courierNumber文本字段
|
||||
* @param pageForm 页面查询条件;welfareOptionId不为空时只导出所选套餐对应的快递单号
|
||||
*/
|
||||
private void fillExportCourierNumbers(List<NutMap> rows, WelfareSelectionSituationPageForm pageForm) {
|
||||
List<String> userIds = rows.stream()
|
||||
.map(row -> row.getString("userId"))
|
||||
.filter(StrUtil::isNotBlank)
|
||||
.distinct()
|
||||
.toList();
|
||||
if (userIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Cnd courierCondition = Cnd.where(WelfareCourierNumber::getWelfareId, "=", pageForm.getProjectId())
|
||||
.and(WelfareCourierNumber::getSelectUserId, "in", userIds)
|
||||
.andEX(WelfareCourierNumber::getSelectOptionId, "=", pageForm.getWelfareOptionId());
|
||||
courierCondition.asc(WelfareCourierNumber::getCreatedAt);
|
||||
Map<String, List<WelfareCourierNumber>> courierNumbersByUser = dao()
|
||||
.query(WelfareCourierNumber.class, courierCondition)
|
||||
.stream()
|
||||
.collect(Collectors.groupingBy(WelfareCourierNumber::getSelectUserId));
|
||||
|
||||
for (NutMap row : rows) {
|
||||
LinkedHashSet<String> courierNumbers = courierNumbersByUser
|
||||
.getOrDefault(row.getString("userId"), Collections.emptyList())
|
||||
.stream()
|
||||
.map(WelfareCourierNumber::getCourierNumber)
|
||||
.filter(StrUtil::isNotBlank)
|
||||
.collect(Collectors.toCollection(LinkedHashSet::new));
|
||||
String legacyCourierNumber = row.getString("legacyCourierNumber");
|
||||
if (StrUtil.isNotBlank(legacyCourierNumber)) {
|
||||
Arrays.stream(legacyCourierNumber.split(","))
|
||||
.map(String::trim)
|
||||
.filter(StrUtil::isNotBlank)
|
||||
.forEach(courierNumbers::add);
|
||||
}
|
||||
row.put("courierNumber", String.join("、", courierNumbers));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将签字文件路径转换为选择情况Excel可识别的图片字节。
|
||||
* 文件记录缺失或文件读取失败时保留空白签字列,避免单个历史文件影响整份导出。
|
||||
*
|
||||
* @param row 选择情况导出行,输入userSign文件路径,输出userSignBytes图片字节
|
||||
*/
|
||||
private void fillExportUserSign(NutMap row) {
|
||||
String userSignPath = row.getString("userSign");
|
||||
if (StrUtil.isBlank(userSignPath)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Sys_file file = dao().fetch(Sys_file.class, Cnd.where(Sys_file::getDownloadPath, "=", userSignPath));
|
||||
if (file == null) {
|
||||
log.warn("选择情况导出未找到签字文件记录,userId={}, userSign={}", row.getString("userId"), userSignPath);
|
||||
return;
|
||||
}
|
||||
row.put("userSignBytes", SysFileMinIoUtil.getFileBytes(file.getBucket(), file.getStoragePath()));
|
||||
} catch (Exception e) {
|
||||
log.warn("选择情况导出签字文件读取失败,userId={}", row.getString("userId"), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receiveXlsx(WelfareSelectionSituationPageForm pageForm, HttpServletResponse response) {
|
||||
|
||||
@@ -278,6 +389,7 @@ public class WelfareSelectionSituationServiceImpl extends BaseServiceImpl<Welfar
|
||||
SELECT
|
||||
t1.*,
|
||||
t2.userSign,
|
||||
MAX(t2.remark) AS remark,
|
||||
t3.username userName,
|
||||
t3.loginname loginName,
|
||||
GROUP_CONCAT(DISTINCT t2.selectOptionId) AS selectOptionIds
|
||||
@@ -308,26 +420,29 @@ public class WelfareSelectionSituationServiceImpl extends BaseServiceImpl<Welfar
|
||||
}
|
||||
}
|
||||
}
|
||||
if (map.get("userSign") != null){
|
||||
if (StrUtil.isNotBlank(map.getString("userSign"))){
|
||||
String userSignPath = map.getString("userSign");
|
||||
try {
|
||||
Sys_file file = dao().fetch(Sys_file.class, Cnd.where(Sys_file::getDownloadPath, "=", map.getString("userSign")));
|
||||
Sys_file file = dao().fetch(Sys_file.class, Cnd.where(Sys_file::getDownloadPath, "=", userSignPath));
|
||||
if (file == null) {
|
||||
// 签字路径没有对应文件记录时保留人员数据,仅将签字单元格导出为空。
|
||||
map.put("userSign", null);
|
||||
log.warn("领取表导出未找到签字文件记录,userId={}, userSign={}", map.getString("userId"), userSignPath);
|
||||
continue;
|
||||
}
|
||||
byte[] userSignBytes = SysFileMinIoUtil.getFileBytes(file.getBucket(), file.getStoragePath());
|
||||
map.put("userSign", userSignBytes);
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
log.error("下载图片失败", e);
|
||||
// 签字文件读取异常不能阻断整张领取表导出,当前人员签字留空。
|
||||
map.put("userSign", null);
|
||||
log.warn("领取表导出签字文件读取失败,userId={}", map.getString("userId"), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<Map<String, Object>> safeList = list.stream().map(nutMap -> {
|
||||
Map<String, Object> map = new HashMap<>(nutMap);
|
||||
return map;
|
||||
}).toList();
|
||||
|
||||
// 分组
|
||||
Map<String, List<Map<String, Object>>> listMap = safeList.stream()
|
||||
.collect(Collectors.groupingBy(n -> (String) n.get("welfareUnionName")));
|
||||
// 保留 NutMap 数据结构,避免 EasyPOI 在 Java 17 下反射普通 HashMap 导致工作表创建失败。
|
||||
Map<String, List<NutMap>> listMap = list.stream()
|
||||
.collect(Collectors.groupingBy(n -> n.getString("welfareUnionName")));
|
||||
|
||||
// 构建 Excel 列
|
||||
List<ExcelExportEntity> entities = new ArrayList<>();
|
||||
@@ -336,22 +451,23 @@ public class WelfareSelectionSituationServiceImpl extends BaseServiceImpl<Welfar
|
||||
optionList.forEach(option -> {
|
||||
entities.add(new ExcelExportEntity(option.getOptionName(), option.getOptionName(), 20));
|
||||
});
|
||||
entities.add(new ExcelExportEntity("备注", "remark", 30));
|
||||
ExcelExportEntity userSignEntity = new ExcelExportEntity("签字", "userSign", 20);
|
||||
userSignEntity.setType(2);
|
||||
userSignEntity.setExportImageType(2);
|
||||
entities.add(userSignEntity);
|
||||
|
||||
// 导出
|
||||
Workbook workbook = new HSSFWorkbook();
|
||||
Workbook workbook = new XSSFWorkbook();
|
||||
listMap.forEach((k, v) -> {
|
||||
ExcelExportService service = new ExcelExportService();
|
||||
ExportParams exportParams = new ExportParams();
|
||||
exportParams.setSheetName(k);
|
||||
exportParams.setType(ExcelType.HSSF);
|
||||
exportParams.setType(ExcelType.XSSF);
|
||||
service.createSheetForMap(workbook, exportParams, entities, v);
|
||||
});
|
||||
|
||||
CommonDownloadUtil.download("领取表.xls", workbook, response);
|
||||
CommonDownloadUtil.download("领取表.xlsx", workbook, response);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+158
-14
@@ -4,11 +4,14 @@ 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.afterturn.easypoi.excel.export.ExcelExportService;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.budwk.app.base.constant.RoleConstant;
|
||||
import com.budwk.app.base.service.impl.BaseServiceImpl;
|
||||
import com.budwk.app.base.utils.CommonDownloadUtil;
|
||||
import com.budwk.app.sys.models.Sys_file;
|
||||
import com.budwk.app.sys.utils.SysFileMinIoUtil;
|
||||
import com.budwk.app.web.commons.auth.utils.AuthUtil;
|
||||
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
|
||||
import com.budwk.app.zhgh.welfare.mode.WelfareExportEntityTc;
|
||||
@@ -31,9 +34,7 @@ import org.nutz.lang.util.NutMap;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -159,9 +160,10 @@ public class WelfareSingleServiceImpl extends BaseServiceImpl implements Welfare
|
||||
u.unitname AS unitName,
|
||||
u.unionname AS unionName,
|
||||
u.mobile,
|
||||
IF(LOCATE('undefined',wpus.receiveAddress)>0,NULL,wpus.receiveAddress) as receiveAddress,
|
||||
GROUP_CONCAT(DISTINCT wpso.optionName ,'(',wpus.selectNum,'份)') optionName,
|
||||
wpus.courierNumber
|
||||
IF(LOCATE('undefined',wpus.receiveAddress)>0,NULL,wpus.receiveAddress) as receiveAddress,
|
||||
GROUP_CONCAT(DISTINCT wpso.optionName ,'(',wpus.selectNum,'份)') optionName,
|
||||
wpus.courierNumber,
|
||||
MAX(wpus.userSign) userSign
|
||||
FROM
|
||||
welfare_project_user_selection wpus
|
||||
LEFT JOIN `vw_user` u ON u.id = wpus.selectUserId
|
||||
@@ -183,6 +185,8 @@ public class WelfareSingleServiceImpl extends BaseServiceImpl implements Welfare
|
||||
|
||||
@Override
|
||||
public Workbook exportReceiveDetail(String projectId, String unionId) {
|
||||
// V4 原横向套餐签领表逻辑保留,当前按 V3 的“每个套餐一个工作表”格式执行。
|
||||
if (false) {
|
||||
|
||||
Sql optionSql = Sqls.create("""
|
||||
SELECT w2.id, w2.optionName
|
||||
@@ -282,9 +286,148 @@ public class WelfareSingleServiceImpl extends BaseServiceImpl implements Welfare
|
||||
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, exportEntities, dataList);
|
||||
|
||||
return workbook;
|
||||
}
|
||||
return exportReceiveDetailV3(projectId, unionId);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成参考项目格式的分工会福利签领表:一个工作表展示人员、套餐份数和签字,并在末行汇总套餐份数。
|
||||
*
|
||||
* @param projectId 福利项目ID
|
||||
* @param unionId 分工会ID
|
||||
* @return XLSX工作簿
|
||||
*/
|
||||
private Workbook exportReceiveDetailV3(String projectId, String unionId) {
|
||||
Sql optionSql = Sqls.create("""
|
||||
SELECT id, optionName
|
||||
FROM welfare_project_subject_option
|
||||
WHERE welfareId = @projectId
|
||||
ORDER BY optionSort, id
|
||||
""");
|
||||
optionSql.setParam("projectId", projectId);
|
||||
List<NutMap> options = listMap(optionSql);
|
||||
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
wl.userId,
|
||||
u.username AS userName,
|
||||
u.loginname AS loginName,
|
||||
wl.welfareUnitName AS unitName,
|
||||
wpus.userSign,
|
||||
wpus.selectOptionId,
|
||||
wpus.selectNum
|
||||
FROM welfare_list wl
|
||||
LEFT JOIN sys_user u ON u.id = wl.userId
|
||||
LEFT JOIN welfare_project_user_selection wpus ON wpus.selectUserId = wl.userId
|
||||
AND wpus.welfareId = wl.projectId
|
||||
WHERE wl.projectId = @projectId AND wl.welfareUnionId = @unionId
|
||||
ORDER BY wl.welfareUnitName, u.loginname, wpus.selectOptionId
|
||||
""");
|
||||
sql.setParam("projectId", projectId);
|
||||
sql.setParam("unionId", unionId);
|
||||
List<NutMap> rows = listMap(sql);
|
||||
rows.forEach(this::fillUserSign);
|
||||
|
||||
Map<String, List<NutMap>> userRows = rows.stream().collect(Collectors.groupingBy(
|
||||
row -> row.getString("userId"), LinkedHashMap::new, Collectors.toList()));
|
||||
List<NutMap> exportRows = new ArrayList<>();
|
||||
int index = 1;
|
||||
for (List<NutMap> selectedRows : userRows.values()) {
|
||||
NutMap userRow = selectedRows.get(0);
|
||||
NutMap exportRow = NutMap.NEW();
|
||||
exportRow.put("no", index++);
|
||||
exportRow.put("userName", userRow.getString("userName"));
|
||||
exportRow.put("loginName", userRow.getString("loginName"));
|
||||
exportRow.put("unitName", userRow.getString("unitName"));
|
||||
exportRow.put("qzBytes", selectedRows.stream()
|
||||
.map(row -> row.get("qzBytes"))
|
||||
.filter(Objects::nonNull)
|
||||
.findFirst()
|
||||
.orElse(null));
|
||||
for (NutMap option : options) {
|
||||
int selectNum = selectedRows.stream()
|
||||
.filter(row -> option.getString("id").equals(row.getString("selectOptionId")))
|
||||
.mapToInt(row -> row.getInt("selectNum", 0))
|
||||
.sum();
|
||||
exportRow.put(option.getString("id"), selectNum);
|
||||
}
|
||||
exportRows.add(exportRow);
|
||||
}
|
||||
|
||||
NutMap totalRow = NutMap.NEW();
|
||||
totalRow.put("no", "合计");
|
||||
for (NutMap option : options) {
|
||||
int total = rows.stream()
|
||||
.filter(row -> option.getString("id").equals(row.getString("selectOptionId")))
|
||||
.mapToInt(row -> row.getInt("selectNum", 0))
|
||||
.sum();
|
||||
totalRow.put(option.getString("id"), total);
|
||||
}
|
||||
exportRows.add(totalRow);
|
||||
|
||||
List<ExcelExportEntity> entities = new ArrayList<>();
|
||||
entities.add(new ExcelExportEntity("序号", "no", 10));
|
||||
entities.add(new ExcelExportEntity("姓名", "userName", 15));
|
||||
entities.add(new ExcelExportEntity("工号", "loginName", 18));
|
||||
ExcelExportEntity unitEntity = new ExcelExportEntity("所在单位", "unitName", 25);
|
||||
unitEntity.setWrap(true);
|
||||
entities.add(unitEntity);
|
||||
for (NutMap option : options) {
|
||||
entities.add(new ExcelExportEntity(option.getString("optionName"), option.getString("id"), 12));
|
||||
}
|
||||
ExcelExportEntity signEntity = new ExcelExportEntity("签字", "qzBytes", 20);
|
||||
signEntity.setType(2);
|
||||
signEntity.setExportImageType(2);
|
||||
entities.add(signEntity);
|
||||
|
||||
ExportParams exportParams = new ExportParams();
|
||||
exportParams.setType(ExcelType.XSSF);
|
||||
return ExcelExportUtil.exportExcel(exportParams, entities, exportRows);
|
||||
}
|
||||
|
||||
/** 从 V4 文件存储读取签名图片,历史文件缺失时保留空白签字格。 */
|
||||
private void fillUserSign(NutMap row) {
|
||||
String userSign = row.getString("userSign");
|
||||
if (StrUtil.isBlank(userSign)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Sys_file file = dao().fetch(Sys_file.class, Cnd.where(Sys_file::getDownloadPath, "=", userSign));
|
||||
if (file != null) {
|
||||
row.put("qzBytes", SysFileMinIoUtil.getFileBytes(file.getBucket(), file.getStoragePath()));
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
// 单个签名文件异常不能影响整份签领表导出。
|
||||
}
|
||||
}
|
||||
|
||||
/** 身份证仅显示前段信息,保持 V3 签领表的脱敏规则。 */
|
||||
private String maskIdCard(String idCard) {
|
||||
if (StrUtil.isBlank(idCard) || idCard.length() <= 4) {
|
||||
return idCard;
|
||||
}
|
||||
return idCard.substring(0, idCard.length() - 4) + "****";
|
||||
}
|
||||
|
||||
/** 将历史收货信息拆为 V3 名单所需的收货人、联系方式和收货地址字段。 */
|
||||
private void fillV3ReceiveAddress(NutMap row) {
|
||||
String receiveAddress = row.getString("receiveAddress");
|
||||
if (StrUtil.isBlank(receiveAddress)) {
|
||||
return;
|
||||
}
|
||||
Map<String, String> values = new HashMap<>();
|
||||
for (String pair : receiveAddress.split("[,,]")) {
|
||||
String[] keyValue = pair.split("[::]", 2);
|
||||
if (keyValue.length == 2) {
|
||||
values.put(keyValue[0].trim(), keyValue[1].trim());
|
||||
}
|
||||
}
|
||||
row.put("recipient", values.get("收件人"));
|
||||
row.put("phone", values.get("联系方式"));
|
||||
row.put("address", values.get("收货地址"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object unclaimedData(String projectId, String unionId, Integer pageNumber, Integer pageSize) {
|
||||
|
||||
@@ -333,6 +476,7 @@ public class WelfareSingleServiceImpl extends BaseServiceImpl implements Welfare
|
||||
}
|
||||
|
||||
List<NutMap> list2 = list.stream().filter(v -> Strings.isNotBlank(v.getString("unionName"))).collect(Collectors.toList());
|
||||
list2.forEach(this::fillV3ReceiveAddress);
|
||||
|
||||
List<WelfareExportEntityTc> welfareExportEntityTcList = list2.stream()
|
||||
.map(map -> JSONUtil.toBean(Json.toJson(map), WelfareExportEntityTc.class))
|
||||
@@ -340,15 +484,15 @@ public class WelfareSingleServiceImpl extends BaseServiceImpl implements Welfare
|
||||
|
||||
welfareExportEntityTcList.forEach(v -> {
|
||||
if (StrUtil.isNotBlank(v.getUserSign())) {
|
||||
ByteArrayOutputStream signOs = new ByteArrayOutputStream();
|
||||
// ftpService.download(v.getUserSign(), signOs);
|
||||
byte[] imageBytes = signOs.toByteArray();
|
||||
if (Lang.isNotEmpty(imageBytes)) {
|
||||
v.setQzBytes(imageBytes);
|
||||
} else {
|
||||
v.setQzBytes(null);
|
||||
try {
|
||||
Sys_file file = dao().fetch(Sys_file.class,
|
||||
Cnd.where(Sys_file::getDownloadPath, "=", v.getUserSign()));
|
||||
if (file != null) {
|
||||
v.setQzBytes(SysFileMinIoUtil.getFileBytes(file.getBucket(), file.getStoragePath()));
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
// 签名文件缺失时保留空白,不中断名单导出。
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
ExportParams exportParams = new ExportParams();
|
||||
|
||||
+168
-6
@@ -17,6 +17,7 @@ import com.budwk.app.web.commons.auth.utils.AuthUtil;
|
||||
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
|
||||
import com.budwk.app.zhgh.welfare.model.WelfareProject;
|
||||
import com.budwk.app.zhgh.welfare.model.WelfareProjectSubjectOption;
|
||||
import com.budwk.app.zhgh.welfare.service.WelfareSingleService;
|
||||
import com.budwk.app.zhgh.welfare.service.WelfareStatisticsService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
@@ -32,14 +33,15 @@ import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@IocBean(args = {"refer:dao"})
|
||||
@Slf4j
|
||||
public class WelfareStatisticsServiceImpl extends BaseServiceImpl<WelfareProject> implements WelfareStatisticsService {
|
||||
@org.nutz.ioc.loader.annotation.Inject
|
||||
private WelfareSingleService welfareSingleService;
|
||||
|
||||
public WelfareStatisticsServiceImpl(Dao dao) {
|
||||
super(dao);
|
||||
}
|
||||
@@ -65,7 +67,7 @@ public class WelfareStatisticsServiceImpl extends BaseServiceImpl<WelfareProject
|
||||
}};
|
||||
|
||||
// 选项数据
|
||||
List<WelfareProjectSubjectOption> welfareOptions = dao().query(WelfareProjectSubjectOption.class, Cnd.where(WelfareProjectSubjectOption::getWelfareId, "=", projectId));
|
||||
List<WelfareProjectSubjectOption> welfareOptions = getNaturalSortedOptions(projectId);
|
||||
|
||||
for (WelfareProjectSubjectOption welfareOption : welfareOptions) {
|
||||
dynamicTableColumns.add(NutMap.NEW().addv("label", welfareOption.getOptionName()).addv("prop", welfareOption.getId()));
|
||||
@@ -176,6 +178,8 @@ public class WelfareStatisticsServiceImpl extends BaseServiceImpl<WelfareProject
|
||||
SqlExpressionGroup seg = new SqlExpressionGroup();
|
||||
seg.orLike("u.loginname", pageForm.getSearchKeyword());
|
||||
seg.orLike("u.username", pageForm.getSearchKeyword());
|
||||
// 已选人员弹窗仅支持按工号或姓名筛选,需将 OR 条件加入查询条件后才会生效。
|
||||
cnd.and(seg);
|
||||
}
|
||||
sql.setCondition(cnd);
|
||||
Pagination pagination = listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
|
||||
@@ -184,6 +188,8 @@ public class WelfareStatisticsServiceImpl extends BaseServiceImpl<WelfareProject
|
||||
|
||||
@Override
|
||||
public void exportSelectedUnionUser(String projectId, String unionId, HttpServletResponse response) {
|
||||
// V4 原已选人员导出逻辑保留,当前按 V3 字段与格式执行。
|
||||
if (false) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
u.loginname AS loginName,
|
||||
@@ -245,6 +251,8 @@ public class WelfareStatisticsServiceImpl extends BaseServiceImpl<WelfareProject
|
||||
} catch (Exception e) {
|
||||
log.error("导出Excel失败", e);
|
||||
}
|
||||
}
|
||||
welfareSingleService.exportReceiveDetailByUnionId(projectId, unionId, true, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -289,6 +297,8 @@ public class WelfareStatisticsServiceImpl extends BaseServiceImpl<WelfareProject
|
||||
|
||||
@Override
|
||||
public void exportUnSelectedUnionUser(String projectId, String unionId, HttpServletResponse response) {
|
||||
// V4 原未选人员导出逻辑保留,当前按 V3 字段与格式执行。
|
||||
if (false) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
u.loginname AS loginName,
|
||||
@@ -344,6 +354,8 @@ public class WelfareStatisticsServiceImpl extends BaseServiceImpl<WelfareProject
|
||||
} catch (Exception e) {
|
||||
log.error("导出Excel失败", e);
|
||||
}
|
||||
}
|
||||
welfareSingleService.exportReceiveDetailByUnionId(projectId, unionId, false, response);
|
||||
}
|
||||
|
||||
|
||||
@@ -377,7 +389,9 @@ public class WelfareStatisticsServiceImpl extends BaseServiceImpl<WelfareProject
|
||||
|
||||
|
||||
@Override
|
||||
public void exportByWelfareOptions(String projectId, HttpServletResponse response) {
|
||||
public void exportByWelfareOptions(String projectId, String unionId, HttpServletResponse response) {
|
||||
// V4 原按福利选项导出逻辑保留,当前按 V3 的品牌分表字段执行。
|
||||
if (false) {
|
||||
Workbook workbook = new XSSFWorkbook();
|
||||
|
||||
List<ExcelExportEntity> exportEntities = new ArrayList<>();
|
||||
@@ -434,6 +448,8 @@ public class WelfareStatisticsServiceImpl extends BaseServiceImpl<WelfareProject
|
||||
} catch (Exception e) {
|
||||
log.error("导出Excel失败", e);
|
||||
}
|
||||
}
|
||||
exportByWelfareOptionsV3(projectId, unionId, response);
|
||||
}
|
||||
|
||||
|
||||
@@ -456,7 +472,7 @@ public class WelfareStatisticsServiceImpl extends BaseServiceImpl<WelfareProject
|
||||
entities.add(unSelectedNumEntity);
|
||||
|
||||
// 选项数据
|
||||
List<WelfareProjectSubjectOption> welfareOptions = dao().query(WelfareProjectSubjectOption.class, Cnd.where(WelfareProjectSubjectOption::getWelfareId, "=", projectId));
|
||||
List<WelfareProjectSubjectOption> welfareOptions = getNaturalSortedOptions(projectId);
|
||||
for (WelfareProjectSubjectOption welfareOption : welfareOptions) {
|
||||
ExcelExportEntity entity = new ExcelExportEntity(welfareOption.getOptionName(), welfareOption.getId(), 20);
|
||||
entity.setType(10);
|
||||
@@ -543,4 +559,150 @@ public class WelfareStatisticsServiceImpl extends BaseServiceImpl<WelfareProject
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 按 V3 品牌导出结构生成工作簿:每个福利选项独立工作表,人员多选时在每个对应选项表中均保留一行。
|
||||
*
|
||||
* @param projectId 福利项目ID
|
||||
* @param unionId 当前筛选的分工会ID;为空时导出当前登录人权限范围内的全部分工会
|
||||
* @param response HTTP响应,返回XLSX格式的品牌分表文件
|
||||
*/
|
||||
private void exportByWelfareOptionsV3(String projectId, String unionId, HttpServletResponse response) {
|
||||
List<ExcelExportEntity> entities = new ArrayList<>();
|
||||
entities.add(new ExcelExportEntity("工号", "loginName", 20));
|
||||
entities.add(new ExcelExportEntity("姓名", "userName", 20));
|
||||
entities.add(new ExcelExportEntity("电话号码", "mobile", 20));
|
||||
entities.add(new ExcelExportEntity("在职状态", "userState", 20));
|
||||
entities.add(new ExcelExportEntity("人员类型", "personType", 20));
|
||||
entities.add(new ExcelExportEntity("所在分工会", "welfareUnionName", 30));
|
||||
entities.add(new ExcelExportEntity("所在单位", "welfareUnitName", 50));
|
||||
entities.add(new ExcelExportEntity("选择份数", "selectNum", 10));
|
||||
entities.add(new ExcelExportEntity("收货人", "recipient", 15));
|
||||
entities.add(new ExcelExportEntity("联系方式", "phone", 20));
|
||||
entities.add(new ExcelExportEntity("收货地址", "address", 60));
|
||||
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
u.loginname AS loginName,
|
||||
u.username AS userName,
|
||||
wpus.mobile,
|
||||
wl.userState,
|
||||
wl.personType,
|
||||
wl.welfareUnionName,
|
||||
wl.welfareUnitName,
|
||||
wpus.receiveAddress,
|
||||
wpus.selectNum,
|
||||
wpus.selectOptionId
|
||||
FROM welfare_project_user_selection wpus
|
||||
LEFT JOIN welfare_list wl ON wl.userId = wpus.selectUserId AND wl.projectId = wpus.welfareId
|
||||
LEFT JOIN sys_user u ON u.id = wpus.selectUserId
|
||||
$condition
|
||||
""");
|
||||
Cnd cnd = Cnd.NEW();
|
||||
cnd.and("wpus.welfareId", "=", projectId);
|
||||
cnd.and("wpus.selectNum", "!=", 0);
|
||||
cnd.andEX("wl.welfareUnionId", "=", resolveExportUnionId(unionId));
|
||||
cnd.asc("wl.welfareUnionName").asc("wl.welfareUnitName").asc("u.loginname");
|
||||
sql.setCondition(cnd);
|
||||
List<NutMap> rows = listMap(sql);
|
||||
rows.forEach(this::fillV3ReceiveAddress);
|
||||
|
||||
try (Workbook workbook = new XSSFWorkbook()) {
|
||||
for (WelfareProjectSubjectOption option : getNaturalSortedOptions(projectId)) {
|
||||
List<NutMap> optionRows = rows.stream()
|
||||
.filter(row -> option.getId().equals(row.getString("selectOptionId")))
|
||||
// EasyPOI 生成工作表时会移除已处理行,必须传入可修改列表。
|
||||
.collect(Collectors.toCollection(ArrayList::new));
|
||||
ExportParams exportParams = new ExportParams();
|
||||
exportParams.setType(ExcelType.XSSF);
|
||||
exportParams.setTitle(option.getOptionName() + "选择情况");
|
||||
exportParams.setSheetName(option.getOptionName());
|
||||
new ExcelExportService().createSheetForMap(workbook, exportParams, entities, optionRows);
|
||||
}
|
||||
CommonDownloadUtil.download("按品牌导出选择情况表.xlsx", workbook, response);
|
||||
} catch (Exception e) {
|
||||
log.error("按品牌导出Excel失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/** 分工会角色只能导出本人工会数据,校级角色可按页面筛选导出。 */
|
||||
private String resolveExportUnionId(String unionId) {
|
||||
if (AuthUtil.hasRole(RoleConstant.BRANCH_UNION_CHAIRMAN.name())
|
||||
|| AuthUtil.hasRole(RoleConstant.BRANCH_UNION_ADMIN.name())
|
||||
|| AuthUtil.hasRole(RoleConstant.BRANCH_UNION_WENTI_SPORTS.name())) {
|
||||
return SecurityUtil.getUnionId();
|
||||
}
|
||||
return unionId;
|
||||
}
|
||||
|
||||
/** 将历史收货信息拆为 V3 导出所需的收货人、联系方式和收货地址三列。 */
|
||||
private void fillV3ReceiveAddress(NutMap row) {
|
||||
String receiveAddress = row.getString("receiveAddress");
|
||||
if (StrUtil.isBlank(receiveAddress)) {
|
||||
return;
|
||||
}
|
||||
Map<String, String> values = new HashMap<>();
|
||||
for (String pair : receiveAddress.split("[,,]")) {
|
||||
String[] keyValue = pair.split("[::]", 2);
|
||||
if (keyValue.length == 2) {
|
||||
values.put(keyValue[0].trim(), keyValue[1].trim());
|
||||
}
|
||||
}
|
||||
row.put("recipient", values.get("收件人"));
|
||||
row.put("phone", values.get("联系方式"));
|
||||
row.put("address", values.get("收货地址"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 按套餐名称进行自然排序,数字片段按数值比较、英文字母忽略大小写比较,避免“套餐10”排在“套餐2”之前。
|
||||
*/
|
||||
private List<WelfareProjectSubjectOption> getNaturalSortedOptions(String projectId) {
|
||||
List<WelfareProjectSubjectOption> options = dao().query(WelfareProjectSubjectOption.class,
|
||||
Cnd.where(WelfareProjectSubjectOption::getWelfareId, "=", projectId));
|
||||
options.sort(Comparator.comparing(WelfareProjectSubjectOption::getOptionName, this::compareOptionNames)
|
||||
.thenComparing(option -> option.getOptionSort() == null ? Integer.MAX_VALUE : option.getOptionSort())
|
||||
.thenComparing(WelfareProjectSubjectOption::getId));
|
||||
return options;
|
||||
}
|
||||
|
||||
private int compareOptionNames(String left, String right) {
|
||||
String leftName = StrUtil.blankToDefault(left, "");
|
||||
String rightName = StrUtil.blankToDefault(right, "");
|
||||
int leftIndex = 0;
|
||||
int rightIndex = 0;
|
||||
while (leftIndex < leftName.length() && rightIndex < rightName.length()) {
|
||||
char leftChar = leftName.charAt(leftIndex);
|
||||
char rightChar = rightName.charAt(rightIndex);
|
||||
if (Character.isDigit(leftChar) && Character.isDigit(rightChar)) {
|
||||
int leftEnd = leftIndex;
|
||||
int rightEnd = rightIndex;
|
||||
while (leftEnd < leftName.length() && Character.isDigit(leftName.charAt(leftEnd))) {
|
||||
leftEnd++;
|
||||
}
|
||||
while (rightEnd < rightName.length() && Character.isDigit(rightName.charAt(rightEnd))) {
|
||||
rightEnd++;
|
||||
}
|
||||
String leftNumber = leftName.substring(leftIndex, leftEnd).replaceFirst("^0+(?!$)", "");
|
||||
String rightNumber = rightName.substring(rightIndex, rightEnd).replaceFirst("^0+(?!$)", "");
|
||||
int numberCompare = Integer.compare(leftNumber.length(), rightNumber.length());
|
||||
if (numberCompare != 0) {
|
||||
return numberCompare;
|
||||
}
|
||||
numberCompare = leftNumber.compareTo(rightNumber);
|
||||
if (numberCompare != 0) {
|
||||
return numberCompare;
|
||||
}
|
||||
leftIndex = leftEnd;
|
||||
rightIndex = rightEnd;
|
||||
continue;
|
||||
}
|
||||
int charCompare = String.valueOf(leftChar).compareToIgnoreCase(String.valueOf(rightChar));
|
||||
if (charCompare != 0) {
|
||||
return charCompare;
|
||||
}
|
||||
leftIndex++;
|
||||
rightIndex++;
|
||||
}
|
||||
return Integer.compare(leftName.length(), rightName.length());
|
||||
}
|
||||
}
|
||||
|
||||
+11
-11
@@ -4,7 +4,6 @@ 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.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.budwk.app.base.constant.RoleConstant;
|
||||
import com.budwk.app.base.exception.BaseException;
|
||||
@@ -59,8 +58,8 @@ public class WelfareUserEvaluationServiceImpl extends BaseServiceImpl<WelfareUse
|
||||
}
|
||||
|
||||
/**
|
||||
* 评价开放时间以后端福利选择记录为准,避免客户端隐藏按钮后仍可直接调用评价接口。
|
||||
* 配送时间按日期粒度比较,到达配送日期当天即可评价。
|
||||
* 评价开放时间以后端福利项目活动结束时间为准,避免客户端隐藏按钮后仍可直接调用评价接口。
|
||||
* 当前用户必须已经存在福利选择记录,且当前时间达到 choiceTimeEnd 才允许评价。
|
||||
*
|
||||
* @param projectId 福利项目ID
|
||||
* @param userId 当前登录用户ID
|
||||
@@ -77,14 +76,15 @@ public class WelfareUserEvaluationServiceImpl extends BaseServiceImpl<WelfareUse
|
||||
if (selections.isEmpty()) {
|
||||
return "当前项目暂无可评价的福利选择";
|
||||
}
|
||||
Date currentDate = DateUtil.beginOfDay(new Date());
|
||||
for (WelfareUserSelection selection : selections) {
|
||||
if (selection.getDeliveryDate() == null) {
|
||||
return "当前福利尚未设置配送时间,暂不能评价";
|
||||
}
|
||||
if (currentDate.before(DateUtil.beginOfDay(selection.getDeliveryDate()))) {
|
||||
return "配送时间未到,暂不能评价";
|
||||
}
|
||||
WelfareProject project = dao().fetch(WelfareProject.class, projectId);
|
||||
if (project == null) {
|
||||
return "福利项目不存在";
|
||||
}
|
||||
if (project.getChoiceTimeEnd() == null) {
|
||||
return "当前福利未设置活动结束时间,暂不能评价";
|
||||
}
|
||||
if (new Date().before(project.getChoiceTimeEnd())) {
|
||||
return "福利活动尚未结束,暂不能评价";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
-- 补充旧版疗休养已完成移植、但未写入新版菜单表的两个 PC 端入口。
|
||||
-- 使用 permission 做幂等判断,脚本可重复执行。
|
||||
INSERT INTO sys_menu (
|
||||
id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled,
|
||||
permission, note, location, hasChildren, createdBy, createdAt, updatedBy, updatedAt,
|
||||
delFlag, platform, moduleId, picIcon, initialPinyinName, isRecommendApp, isRecommendService
|
||||
)
|
||||
SELECT
|
||||
'8a691f4623db4d82927668b05d10b401',
|
||||
parent_menu.id,
|
||||
CONCAT(
|
||||
parent_menu.path,
|
||||
LPAD(
|
||||
IFNULL((
|
||||
SELECT MAX(CAST(RIGHT(child.path, 4) AS UNSIGNED))
|
||||
FROM sys_menu child
|
||||
WHERE child.parentId = parent_menu.id
|
||||
), 0) + 1,
|
||||
4,
|
||||
'0'
|
||||
)
|
||||
),
|
||||
'线路成团结果通知',
|
||||
'Line Group Result Notice',
|
||||
'menu',
|
||||
'/platform/recuperation/lineStatistics',
|
||||
'data-pjax',
|
||||
'',
|
||||
1,
|
||||
0,
|
||||
'recuperation.lineStatistics',
|
||||
'查询线路报名统计并发送成团或未成团通知',
|
||||
IFNULL((
|
||||
SELECT MAX(child.location)
|
||||
FROM sys_menu child
|
||||
WHERE child.parentId = parent_menu.id
|
||||
), parent_menu.location) + 1,
|
||||
0,
|
||||
'',
|
||||
UNIX_TIMESTAMP(NOW()) * 1000,
|
||||
'',
|
||||
UNIX_TIMESTAMP(NOW()) * 1000,
|
||||
0,
|
||||
'PC',
|
||||
parent_menu.moduleId,
|
||||
NULL,
|
||||
'x',
|
||||
0,
|
||||
0
|
||||
FROM sys_menu parent_menu
|
||||
WHERE parent_menu.permission = 'recuperation'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM (SELECT id FROM sys_menu WHERE permission = 'recuperation.lineStatistics') existing_menu
|
||||
);
|
||||
|
||||
INSERT INTO sys_menu (
|
||||
id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled,
|
||||
permission, note, location, hasChildren, createdBy, createdAt, updatedBy, updatedAt,
|
||||
delFlag, platform, moduleId, picIcon, initialPinyinName, isRecommendApp, isRecommendService
|
||||
)
|
||||
SELECT
|
||||
'3074a944a1554982835793d1c164183f',
|
||||
parent_menu.id,
|
||||
CONCAT(
|
||||
parent_menu.path,
|
||||
LPAD(
|
||||
IFNULL((
|
||||
SELECT MAX(CAST(RIGHT(child.path, 4) AS UNSIGNED))
|
||||
FROM sys_menu child
|
||||
WHERE child.parentId = parent_menu.id
|
||||
), 0) + 1,
|
||||
4,
|
||||
'0'
|
||||
)
|
||||
),
|
||||
'省内灵活组团查询',
|
||||
'Flexible Group Query',
|
||||
'menu',
|
||||
'/platform/recuperation/flexibleGroupQuery',
|
||||
'data-pjax',
|
||||
'',
|
||||
1,
|
||||
0,
|
||||
'recuperation.flexibleGroupQuery',
|
||||
'查询省内灵活组团、团队及报名人员',
|
||||
IFNULL((
|
||||
SELECT MAX(child.location)
|
||||
FROM sys_menu child
|
||||
WHERE child.parentId = parent_menu.id
|
||||
), parent_menu.location) + 1,
|
||||
0,
|
||||
'',
|
||||
UNIX_TIMESTAMP(NOW()) * 1000,
|
||||
'',
|
||||
UNIX_TIMESTAMP(NOW()) * 1000,
|
||||
0,
|
||||
'PC',
|
||||
parent_menu.moduleId,
|
||||
NULL,
|
||||
's',
|
||||
0,
|
||||
0
|
||||
FROM sys_menu parent_menu
|
||||
WHERE parent_menu.permission = 'recuperation'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM (SELECT id FROM sys_menu WHERE permission = 'recuperation.flexibleGroupQuery') existing_menu
|
||||
);
|
||||
|
||||
-- 系统管理员默认拥有补充菜单权限,其他业务角色可在角色管理中按需分配。
|
||||
INSERT INTO sys_role_menu (roleId, menuId)
|
||||
SELECT role_info.id, menu_info.id
|
||||
FROM sys_role role_info
|
||||
JOIN sys_menu menu_info ON menu_info.permission IN (
|
||||
'recuperation.lineStatistics',
|
||||
'recuperation.flexibleGroupQuery'
|
||||
)
|
||||
LEFT JOIN sys_role_menu role_menu
|
||||
ON role_menu.roleId = role_info.id
|
||||
AND role_menu.menuId = menu_info.id
|
||||
WHERE role_info.code = 'SYSADMIN'
|
||||
AND role_menu.roleId IS NULL;
|
||||
@@ -21,8 +21,10 @@
|
||||
<button v-if="signatureContent" @click="openSignature" class="signature-reset-button"
|
||||
type="button">重新签名</button>
|
||||
</div>
|
||||
<van-popup v-model="showSignaturePanel" :close-on-click-overlay="false"
|
||||
:style="{ height: '100vh', width: '100vw' }" @close="handleSignaturePopupClose">
|
||||
<!-- 签字板挂载到 body,避免 iOS 将 fixed 子元素裁切在外层带 transform 的滚动 Popup 内。 -->
|
||||
<van-popup v-model="showSignaturePanel" class="signature-full-popup" get-container="body"
|
||||
:close-on-click-overlay="false" :style="{ height: '100%', width: '100%' }"
|
||||
@close="handleSignaturePopupClose">
|
||||
<signature @save="save"></signature>
|
||||
</van-popup>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="signature-wrap">
|
||||
<canvas id="canvas"></canvas>
|
||||
<canvas ref="signatureCanvas"></canvas>
|
||||
<div :class="isLikelyMobile() ? 'action-buttons' : 'action-buttons pc-action-buttons'">
|
||||
<button @click="clear" class="action-button-danger" type="button">清空</button>
|
||||
<button @click="undo" class="action-button-warning" type="button">撤销</button>
|
||||
@@ -80,15 +80,13 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
const innerWidth = window.innerWidth
|
||||
const innerHeight = window.innerHeight
|
||||
|
||||
const canvas = document.getElementById("canvas")
|
||||
canvas.width = window.innerWidth
|
||||
canvas.height = window.innerHeight
|
||||
// 使用签字弹层的实际可视尺寸,避免 iOS WebView 中 100vh/innerHeight 包含不可见浏览器区域。
|
||||
const containerWidth = this.$el.clientWidth || window.innerWidth
|
||||
const containerHeight = this.$el.clientHeight || window.innerHeight
|
||||
const canvas = this.$refs.signatureCanvas
|
||||
signature = new SmoothSignature(canvas, {
|
||||
width: innerWidth - 30,
|
||||
height: innerHeight - 30,
|
||||
width: Math.max(containerWidth - 30, 1),
|
||||
height: Math.max(containerHeight - 30, 1),
|
||||
scale: 2,
|
||||
minWidth: 4,
|
||||
maxWidth: 10,
|
||||
@@ -101,11 +99,14 @@ module.exports = {
|
||||
|
||||
<style scoped>
|
||||
.signature-wrap {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
padding: 15px;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
@@ -121,8 +122,9 @@ module.exports = {
|
||||
transform: rotate(90deg);
|
||||
display: flex;
|
||||
column-gap: 10px;
|
||||
position: fixed;
|
||||
bottom: 0px;
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
bottom: calc(15px + env(safe-area-inset-bottom));
|
||||
left: 60px;
|
||||
transform: rotate(90deg);
|
||||
display: flex;
|
||||
|
||||
@@ -621,7 +621,7 @@
|
||||
</script>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
const h5ComponentVersion = "20260821_2"
|
||||
const h5ComponentVersion = "20260907_1"
|
||||
Vue.component("rich-text", httpVueLoader("/components/plugins/sysRichTextView/index.vue?v=" + h5ComponentVersion))
|
||||
Vue.component("h5-file-upload", httpVueLoader("/components/plugins/sysUpload/h5Index.vue?v=" + h5ComponentVersion))
|
||||
Vue.component("h5-signature", httpVueLoader("/components/plugins/sysSignature/h5Index.vue?v=" + h5ComponentVersion))
|
||||
|
||||
+276
-30
@@ -1,43 +1,289 @@
|
||||
<!--# layout("/layouts/platform.html"){ #-->
|
||||
<style>
|
||||
.query-row {
|
||||
height: 70px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.query-row .el-col { overflow: hidden; }
|
||||
.query-row:not(:last-child) { border-bottom: 1px dashed rgb(230, 230, 230); }
|
||||
.query-row-title { width: 120px; }
|
||||
.el-table-container { padding-top: 0; }
|
||||
</style>
|
||||
<div id="app" v-cloak>
|
||||
<guava ref="guava">
|
||||
<template>
|
||||
<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-row align="middle" class="query-row" type="flex">
|
||||
<el-col class="query-row-title"></el-col>
|
||||
<el-col class="query-row-content">
|
||||
<el-row><el-col :span="12">
|
||||
<span>年  度:</span>
|
||||
<el-date-picker :clearable="false" v-model="pageForm.startYear" type="year" value-format="yyyy" placeholder="选择年" style="width: 38%" @change="yearChange"></el-date-picker>
|
||||
<span>至</span>
|
||||
<el-date-picker :clearable="false" v-model="pageForm.endYear" type="year" value-format="yyyy" placeholder="请选择年" style="width: 38%" @change="yearChange"></el-date-picker>
|
||||
</el-col></el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row align="middle" class="query-row" type="flex">
|
||||
<el-col class="query-row-title"></el-col>
|
||||
<el-col class="query-row-content">
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<span>组团名称:</span>
|
||||
<el-select v-model="pageForm.groupName" placeholder="请选择组团名称" filterable clearable style="width: 80%" @change="doSearch">
|
||||
<el-option v-for="item in flexibleGroupList" :key="item.id" :label="item.groupName" :value="item.groupName"></el-option>
|
||||
</el-select>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<span>旅 行 社:</span>
|
||||
<el-select v-model="pageForm.travelAgencyId" placeholder="请选择旅行社" filterable clearable style="width: 80%" @change="doSearch">
|
||||
<el-option v-for="item in travelAgencyList" :key="item.id" :label="item.travelAgencyName + (item.serialNumber ? '(' + item.serialNumber + ')' : '')" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row align="middle" class="query-row" type="flex">
|
||||
<el-col class="query-row-title"></el-col>
|
||||
<el-col class="query-row-content">
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<span>姓名/工号:</span>
|
||||
<el-input :value="pageForm.searchKeyword" clearable placeholder="请输入姓名或工号" style="width: 80%" @input="personKeywordChange" @clear="clearPersonKeyword" @keyup.enter.native="doSearch"></el-input>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-button icon="el-icon-search" size="small" type="primary" @click="doSearch">查询</el-button>
|
||||
<el-button icon="el-icon-refresh-left" size="small" @click="clearPersonKeyword">重置</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</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-card shadow="never" class="mt10">
|
||||
<table-tool label="灵活组团查询" :app="this" ref="table_tool">
|
||||
<el-button icon="el-icon-s-promotion" size="small" type="primary" @click="doExport">导出Excel</el-button>
|
||||
</table-tool>
|
||||
<el-table :data="tableData" :row-key="tableRowKey" style="width: 100%" ref="table" v-loading="tableLoading" @sort-change="pageOrder">
|
||||
<el-table-column align="center" header-align="center" type="index" label="序号" width="80" key="#index">
|
||||
<template v-slot="scope"><span>{{scope.$index + (pageForm.pageNumber - 1) * pageForm.pageSize + 1}}</span></template>
|
||||
</el-table-column>
|
||||
<el-table-column v-for="column in tableColumns" v-if="!column.personOnly || pageForm.searchKeyword" :key="column.prop" :label="column.label" :prop="column.prop" :width="column.width" :sortable="column.sortable" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="操作" width="200">
|
||||
<template v-slot="{row}"><el-button @click="openView(row)" size="mini" type="primary">查看团</el-button><el-button @click="openViewUser(row)" size="mini" type="primary">查看人员</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>
|
||||
</template>
|
||||
</guava>
|
||||
<el-dialog :visible.sync="userVisible" :close-on-click-modal="false" top="2%" title="报名人员" width="65%">
|
||||
<el-table :data="signUserData" style="width: 100%" ref="userTable" v-loading="userLoading">
|
||||
<el-table-column align="center" header-align="center" type="index" label="序号" width="80"></el-table-column>
|
||||
<el-table-column prop="userName" label="姓名" header-align="center" align="center"></el-table-column>
|
||||
<el-table-column prop="loginName" label="工号" header-align="center" align="center"></el-table-column>
|
||||
<el-table-column prop="unionName" label="所属工会" header-align="center" align="center"></el-table-column>
|
||||
<el-table-column prop="unitName" label="单位" header-align="center" align="center"></el-table-column>
|
||||
<el-table-column prop="sex" label="性别" header-align="center" align="center"></el-table-column>
|
||||
<el-table-column prop="mobile" label="手机号" header-align="center" align="center" width="120"></el-table-column>
|
||||
<el-table-column prop="identity" label="身份" header-align="center" align="center"></el-table-column>
|
||||
<el-table-column prop="isFamily" label="是否有家属" header-align="center" align="center"><template v-slot="{row}"><span v-if="row.isFamily > 0">是</span><span v-else>否</span></template></el-table-column>
|
||||
<el-table-column label="操作" width="100"><template v-slot="{row}"><el-button @click="deleteUser(row)" size="mini" type="danger">删除</el-button></template></el-table-column>
|
||||
</el-table>
|
||||
<span slot="footer" class="dialog-footer"><el-button @click="closeUserDialog" type="primary">取 消</el-button></span>
|
||||
</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 :visible.sync="groupVisible" :close-on-click-modal="false" top="2%" title="团信息">
|
||||
<el-table :data="groupTableData" max-height="600" v-loading="dialogLoading">
|
||||
<el-table-column label="序号" type="index" header-align="center" align="center" width="50"></el-table-column>
|
||||
<el-table-column prop="groupLeaderUserName" label="团长" header-align="center" align="center" sortable><template v-slot="{row}"><span v-if="row.groupLeaderUserName">{{row.groupLeaderUserName}}({{row.groupLeaderLoginName}})</span><span v-else>未选择</span></template></el-table-column>
|
||||
<el-table-column prop="groupLeaderPassword" label="口令" header-align="center" align="center"><template v-slot="{row}"><span>{{row.groupLeaderPassword || '-'}}</span></template></el-table-column>
|
||||
<el-table-column prop="userCount" label="报名人数" header-align="center" align="center" sortable></el-table-column>
|
||||
<el-table-column label="操作" width="150"><template v-slot="{row}"><el-button @click="openViewUserByTime(row)" size="mini" type="primary">查看人员</el-button></template></el-table-column>
|
||||
</el-table>
|
||||
<span slot="footer" class="dialog-footer"><el-button @click="closeGroupDialog" type="primary">取 消</el-button></span>
|
||||
</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()}
|
||||
})
|
||||
const vue = new Vue({
|
||||
el: '#app',
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
const currentYear = moment().format('YYYY')
|
||||
return {
|
||||
groupVisible: false,
|
||||
userVisible: false,
|
||||
userLoading: false,
|
||||
dialogLoading: false,
|
||||
pageForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
startYear: currentYear,
|
||||
endYear: currentYear,
|
||||
groupName: '',
|
||||
travelAgencyId: '',
|
||||
searchKeyword: ''
|
||||
},
|
||||
tableColumns: [
|
||||
{prop: 'year', label: '年度', width: 60},
|
||||
{prop: 'groupName', label: '组团名称', sortable: true},
|
||||
{prop: 'travelAgencyName', label: '旅行社', sortable: true},
|
||||
{prop: 'groupCount', label: '团数量'},
|
||||
{prop: 'signCount', label: '报名人数'},
|
||||
{prop: 'signUserName', label: '报名人员', personOnly: true},
|
||||
{prop: 'signLoginName', label: '工号', personOnly: true}
|
||||
],
|
||||
travelAgencyList: [],
|
||||
flexibleGroupList: [],
|
||||
groupTableData: [],
|
||||
signUserData: [],
|
||||
currentGroupId: ''
|
||||
}
|
||||
},
|
||||
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)
|
||||
})
|
||||
},
|
||||
tableRowKey(row) {
|
||||
return row.rowKey || row.id
|
||||
},
|
||||
closeUserDialog() {
|
||||
this.$set(this, 'userVisible', false)
|
||||
},
|
||||
closeGroupDialog() {
|
||||
this.$set(this, 'groupVisible', false)
|
||||
},
|
||||
personKeywordChange(value) {
|
||||
this.$set(this.pageForm, 'searchKeyword', value)
|
||||
},
|
||||
clearPersonKeyword() {
|
||||
this.$set(this.pageForm, 'searchKeyword', '')
|
||||
this.doSearch()
|
||||
},
|
||||
yearChange() {
|
||||
this.getTravelAgency()
|
||||
this.getFlexibleGroupList()
|
||||
this.doSearch()
|
||||
},
|
||||
getTravelAgency() {
|
||||
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, 'travelAgencyList', res.data || [])
|
||||
}
|
||||
})
|
||||
},
|
||||
getFlexibleGroupList() {
|
||||
const params = {
|
||||
startYear: this.pageForm.startYear,
|
||||
endYear: this.pageForm.endYear
|
||||
}
|
||||
this.$axios.post(loc() + '/selectFlexibleGroupList', params)
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$set(this, 'flexibleGroupList', res.data || [])
|
||||
}
|
||||
})
|
||||
},
|
||||
doExport() {
|
||||
window.open(loc() + '/doExport?' + $.param(this.pageForm))
|
||||
},
|
||||
openView(row) {
|
||||
this.$set(this, 'currentGroupId', row.id)
|
||||
this.$set(this, 'dialogLoading', true)
|
||||
this.$axios.post(loc() + '/getGroupInfo', {groupId: row.id})
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$set(this, 'groupTableData', res.data || [])
|
||||
this.$set(this, 'groupVisible', true)
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.$set(this, 'dialogLoading', false)
|
||||
})
|
||||
},
|
||||
openViewUser(row) {
|
||||
this.$set(this, 'currentGroupId', row.id)
|
||||
this.loadAllSignUser()
|
||||
},
|
||||
openViewUserByTime(row) {
|
||||
this.loadSignUserByTime(row.groupLeaderUserId, row.groupLeaderLoginName, !row.groupLeaderUserId && !row.groupLeaderLoginName)
|
||||
},
|
||||
loadAllSignUser() {
|
||||
// groupId 传灵活组团配置主键,接口返回该组团下全部报名人员的分页结构。
|
||||
this.$set(this, 'userLoading', true)
|
||||
this.$axios.post(loc() + '/getAllSignUser', {
|
||||
groupId: this.currentGroupId,
|
||||
pageNumber: 1,
|
||||
pageSize: 1000
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$set(this, 'signUserData', res.data.list || [])
|
||||
this.$set(this, 'userVisible', true)
|
||||
}
|
||||
}).finally(() => {
|
||||
this.$set(this, 'userLoading', false)
|
||||
})
|
||||
},
|
||||
loadSignUserByTime(groupLeaderUserId, groupLeaderLoginName, noGroupLeader) {
|
||||
// 参数依次标识团长用户、团长工号和未选团长分组,接口返回对应团内人员分页结构。
|
||||
this.$set(this, 'userLoading', true)
|
||||
this.$axios.post(loc() + '/getSignUser', {
|
||||
groupId: this.currentGroupId,
|
||||
groupLeaderUserId: groupLeaderUserId,
|
||||
groupLeaderLoginName: groupLeaderLoginName,
|
||||
noGroupLeader: noGroupLeader,
|
||||
pageNumber: 1,
|
||||
pageSize: 1000
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$set(this, 'signUserData', res.data.list || [])
|
||||
this.$set(this, 'userVisible', true)
|
||||
}
|
||||
}).finally(() => {
|
||||
this.$set(this, 'userLoading', false)
|
||||
})
|
||||
},
|
||||
deleteUser(row) {
|
||||
this.$confirm('您确定要删除该人员吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.$axios.post(loc() + '/deleteJoinUser', {id: row.id})
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success('删除成功')
|
||||
this.$set(this, 'signUserData', this.signUserData.filter((item) => item.id !== row.id))
|
||||
this.pageData()
|
||||
} else {
|
||||
this.$message.error(res.msg)
|
||||
}
|
||||
})
|
||||
}).catch(() => {})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getTravelAgency()
|
||||
this.getFlexibleGroupList()
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<!--# } #-->
|
||||
|
||||
+425
-20
@@ -1,26 +1,431 @@
|
||||
<!--# layout("/layouts/platform.html"){ #-->
|
||||
<style>
|
||||
.line-statistics-query-row {
|
||||
min-height: 70px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.line-statistics-query-row:not(:last-child) {
|
||||
border-bottom: 1px dashed #e6e6e6;
|
||||
}
|
||||
|
||||
.line-statistics-query-content {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
<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>
|
||||
<guava ref="guava">
|
||||
<template>
|
||||
<el-card shadow="never">
|
||||
<el-row class="line-statistics-query-row" type="flex" align="middle">
|
||||
<el-col class="line-statistics-query-content">
|
||||
<el-row :gutter="30">
|
||||
<el-col :span="12">
|
||||
<span>年  度:</span>
|
||||
<el-date-picker v-model="pageForm.startYear" type="year" value-format="yyyy"
|
||||
:clearable="false" placeholder="选择年" style="width: 38%"
|
||||
@change="yearChange"></el-date-picker>
|
||||
<span>至</span>
|
||||
<el-date-picker v-model="pageForm.endYear" type="year" value-format="yyyy"
|
||||
:clearable="false" placeholder="选择年" style="width: 38%"
|
||||
@change="yearChange"></el-date-picker>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<span>所属工会:</span>
|
||||
<el-select v-model="pageForm.unionId" filterable clearable placeholder="请选择所属工会"
|
||||
style="width: 80%" @change="doSearch">
|
||||
<el-option v-for="item in unionOptions" :key="item.id" :label="item.name"
|
||||
:value="item.id"></el-option>
|
||||
</el-select>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row class="line-statistics-query-row" type="flex" align="middle">
|
||||
<el-col class="line-statistics-query-content">
|
||||
<el-row :gutter="30">
|
||||
<el-col :span="12">
|
||||
<span>线路类型:</span>
|
||||
<el-select v-model="pageForm.regionalNature" filterable placeholder="请选择线路类型"
|
||||
style="width: 80%" @change="lineTypeChange">
|
||||
<el-option label="全部" value=""></el-option>
|
||||
<el-option label="省内" value="省内"></el-option>
|
||||
<el-option label="省外" value="省外"></el-option>
|
||||
</el-select>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<span>线  路:</span>
|
||||
<el-select v-model="pageForm.takePartInLineId" filterable clearable
|
||||
placeholder="请选择线路" style="width: 80%" @change="lineChange">
|
||||
<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>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row class="line-statistics-query-row" type="flex" align="middle">
|
||||
<el-col class="line-statistics-query-content">
|
||||
<el-row :gutter="30">
|
||||
<el-col :span="12">
|
||||
<span>标  段:</span>
|
||||
<el-select v-model="pageForm.lotId" filterable clearable placeholder="请选择标段"
|
||||
style="width: 80%" @change="doSearch">
|
||||
<el-option v-for="item in lots" :key="item.id" :label="item.lotName"
|
||||
:value="item.id"></el-option>
|
||||
</el-select>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<span>出行时间:</span>
|
||||
<el-select v-model="pageForm.selectId" filterable clearable placeholder="请选择出行时间"
|
||||
style="width: 80%" @change="doSearch">
|
||||
<el-option v-for="item in linePlayTimes" :key="item.selectId" :label="item.times"
|
||||
:value="item.selectId"></el-option>
|
||||
</el-select>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
<el-card shadow="never" class="mt10">
|
||||
<table-tool label="数据列表">
|
||||
<el-button icon="el-icon-s-promotion" size="small" type="primary" class="mr10" @click="doExport">
|
||||
导出成团线路人员
|
||||
</el-button>
|
||||
</table-tool>
|
||||
<el-table ref="table" row-key="lineUId" :data="tableData" v-loading="tableLoading" style="width: 100%"
|
||||
@sort-change="pageOrder">
|
||||
<el-table-column align="center" header-align="center" type="index" label="序号" :index="indexMethod"
|
||||
width="80"></el-table-column>
|
||||
<el-table-column v-for="column in tableColumns" :key="column.prop" align="center"
|
||||
header-align="center" show-overflow-tooltip :label="column.label"
|
||||
:prop="column.prop" :width="column.width" :sortable="column.sortable">
|
||||
<template v-slot="{row}">
|
||||
<el-link v-if="column.prop==='lineName'" type="primary" @click="openLine(row)">
|
||||
{{row.lineName}}
|
||||
</el-link>
|
||||
<el-link v-else-if="column.prop==='lineNum'" type="primary" @click="openUserData(row)">
|
||||
{{Number(row.lineNum||0)+Number(row.signUpUserFamilyNum||0)}}({{row.signUpUserFamilyNum||0}})
|
||||
</el-link>
|
||||
<span v-else>{{row[column.prop]}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="380">
|
||||
<template v-slot="{row}">
|
||||
<el-button size="mini" type="primary" @click="openUserData(row)">查看人员</el-button>
|
||||
<el-button size="mini" type="primary" @click="sendNotice(row,true,true)">发送成团通知
|
||||
</el-button>
|
||||
<el-dropdown class="ml10 mr10" 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="sendNotice(row,false,true)">保留报名记录
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click.native="sendNotice(row,false,false)">删除报名记录
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
</template>
|
||||
<template #public>
|
||||
<line-info ref="viewLineInfo"></line-info>
|
||||
</template>
|
||||
<template #edit>
|
||||
<el-card shadow="never">
|
||||
<div class="search">
|
||||
<div class="search-item">
|
||||
<div class="search-item-label">工号姓名</div>
|
||||
<div class="search-item-option">
|
||||
<el-input v-model="userPage.searchKeyword" maxlength="30" clearable></el-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="search-item" v-if="$auth.hasRole('SYSADMIN') || $auth.hasRole('SCHOOL_UNION_ADMIN')">
|
||||
<div class="search-item-label">所属工会</div>
|
||||
<div class="search-item-option">
|
||||
<el-select v-model="userPage.unionId" filterable clearable style="width: 100%"
|
||||
@change="flushUnits">
|
||||
<el-option v-for="item in unionOptions" :key="item.id" :label="item.name"
|
||||
:value="item.id"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="search-item">
|
||||
<div class="search-item-label">所属单位</div>
|
||||
<div class="search-item-option">
|
||||
<el-select v-model="userPage.unitId" filterable clearable style="width: 100%">
|
||||
<el-option v-for="item in units" :key="item.id" :label="item.name"
|
||||
:value="item.id"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="search-query">
|
||||
<el-button type="primary" icon="el-icon-search" @click="loadUsers">搜索</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card shadow="never" class="mt10">
|
||||
<el-table :data="users" v-loading="userLoading">
|
||||
<el-table-column v-for="column in userColumns" :key="column.prop" align="center"
|
||||
header-align="center" show-overflow-tooltip :label="column.label"
|
||||
:prop="column.prop" :sortable="column.sortable">
|
||||
<template v-slot="{row}">
|
||||
<el-link v-if="column.prop==='familyCount'" type="primary">
|
||||
{{Number(row.familyCount||0)>0?'携带':'未携带'}}({{row.familyCount||0}})
|
||||
</el-link>
|
||||
<span v-else>{{row[column.prop]}}</span></template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-row class="el-pagination-container" style="margin-bottom: 0">
|
||||
<el-pagination :current-page="userPage.pageNumber" :page-size="userPage.pageSize"
|
||||
:page-sizes="[5,10,20,30,50]" :total="userPage.totalCount"
|
||||
layout="total, sizes, prev, pager, next" @size-change="userSizeChange"
|
||||
@current-change="userPageChange"></el-pagination>
|
||||
</el-row>
|
||||
</el-card>
|
||||
</template>
|
||||
</guava>
|
||||
</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()}})
|
||||
<!--#include('../line/info.js'){}#-->
|
||||
new Vue({
|
||||
el: '#app',
|
||||
mixins: [initTableMixins],
|
||||
components: {'line-info': info},
|
||||
data() {
|
||||
const year = new Date().getFullYear().toString()
|
||||
return {
|
||||
pageForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
startYear: year,
|
||||
endYear: year,
|
||||
unionId: '',
|
||||
regionalNature: '',
|
||||
takePartInLineId: '',
|
||||
lotId: '',
|
||||
selectId: '',
|
||||
signUpMode: ''
|
||||
},
|
||||
tableColumns: [
|
||||
{prop: 'year', label: '年度', width: 60}, {
|
||||
prop: 'lineName',
|
||||
label: '线路名称',
|
||||
sortable: true,
|
||||
width: 200
|
||||
},
|
||||
{prop: 'linePlayTime', label: '出行时间', width: 180}, {
|
||||
prop: 'travelAgencyName',
|
||||
label: '承担旅行社',
|
||||
sortable: true,
|
||||
width: 180
|
||||
},
|
||||
{prop: 'unionName', label: '选择线路工会', sortable: true}, {
|
||||
prop: 'regionalNature',
|
||||
label: '线路类型',
|
||||
sortable: true
|
||||
},
|
||||
{prop: 'contact', label: '联系人', sortable: true, checked: 0}, {
|
||||
prop: 'contactMobileNumber',
|
||||
label: '联系方式',
|
||||
checked: 0
|
||||
},
|
||||
{prop: 'minimumGroupSize', label: '最少成团人数(含家属)', width: 140}, {
|
||||
prop: 'lineNum',
|
||||
label: '报名人数(含家属)',
|
||||
width: 140
|
||||
}
|
||||
],
|
||||
unionOptions: [],
|
||||
lineOptions: [],
|
||||
linePlayTimes: [],
|
||||
lots: [],
|
||||
units: [],
|
||||
lineUId: '',
|
||||
users: [],
|
||||
userLoading: false,
|
||||
userPage: {pageNumber: 1, pageSize: 10, totalCount: 0, searchKeyword: '', unionId: '', unitId: ''},
|
||||
userColumns: [
|
||||
{prop: 'loginName', label: '工号'}, {prop: 'userName', label: '姓名'}, {
|
||||
prop: 'unionName',
|
||||
label: '所属工会'
|
||||
},
|
||||
{prop: 'unitName', label: '所属单位', sortable: true}, {prop: 'familyCount', label: '是否携带家属'},
|
||||
{prop: 'linePlayTime', label: '出行时间', sortable: true}
|
||||
]
|
||||
}
|
||||
},
|
||||
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() {
|
||||
this.$set(this.pageForm, 'takePartInLineId', '');
|
||||
this.$set(this.pageForm, 'selectId', '');
|
||||
this.$set(this, 'linePlayTimes', [])
|
||||
this.loadLines();
|
||||
this.doSearch()
|
||||
},
|
||||
lineTypeChange() {
|
||||
this.$set(this.pageForm, 'takePartInLineId', '');
|
||||
this.$set(this.pageForm, 'selectId', '');
|
||||
this.$set(this, 'linePlayTimes', [])
|
||||
this.loadLines();
|
||||
this.doSearch()
|
||||
},
|
||||
lineChange() {
|
||||
this.$set(this.pageForm, 'selectId', '');
|
||||
this.loadLinePlayTimes();
|
||||
this.doSearch()
|
||||
},
|
||||
loadLines() {
|
||||
this.$axios.post(loc() + '/getLineOptions', {
|
||||
startYear: this.pageForm.startYear,
|
||||
endYear: this.pageForm.endYear,
|
||||
signUpMode: '',
|
||||
regionalNature: this.pageForm.regionalNature
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code === 0) this.$set(this, 'lineOptions', res.data || [])
|
||||
})
|
||||
},
|
||||
loadLinePlayTimes() {
|
||||
if (!this.pageForm.takePartInLineId) {
|
||||
this.$set(this, 'linePlayTimes', []);
|
||||
return
|
||||
}
|
||||
this.$axios.post(loc() + '/getLinePlayTimes', {
|
||||
lineId: this.pageForm.takePartInLineId,
|
||||
startYear: this.pageForm.startYear,
|
||||
endYear: this.pageForm.endYear
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code === 0) this.$set(this, 'linePlayTimes', res.data || [])
|
||||
})
|
||||
},
|
||||
doExport() {
|
||||
this.$downLoad(loc() + '/doExport', this.pageForm)
|
||||
},
|
||||
openLine(row) {
|
||||
this.$refs.guava.public()
|
||||
this.$nextTick(() => {
|
||||
this.$refs.viewLineInfo.findOne(row.lineId, row.usUnionId)
|
||||
this.$refs.viewLineInfo.findUnionSelectLineData(row.lineId, row.usUnionId)
|
||||
this.$set(this.$refs.viewLineInfo, 'showTravelTimePeriod', true)
|
||||
})
|
||||
},
|
||||
openUserData(row) {
|
||||
this.$set(this, 'lineUId', row.lineUId)
|
||||
this.$set(this, 'userPage', {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
searchKeyword: '',
|
||||
unionId: '',
|
||||
unitId: ''
|
||||
})
|
||||
this.$set(this, 'units', []);
|
||||
this.$refs.guava.edit();
|
||||
this.loadUsers()
|
||||
},
|
||||
flushUnits() {
|
||||
this.$set(this.userPage, 'unitId', '')
|
||||
if (!this.userPage.unionId) {
|
||||
this.$set(this, 'units', []);
|
||||
return
|
||||
}
|
||||
getUnits(this.userPage.unionId).then((res) => {
|
||||
this.$set(this, 'units', res || [])
|
||||
})
|
||||
},
|
||||
loadUsers() {
|
||||
this.$set(this, 'userLoading', true)
|
||||
const params = Object.assign({}, this.userPage, {takePartLineId: this.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()
|
||||
},
|
||||
sendNotice(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
|
||||
}
|
||||
const noticeName = success ? '成团通知' : '未成团通知'
|
||||
const recordAction = success ? '' : ',并' + (keep ? '保留' : '删除') + '报名记录'
|
||||
const message = '当前将通过' + (data.sendTypeName || '微信、钉钉') + '发送' + noticeName + data.sendCount + '人' + recordAction + ',请确认或修改消息内容。'
|
||||
this.$prompt(message, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
inputType: 'textarea',
|
||||
inputValue: data.content || '',
|
||||
inputValidator: (value) => !!(value && value.trim()),
|
||||
inputErrorMessage: '消息内容不能为空'
|
||||
}).then((prompt) => {
|
||||
const url = success ? '/sendSuccess' : '/sendFail'
|
||||
const params = {id: row.lineUId, content: prompt.value.trim()}
|
||||
if (!success) this.$set(params, 'type', keep)
|
||||
this.$axios.post(loc() + url, params).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) {
|
||||
const source = (res.data && res.data.lots) || []
|
||||
const lots = source.filter((item, index, list) => item && item.id && list.findIndex((lot) => lot && lot.id === item.id) === index)
|
||||
this.$set(this, 'lots', lots)
|
||||
}
|
||||
})
|
||||
this.loadLines();
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<!--# } #-->
|
||||
|
||||
+684
-15
@@ -1,19 +1,688 @@
|
||||
<!--# layout("/layouts/platform.html"){ #-->
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
|
||||
<style>
|
||||
|
||||
.center-col {
|
||||
background-color: white;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
padding: 20px;
|
||||
width: calc((100% - (20px * 2)) / 2);
|
||||
}
|
||||
|
||||
.col-title {
|
||||
color: rgba(0, 0, 0, .45);
|
||||
font-size: 14px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.col-button {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.rating-summary {
|
||||
float: right;
|
||||
color: #ff9900;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.travel-agency-summary {
|
||||
display: flex;
|
||||
padding: 22px 0;
|
||||
}
|
||||
|
||||
.travel-agency-summary-item {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
padding: 0 24px;
|
||||
border-right: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.travel-agency-summary-item:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.travel-agency-summary-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: 0 0 auto;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
margin-right: 14px;
|
||||
border-radius: 50%;
|
||||
color: #1867b0;
|
||||
background-color: #eef6ff;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.travel-agency-summary-label {
|
||||
color: rgba(0, 0, 0, .65);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.travel-agency-summary-score {
|
||||
margin-top: 2px;
|
||||
color: #1867b0;
|
||||
font-size: 26px;
|
||||
font-weight: 600;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
.travel-agency-summary-score span {
|
||||
margin-left: 2px;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.travel-agency-summary-count {
|
||||
margin-top: 3px;
|
||||
color: rgba(0, 0, 0, .45);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.scheme-service-card {
|
||||
min-height: 360px;
|
||||
}
|
||||
|
||||
.scheme-service-title {
|
||||
margin-bottom: 14px;
|
||||
color: rgba(0, 0, 0, .85);
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.scheme-type-switch {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.scheme-selector {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.scheme-selector-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: calc((100% - 36px) / 4);
|
||||
min-width: 200px;
|
||||
height: 56px;
|
||||
margin: 0 !important;
|
||||
padding: 0 16px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.scheme-selector-name {
|
||||
overflow: hidden;
|
||||
margin-right: 12px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.scheme-selector-score {
|
||||
flex: 0 0 auto;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.scheme-detail {
|
||||
padding: 22px 24px;
|
||||
border: 1px solid #e6eef8;
|
||||
border-radius: 4px;
|
||||
background-color: #fcfdff;
|
||||
}
|
||||
|
||||
.scheme-detail-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 22px;
|
||||
}
|
||||
|
||||
.scheme-detail-name {
|
||||
color: rgba(0, 0, 0, .85);
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.scheme-detail-agency {
|
||||
margin-top: 8px;
|
||||
color: rgba(0, 0, 0, .45);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.scheme-detail-composite {
|
||||
min-width: 130px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.scheme-detail-composite-label {
|
||||
color: rgba(0, 0, 0, .45);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.scheme-detail-composite-score {
|
||||
margin-top: 3px;
|
||||
color: #1867b0;
|
||||
font-size: 30px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.scheme-detail-composite-score span {
|
||||
margin-left: 2px;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.scheme-detail-composite-count {
|
||||
margin-top: 3px;
|
||||
color: rgba(0, 0, 0, .45);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.scheme-metric-list {
|
||||
display: flex;
|
||||
border-top: 1px solid #edf1f5;
|
||||
}
|
||||
|
||||
.scheme-metric-item {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
padding: 18px 14px 8px;
|
||||
text-align: center;
|
||||
border-right: 1px solid #edf1f5;
|
||||
}
|
||||
|
||||
.scheme-metric-item:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.scheme-metric-icon {
|
||||
color: #1867b0;
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.scheme-metric-label {
|
||||
margin-top: 8px;
|
||||
color: rgba(0, 0, 0, .65);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.scheme-metric-score {
|
||||
margin-top: 5px;
|
||||
color: #1867b0;
|
||||
font-size: 23px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.scheme-metric-score span {
|
||||
margin-left: 2px;
|
||||
font-size: 13px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.scheme-metric-count {
|
||||
margin-top: 3px;
|
||||
color: rgba(0, 0, 0, .45);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.scheme-feedback-action {
|
||||
padding-top: 18px;
|
||||
text-align: center;
|
||||
border-top: 1px solid #edf1f5;
|
||||
}
|
||||
|
||||
.scheme-feedback-list {
|
||||
max-height: 500px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.scheme-feedback-row {
|
||||
padding: 16px 4px;
|
||||
border-bottom: 1px solid #edf1f5;
|
||||
}
|
||||
|
||||
.scheme-feedback-row:first-child {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.scheme-feedback-row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.scheme-feedback-user {
|
||||
color: rgba(0, 0, 0, .85);
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.scheme-feedback-union {
|
||||
margin-left: 10px;
|
||||
color: rgba(0, 0, 0, .45);
|
||||
font-size: 13px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.scheme-feedback-content {
|
||||
margin-top: 9px;
|
||||
color: rgba(0, 0, 0, .65);
|
||||
line-height: 22px;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.scheme-empty {
|
||||
padding: 64px 0;
|
||||
color: rgba(0, 0, 0, .45);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.feedback-content {
|
||||
line-height: 22px;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.feedback-summary {
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
vertical-align: middle;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.feedback-score-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin: 0 -6px 14px;
|
||||
}
|
||||
|
||||
.feedback-score-item {
|
||||
box-sizing: border-box;
|
||||
width: 33.33%;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.feedback-score-item-inner {
|
||||
padding: 10px 12px;
|
||||
border-radius: 4px;
|
||||
background-color: #f7faff;
|
||||
}
|
||||
|
||||
.feedback-score-label {
|
||||
color: rgba(0, 0, 0, .45);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.feedback-score-value {
|
||||
margin-top: 3px;
|
||||
color: #1867b0;
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.feedback-pagination {
|
||||
margin-top: 20px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<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>
|
||||
<guava ref="guava">
|
||||
<template>
|
||||
<el-card shadow="never">
|
||||
<div class="btn-group tool-button">
|
||||
<el-date-picker
|
||||
v-model="year"
|
||||
type="year"
|
||||
value-format="yyyy"
|
||||
placeholder="选择年"
|
||||
style="width: 100%" @change="doSearch">
|
||||
</el-date-picker>
|
||||
</div>
|
||||
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never" class="mt20">
|
||||
<div class="travel-agency-summary">
|
||||
<div class="travel-agency-summary-item" v-for="item in travelAgencyCompositeRatings" :key="item.travelAgencyName">
|
||||
<div class="travel-agency-summary-icon">
|
||||
<i class="el-icon-guide"></i>
|
||||
</div>
|
||||
<div>
|
||||
<div class="travel-agency-summary-label">{{item.travelAgencyName}}</div>
|
||||
<div class="travel-agency-summary-score" v-if="hasRatingScore(item.avg)">{{formatRatingScore(item.avg)}}<span>分</span></div>
|
||||
<div class="travel-agency-summary-score" v-else>暂无评价</div>
|
||||
<div class="travel-agency-summary-count">评价数 {{item.ratingCount}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never" class="mt20 scheme-service-card">
|
||||
<div class="scheme-service-title">方案服务评分</div>
|
||||
<el-radio-group class="scheme-type-switch" :value="schemeType" @input="changeSchemeType" size="medium">
|
||||
<el-radio-button label="线路"></el-radio-button>
|
||||
<el-radio-button label="灵活组团"></el-radio-button>
|
||||
<el-radio-button label="定点"></el-radio-button>
|
||||
</el-radio-group>
|
||||
<div class="scheme-selector" v-if="getSchemeRatingsByType().length">
|
||||
<el-button
|
||||
v-for="item in getSchemeRatingsByType()"
|
||||
:key="item.schemeId"
|
||||
class="scheme-selector-item"
|
||||
:type="isSelectedScheme(item) ? 'primary' : 'default'"
|
||||
:plain="!isSelectedScheme(item)"
|
||||
@click="selectScheme(item)">
|
||||
<span class="scheme-selector-name">{{item.schemeName}}</span>
|
||||
<span class="scheme-selector-score">{{formatRatingScore(item.compositeAvg)}} 分</span>
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="scheme-detail" v-if="getSelectedSchemeRating()">
|
||||
<div class="scheme-detail-header">
|
||||
<div>
|
||||
<div class="scheme-detail-name">{{getSelectedSchemeRating().schemeName}}</div>
|
||||
<div class="scheme-detail-agency">旅行社:{{getSelectedSchemeRating().travelAgencyName || '暂无旅行社信息'}}</div>
|
||||
</div>
|
||||
<div class="scheme-detail-composite">
|
||||
<div class="scheme-detail-composite-label">综合评分</div>
|
||||
<div class="scheme-detail-composite-score">{{formatRatingScore(getSelectedSchemeRating().compositeAvg)}}<span>分</span></div>
|
||||
<div class="scheme-detail-composite-count">评价数 {{getSelectedSchemeRating().ratingCount || 0}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scheme-metric-list">
|
||||
<div class="scheme-metric-item" v-for="item in getSelectedSchemeMetrics()" :key="item.key">
|
||||
<i class="scheme-metric-icon" :class="item.icon"></i>
|
||||
<div class="scheme-metric-label">{{item.label}}</div>
|
||||
<div class="scheme-metric-score">{{formatRatingScore(item.avg)}}<span>分</span></div>
|
||||
<div class="scheme-metric-count">评价数 {{item.count || 0}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scheme-feedback-action">
|
||||
<el-button type="primary" plain @click="openSchemeFeedbackDialog">查看评价建议</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scheme-empty" v-else>暂无可查看的方案评分</div>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never" class="mt20">
|
||||
<div class="col-title">
|
||||
评价明细
|
||||
<div style="float: right">
|
||||
<el-button size="medium" type="primary" icon="el-icon-download" style="margin-right: 10px" @click="exportFeedbackDetails">导出</el-button>
|
||||
<el-radio-group :value="feedbackType" @input="changeFeedbackType" size="medium">
|
||||
<el-radio-button label="全部"></el-radio-button>
|
||||
<el-radio-button label="线路"></el-radio-button>
|
||||
<el-radio-button label="灵活组团"></el-radio-button>
|
||||
<el-radio-button label="定点"></el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
<el-table :data="getPagedFeedbackDetails()" style="width: 100%">
|
||||
<el-table-column align="center" header-align="center" type="index" label="序号" :index="feedbackIndexMethod" width="80"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" prop="typeName" label="报名方式" width="100"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" prop="projectName" label="方案名称" min-width="160" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column align="center" header-align="center" prop="travelAgencyName" label="旅行社" min-width="140" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column align="center" header-align="center" prop="userName" label="评价人" width="100"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" prop="unitName" label="所属单位" min-width="150" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="综合评分" width="110">
|
||||
<template slot-scope="scope">
|
||||
{{formatFeedbackScore(scope.row.compositeScore)}}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="left" header-align="center" label="评价建议" min-width="250">
|
||||
<template slot-scope="scope">
|
||||
<span class="feedback-summary">{{getFeedbackContentSummary(scope.row.feedbackContent)}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="操作" width="90">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" size="mini" @click="openFeedbackDetail(scope.row)">查看</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-pagination
|
||||
class="feedback-pagination"
|
||||
:current-page="feedbackPage"
|
||||
:page-size="feedbackPageSize"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:total="getFilteredFeedbackDetails().length"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@current-change="changeFeedbackPage"
|
||||
@size-change="changeFeedbackPageSize">
|
||||
</el-pagination>
|
||||
</el-card>
|
||||
</template>
|
||||
</guava>
|
||||
|
||||
<el-dialog title="评价详情" :visible="feedbackDetailVisible" width="620px" @close="closeFeedbackDetail">
|
||||
<div class="feedback-score-grid">
|
||||
<div class="feedback-score-item">
|
||||
<div class="feedback-score-item-inner">
|
||||
<div class="feedback-score-label">综合评分</div>
|
||||
<div class="feedback-score-value">{{formatFeedbackScore(feedbackDetail.compositeScore)}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="feedback-score-item">
|
||||
<div class="feedback-score-item-inner">
|
||||
<div class="feedback-score-label">旅行社评分</div>
|
||||
<div class="feedback-score-value">{{formatFeedbackScore(feedbackDetail.evaluationForTravelAgency)}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="feedback-score-item" v-if="feedbackDetail.typeName === '线路'">
|
||||
<div class="feedback-score-item-inner">
|
||||
<div class="feedback-score-label">行程评分</div>
|
||||
<div class="feedback-score-value">{{formatFeedbackScore(feedbackDetail.evaluationForJourney)}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="feedback-score-item">
|
||||
<div class="feedback-score-item-inner">
|
||||
<div class="feedback-score-label">{{feedbackDetail.typeName === '线路' ? '住宿评分' : '酒店评分'}}</div>
|
||||
<div class="feedback-score-value">{{formatFeedbackScore(feedbackDetail.evaluationForAccommodation)}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="feedback-score-item" v-if="feedbackDetail.typeName === '线路'">
|
||||
<div class="feedback-score-item-inner">
|
||||
<div class="feedback-score-label">餐饮评分</div>
|
||||
<div class="feedback-score-value">{{formatFeedbackScore(feedbackDetail.evaluationForDining)}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="feedback-score-item" v-if="feedbackDetail.typeName === '线路'">
|
||||
<div class="feedback-score-item-inner">
|
||||
<div class="feedback-score-label">交通评分</div>
|
||||
<div class="feedback-score-value">{{formatFeedbackScore(feedbackDetail.evaluationForTransportation)}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="feedback-content">{{feedbackDetail.feedbackContent || '暂无评价建议'}}</div>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="评价建议" :visible="schemeFeedbackVisible" width="720px" @close="closeSchemeFeedbackDialog">
|
||||
<div class="scheme-feedback-list" v-if="getSelectedSchemeFeedbackDetails().length">
|
||||
<div class="scheme-feedback-row" v-for="item in getSelectedSchemeFeedbackDetails()" :key="item.id">
|
||||
<div class="scheme-feedback-user">
|
||||
{{item.userName || '匿名评价人'}}
|
||||
<span class="scheme-feedback-union">{{item.unitName || '暂无所属单位'}}</span>
|
||||
</div>
|
||||
<div class="scheme-feedback-content">{{item.feedbackContent}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scheme-empty" v-else>当前方案暂无评价建议</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
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()}})
|
||||
new Vue({
|
||||
el: '#app',
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
year: new Date().getFullYear().toString(),
|
||||
summaryLoading: false,
|
||||
feedbackLoading: false,
|
||||
travelAgencyCompositeRatings: [],
|
||||
schemeServiceRatings: [],
|
||||
schemeType: '线路',
|
||||
selectedSchemeId: '',
|
||||
feedbackDetails: [],
|
||||
feedbackType: '全部',
|
||||
feedbackPage: 1,
|
||||
feedbackPageSize: 10,
|
||||
feedbackDetail: {},
|
||||
feedbackDetailVisible: false,
|
||||
schemeFeedbackVisible: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
doSearch() {
|
||||
this.getSatisfactionRatingStatistics()
|
||||
this.getSatisfactionFeedbackDetails()
|
||||
},
|
||||
getSatisfactionRatingStatistics() {
|
||||
this.$set(this, 'summaryLoading', true)
|
||||
this.$axios.post(loc() + '/getSatisfactionRatingStatistics', {year: this.year}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
const statisticData = res.data || {}
|
||||
this.$set(this, 'travelAgencyCompositeRatings', statisticData.travelAgencyCompositeRatings || [])
|
||||
this.$set(this, 'schemeServiceRatings', statisticData.schemeServiceRatings || [])
|
||||
this.resetSelectedScheme()
|
||||
} else {
|
||||
this.$message.warning(res.msg)
|
||||
}
|
||||
}).finally(() => {
|
||||
this.$set(this, 'summaryLoading', false)
|
||||
})
|
||||
},
|
||||
getSatisfactionFeedbackDetails() {
|
||||
this.$set(this, 'feedbackLoading', true)
|
||||
this.$axios.post(loc() + '/getSatisfactionFeedbackDetails', {year: this.year}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$set(this, 'feedbackDetails', res.data || [])
|
||||
this.$set(this, 'feedbackPage', 1)
|
||||
} else {
|
||||
this.$message.warning(res.msg)
|
||||
}
|
||||
}).finally(() => {
|
||||
this.$set(this, 'feedbackLoading', false)
|
||||
})
|
||||
},
|
||||
formatRatingScore(score) {
|
||||
if (score === null || score === undefined || score === '') return '-'
|
||||
return Number(score).toFixed(1)
|
||||
},
|
||||
hasRatingScore(score) {
|
||||
return score !== null && score !== undefined && score !== ''
|
||||
},
|
||||
formatFeedbackScore(score) {
|
||||
if (score === null || score === undefined || score === '') return '-'
|
||||
return Number(score).toFixed(1) + ' 分'
|
||||
},
|
||||
getFeedbackContentSummary(content) {
|
||||
const feedbackContent = String(content || '').trim()
|
||||
if (!feedbackContent) return '暂无评价建议'
|
||||
return feedbackContent.length > 36 ? feedbackContent.substring(0, 36) + '…' : feedbackContent
|
||||
},
|
||||
changeSchemeType(value) {
|
||||
this.$set(this, 'schemeType', value)
|
||||
this.resetSelectedScheme()
|
||||
},
|
||||
selectScheme(schemeRating) {
|
||||
this.$set(this, 'selectedSchemeId', schemeRating.schemeId)
|
||||
},
|
||||
isSelectedScheme(schemeRating) {
|
||||
return String(this.selectedSchemeId) === String(schemeRating.schemeId)
|
||||
},
|
||||
getSchemeRatingsByType() {
|
||||
return this.schemeServiceRatings.filter((item) => item.typeName === this.schemeType)
|
||||
},
|
||||
getSelectedSchemeRating() {
|
||||
return this.getSchemeRatingsByType().find((item) => this.isSelectedScheme(item))
|
||||
},
|
||||
resetSelectedScheme() {
|
||||
const schemeRatings = this.getSchemeRatingsByType()
|
||||
this.$set(this, 'selectedSchemeId', schemeRatings.length ? schemeRatings[0].schemeId : '')
|
||||
},
|
||||
getSelectedSchemeMetrics() {
|
||||
const schemeRating = this.getSelectedSchemeRating() || {}
|
||||
const metrics = [
|
||||
{key: 'travelAgency', label: '旅行社', icon: 'el-icon-guide', avg: schemeRating.travelAgencyAvg, count: schemeRating.travelAgencyCount},
|
||||
{key: 'journey', label: '行程', icon: 'el-icon-map-location', avg: schemeRating.journeyAvg, count: schemeRating.journeyCount},
|
||||
{key: 'accommodation', label: '住宿', icon: 'el-icon-house', avg: schemeRating.accommodationAvg, count: schemeRating.accommodationCount},
|
||||
{key: 'dining', label: '餐饮', icon: 'el-icon-food', avg: schemeRating.diningAvg, count: schemeRating.diningCount},
|
||||
{key: 'transportation', label: '交通', icon: 'el-icon-truck', avg: schemeRating.transportationAvg, count: schemeRating.transportationCount}
|
||||
]
|
||||
if (schemeRating.typeName === '线路') return metrics
|
||||
return [
|
||||
metrics[0],
|
||||
{key: 'accommodation', label: '酒店', icon: 'el-icon-house', avg: schemeRating.accommodationAvg, count: schemeRating.accommodationCount}
|
||||
]
|
||||
},
|
||||
getSelectedSchemeFeedbackDetails() {
|
||||
const schemeRating = this.getSelectedSchemeRating()
|
||||
if (!schemeRating) return []
|
||||
return this.feedbackDetails.filter((item) => item.typeName === schemeRating.typeName
|
||||
&& String(item.schemeId) === String(schemeRating.schemeId)
|
||||
&& String(item.feedbackContent || '').trim())
|
||||
},
|
||||
openSchemeFeedbackDialog() {
|
||||
this.$set(this, 'schemeFeedbackVisible', true)
|
||||
},
|
||||
closeSchemeFeedbackDialog() {
|
||||
this.$set(this, 'schemeFeedbackVisible', false)
|
||||
},
|
||||
changeFeedbackType(value) {
|
||||
this.$set(this, 'feedbackType', value)
|
||||
this.$set(this, 'feedbackPage', 1)
|
||||
},
|
||||
exportFeedbackDetails() {
|
||||
this.$downLoad(loc() + '/exportSatisfactionFeedbackDetails', {
|
||||
year: this.year,
|
||||
typeName: this.feedbackType
|
||||
})
|
||||
},
|
||||
getFilteredFeedbackDetails() {
|
||||
if (this.feedbackType === '全部') return this.feedbackDetails
|
||||
return this.feedbackDetails.filter((item) => item.typeName === this.feedbackType)
|
||||
},
|
||||
getPagedFeedbackDetails() {
|
||||
const start = (this.feedbackPage - 1) * this.feedbackPageSize
|
||||
return this.getFilteredFeedbackDetails().slice(start, start + this.feedbackPageSize)
|
||||
},
|
||||
feedbackIndexMethod(index) {
|
||||
return (this.feedbackPage - 1) * this.feedbackPageSize + index + 1
|
||||
},
|
||||
changeFeedbackPage(page) {
|
||||
this.$set(this, 'feedbackPage', page)
|
||||
},
|
||||
changeFeedbackPageSize(pageSize) {
|
||||
this.$set(this, 'feedbackPageSize', pageSize)
|
||||
this.$set(this, 'feedbackPage', 1)
|
||||
},
|
||||
openFeedbackDetail(feedbackDetail) {
|
||||
this.$set(this, 'feedbackDetail', feedbackDetail)
|
||||
this.$set(this, 'feedbackDetailVisible', true)
|
||||
},
|
||||
closeFeedbackDetail() {
|
||||
this.$set(this, 'feedbackDetailVisible', false)
|
||||
this.$set(this, 'feedbackDetail', {})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.doSearch()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<!--# } #-->
|
||||
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
|
||||
@@ -319,6 +319,18 @@ layout("/layouts/platform.html"){
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<el-form-item label="福利通知附件" prop="noticeAttachment">
|
||||
<file-upload
|
||||
:value.sync="formData.noticeAttachment"
|
||||
:upload_number="1"
|
||||
accept=".doc,.docx"
|
||||
upload_mode="file"
|
||||
upload_text="上传福利通知附件"
|
||||
upload_result_category="interval"
|
||||
upload_result_type="url"
|
||||
></file-upload>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="封面图片" prop="cover">
|
||||
<file-upload
|
||||
:value.sync="formData.cover"
|
||||
@@ -373,7 +385,6 @@ layout("/layouts/platform.html"){
|
||||
formRules: {
|
||||
name: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
|
||||
festival: [{ required: true, message: "必选", trigger: ["blur", "change"] }],
|
||||
provideTime: [{ required: true, message: "必选", trigger: ["blur", "change"] }],
|
||||
choiceTime: [{ required: true, message: "必选", trigger: ["blur", "change"] }],
|
||||
provideAddress: [{ required: true, message: "必选", trigger: ["blur", "change"] }],
|
||||
provideMode: [{ required: true, message: "必选", trigger: ["blur", "change"] }],
|
||||
@@ -486,6 +497,7 @@ layout("/layouts/platform.html"){
|
||||
noticePushMode: 1,
|
||||
groupRequired: false,
|
||||
groupSelectionConfigs: [],
|
||||
noticeAttachment: "",
|
||||
options: [],
|
||||
welfareProjectSubjects: [],
|
||||
flexibleGifts: [{}]
|
||||
|
||||
@@ -43,12 +43,10 @@ const welfareOption = {
|
||||
|
||||
<el-table-column align="center" header-align="center" label="系统默认选择" width="140">
|
||||
<template slot-scope="{row}">
|
||||
<el-radio
|
||||
v-model="systemDefaultOption"
|
||||
:label="row"
|
||||
@change="setSystemDefault(row)">
|
||||
|
||||
</el-radio>
|
||||
<el-checkbox
|
||||
:value="!!row.isSystemDefault"
|
||||
@change="setSystemDefault(row, $event)">
|
||||
</el-checkbox>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
@@ -170,7 +168,6 @@ const welfareOption = {
|
||||
data() {
|
||||
return {
|
||||
welfareList: [],
|
||||
systemDefaultOption: null,
|
||||
localGroupRequired: false,
|
||||
descriptionDialog: {
|
||||
visible: false,
|
||||
@@ -187,10 +184,6 @@ const welfareOption = {
|
||||
created() {
|
||||
this.welfareList = this.value ? JSON.parse(JSON.stringify(this.value)) : [];
|
||||
this.localGroupRequired = Boolean(this.groupRequired);
|
||||
this.systemDefaultOption = this.welfareList.find((item) => item.isSystemDefault) || null;
|
||||
if (this.systemDefaultOption) {
|
||||
this.setSystemDefault(this.systemDefaultOption);
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
@@ -298,22 +291,16 @@ const welfareOption = {
|
||||
return true;
|
||||
},
|
||||
|
||||
// 每个福利项目只允许配置一个系统默认选项,保存时会随福利选项一并提交。
|
||||
setSystemDefault(defaultOption) {
|
||||
this.welfareList.forEach((item) => {
|
||||
this.$set(item, 'isSystemDefault', item === defaultOption);
|
||||
});
|
||||
// 每个福利选项独立维护系统默认状态,支持同时勾选多个选项或取消已有勾选。
|
||||
setSystemDefault(option, selected) {
|
||||
this.$set(option, 'isSystemDefault', selected);
|
||||
},
|
||||
|
||||
deleteOption(index) {
|
||||
this.$confirm('确认删除该福利选项?', '提示', {
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
const deletedOption = this.welfareList[index];
|
||||
this.welfareList.splice(index, 1);
|
||||
if (this.systemDefaultOption === deletedOption) {
|
||||
this.systemDefaultOption = null;
|
||||
}
|
||||
this.updateSortNumbers();
|
||||
});
|
||||
},
|
||||
@@ -442,7 +429,7 @@ const welfareOption = {
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.welfare-option .el-radio__label {
|
||||
.welfare-option .el-checkbox__label {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -135,11 +135,6 @@ layout("/layouts/platform.html"){
|
||||
<span v-else>暂无</span>
|
||||
</template>
|
||||
|
||||
<template scope="{row}" v-else-if="column.prop=='deliveryDate'">
|
||||
<span v-if="row.deliveryDate">{{$moment(row.deliveryDate).format('YYYY-MM-DD')}}</span>
|
||||
<span v-else>暂无</span>
|
||||
</template>
|
||||
|
||||
<template scope="{row}" v-else-if="column.prop=='remark'">
|
||||
<span v-if="row.remark">{{row.remark}}</span>
|
||||
<span v-else>暂无</span>
|
||||
@@ -224,7 +219,6 @@ layout("/layouts/platform.html"){
|
||||
{ prop: "choiceTime", label: "选择时间" },
|
||||
{ prop: "isChoose", label: "是否选择" },
|
||||
{ prop: "gist_list", label: "所选福利" },
|
||||
{ prop: "deliveryDate", label: "配送时间" },
|
||||
{ prop: "remark", label: "备注" }
|
||||
// { prop: "receiveAddress", label: "收货地址" }
|
||||
],
|
||||
@@ -289,15 +283,15 @@ layout("/layouts/platform.html"){
|
||||
},
|
||||
|
||||
/**
|
||||
* 配送日期到达后才显示评价入口;没有配送日期的历史数据不允许评价。
|
||||
* @param row 福利项目列表行,deliveryDate 为 yyyy-MM-dd 日期
|
||||
* @return {boolean} 当前日期达到配送日期时返回 true
|
||||
* 用户已选择福利且活动结束后显示评价入口。
|
||||
* @param row 福利项目列表行,choiceTimeEnd 为活动结束时间
|
||||
* @return {boolean} 当前时间达到活动结束时间时返回 true
|
||||
*/
|
||||
canEvaluate(row) {
|
||||
if (!row.isChoose || !row.deliveryDate) {
|
||||
if (!row.isChoose || !row.choiceTimeEnd) {
|
||||
return false
|
||||
}
|
||||
return this.$moment().startOf("day").valueOf() >= this.$moment(row.deliveryDate).startOf("day").valueOf()
|
||||
return this.$moment().valueOf() >= this.$moment(row.choiceTimeEnd).valueOf()
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -172,19 +172,6 @@ const optionSelect = {
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item prop="deliveryDate" label="配送时间">
|
||||
<el-date-picker
|
||||
v-model="contactForm.deliveryDate"
|
||||
type="date"
|
||||
value-format="yyyy-MM-dd"
|
||||
format="yyyy-MM-dd"
|
||||
placeholder="请选择配送时间"
|
||||
:picker-options="deliveryDatePickerOptions"
|
||||
style="width: 100%">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item prop="remark" label="备注">
|
||||
<el-input
|
||||
@@ -285,7 +272,6 @@ const optionSelect = {
|
||||
receiveAddress: "",
|
||||
userName: "", // 收货人
|
||||
userSign: "",
|
||||
deliveryDate: "", // 配送日期,按 yyyy-MM-dd 提交
|
||||
remark: "" // 用户自选和管理员代选共用的选择备注
|
||||
},
|
||||
contactRules: {
|
||||
@@ -299,15 +285,8 @@ const optionSelect = {
|
||||
message: "请输入正确的手机号码",
|
||||
trigger: "blur"
|
||||
}
|
||||
],
|
||||
deliveryDate: [
|
||||
{required: true, message: "请选择配送时间", trigger: "change"}
|
||||
]
|
||||
},
|
||||
deliveryDatePickerOptions: {
|
||||
// 配送日期按天校验,选择当天允许提交。
|
||||
disabledDate: (time) => time.getTime() < new Date().setHours(0, 0, 0, 0)
|
||||
},
|
||||
addressOptions: []
|
||||
}
|
||||
},
|
||||
@@ -447,7 +426,6 @@ const optionSelect = {
|
||||
receiveAddress: "",
|
||||
userName: "",
|
||||
userSign: "",
|
||||
deliveryDate: "",
|
||||
remark: ""
|
||||
}
|
||||
|
||||
@@ -504,8 +482,6 @@ const optionSelect = {
|
||||
// 本人选择按字段回退到登录人信息;代选只使用目标人员已有选择数据。
|
||||
this.$set(this.contactForm, "mobile", firstSelection.mobile || (!this.isProxySelect ? currentUser.mobile || "" : ""))
|
||||
this.$set(this.contactForm, "userName", firstSelection.userName || (!this.isProxySelect ? currentUser.username || "" : ""))
|
||||
this.$set(this.contactForm, "deliveryDate", this.userSelection[0].deliveryDate
|
||||
? this.$moment(this.userSelection[0].deliveryDate).format("YYYY-MM-DD") : "")
|
||||
this.$set(this.contactForm, "remark", this.userSelection[0].remark || "")
|
||||
} else {
|
||||
if (this.isProxySelect) {
|
||||
@@ -640,7 +616,6 @@ const optionSelect = {
|
||||
userName: this.showReceivingContact ? this.contactForm.userName : "",
|
||||
userSign: this.contactForm.userSign,
|
||||
receiveAddress: this.showReceivingContact ? this.contactForm.receiveAddress : "",
|
||||
deliveryDate: this.contactForm.deliveryDate,
|
||||
remark: this.contactForm.remark
|
||||
}
|
||||
]
|
||||
@@ -655,7 +630,6 @@ const optionSelect = {
|
||||
userName: this.showReceivingContact ? this.contactForm.userName : "",
|
||||
userSign: this.contactForm.userSign,
|
||||
receiveAddress: this.showReceivingContact ? this.contactForm.receiveAddress : "",
|
||||
deliveryDate: this.contactForm.deliveryDate,
|
||||
remark: this.contactForm.remark
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -29,8 +29,8 @@ layout("/layouts/platform.html"){
|
||||
</el-select>
|
||||
</search-item>
|
||||
|
||||
<search-item label="福利选项">
|
||||
<el-select clearable filterable placeholder="请选择福利选项" style="width: 100%" v-model="pageForm.welfareOptionId">
|
||||
<search-item label="所选套餐">
|
||||
<el-select clearable filterable placeholder="请选择所选套餐" style="width: 100%" v-model="pageForm.welfareOptionId">
|
||||
<el-option :key="item.id" :label="item.optionName" :value="item.id" v-for="item in welfareOptions"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
@@ -66,10 +66,33 @@ layout("/layouts/platform.html"){
|
||||
<el-option :key="item.id" :label="item.name" :value="item.id" v-for="item in unitOptions"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="人员分类">
|
||||
<dict-select v-model="pageForm.aidFundMemberUserType" placeholder="请选择人员分类" @change="doSearch"
|
||||
code="AIDFUND_MEMBER_USER_TYPE"></dict-select>
|
||||
</search-item>
|
||||
<search-item label="人员类型">
|
||||
<dict-select
|
||||
clearable
|
||||
code="USER_PERSON_TYPE"
|
||||
multiple
|
||||
collapse-tags
|
||||
placeholder="请选择人员类型"
|
||||
v-model="pageForm.personTypes"
|
||||
></dict-select>
|
||||
</search-item>
|
||||
|
||||
<search-item label="在职状态">
|
||||
<dict-select
|
||||
clearable
|
||||
code="USER_STATE"
|
||||
multiple
|
||||
collapse-tags
|
||||
placeholder="请选择人员状态"
|
||||
v-model="pageForm.userStates"
|
||||
></dict-select>
|
||||
</search-item>
|
||||
|
||||
<!-- <search-item label="人员分类">-->
|
||||
<!-- <el-select clearable filterable placeholder="请选择人员分类" style="width: 100%" v-model="pageForm.aidFundMemberUserType">-->
|
||||
<!-- <el-option :key="item.code" :label="item.name" :value="item.code" v-for="item in aidFundMemberUserTypeOptions"></el-option>-->
|
||||
<!-- </el-select>-->
|
||||
<!-- </search-item>-->
|
||||
</search>
|
||||
</el-card>
|
||||
|
||||
@@ -130,6 +153,7 @@ layout("/layouts/platform.html"){
|
||||
></el-table-column>
|
||||
|
||||
<el-table-column label="收货地址" prop="receiveAddress" v-if="provideMode===3"></el-table-column>
|
||||
<el-table-column label="备注" prop="remark" show-overflow-tooltip></el-table-column>
|
||||
|
||||
<el-table-column align="center" fixed="right" header-align="center" label="操作" width="100px">
|
||||
<template slot-scope="{row}">
|
||||
@@ -185,10 +209,13 @@ layout("/layouts/platform.html"){
|
||||
pageForm: {
|
||||
searchName: "username",
|
||||
year: new Date().getFullYear().toString(),
|
||||
isSelect: null
|
||||
isSelect: null,
|
||||
personTypes: [],
|
||||
userStates: []
|
||||
},
|
||||
unionOptions: [],
|
||||
unitOptions: [],
|
||||
aidFundMemberUserTypeOptions: [],
|
||||
projectOptions: [],
|
||||
tableColumns: [
|
||||
{ prop: "loginName", label: "工号" },
|
||||
@@ -198,7 +225,6 @@ layout("/layouts/platform.html"){
|
||||
{ prop: "welfareUnionName", label: "所属工会", sortable: true },
|
||||
{ prop: "welfareUnitName", label: "所属单位", sortable: true },
|
||||
{ prop: "selectedOptions", label: "所选福利", sortable: true },
|
||||
{ prop: "deliveryDate", label: "配送时间", sortable: true },
|
||||
{ prop: "mobile", label: "联系电话", sortable: true }
|
||||
],
|
||||
optionSelectVisible: false,
|
||||
@@ -213,6 +239,13 @@ layout("/layouts/platform.html"){
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
noticeDetailUrl() {
|
||||
if (!this.pageForm.projectId) {
|
||||
return ""
|
||||
}
|
||||
const appDomain = typeof APP_DOMAIN !== "undefined" && APP_DOMAIN ? APP_DOMAIN : window.location.origin
|
||||
return appDomain.replace(/\/+$/, "") + "/platform/welfare/notice/index?id=" + encodeURIComponent(this.pageForm.projectId)
|
||||
},
|
||||
welfareOptions() {
|
||||
if (this.pageForm.projectId) {
|
||||
return this.projectOptions.find((item) => item.id === this.pageForm.projectId)?.options
|
||||
@@ -286,7 +319,7 @@ layout("/layouts/platform.html"){
|
||||
this.$message.warning("请先选择福利项目")
|
||||
return
|
||||
}
|
||||
this.$set(this.messageDialog, "messageContent", "")
|
||||
this.$set(this.messageDialog, "messageContent", "查看详情:" + this.noticeDetailUrl)
|
||||
this.$set(this.messageDialog, "visible", true)
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.messageForm) {
|
||||
@@ -312,7 +345,7 @@ layout("/layouts/platform.html"){
|
||||
this.$message.warning("请输入有效的消息内容")
|
||||
return
|
||||
}
|
||||
this.messageSending = true
|
||||
this.$set(this, "messageSending", true)
|
||||
this.$axios.post("/platform/welfare/selection/situation/sendMessage", {
|
||||
pageForm: JSON.stringify(this.pageForm),
|
||||
messageContent: messageContent
|
||||
@@ -324,67 +357,7 @@ layout("/layouts/platform.html"){
|
||||
this.$message.error(res.msg)
|
||||
}
|
||||
}).finally(() => {
|
||||
this.messageSending = false
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 按当前页面全部查询条件导出签收单模版,后台会再次叠加登录人的数据权限。
|
||||
exportReceiveTemplate() {
|
||||
if (!this.pageForm.projectId) {
|
||||
this.$message.warning("请先选择福利项目")
|
||||
return
|
||||
}
|
||||
this.$downLoad("/platform/welfare/selection/situation/exportReceiveTemplate", {
|
||||
pageForm: JSON.stringify(this.pageForm)
|
||||
})
|
||||
},
|
||||
|
||||
// 打开发送窗口时清空上次编辑内容,实际接收人由后台按当前查询条件重新计算。
|
||||
openMessageDialog() {
|
||||
if (!this.pageForm.projectId) {
|
||||
this.$message.warning("请先选择福利项目")
|
||||
return
|
||||
}
|
||||
this.$set(this.messageDialog, "messageContent", "")
|
||||
this.$set(this.messageDialog, "visible", true)
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.messageForm) {
|
||||
this.$refs.messageForm.clearValidate()
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
closeMessageDialog(done) {
|
||||
this.$set(this.messageDialog, "visible", false)
|
||||
if (typeof done === "function") {
|
||||
done()
|
||||
}
|
||||
},
|
||||
|
||||
sendMessage() {
|
||||
this.$refs.messageForm.validate((valid) => {
|
||||
if (!valid) {
|
||||
return
|
||||
}
|
||||
const messageContent = (this.messageDialog.messageContent || "").trim()
|
||||
if (!messageContent) {
|
||||
this.$message.warning("请输入有效的消息内容")
|
||||
return
|
||||
}
|
||||
this.messageSending = true
|
||||
this.$axios.post("/platform/welfare/selection/situation/sendMessage", {
|
||||
pageForm: JSON.stringify(this.pageForm),
|
||||
messageContent: messageContent
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
this.closeMessageDialog()
|
||||
} else {
|
||||
this.$message.error(res.msg)
|
||||
}
|
||||
}).finally(() => {
|
||||
this.messageSending = false
|
||||
this.$set(this, "messageSending", false)
|
||||
})
|
||||
})
|
||||
},
|
||||
@@ -401,6 +374,9 @@ layout("/layouts/platform.html"){
|
||||
this.getWelfareList()
|
||||
this.unionOptions = await this.$businessTool.listUnion()
|
||||
this.unitOptions = await this.$businessTool.listUnit()
|
||||
// 人员分类下拉仅在当前页面排除离退休人员,不影响全局字典及其他页面。
|
||||
this.$set(this, "aidFundMemberUserTypeOptions", (await this.$businessTool.getDictOptions("AIDFUND_MEMBER_USER_TYPE"))
|
||||
.filter((item) => item.code !== "离退休人员"))
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -33,15 +33,21 @@ layout("/layouts/platform.html"){
|
||||
|
||||
<el-card class="mt10" shadow="never" v-loading="tableLoading">
|
||||
<table-tool label="工会列表">
|
||||
<el-button @click="exportByWelfareOptions" icon="el-icon-printer" size="small" type="primary">按福利选项导出</el-button>
|
||||
<!-- V4 原导出按钮保留,不再作为当前页面入口。 -->
|
||||
<!-- <el-button @click="exportByWelfareOptions" icon="el-icon-printer" size="small" type="primary">按福利选项导出</el-button> -->
|
||||
<el-button :disabled="!pageForm.projectId" @click="remindUnSelectedPlaceholder" icon="el-icon-bell" size="small" type="primary">
|
||||
提醒未选择人员
|
||||
</el-button>
|
||||
<template v-if="$auth.hasRoleOr(['SYSADMIN','SCHOOL_UNION_ADMIN','SCHOOL_UNION_WELFARE_ADMIN'])">
|
||||
<el-button :disabled="!pageForm.projectId" @click="exportSummary" icon="el-icon-printer" size="small" type="primary">
|
||||
导出汇总表
|
||||
<!-- V4 原汇总导出按钮保留,不再作为当前页面入口。 -->
|
||||
<!-- <el-button :disabled="!pageForm.projectId" @click="exportSummary" icon="el-icon-printer" size="small" type="primary">导出汇总表</el-button> -->
|
||||
<el-button :disabled="!pageForm.projectId" @click="allExportReceiveDetailByUnionIdWord" icon="el-icon-printer" size="small" type="primary">
|
||||
导出各分工会福利签领表
|
||||
</el-button>
|
||||
</template>
|
||||
<el-button :disabled="!pageForm.projectId" @click="exportByWelfareOptionsV3" icon="el-icon-printer" size="small" type="primary">按品牌导出</el-button>
|
||||
<el-button :disabled="!pageForm.projectId" @click="exportReceiveDetailByUnionId(true)" icon="el-icon-printer" size="small" type="primary">导出已选名单</el-button>
|
||||
<el-button :disabled="!pageForm.projectId" @click="exportReceiveDetailByUnionId(false)" icon="el-icon-printer" size="small" type="primary">导出未选名单</el-button>
|
||||
</table-tool>
|
||||
<el-table
|
||||
:data="tableData"
|
||||
@@ -74,6 +80,12 @@ layout("/layouts/platform.html"){
|
||||
<el-link @click="openUnSelected(row)" type="primary">{{row.unSelectedNum}}</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" fixed="right" header-align="center" label="操作" width="260">
|
||||
<template slot-scope="{row}">
|
||||
<el-button plain size="mini" type="primary" @click="exportReceiveDetailByUnionIdWord(row)">导出福利签领表</el-button>
|
||||
<el-button size="mini" type="primary" @click="openSelected(row)">已选人员</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</template>
|
||||
@@ -113,7 +125,7 @@ layout("/layouts/platform.html"){
|
||||
remindUnSelectedPlaceholder() {
|
||||
this.$message.info("提醒未选择人员功能待开发")
|
||||
},
|
||||
// 导出汇总表
|
||||
// V4 原汇总表导出方法保留,页面已改用 V3 风格的导出入口。
|
||||
exportSummary() {
|
||||
this.$downLoad("/platform/welfare/statistics/exportSummary", {
|
||||
projectId: this.pageForm.projectId
|
||||
@@ -129,7 +141,7 @@ layout("/layouts/platform.html"){
|
||||
this.$refs.unSelectedUserRef.onOpen(this.pageForm.projectId, row)
|
||||
},
|
||||
|
||||
// 按福利选项导出
|
||||
// V4 原按福利选项导出方法保留,页面已改用 V3 的按品牌导出入口。
|
||||
exportByWelfareOptions() {
|
||||
this.$downLoad("/platform/welfare/statistics/exportByWelfareOptions", {
|
||||
projectId: this.pageForm.projectId,
|
||||
@@ -137,6 +149,38 @@ layout("/layouts/platform.html"){
|
||||
})
|
||||
},
|
||||
|
||||
// V3 按品牌导出:按套餐拆分工作表,并传递当前分工会筛选范围。
|
||||
exportByWelfareOptionsV3() {
|
||||
this.$downLoad("/platform/welfare/statistics/exportByWelfareOptions", {
|
||||
projectId: this.pageForm.projectId,
|
||||
unionId: this.pageForm.unionId
|
||||
})
|
||||
},
|
||||
|
||||
// V3 已选、未选名单导出:未选择分工会时由后台导出当前数据权限内的全部人员。
|
||||
exportReceiveDetailByUnionId(flag) {
|
||||
this.$downLoad("/platform/welfare/statistics/exportReceiveDetailByUnionId", {
|
||||
projectId: this.pageForm.projectId,
|
||||
unionId: this.pageForm.unionId,
|
||||
flag: flag
|
||||
})
|
||||
},
|
||||
|
||||
// V3 单个分工会福利签领表导出。
|
||||
exportReceiveDetailByUnionIdWord(row) {
|
||||
this.$downLoad("/platform/welfare/statistics/exportReceiveDetailByUnionIdWord", {
|
||||
projectId: this.pageForm.projectId,
|
||||
unionId: row.id
|
||||
})
|
||||
},
|
||||
|
||||
// V3 全部分工会福利签领表导出,后台生成 ZIP 文件。
|
||||
allExportReceiveDetailByUnionIdWord() {
|
||||
this.$downLoad("/platform/welfare/statistics/allExportReceiveDetailByUnionIdWord", {
|
||||
projectId: this.pageForm.projectId
|
||||
})
|
||||
},
|
||||
|
||||
async pageData() {
|
||||
this.tableLoading = true
|
||||
const resp = await this.$axios.post("/platform/welfare/statistics/pageData", this.pageForm)
|
||||
|
||||
@@ -6,7 +6,7 @@ const selectedUser = {
|
||||
<el-input
|
||||
v-model="pageForm.searchKeyword"
|
||||
placeholder="请输入姓名或工号"
|
||||
style="width: 200px;"
|
||||
style="width: 300px;"
|
||||
size="small"
|
||||
clearable
|
||||
@keyup.enter.native="doSearch"
|
||||
@@ -51,7 +51,7 @@ const selectedUser = {
|
||||
{ label: "生日", prop: "birthday" },
|
||||
{ prop: "userState", label: "在职状态", width: 120, sortable: true },
|
||||
{ prop: "personType", label: "人员类型", width: 120, sortable: true },
|
||||
{ prop: "postDoctoralJoinDate", label: "进站时间", sortable: true },
|
||||
// { prop: "postDoctoralJoinDate", label: "进站时间", sortable: true },
|
||||
{ label: "单位", prop: "welfareUnitName" },
|
||||
{ label: "手机号", prop: "mobile" },
|
||||
{ label: "所选福利", prop: "selectOptionName" }
|
||||
|
||||
@@ -63,7 +63,7 @@ const addUser = {
|
||||
</search-item>
|
||||
|
||||
<search-item label="所属单位">
|
||||
<el-select @change="unitChange" clearable
|
||||
<el-select clearable
|
||||
filterable
|
||||
placeholder="请选择所属单位" style="width: 100%"
|
||||
v-model="pageForm.unitId">
|
||||
@@ -76,18 +76,6 @@ const addUser = {
|
||||
</el-select>
|
||||
</search-item>
|
||||
|
||||
<search-item label="三级单位">
|
||||
<el-select clearable filterable placeholder="请先选择所属单位" style="width: 100%"
|
||||
v-model="pageForm.threeUnitId">
|
||||
<el-option
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
v-for="item in threeUnitOptions">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
|
||||
<search-item label="人员类型:">
|
||||
<dict-select clearable code="USER_PERSON_TYPE"
|
||||
multiple
|
||||
@@ -163,7 +151,6 @@ const addUser = {
|
||||
birthMonths: [],
|
||||
unionId: null,
|
||||
unitId: null,
|
||||
threeUnitId: null,
|
||||
personTypes: [],
|
||||
preparedBys: [],
|
||||
userStates: [],
|
||||
@@ -172,7 +159,6 @@ const addUser = {
|
||||
sexOptions: ["男", "女"],
|
||||
unionOptions: [],
|
||||
unitOptions: [],
|
||||
threeUnitOptions: [],
|
||||
tableColumns: [
|
||||
{ prop: "loginname", label: "工号" },
|
||||
{ prop: "username", label: "姓名" },
|
||||
@@ -181,8 +167,7 @@ const addUser = {
|
||||
{ prop: "personType", label: "人员类型", sortable: true },
|
||||
{ prop: "userState", label: "在职状态", sortable: true },
|
||||
{ prop: "unionName", label: "所属工会", sortable: true },
|
||||
{ prop: "unitName", label: "所属单位", sortable: true },
|
||||
{ prop: "threeUnitName", label: "三级单位", sortable: true }
|
||||
{ prop: "unitName", label: "所属单位", sortable: true }
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -204,8 +189,6 @@ const addUser = {
|
||||
this.$set(this.pageForm, "birthMonths", [])
|
||||
this.$set(this.pageForm, "unionId", null)
|
||||
this.$set(this.pageForm, "unitId", null)
|
||||
this.$set(this.pageForm, "threeUnitId", null)
|
||||
this.$set(this, "threeUnitOptions", [])
|
||||
this.$set(this.pageForm, "personTypes", [])
|
||||
this.$set(this.pageForm, "preparedBys", [])
|
||||
this.$set(this.pageForm, "userStates", [])
|
||||
@@ -214,7 +197,6 @@ const addUser = {
|
||||
|
||||
async unionIdChange(val) {
|
||||
this.$set(this.pageForm, "unitId", null)
|
||||
this.unitChange(null)
|
||||
if (val) {
|
||||
this.$set(this, "unitOptions", await this.$businessTool.listUnit(val))
|
||||
} else {
|
||||
@@ -222,26 +204,6 @@ const addUser = {
|
||||
}
|
||||
},
|
||||
|
||||
// 所属单位改变后,仅加载该单位直属的三级单位,避免跨单位筛选。
|
||||
unitChange(unitId) {
|
||||
this.$set(this.pageForm, "threeUnitId", null)
|
||||
this.$set(this, "threeUnitOptions", [])
|
||||
if (!unitId) {
|
||||
return
|
||||
}
|
||||
this.$axios.post("/platform/sys/unit/child", { pid: unitId })
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$set(this, "threeUnitOptions", res.data || [])
|
||||
} else {
|
||||
this.$message.error(res.msg || "三级单位查询失败")
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.$message.error("三级单位查询失败,请稍后重试")
|
||||
})
|
||||
},
|
||||
|
||||
pageData() {
|
||||
this.tableLoading = true
|
||||
this.$axios
|
||||
|
||||
@@ -25,7 +25,7 @@ layout("/layouts/platform.html"){
|
||||
</search-item>
|
||||
|
||||
<search-item label="福利项目">
|
||||
<el-select :clearable="false" filterable placeholder="请选择福利项目" style="width: 100%" v-model="pageForm.projectId">
|
||||
<el-select :clearable="false" filterable placeholder="请选择福利项目" style="width: 100%" v-model="pageForm.projectId" @change="projectChange">
|
||||
<el-option :key="item.id" :label="item.name" :value="item.id" v-for="item in projectSelectOptions"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
@@ -133,11 +133,6 @@ layout("/layouts/platform.html"){
|
||||
<!-- <dict-select v-model="pageForm.aidFundMemberUserType" placeholder="请选择人员分类" @change="doSearch"-->
|
||||
<!-- code="AIDFUND_MEMBER_USER_TYPE"></dict-select>-->
|
||||
<!-- </search-item>-->
|
||||
<search-item label="人员属性">
|
||||
<dict-select v-model="pageForm.userAttributes" placeholder="请选择人员属性" @change="doSearch"
|
||||
code="USER_ATTRIBUTE" clearable multiple collapse-tags></dict-select>
|
||||
</search-item>
|
||||
|
||||
<!-- <search-item label="所选福利:">-->
|
||||
<!-- <el-select clearable placeholder="请选择所选福利" style="width: 100%" v-model="pageForm.optionId">-->
|
||||
<!-- <el-option :key="item.id" :label="item.optionName" :value="item.id" v-for="item in pageFormProject"></el-option>-->
|
||||
@@ -148,6 +143,35 @@ layout("/layouts/platform.html"){
|
||||
|
||||
<el-card class="mt10 welfare-list-card" shadow="never">
|
||||
<table-tool :app="this" label="福利名单">
|
||||
<el-select
|
||||
:disabled="!isExpressWelfareProject"
|
||||
clearable
|
||||
filterable
|
||||
placeholder="请选择套餐"
|
||||
size="small"
|
||||
style="width: 180px"
|
||||
v-model="pageForm.optionId"
|
||||
>
|
||||
<el-option :key="item.id" :label="item.optionName" :value="item.id" v-for="item in pageFormProject"></el-option>
|
||||
</el-select>
|
||||
<el-button
|
||||
:disabled="!isExpressWelfareProject || !pageForm.optionId"
|
||||
@click="doExcelByOptionId"
|
||||
icon="el-icon-printer"
|
||||
size="small"
|
||||
type="primary"
|
||||
>
|
||||
导出报销表
|
||||
</el-button>
|
||||
<el-button
|
||||
:disabled="!isExpressWelfareProject"
|
||||
@click="openImportExpressExcel"
|
||||
icon="el-icon-upload2"
|
||||
size="small"
|
||||
type="primary"
|
||||
>
|
||||
导入快递单号
|
||||
</el-button>
|
||||
<el-button :disabled="!pageForm.projectId" :loading="batchDeleteLoading" @click="deleteSearchUsers" icon="el-icon-delete" size="small" type="danger">
|
||||
删除人员
|
||||
</el-button>
|
||||
@@ -254,6 +278,8 @@ layout("/layouts/platform.html"){
|
||||
width="700px"
|
||||
:extra_params="{projectId:pageForm.projectId}"
|
||||
></excel-import>
|
||||
|
||||
<import-courier-number-dialog ref="importCourierNumberRef"></import-courier-number-dialog>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
@@ -270,11 +296,15 @@ layout("/layouts/platform.html"){
|
||||
if (this.pageForm.projectId) {
|
||||
const projectInfo = this.projectSelectOptions.find((v) => v.id === this.pageForm.projectId)
|
||||
if (projectInfo) {
|
||||
return projectInfo.optionList
|
||||
return projectInfo.options || []
|
||||
}
|
||||
return null
|
||||
return []
|
||||
}
|
||||
return null
|
||||
return []
|
||||
},
|
||||
isExpressWelfareProject() {
|
||||
const projectInfo = this.projectSelectOptions.find((v) => v.id === this.pageForm.projectId)
|
||||
return projectInfo && projectInfo.provideMode === 3
|
||||
},
|
||||
canSyncUnion() {
|
||||
const projectInfo = this.projectSelectOptions.find((v) => v.id === this.pageForm.projectId)
|
||||
@@ -288,7 +318,8 @@ layout("/layouts/platform.html"){
|
||||
add_welfare_list_by_select: ADD_WELFARE_LIST_BY_SELECT,
|
||||
"file-import": httpVueLoader("/components/plugins/sysImport/index.vue?v=" + new Date().getTime()),
|
||||
"add-user": addUser,
|
||||
"update-remark": updateRemark
|
||||
"update-remark": updateRemark,
|
||||
"import-courier-number-dialog": IMPORT_COURIER_NUMBER_DIALOG
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -326,6 +357,25 @@ layout("/layouts/platform.html"){
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 切换福利项目后清空旧套餐,避免将上一个项目的套餐用于导出或导入。
|
||||
projectChange() {
|
||||
this.$set(this.pageForm, "optionId", null)
|
||||
this.doSearch()
|
||||
},
|
||||
|
||||
// 导出当前福利项目中指定套餐的报销表。
|
||||
doExcelByOptionId() {
|
||||
this.$downLoad("/platform/welfare/list/mange/doExcelByOptionId", {
|
||||
projectId: this.pageForm.projectId,
|
||||
optionId: this.pageForm.optionId
|
||||
})
|
||||
},
|
||||
|
||||
// 打开快递单号导入弹窗,并把当前福利项目的套餐列表传给弹窗选择。
|
||||
openImportExpressExcel() {
|
||||
this.$refs.importCourierNumberRef.openImportExpressExcel(this.pageForm.projectId, this.pageFormProject)
|
||||
},
|
||||
|
||||
// 导出名单
|
||||
openExport() {
|
||||
this.$downLoad("/platform/welfare/list/mange/exportXlsx", {
|
||||
@@ -430,6 +480,7 @@ layout("/layouts/platform.html"){
|
||||
getWelfareList() {
|
||||
this.$axios.post("/platform/welfare/project/mange/getWelfareList", { year: this.pageForm.year }).then((res) => {
|
||||
this.projectSelectOptions = res.data
|
||||
this.$set(this.pageForm, "optionId", null)
|
||||
if (res.data && res.data.length > 0) {
|
||||
this.$set(this.pageForm, "projectId", res.data[0].id)
|
||||
} else {
|
||||
|
||||
@@ -1351,15 +1351,15 @@ layout("/layouts/platform_h5.html"){
|
||||
},
|
||||
|
||||
/**
|
||||
* 配送日期到达后才显示移动端评价入口;历史记录未设置配送日期时保持隐藏。
|
||||
* @param row 我的福利列表行,deliveryDate 为 yyyy-MM-dd 日期
|
||||
* @return {boolean} 当前日期达到配送日期时返回 true
|
||||
* 当前用户已选择福利且项目活动结束后显示移动端评价入口。
|
||||
* @param row 我的福利列表行,choiceTimeEnd 为活动结束时间
|
||||
* @return {boolean} 当前时间达到活动结束时间时返回 true
|
||||
*/
|
||||
canEvaluate(row) {
|
||||
if (!row || !row.deliveryDate) {
|
||||
if (!row || !row.isChoose || !row.choiceTimeEnd) {
|
||||
return false
|
||||
}
|
||||
return this.$moment().startOf("day").valueOf() >= this.$moment(row.deliveryDate).startOf("day").valueOf()
|
||||
return this.$moment().valueOf() >= this.$moment(row.choiceTimeEnd).valueOf()
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
<!--#
|
||||
layout("/layouts/platform_h5.html"){
|
||||
#-->
|
||||
|
||||
<style>
|
||||
.welfare-notice-page {
|
||||
min-height: 100vh;
|
||||
background: #f7f8fa;
|
||||
}
|
||||
|
||||
.welfare-notice-card {
|
||||
margin: 12px;
|
||||
padding: 14px;
|
||||
background: #ffffff;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.welfare-notice-title {
|
||||
margin-bottom: 12px;
|
||||
color: #323233;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.welfare-notice-viewer {
|
||||
width: 100%;
|
||||
height: calc(100vh - 150px);
|
||||
border: 0;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.welfare-notice-empty {
|
||||
padding: 50px 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" v-cloak class="welfare-notice-page" v-loading="pageLoading">
|
||||
<van-nav-bar title="福利通知" left-text="返回" left-arrow fixed placeholder @click-left="back"></van-nav-bar>
|
||||
<div class="welfare-notice-card" v-if="notice.fileId">
|
||||
<div class="welfare-notice-title">{{notice.projectName}}</div>
|
||||
<iframe class="welfare-notice-viewer" :src="viewerUrl" :title="notice.fileName"></iframe>
|
||||
<van-button block type="primary" plain @click="download">下载原文件</van-button>
|
||||
</div>
|
||||
<van-empty v-else-if="!pageLoading" class="welfare-notice-empty" description="暂无福利通知附件"></van-empty>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
data() {
|
||||
const query = new URLSearchParams(window.location.search)
|
||||
return {
|
||||
projectId: query.get("id") || "",
|
||||
notice: {},
|
||||
pageLoading: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
viewerUrl() {
|
||||
if (!this.notice.previewUrl) {
|
||||
return ""
|
||||
}
|
||||
return "/assets/platform/plugins/pdfJs/web/viewer.html?file=" + encodeURIComponent(this.notice.previewUrl)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
back() {
|
||||
window.history.back()
|
||||
},
|
||||
loadNotice() {
|
||||
this.$set(this, "pageLoading", true)
|
||||
this.$axios.post("/platform/welfare/notice/detailData", {id: this.projectId}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$set(this, "notice", res.data || {})
|
||||
} else {
|
||||
this.$toast.fail(res.msg)
|
||||
}
|
||||
}).finally(() => {
|
||||
this.$set(this, "pageLoading", false)
|
||||
})
|
||||
},
|
||||
download() {
|
||||
if (this.notice.attachmentUrl) {
|
||||
window.location.href = this.notice.attachmentUrl
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadNotice()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -242,7 +242,8 @@ layout("/layouts/platform_h5.html"){
|
||||
.welfare-detail-link {
|
||||
display: inline-flex;
|
||||
height: 20px;
|
||||
margin-left: 6px;
|
||||
margin-left: auto;
|
||||
padding-left: 6px;
|
||||
flex: none;
|
||||
align-items: center;
|
||||
color: #1989fa;
|
||||
@@ -827,8 +828,8 @@ layout("/layouts/platform_h5.html"){
|
||||
<span>{{ optionRankText(option) }}</span>
|
||||
</div>
|
||||
|
||||
<div class="welfare-option-desc-row" v-if="optionBrief(option)">
|
||||
<div class="welfare-option-desc">
|
||||
<div class="welfare-option-desc-row">
|
||||
<div class="welfare-option-desc" v-if="optionBrief(option)">
|
||||
{{ optionBrief(option) }}
|
||||
</div>
|
||||
<div class="welfare-detail-link" @click.stop="showOptionDetail(option)">
|
||||
@@ -847,7 +848,8 @@ layout("/layouts/platform_h5.html"){
|
||||
integer
|
||||
disable-input
|
||||
:min="option.isSystemDefault ? 1 : 0"
|
||||
:max="projectInfo.isCheckBox === 'radio' ? 1 : selectionLimit"
|
||||
:max="getOptionMaxSelectNum(option)"
|
||||
:disable-plus="isOptionIncreaseDisabled(option)"
|
||||
:disabled="isDeadlinePassed || (projectInfo.isCheckBox === 'radio' && systemDefaultOption && systemDefaultOption.id !== option.id)"
|
||||
input-width="40px"
|
||||
button-size="22px"
|
||||
@@ -918,16 +920,6 @@ layout("/layouts/platform_h5.html"){
|
||||
maxlength="11"
|
||||
required
|
||||
></van-field>
|
||||
<van-field
|
||||
v-model="formData.deliveryDate"
|
||||
label="配送时间"
|
||||
placeholder="请选择配送时间"
|
||||
readonly
|
||||
clickable
|
||||
is-link
|
||||
@click="openDeliveryCalendar"
|
||||
required
|
||||
></van-field>
|
||||
<van-field
|
||||
v-model="formData.remark"
|
||||
label="备注"
|
||||
@@ -990,27 +982,15 @@ layout("/layouts/platform_h5.html"){
|
||||
</div>
|
||||
</van-popup>
|
||||
|
||||
<!-- 配送日期使用 Vant 日历快捷选择,点击日期后立即确认;弹层同步接入全局历史栈。 -->
|
||||
<van-calendar
|
||||
v-model="showDeliveryCalendar"
|
||||
title="选择配送时间"
|
||||
type="single"
|
||||
color="#1989fa"
|
||||
:show-confirm="false"
|
||||
:close-on-click-overlay="false"
|
||||
:min-date="deliveryCalendarMinDate"
|
||||
:default-date="deliveryCalendarDefaultDate"
|
||||
@confirm="confirmDeliveryDate"
|
||||
@close="handleHistoryLayerComponentClose('delivery-calendar')">
|
||||
</van-calendar>
|
||||
|
||||
<!-- 选项详情弹窗 -->
|
||||
<van-action-sheet v-model="showOptionDetailDialog" class="welfare-detail-sheet"
|
||||
:style="{ height: '78%' }" :close-on-click-overlay="true"
|
||||
:title="selectedOption ? selectedOption.optionName : ''" cancel-text="取消"
|
||||
@close="handleHistoryLayerComponentClose('detail')">
|
||||
@close="handleHistoryLayerComponentClose('detail')">
|
||||
<div class="welfare-detail-popup" v-if="selectedOption">
|
||||
<div class="welfare-detail-content" v-html="selectedOption.description"></div>
|
||||
<div class="welfare-detail-content" v-if="hasOptionDetail(selectedOption)"
|
||||
v-html="selectedOption.description"></div>
|
||||
<van-empty v-else description="暂无详情"></van-empty>
|
||||
</div>
|
||||
</van-action-sheet>
|
||||
|
||||
@@ -1051,8 +1031,6 @@ layout("/layouts/platform_h5.html"){
|
||||
projectId: "",
|
||||
selectedRadioId: "", // 单选模式下选中的选项ID
|
||||
showConfirmDialog: false,
|
||||
showDeliveryCalendar: false, // 配送日期日历弹层
|
||||
deliveryCalendarDefaultDate: new Date(), // 日历打开时默认定位的日期
|
||||
isSubmitting: false,
|
||||
hasSubmittedBefore: false, // 是否之前提交过
|
||||
showOptionDetailDialog: false, // 选项详情弹窗
|
||||
@@ -1065,7 +1043,6 @@ layout("/layouts/platform_h5.html"){
|
||||
address: "",// 收货地址
|
||||
receiveAddress: "", // 确认后的完整收货地址
|
||||
userName: "",//收货人
|
||||
deliveryDate: "", // 配送日期,按 yyyy-MM-dd 提交
|
||||
remark: "" // 用户选择备注
|
||||
},
|
||||
|
||||
@@ -1077,11 +1054,6 @@ layout("/layouts/platform_h5.html"){
|
||||
|
||||
computed: {
|
||||
|
||||
// Vant 日历最小可选日期为当天零点,允许用户选择当天配送。
|
||||
deliveryCalendarMinDate() {
|
||||
return this.$moment().startOf("day").toDate()
|
||||
},
|
||||
|
||||
// 系统默认福利必须被保留,管理端限制每个项目最多配置一个。
|
||||
systemDefaultOption() {
|
||||
if (!this.projectInfo.options) return null
|
||||
@@ -1142,7 +1114,7 @@ layout("/layouts/platform_h5.html"){
|
||||
return Math.max(0, this.selectionLimit - this.selectedCount)
|
||||
},
|
||||
|
||||
// 意向选择不采集收货人、联系电话和收货地址,配送时间及备注仍按原业务要求保留。
|
||||
// 意向选择不采集收货人、联系电话和收货地址,备注仍按原业务要求保留。
|
||||
showReceivingContact() {
|
||||
return Number(this.projectInfo.provideMode) !== 2
|
||||
},
|
||||
@@ -1174,7 +1146,6 @@ layout("/layouts/platform_h5.html"){
|
||||
// 根据浏览器历史中的弹层栈统一显示或关闭弹框,保证返回键与页面状态一致。
|
||||
syncHistoryLayerStack(stack) {
|
||||
this.showConfirmDialog = stack.includes("confirm")
|
||||
this.$set(this, "showDeliveryCalendar", stack.includes("delivery-calendar"))
|
||||
this.showAddressPopup = stack.includes("address")
|
||||
this.showOptionDetailDialog = stack.includes("detail")
|
||||
},
|
||||
@@ -1212,36 +1183,10 @@ layout("/layouts/platform_h5.html"){
|
||||
|
||||
setHistoryLayerVisible(layerName, visible) {
|
||||
if (layerName === "confirm") this.showConfirmDialog = visible
|
||||
if (layerName === "delivery-calendar") this.$set(this, "showDeliveryCalendar", visible)
|
||||
if (layerName === "address") this.showAddressPopup = visible
|
||||
if (layerName === "detail") this.showOptionDetailDialog = visible
|
||||
},
|
||||
|
||||
/**
|
||||
* 打开配送日期日历。已有 deliveryDate 时定位到该日期,否则默认定位当天;无返回值。
|
||||
* 日历作为确认选择弹层的下一层历史记录,系统返回键只关闭日历。
|
||||
*/
|
||||
openDeliveryCalendar() {
|
||||
const selectedDate = this.formData.deliveryDate
|
||||
? this.$moment(this.formData.deliveryDate, "YYYY-MM-DD").toDate()
|
||||
: new Date()
|
||||
// 历史配送日期早于今天时定位到今天,避免默认日期超出日历可选范围。
|
||||
const defaultDate = this.$moment(selectedDate).startOf("day").valueOf()
|
||||
< this.$moment().startOf("day").valueOf() ? new Date() : selectedDate
|
||||
this.$set(this, "deliveryCalendarDefaultDate", defaultDate)
|
||||
this.openHistoryLayer("delivery-calendar")
|
||||
},
|
||||
|
||||
/**
|
||||
* 快捷选中配送日期。date 为 van-calendar 返回的 Date,写入 yyyy-MM-dd 字符串后同步回退日历历史;无返回值。
|
||||
*
|
||||
* @param date Vant 日历当前选中的日期
|
||||
*/
|
||||
confirmDeliveryDate(date) {
|
||||
this.$set(this.formData, "deliveryDate", this.$moment(date).format("YYYY-MM-DD"))
|
||||
this.closeHistoryLayer("delivery-calendar")
|
||||
},
|
||||
|
||||
optionRankText(option) {
|
||||
if (option.rankNo) {
|
||||
return "本次福利排行榜第" + option.rankNo + "名"
|
||||
@@ -1255,6 +1200,20 @@ layout("/layouts/platform_h5.html"){
|
||||
const text = (div.textContent || div.innerText || "").replace(/\s+/g, " ").trim()
|
||||
return text
|
||||
},
|
||||
/**
|
||||
* 判断福利选项是否存在可展示的详情内容。
|
||||
* @param option 福利选项,详情来源为 description 字段。
|
||||
* @returns {boolean} 存在文字或富媒体内容时返回 true,否则详情弹框显示“暂无详情”。
|
||||
*/
|
||||
hasOptionDetail(option) {
|
||||
if (!option || !option.description) {
|
||||
return false
|
||||
}
|
||||
const div = document.createElement("div")
|
||||
div.innerHTML = option.description
|
||||
const text = (div.textContent || div.innerText || "").replace(/\s+/g, " ").trim()
|
||||
return !!text || !!div.querySelector("img,table,video,audio,iframe")
|
||||
},
|
||||
selectNumChange(index, newValue) {
|
||||
const option = this.projectInfo.options[index];
|
||||
if (option.isSystemDefault && Number(newValue) < 1) {
|
||||
@@ -1262,6 +1221,12 @@ layout("/layouts/platform_h5.html"){
|
||||
this.$toast.fail("系统默认福利不可取消")
|
||||
return
|
||||
}
|
||||
if (!this.projectInfo.singleOptionSupportMultipleSelection && Number(newValue) > 1) {
|
||||
this.$set(option, "selectNum", 1)
|
||||
this.$set(option, "selectNumKey", (option.selectNumKey || 0) + 1)
|
||||
this.$toast.fail("此套餐最多只能选择1份")
|
||||
return
|
||||
}
|
||||
const maxSelect = this.projectInfo.multiSelectNum || this.projectInfo.options.length;
|
||||
|
||||
// 计算其他所有选项的总和(不包括当前修改的选项)
|
||||
@@ -1282,7 +1247,7 @@ layout("/layouts/platform_h5.html"){
|
||||
|
||||
// 如果当前选项的值大于允许的最大值,则调整它
|
||||
if (option.selectNum > maxAllowedForThisOption) {
|
||||
option.selectNum = maxAllowedForThisOption;
|
||||
this.$set(option, "selectNum", maxAllowedForThisOption)
|
||||
}
|
||||
// 如果当前选项的值小于或等于允许的最大值(例如,用户减少了数量导致总数超标,但实际上减少后并未超过 maxSelect,
|
||||
// 这种情况理论上不应该在 change 时发生,因为 change 是值改变后触发的,且 change 后的值导致了超标),
|
||||
@@ -1306,6 +1271,44 @@ layout("/layouts/platform_h5.html"){
|
||||
return Number(option.selectNum) || 0
|
||||
},
|
||||
|
||||
/**
|
||||
* 计算当前选项可达到的最大份数。
|
||||
* 多选项目同时受项目剩余份数和单个套餐是否允许多份控制;单选项目固定为1。
|
||||
* @param option 当前福利选项,selectNum为已选份数
|
||||
* @return {number} 传给步进器max属性的最大份数
|
||||
*/
|
||||
getOptionMaxSelectNum(option) {
|
||||
if (this.projectInfo.isCheckBox === "radio") {
|
||||
return 1
|
||||
}
|
||||
const currentQuantity = Number(option.selectNum) || 0
|
||||
const globalAllowedQuantity = currentQuantity + this.remainingSelectionCount
|
||||
if (!this.projectInfo.singleOptionSupportMultipleSelection) {
|
||||
return Math.min(1, globalAllowedQuantity)
|
||||
}
|
||||
return globalAllowedQuantity
|
||||
},
|
||||
|
||||
/**
|
||||
* 判断当前选项是否还能增加份数。
|
||||
* 达到项目总上限或单个套餐已达到1份且不允许多份时禁用加号,但不影响减号取消。
|
||||
* @param option 当前福利选项,selectNum为已选份数
|
||||
* @return {boolean} true表示禁用当前步进器的加号
|
||||
*/
|
||||
isOptionIncreaseDisabled(option) {
|
||||
if (this.isDeadlinePassed) {
|
||||
return true
|
||||
}
|
||||
if (this.projectInfo.isCheckBox === "radio") {
|
||||
return this.selectedRadioId === option.id
|
||||
}
|
||||
const currentQuantity = Number(option.selectNum) || 0
|
||||
if (!this.projectInfo.singleOptionSupportMultipleSelection && currentQuantity >= 1) {
|
||||
return true
|
||||
}
|
||||
return this.remainingSelectionCount <= 0
|
||||
},
|
||||
|
||||
/**
|
||||
* 统一处理单选和多选项目的数量变化,保持原有默认项及总数量限制。
|
||||
* @param index 福利选项索引
|
||||
@@ -1320,16 +1323,28 @@ layout("/layouts/platform_h5.html"){
|
||||
return
|
||||
}
|
||||
if (option.isSystemDefault) return
|
||||
this.selectedRadioId = ""
|
||||
this.projectInfo.options.forEach((item) => {
|
||||
item.selectNum = 0
|
||||
})
|
||||
this.syncRadioOptionSelection("")
|
||||
return
|
||||
}
|
||||
this.$set(option, "selectNum", quantity)
|
||||
this.selectNumChange(index, quantity)
|
||||
},
|
||||
|
||||
/**
|
||||
* 同步单选项目的选中项和全部步进器显示值。
|
||||
* @param optionId 当前选中的福利选项 ID;传空字符串表示取消当前选择
|
||||
* @return {void} 无返回值,未选项统一显示 0,选中项显示 1
|
||||
*/
|
||||
syncRadioOptionSelection(optionId) {
|
||||
this.$set(this, "selectedRadioId", optionId || "")
|
||||
if (!this.projectInfo.options) return
|
||||
this.projectInfo.options.forEach((option) => {
|
||||
this.$set(option, "selectNum", option.id === optionId ? 1 : 0)
|
||||
// Vant Stepper 会保留内部显示值,切换单选项时更新 key 强制同步所有控件。
|
||||
this.$set(option, "selectNumKey", (option.selectNumKey || 0) + 1)
|
||||
})
|
||||
},
|
||||
|
||||
// 保留原有首次选择方法,兼容其他调用入口。
|
||||
selectCheckboxOption(index) {
|
||||
if (this.isDeadlinePassed) {
|
||||
@@ -1387,10 +1402,8 @@ layout("/layouts/platform_h5.html"){
|
||||
this.userSelection = resp.data
|
||||
this.hasSubmittedBefore = this.userSelection.length > 0
|
||||
|
||||
// 配送时间和备注属于整次选择,统一从首条选择记录回显。
|
||||
// 备注属于整次选择,统一从首条选择记录回显。
|
||||
if (this.userSelection && this.userSelection.length > 0) {
|
||||
this.$set(this.formData, "deliveryDate", this.userSelection[0].deliveryDate
|
||||
? this.$moment(this.userSelection[0].deliveryDate).format("YYYY-MM-DD") : "")
|
||||
this.$set(this.formData, "remark", this.userSelection[0].remark || "")
|
||||
}
|
||||
|
||||
@@ -1427,11 +1440,7 @@ layout("/layouts/platform_h5.html"){
|
||||
if (this.projectInfo.options && this.userSelection.length > 0) {
|
||||
// 单选模式
|
||||
if (this.projectInfo.isCheckBox === "radio" && this.userSelection[0]) {
|
||||
this.selectedRadioId = this.userSelection[0].selectOptionId
|
||||
const selectedOption = this.projectInfo.options.find((opt) => opt.id === this.selectedRadioId)
|
||||
if (selectedOption) {
|
||||
selectedOption.selectNum = 1
|
||||
}
|
||||
this.syncRadioOptionSelection(this.userSelection[0].selectOptionId)
|
||||
}
|
||||
// 多选模式
|
||||
else {
|
||||
@@ -1543,11 +1552,6 @@ layout("/layouts/platform_h5.html"){
|
||||
return
|
||||
}
|
||||
|
||||
// 配送时间是评价开放依据,提交前必须确保不早于当前选择日期。
|
||||
if (!this.validateDeliveryDate()) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this.isSubmitting) return
|
||||
this.isSubmitting = true
|
||||
|
||||
@@ -1568,7 +1572,6 @@ layout("/layouts/platform_h5.html"){
|
||||
selectNum: 1,
|
||||
mobile: this.showReceivingContact ? this.formData.mobile : "",
|
||||
userName: this.showReceivingContact ? this.formData.userName : "",
|
||||
deliveryDate: this.formData.deliveryDate,
|
||||
remark: this.formData.remark
|
||||
}
|
||||
]
|
||||
@@ -1581,7 +1584,6 @@ layout("/layouts/platform_h5.html"){
|
||||
selectNum: option.selectNum,
|
||||
mobile: this.showReceivingContact ? this.formData.mobile : "",
|
||||
userName: this.showReceivingContact ? this.formData.userName : "",
|
||||
deliveryDate: this.formData.deliveryDate,
|
||||
remark: this.formData.remark
|
||||
}))
|
||||
}
|
||||
@@ -1653,26 +1655,6 @@ layout("/layouts/platform_h5.html"){
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* 校验配送日期。deliveryDate 应传 yyyy-MM-dd,返回布尔值表示是否允许继续提交。
|
||||
* 日期按天比较,配送日期等于选择当天时允许提交。
|
||||
*
|
||||
* @return {boolean} 配送日期有效返回 true,否则提示错误并返回 false
|
||||
*/
|
||||
validateDeliveryDate() {
|
||||
if (!this.formData.deliveryDate) {
|
||||
this.$toast.fail("请选择配送时间")
|
||||
return false
|
||||
}
|
||||
const deliveryDate = this.$moment(this.formData.deliveryDate).startOf("day").valueOf()
|
||||
const currentDate = this.$moment().startOf("day").valueOf()
|
||||
if (deliveryDate < currentDate) {
|
||||
this.$toast.fail("配送时间不能早于选择时间")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
|
||||
// 提交选择
|
||||
submitSelection() {
|
||||
if (!this.hasSelection) {
|
||||
@@ -1708,22 +1690,11 @@ layout("/layouts/platform_h5.html"){
|
||||
return
|
||||
}
|
||||
if (this.systemDefaultOption && this.systemDefaultOption.id !== optionId) {
|
||||
this.selectedRadioId = this.systemDefaultOption.id
|
||||
this.syncRadioOptionSelection(this.systemDefaultOption.id)
|
||||
this.$toast.fail("系统默认福利不可取消")
|
||||
return
|
||||
}
|
||||
this.selectedRadioId = optionId
|
||||
|
||||
// 重置所有选项的selectNum
|
||||
if (this.projectInfo.options) {
|
||||
this.projectInfo.options.forEach((option) => {
|
||||
if (option.id === optionId) {
|
||||
option.selectNum = 1
|
||||
} else {
|
||||
option.selectNum = 0
|
||||
}
|
||||
})
|
||||
}
|
||||
this.syncRadioOptionSelection(optionId)
|
||||
},
|
||||
|
||||
// 默认项只在首次选择时自动带入,已有历史选择不自动覆盖。
|
||||
|
||||
@@ -505,10 +505,6 @@ layout("/layouts/platform_h5.html"){
|
||||
<van-icon name="clock-o"></van-icon>
|
||||
<span>选择时间:未设置</span>
|
||||
</div>
|
||||
<div class="welfare-list-card__time" v-if="Number(row.isChoose) === 1">
|
||||
<van-icon name="logistics"></van-icon>
|
||||
<span>配送时间:{{ row.deliveryDate ? $moment(row.deliveryDate).format('YYYY-MM-DD') : '暂无' }}</span>
|
||||
</div>
|
||||
<div class="welfare-list-card__time" v-if="Number(row.isChoose) === 1 && row.remark">
|
||||
<van-icon name="notes-o"></van-icon>
|
||||
<span>备注:{{ row.remark }}</span>
|
||||
|
||||
Reference in New Issue
Block a user