福利导出整改

This commit is contained in:
2026-09-04 18:29:45 +08:00
parent 934eff12c4
commit a1225d4d76
18 changed files with 671 additions and 138 deletions
+16 -1
View File
@@ -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" />
@@ -27,4 +42,4 @@
<option name="url" value="https://maven.aliyun.com/repository/public" />
</remote-repository>
</component>
</project>
</project>
+1 -2
View File
@@ -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>
@@ -10,4 +9,4 @@
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK" />
</project>
</project>
@@ -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("编辑备注")
@@ -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
@@ -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;
@@ -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);
@@ -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);
/**
* 导出汇总表
@@ -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));
// 设置导出参数
@@ -169,6 +169,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());
}
@@ -221,6 +223,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()) {
@@ -308,26 +312,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<>();
@@ -342,16 +349,16 @@ public class WelfareSelectionSituationServiceImpl extends BaseServiceImpl<Welfar
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);
}
/**
@@ -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();
@@ -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());
}
}
@@ -373,7 +373,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"] }],
@@ -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>
@@ -185,10 +208,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: "工号" },
@@ -401,6 +427,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 {