职工福利功能优化
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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,50 @@
|
||||
package com.budwk.app.zhgh.welfare.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaCheckLogin;
|
||||
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("detail")
|
||||
@Ok("beetl:/platform/zhghh5/welfare/notice/detail.html")
|
||||
@SaCheckLogin
|
||||
@ApiOperation("福利通知附件详情页")
|
||||
public void detail() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询福利通知附件详情。
|
||||
*
|
||||
* @param id 福利项目 ID
|
||||
* @return JSON 结果,data 中包含项目名称、文件名称、下载地址和 PDF 预览地址
|
||||
*/
|
||||
@At("detailData")
|
||||
@SaCheckLogin
|
||||
@ApiOperation("查询福利通知附件详情")
|
||||
public Result detailData(@Valid String id) {
|
||||
if (StrUtil.isBlank(id)) {
|
||||
return Result.error("福利项目ID不能为空");
|
||||
}
|
||||
return Result.success(projectService.getNoticeAttachmentInfo(id));
|
||||
}
|
||||
}
|
||||
@@ -57,7 +57,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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,10 @@ public interface WelfareProjectService extends BaseService<WelfareProject> {
|
||||
String validateGroupSelectionQuantity(String projectId, WelfareUserSelection[] selections);
|
||||
|
||||
/**
|
||||
* 校验福利选择附加信息。配送时间为必填项,必须按日期粒度不早于本次选择时间;
|
||||
* 同一次提交的全部福利项必须使用相同配送时间和备注。
|
||||
* 校验福利选择附加信息。同一次提交的全部福利项必须使用相同备注,配送时间字段已停用。
|
||||
*
|
||||
* @param selections 用户提交的福利选择数组,每项需包含 deliveryDate,可选包含 remark
|
||||
* @param selectTime 后端生成的本次选择时间,用于防止客户端伪造日期
|
||||
* @param selections 用户提交的福利选择数组,每项可包含 remark
|
||||
* @param selectTime 后端生成的本次选择时间,保留该参数以兼容现有调用接口
|
||||
* @return 校验通过返回 {@code null},否则返回可直接展示的错误提示
|
||||
*/
|
||||
String validateSelectionAdditionalInfo(WelfareUserSelection[] selections, Date selectTime);
|
||||
|
||||
+7
-1
@@ -15,11 +15,17 @@ 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);
|
||||
void receiveXlsx(WelfareSelectionSituationPageForm pageForm, 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
|
||||
|
||||
+83
-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,8 @@ public class WelfareProjectServiceImpl extends BaseServiceImpl<WelfareProject> i
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验一次选择提交中的配送日期和备注。日期比较统一截断到当天零点,允许选择当天配送;
|
||||
* 多项福利的附加信息必须一致,避免同一次选择产生互相冲突的评价开放时间。
|
||||
* 校验一次选择提交中的备注。配送时间字段已停用,新提交数据统一清空该字段;
|
||||
* 多项福利的备注必须一致,避免同一次选择出现互相冲突的附加信息。
|
||||
*
|
||||
* @param selections 当前提交的福利选择数据
|
||||
* @param selectTime 后端生成的选择时间
|
||||
@@ -194,30 +214,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 +270,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 +286,7 @@ public class WelfareProjectServiceImpl extends BaseServiceImpl<WelfareProject> i
|
||||
@Override
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public void updateProject(WelfareProject project) {
|
||||
normalizeNoticeAttachment(project);
|
||||
// 修改项目时同样归一化分组配置,确保新增、编辑两条保存链路规则一致。
|
||||
normalizeGroupSelectionConfigs(project);
|
||||
// 更新项目
|
||||
@@ -325,6 +340,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 时表示不限,不参与最小值与最大值的大小比较。
|
||||
|
||||
+91
-2
@@ -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;
|
||||
@@ -75,7 +77,6 @@ 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,
|
||||
t4.username AS userName,
|
||||
t4.loginname AS loginName,
|
||||
t4.sex,
|
||||
@@ -143,7 +144,16 @@ 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/detail?id=" + project.getId();
|
||||
String detailLine = "查看详情:" + detailUrl;
|
||||
// 移除前端预置的详情行后统一追加到正文末尾,既避免重复链接,也保证最终消息排版一致。
|
||||
String messageBody = Arrays.stream(plainContent.split("\\R"))
|
||||
.filter(line -> !line.trim().startsWith("查看详情:"))
|
||||
.collect(Collectors.joining("\n"))
|
||||
.trim();
|
||||
String sendContent = StrUtil.isBlank(messageBody) ? detailLine : messageBody + "\n" + detailLine;
|
||||
boolean success = smsService.sendMsg("6", loginNames, null, title, sendContent, detailUrl, detailUrl);
|
||||
if (!success) {
|
||||
throw new BaseException("钉钉消息发送失败,请检查消息发送配置或稍后重试");
|
||||
}
|
||||
@@ -190,12 +200,15 @@ 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,
|
||||
t4.username AS userName,
|
||||
t4.loginname AS loginName,
|
||||
t4.sex
|
||||
@@ -244,6 +257,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());
|
||||
@@ -263,6 +278,13 @@ public class WelfareSelectionSituationServiceImpl extends BaseServiceImpl<Welfar
|
||||
if(project.getProvideMode() == 3){
|
||||
entities.add(new ExcelExportEntity("收货地址", "receiveAddress", 40));
|
||||
}
|
||||
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();
|
||||
@@ -275,6 +297,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) {
|
||||
|
||||
|
||||
+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;
|
||||
@@ -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"
|
||||
@@ -485,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
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -88,11 +88,11 @@ layout("/layouts/platform.html"){
|
||||
></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-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>
|
||||
|
||||
@@ -224,7 +224,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,
|
||||
@@ -239,6 +238,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/detail?id=" + encodeURIComponent(this.pageForm.projectId)
|
||||
},
|
||||
welfareOptions() {
|
||||
if (this.pageForm.projectId) {
|
||||
return this.projectOptions.find((item) => item.id === this.pageForm.projectId)?.options
|
||||
@@ -312,7 +318,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) {
|
||||
@@ -338,7 +344,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
|
||||
@@ -350,67 +356,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)
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
@@ -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>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -918,16 +918,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,20 +980,6 @@ 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"
|
||||
@@ -1051,8 +1027,6 @@ layout("/layouts/platform_h5.html"){
|
||||
projectId: "",
|
||||
selectedRadioId: "", // 单选模式下选中的选项ID
|
||||
showConfirmDialog: false,
|
||||
showDeliveryCalendar: false, // 配送日期日历弹层
|
||||
deliveryCalendarDefaultDate: new Date(), // 日历打开时默认定位的日期
|
||||
isSubmitting: false,
|
||||
hasSubmittedBefore: false, // 是否之前提交过
|
||||
showOptionDetailDialog: false, // 选项详情弹窗
|
||||
@@ -1065,7 +1039,6 @@ layout("/layouts/platform_h5.html"){
|
||||
address: "",// 收货地址
|
||||
receiveAddress: "", // 确认后的完整收货地址
|
||||
userName: "",//收货人
|
||||
deliveryDate: "", // 配送日期,按 yyyy-MM-dd 提交
|
||||
remark: "" // 用户选择备注
|
||||
},
|
||||
|
||||
@@ -1077,11 +1050,6 @@ layout("/layouts/platform_h5.html"){
|
||||
|
||||
computed: {
|
||||
|
||||
// Vant 日历最小可选日期为当天零点,允许用户选择当天配送。
|
||||
deliveryCalendarMinDate() {
|
||||
return this.$moment().startOf("day").toDate()
|
||||
},
|
||||
|
||||
// 系统默认福利必须被保留,管理端限制每个项目最多配置一个。
|
||||
systemDefaultOption() {
|
||||
if (!this.projectInfo.options) return null
|
||||
@@ -1142,7 +1110,7 @@ layout("/layouts/platform_h5.html"){
|
||||
return Math.max(0, this.selectionLimit - this.selectedCount)
|
||||
},
|
||||
|
||||
// 意向选择不采集收货人、联系电话和收货地址,配送时间及备注仍按原业务要求保留。
|
||||
// 意向选择不采集收货人、联系电话和收货地址,备注仍按原业务要求保留。
|
||||
showReceivingContact() {
|
||||
return Number(this.projectInfo.provideMode) !== 2
|
||||
},
|
||||
@@ -1174,7 +1142,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 +1179,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 + "名"
|
||||
@@ -1387,10 +1328,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 || "")
|
||||
}
|
||||
|
||||
@@ -1543,11 +1482,6 @@ layout("/layouts/platform_h5.html"){
|
||||
return
|
||||
}
|
||||
|
||||
// 配送时间是评价开放依据,提交前必须确保不早于当前选择日期。
|
||||
if (!this.validateDeliveryDate()) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this.isSubmitting) return
|
||||
this.isSubmitting = true
|
||||
|
||||
@@ -1568,7 +1502,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 +1514,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 +1585,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) {
|
||||
|
||||
@@ -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