体检管理

This commit is contained in:
2026-11-12 09:39:55 +08:00
parent c05014fd87
commit 0397547b19
49 changed files with 7304 additions and 2287 deletions
+1 -1
View File
@@ -182,8 +182,8 @@
<properties>
<easypoi.version>4.5.0</easypoi.version>
<jsoup.veision>1.15.3</jsoup.veision>
<tomcat.version>9.0.121</tomcat.version>
<mysql-connector-java.version>8.0.28</mysql-connector-java.version>
<tomcat.version>9.0.121</tomcat.version>
<sa-token.version>1.25.0</sa-token.version>
<easy-captcha.version>1.6.2</easy-captcha.version>
<emoji-java.veision>4.0.0</emoji-java.veision>
@@ -10,6 +10,7 @@ import com.budwk.app.base.result.Result;
import com.budwk.app.sys.models.Sys_home_activity;
import com.budwk.app.sys.models.Sys_menu;
import com.budwk.app.sys.services.SysMenuService;
import com.budwk.app.sys.services.SysHomeActivityService;
import com.budwk.app.sys.services.SysUserService;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.activity.basic.models.ActivityUserScope;
@@ -57,6 +58,8 @@ public class SysHomeController {
@Inject
private SysUserService sysUserService;
@Inject
private SysHomeActivityService sysHomeActivityService;
@Inject
private RedisService redisService;
@At("")
@@ -195,9 +198,9 @@ public class SysHomeController {
@SaCheckLogin
@ApiOperation("首页推送活动")
@Ok("json")
public Result listHomeActivity() {
FieldFilter fieldFilter = FieldFilter.locked(Sys_home_activity.class, "classPath");
List<Sys_home_activity> list = Daos.ext(dao, fieldFilter).query(Sys_home_activity.class, Cnd.where("enable", "=", 1).desc("top"));
public Result listHomeActivity(String platform) {
// platform=PC 仅过滤电脑首页;手机端未传此参数时保持原范围,返回 Result.data 活动列表。
List<Sys_home_activity> list = sysHomeActivityService.listHomeCandidates(platform);
String userId = SecurityUtil.getUserId();
List<Sys_home_activity> allowActivityList = new ArrayList<>();
// 今天的时间
@@ -3,5 +3,13 @@ package com.budwk.app.sys.services;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.sys.models.Sys_home_activity;
import java.util.List;
public interface SysHomeActivityService extends BaseService<Sys_home_activity> {
/**
* 查询首页启用活动,PC 端排除仅在手机端报名的体检项目。
* @param platform PC 表示电脑首页;其他值保留原活动范围
* @return 按置顶顺序排列的候选活动,人员范围和时间仍由原流程判断
*/
List<Sys_home_activity> listHomeCandidates(String platform);
}
@@ -4,6 +4,11 @@ import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.sys.models.Sys_home_activity;
import com.budwk.app.sys.services.SysHomeActivityService;
import org.nutz.dao.Dao;
import org.nutz.dao.Cnd;
import org.nutz.dao.FieldFilter;
import org.nutz.dao.util.Daos;
import com.budwk.app.zhgh.dayofficework.healthCheckup.model.HealthCheckupProject;
import java.util.List;
import org.nutz.ioc.loader.annotation.IocBean;
@IocBean(args = {"refer:dao"})
@@ -12,4 +17,17 @@ public class SysHomeActivityServiceImpl extends BaseServiceImpl<Sys_home_activit
public SysHomeActivityServiceImpl(Dao dao) {
super(dao);
}
/** {@inheritDoc} */
@Override
public List<Sys_home_activity> listHomeCandidates(String platform) {
Cnd condition = Cnd.where("enable", "=", 1);
if ("PC".equalsIgnoreCase(platform)) {
// 保留未绑定业务类的手工活动,仅排除体检项目生成的首页活动。
condition.and(Cnd.exps("classPath", "is", null)
.or("classPath", "!=", HealthCheckupProject.class.getName()));
}
condition.desc("top");
return Daos.ext(dao(), FieldFilter.locked(Sys_home_activity.class, "classPath"))
.query(Sys_home_activity.class, condition);
}
}
@@ -1,25 +1,21 @@
package com.budwk.app.zhgh.dayofficework.healthCheckup.controller;
import com.budwk.app.zhgh.dayofficework.healthCheckup.service.HealthCheckupCampusApplicationService;
import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.annotation.SLog;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.dayofficework.healthCheckup.model.HealthCheckupCampus;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.nutz.dao.Cnd;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
import java.util.List;
/**
* @ClassName HealthCheckupCampusController
@@ -36,9 +32,10 @@ import java.util.List;
public class HealthCheckupCampusController {
@Inject
private BaseService baseService;
private HealthCheckupCampusApplicationService applicationService;
@At("")
@At("")
@Ok("beetl:/platform/zhgh/dayofficework/healthCheckup/campus/index.html")
@SaCheckPermission("healthCheckup.campus")
public void index() {
@@ -48,51 +45,25 @@ public class HealthCheckupCampusController {
@ApiOperation("分页查询")
@SaCheckPermission("healthCheckup.campus")
public Result pageData(PageForm pageForm, @Param(value = "campusName") String campusName) {
Cnd cnd = Cnd.NEW();
Sql sql = Sqls.create("select * from health_checkup_campus $condition");
cnd.and(Cnd.likeEX("campusName", campusName));
cnd.asc("campusCode");
sql.setCondition(cnd);
return Result.success(baseService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql));
return applicationService.pageData(pageForm, campusName);
}
@At
@ApiOperation("添加院区")
@SLog(type = "healthCheckup", tag = "体检管理", msg = "添加院区")
@SaCheckPermission("healthCheckup.campus")
@org.nutz.ioc.aop.Aop(org.nutz.aop.interceptor.ioc.TransAop.READ_COMMITTED)
public Result doAdd(HealthCheckupCampus campus) {
try {
int count = baseService.dao().count(HealthCheckupCampus.class, Cnd.where(HealthCheckupCampus::getCampusCode, "=", campus.getCampusCode()));
if (count > 0) {
return Result.error("编码重复");
}
baseService.dao().insert(campus);
return Result.success();
} catch (Exception e) {
e.printStackTrace();
return Result.error();
}
return applicationService.doAdd(campus);
}
@At
@ApiOperation("编辑院区")
@SLog(type = "healthCheckup", tag = "体检管理", msg = "编辑院区")
@SaCheckPermission("healthCheckup.campus")
@org.nutz.ioc.aop.Aop(org.nutz.aop.interceptor.ioc.TransAop.READ_COMMITTED)
public Result doEdit(HealthCheckupCampus campus) {
try {
int count = baseService.dao().count(HealthCheckupCampus.class,
Cnd.where(HealthCheckupCampus::getCampusCode, "=", campus.getCampusCode())
.and(HealthCheckupCampus::getId, "!=", campus.getId()));
if (count > 0) {
return Result.error("编码重复");
}
baseService.dao().update(campus);
return Result.success();
} catch (Exception e) {
e.printStackTrace();
return Result.error();
}
return applicationService.doEdit(campus);
}
@@ -100,26 +71,15 @@ public class HealthCheckupCampusController {
@ApiOperation("编辑院区")
@SLog(type = "healthCheckup", tag = "体检管理", msg = "删除院区")
@SaCheckPermission("healthCheckup.campus")
@org.nutz.ioc.aop.Aop(org.nutz.aop.interceptor.ioc.TransAop.READ_COMMITTED)
public Result doDelete(String id) {
try {
baseService.dao().clear(HealthCheckupCampus.class, Cnd.where(HealthCheckupCampus::getId, "=", id));
return Result.success();
} catch (Exception e) {
e.printStackTrace();
return Result.error();
}
return applicationService.doDelete(id);
}
@At
@ApiOperation("查询所有院区")
@SaCheckLogin
public Result queryCampus() {
try {
List<HealthCheckupCampus> campusList = baseService.dao().query(HealthCheckupCampus.class, Cnd.NEW().asc("campusCode"));
return Result.success(campusList);
} catch (Exception e) {
e.printStackTrace();
return Result.error();
}
return applicationService.queryCampus();
}
}
@@ -1,45 +1,21 @@
package com.budwk.app.zhgh.dayofficework.healthCheckup.controller;
import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import com.budwk.app.zhgh.dayofficework.healthCheckup.service.HealthCheckupListApplicationService;
import com.budwk.app.zhgh.dayofficework.healthCheckup.param.HealthCheckupFilterUserPageForm;
import javax.validation.Valid;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.budwk.app.base.annotation.SLog;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.model.ExcelImportRes;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.utils.CommonDownloadUtil;
import com.budwk.app.base.utils.easyexcel.EasyExcelUtil;
import com.budwk.app.sys.views.View_user;
import com.budwk.app.web.commons.auth.utils.AuthUtil;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.activity.basic.models.ActivityUserScope;
import com.budwk.app.zhgh.dayofficework.healthCheckup.model.*;
import com.budwk.app.zhgh.dayofficework.healthCheckup.service.HealthCheckupProjectService;
import com.budwk.app.zhgh.dayofficework.healthCheckup.template.HealthCheckupImportTemp;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.formula.functions.T;
import org.apache.poi.ss.usermodel.Workbook;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Lang;
import org.nutz.lang.Strings;
import org.nutz.lang.util.NutMap;
import org.nutz.mvc.annotation.AdaptBy;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
@@ -47,11 +23,7 @@ import org.nutz.mvc.upload.TempFile;
import org.nutz.mvc.upload.UploadAdaptor;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
/**
* @ClassName HealthCheckupListController
@@ -67,88 +39,53 @@ import java.util.stream.Collectors;
public class HealthCheckupListController {
@Inject
private Dao dao;
private HealthCheckupListApplicationService applicationService;
@Inject
@Inject
private HealthCheckupProjectService healthCheckupProjectService;
@At("")
@At("")
@Ok("beetl:/platform/zhgh/dayofficework/healthCheckup/listMange/index.html")
@SaCheckPermission("healthCheckup.list.mange")
public void index() {
}
@At("/feedback/query")
@Ok("beetl:/platform/zhgh/dayofficework/healthCheckup/feedbackQuery/index.html")
@SaCheckPermission("healthCheckup.list.mange.feedback")
public void feedbackQueryIndex() {
}
/**
* @param pageForm 当前项目 ID、人员筛选及分页排序参数;空筛选项表示不限。
* @return Result,code=0 时 data.list 为候选人员,data.totalCount 为总人数。
*/
@At
@SaCheckPermission("healthCheckup.list.import")
@Ok("json:{dateFormat:'yyyy-MM-dd'}")
public Result candidatePageData(@Valid HealthCheckupFilterUserPageForm pageForm) {
return applicationService.candidatePageData(pageForm);
}
/**
* @param pageForm 当前项目 ID 和人员筛选条件;添加全部匹配人员,不受分页限制。
* @return Result,code=0 表示成功,msg 说明实际新增人数。
*/
@At
@SaCheckPermission("healthCheckup.list.import")
@Aop(TransAop.READ_COMMITTED)
@SLog(type = "healthCheckup", tag = "体检管理", msg = "管理员添加体检人员")
public Result addUsers(@Valid HealthCheckupFilterUserPageForm pageForm) {
return applicationService.addUsers(pageForm);
}
@At
@ApiOperation("分页查询")
@SaCheckPermission("healthCheckup.list.mange")
public Result pageData(PageForm pageForm,
String projectId,
String unionId,
String unitId) {
List<HealthCheckupUnionConfirm> confirms = dao.query(HealthCheckupUnionConfirm.class, Cnd.where("projectId", "=", projectId));
HealthCheckupProject project = healthCheckupProjectService.findOne(projectId);
Cnd cnd = Cnd.NEW();
Sql sql = Sqls.create("""
SELECT
hcu.userId AS id,
hcu.loginName,
hcu.userName,
hcu.sex,
hcu.unionId,
hcu.unionName,
hcu.unitName,
hcus.selectTime,
hcus.campus,
hcus.subjectId,
hcus.bz,
hcus.receiveAddress,
ca.campusName
FROM
health_checkup_user hcu
LEFT JOIN health_checkup_user_selection hcus ON hcu.userId = hcus.selectUserId
AND hcus.projectId=@projectId
LEFT JOIN health_checkup_campus ca on ca.id=hcus.campus
$condition
""").setParam("projectId", projectId);
sql.setCondition(cnd);
cnd.andEX("hcu.projectId", "=", projectId);
cnd.andEX("hcu.unionId", "=", unionId);
cnd.andEX("hcu.unitId", "=", unitId);
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) {
cnd.andEX("hcu.unionId", "=", SecurityUtil.getUnionId());
}
if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
SqlExpressionGroup group = new SqlExpressionGroup();
group.orLike("hcu.userName", pageForm.getSearchKeyword());
group.orLike("hcu.loginName", pageForm.getSearchKeyword());
cnd.and(group);
}
cnd.groupBy("hcu.userId");
if (StrUtil.isNotBlank(pageForm.getPageOrderName()) && Strings.isNotBlank(pageForm.getPageOrderBy())) {
cnd.orderBy(pageForm.getPageOrderName(), "ascending".equalsIgnoreCase(pageForm.getPageOrderBy()) ? "ASC" : "DESC");
} else {
cnd.desc("hcus.selectTime").asc("hcu.unitId");
}
Pagination pagination = healthCheckupProjectService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
List<NutMap> paginationList = pagination.getList();
paginationList.forEach(v -> {
if (StrUtil.isNotBlank(v.getString("subjectId"))) {
List<HealthCheckupProjectSubject> subjects = project.getHealthCheckupProjectSubjects();
HealthCheckupProjectSubject subject = subjects.stream().filter(s -> s.getId().equals(v.getString("subjectId"))).findFirst().orElse(null);
v.setv("optionName", subject.getOptionName());
}
if (StrUtil.isNotBlank(v.getString("unionId"))) {
HealthCheckupUnionConfirm confirm = confirms.stream().filter(o -> o.getUnionId().equals(v.getString("unionId"))).findFirst().orElse(null);
v.setv("isAudit", confirm != null ? confirm.getIsAudit() : "");
v.setv("auditTime", confirm != null ? confirm.getAuditTime() : "");
}
});
return Result.success(pagination);
public Result pageData(PageForm pageForm, String projectId, String subjectId,
String unionId, String unitId, String physicalExaminationStatus, String isAudit) {
return applicationService.pageData(pageForm, projectId, subjectId, unionId, unitId, physicalExaminationStatus, isAudit);
}
@At
@@ -159,21 +96,7 @@ public class HealthCheckupListController {
public Result doAuditUser(String projectId,
String unionId,
Boolean flag) {
if (StrUtil.isBlank(unionId)) {
unionId = SecurityUtil.getUnionId();
}
if (flag) {
dao.update(HealthCheckupUnionConfirm.class, Chain.make("isAudit", true)
.add("auditTime", DateUtil.date())
.add("auditUser", SecurityUtil.getUserId()),
Cnd.where("projectId", "=", projectId).and("unionId", "=", unionId));
} else {
dao.update(HealthCheckupUnionConfirm.class, Chain.make("isAudit", null)
.add("auditTime", null)
.add("auditUser", null),
Cnd.where("projectId", "=", projectId).and("unionId", "=", unionId));
}
return Result.success();
return applicationService.doAuditUser(projectId, unionId, flag);
}
@@ -181,93 +104,20 @@ public class HealthCheckupListController {
@ApiOperation("体检名单管理员编辑选择记录")
@SLog(type = "healthCheckup", tag = "体检管理", msg = "管理员编辑了一条选择记录")
@SaCheckPermission("healthCheckup.list.mange")
@org.nutz.ioc.aop.Aop(org.nutz.aop.interceptor.ioc.TransAop.READ_COMMITTED)
public Result doEditHealthCheckupData(String subjectId, String campus, String projectId, String userId, String bz) {
//查询用户有没有选择过套餐
HealthCheckupUserSelection userSelection = healthCheckupProjectService.dao().fetch(HealthCheckupUserSelection.class,
Cnd.where("projectId", "=", projectId).and("selectUserId", "=", userId));
//如果选择过套餐就修改选择的套餐id
if (Lang.isNotEmpty(userSelection)) {
userSelection.setSubjectId(subjectId);
userSelection.setCampus(campus);
userSelection.setBz(bz);
healthCheckupProjectService.update(userSelection);
} else {
//如果没有选择过套餐添加一条记录
HealthCheckupUserSelection checkupUserSelection = new HealthCheckupUserSelection();
checkupUserSelection.setSelectUserId(userId);
checkupUserSelection.setProjectId(projectId);
checkupUserSelection.setSubjectId(subjectId);
checkupUserSelection.setCampus(campus);
checkupUserSelection.setSelectTime(new Date());
checkupUserSelection.setBz(bz);
healthCheckupProjectService.insert(checkupUserSelection);
}
return Result.success();
return applicationService.doEditHealthCheckupData(subjectId, campus, projectId, userId, bz);
}
@At
@ApiOperation("体检名单导入名单")
@SLog(type = "healthCheckup", tag = "体检管理", msg = "管理员导入了体检名单")
@SaCheckPermission("healthCheckup.list.mange")
@SaCheckPermission("healthCheckup.list.import")
@Aop(TransAop.READ_COMMITTED)
@AdaptBy(type = UploadAdaptor.class, args = {"ioc:fileUpload"})
public Result healthImport(TempFile file, String healthProjectId) {
// 读取数据
List<T> ts = EasyExcelUtil.syncReadModel(file.getFile(), HealthCheckupImportTemp.class, 0, 1);
List<HealthCheckupImportTemp> healthCheckupImportTemps = JSONUtil.parseArray(JSONUtil.toJsonStr(ts)).toList(HealthCheckupImportTemp.class);
List<String> loginNames = healthCheckupImportTemps.stream().filter(v -> Strings.isNotBlank(v.getLoginName())).map(v -> v.getLoginName()).collect(Collectors.toList());
//全部的体检名单
List<HealthCheckupUser> healthCheckupUserList = dao.query(HealthCheckupUser.class, Cnd.where(HealthCheckupUser::getProjectId, "=", healthProjectId));
List<View_user> userList = dao.query(View_user.class, Cnd.where(View_user::getLoginname, "in", loginNames));
HealthCheckupProject checkupProject = dao.fetch(HealthCheckupProject.class, healthProjectId);
ActivityUserScope userScope = dao.fetch(ActivityUserScope.class, Cnd.where("groupId", "=", checkupProject.getActivityGroupId()));
List<ActivityUserScope> userScopeList = new ArrayList<>();
for (int i = 0; i < healthCheckupImportTemps.size(); i++) {
HealthCheckupImportTemp excel = healthCheckupImportTemps.get(i);
if (StrUtil.isBlank(excel.getLoginName())) {
excel.setErrInfo("工号为空", i + 1);
continue;
}
// 不能在excel里重复
if (healthCheckupImportTemps.stream().filter(s -> s.getLoginName().equals(excel.getLoginName())).count() > 1) {
excel.setErrInfo("重复数据", i + 1);
}
View_user user = userList.stream().filter(s -> s.getLoginname().equals(excel.getLoginName())).findFirst().orElse(null);
if (user == null) {
excel.setErrInfo("无此用户", i + 1);
continue;
}
if (healthCheckupUserList.stream().anyMatch(s -> s.getUserId().equals(user.getId()))) {
excel.setErrInfo("该用户已加入体检名单", i + 1);
continue;
}
ActivityUserScope s = new ActivityUserScope();
s.setGroupId(userScope.getGroupId());
s.setGroupName(userScope.getGroupName());
s.setUserId(user.getId());
s.setCreator(SecurityUtil.getUserId());
userScopeList.add(s);
}
;
dao.insert(userScopeList);
// 创建结果集
ExcelImportRes<HealthCheckupImportTemp> excelImportRes = new ExcelImportRes<>();
excelImportRes.setTotalRecords(healthCheckupImportTemps.size());
// 添加错误记录
excelImportRes.setErrorDetails(healthCheckupImportTemps.stream().filter(s -> StrUtil.isNotBlank(s.getErrMsg())).collect(Collectors.toList()));
excelImportRes.setFailedCount(excelImportRes.getErrorDetails().size());
return Result.success(excelImportRes);
return applicationService.healthImport(file, healthProjectId);
}
@At
@@ -275,12 +125,106 @@ public class HealthCheckupListController {
@ApiOperation("下载体检参加人员导入模版")
@SaCheckPermission("healthCheckup.list.mange")
public void downloadTem(HttpServletResponse response) {
List<ExcelExportEntity> entities = new ArrayList<>();
entities.add(new ExcelExportEntity("工号", "loginName", 20));
entities.add(new ExcelExportEntity("姓名", "userName", 20));
ExportParams exportParams = new ExportParams();
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entities, Collections.emptyList());
CommonDownloadUtil.download("参加人员导入模版.xlsx", workbook, response);
applicationService.downloadTem(response);
}
@At
@ApiOperation("体检名单删除")
@SaCheckPermission("healthCheckup.list.delete")
@Aop(TransAop.READ_COMMITTED)
public Result doDelete(String id) {
return applicationService.doDelete(id);
}
@At
@Ok("void")
@ApiOperation("下载体检报告导入模版")
@SaCheckPermission("healthCheckup.list.mange")
public void downloadTemMedicalExaminationReport(HttpServletResponse response) {
applicationService.downloadTemMedicalExaminationReport(response);
}
@At
@ApiOperation("体检名单导入名单")
@SLog(type = "healthCheckup", tag = "体检管理", msg = "管理员导入了体检名单")
@SaCheckPermission("healthCheckup.list.import.medicalExamination")
@Aop(TransAop.READ_COMMITTED)
@AdaptBy(type = UploadAdaptor.class, args = {"ioc:fileUpload"})
public Result healthImportMedicalExaminationReport(TempFile file, String healthProjectId) {
return applicationService.healthImportMedicalExaminationReport(file, healthProjectId);
}
@At
@ApiOperation("套餐选择器查询")
@SaCheckPermission("healthCheckup.list.mange")
public Result listSubjects(String projectId){
return applicationService.listSubjects(projectId);
}
@At
@ApiOperation("体检反馈列表")
@SaCheckPermission("healthCheckup.list.mange")
public Result feedbackList(String year, String projectId, String unionId) {
return applicationService.feedbackList(year, projectId, unionId);
}
@At
@ApiOperation("保存体检反馈")
@SLog(type = "healthCheckup", tag = "体检管理", msg = "新增体检反馈")
@SaCheckPermission("healthCheckup.list.mange")
@Aop(TransAop.READ_COMMITTED)
public Result saveFeedback(String year, String projectId, String unionId, String feedbackInfo) {
return applicationService.saveFeedback(year, projectId, unionId, feedbackInfo);
}
@At
@ApiOperation("删除体检反馈")
@SLog(type = "healthCheckup", tag = "体检管理", msg = "删除体检反馈")
@SaCheckPermission("healthCheckup.list.mange")
@Aop(TransAop.READ_COMMITTED)
public Result deleteFeedback(String id) {
return applicationService.deleteFeedback(id);
}
@At
@ApiOperation("批量删除体检反馈")
@SLog(type = "healthCheckup", tag = "体检管理", msg = "批量删除体检反馈")
@SaCheckPermission("healthCheckup.list.mange")
@Aop(TransAop.READ_COMMITTED)
public Result deleteFeedbackBatch(String ids) {
return applicationService.deleteFeedbackBatch(ids);
}
@At("/feedback/query/pageData")
@ApiOperation("问题反馈列表分页")
@SaCheckPermission("healthCheckup.list.mange")
public Result feedbackQueryPageData(PageForm pageForm, String year, String projectId, String unionId) {
return applicationService.feedbackQueryPageData(pageForm, year, projectId, unionId);
}
@At
@ApiOperation("体检名单编辑备注")
@SLog(type = "healthCheckup", tag = "体检管理", msg = "编辑备注")
@SaCheckPermission("healthCheckup.list.mange")
@org.nutz.ioc.aop.Aop(org.nutz.aop.interceptor.ioc.TransAop.READ_COMMITTED)
public Result doEditRemarks(String userId, String projectId,String remarks) {
return applicationService.doEditRemarks(userId, projectId, remarks);
}
@At
@Ok("void")
@ApiOperation("导出")
@SaCheckPermission("healthCheckup.list.mange")
public void exportHealthUser(String projectId,
String unionId,
String searchKeyword,
String subjectId,
String unitId,
String physicalExaminationStatus,
String pageOrderName,
String pageOrderBy,
HttpServletResponse response) {
applicationService.exportHealthUser(projectId, unionId, searchKeyword, subjectId, unitId, physicalExaminationStatus, pageOrderName, pageOrderBy, response);
}
}
@@ -0,0 +1,44 @@
package com.budwk.app.zhgh.dayofficework.healthCheckup.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.zhgh.dayofficework.healthCheckup.service.HealthCheckupPersonConfirmStatisticsService;
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;
/**
* @ClassName HealthCheckupPersonConfirmStatisticsController
* @Description 工会确认情况统计
* @Author Codex
* @Date 2026/4/28
*/
@IocBean
@Ok("json:full")
@Api(tags = "体检人员确认情况统计")
@At("/platform/healthCheckup/statistics/personconfirm")
public class HealthCheckupPersonConfirmStatisticsController {
@Inject
private HealthCheckupPersonConfirmStatisticsService healthCheckupPersonConfirmStatisticsService;
@At("")
@Ok("beetl:/platform/zhgh/dayofficework/healthCheckup/personconfirm/index.html")
@SaCheckPermission("healthCheckup.personconfirm")
public void index() {
}
@At
@ApiOperation("工会确认情况分页统计")
@SaCheckPermission("healthCheckup.personconfirm")
public Result pageData(PageForm pageForm, Integer year, String projectId, String unionId) {
// 统计报表按年度、体检项目查询,工会条件选填,统一返回分页结果供前端表格展示。
Pagination statistics = healthCheckupPersonConfirmStatisticsService.personConfirmPageData(pageForm.getPageNumber(), pageForm.getPageSize(), year, projectId, unionId);
return Result.success(statistics);
}
}
@@ -1,33 +1,22 @@
package com.budwk.app.zhgh.dayofficework.healthCheckup.controller;
import com.budwk.app.zhgh.dayofficework.healthCheckup.service.HealthCheckupProjectMangeApplicationService;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.annotation.SLog;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.sys.models.Sys_union;
import com.budwk.app.zhgh.activity.culture.service.ActivityCultureService;
import com.budwk.app.zhgh.dayofficework.healthCheckup.model.*;
import com.budwk.app.zhgh.dayofficework.healthCheckup.service.HealthCheckupProjectService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Cnd;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.Daos;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Lang;
import org.nutz.lang.util.NutMap;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* @ClassName HealthCheckupProjectMangeController
@@ -42,7 +31,11 @@ import java.util.stream.Collectors;
public class HealthCheckupProjectMangeController {
@Inject
private HealthCheckupProjectService healthCheckupProjectService;
private HealthCheckupProjectMangeApplicationService applicationService;
@Inject
private ActivityCultureService activityCultureService;
@At("")
@@ -58,102 +51,25 @@ public class HealthCheckupProjectMangeController {
public Result pageData(PageForm pageForm,
Integer year,
String name) {
Sql sql = Sqls.create("""
SELECT
hcp.id,
hcp.`year`,
hcp.`name`,
hcp.choiceTimeStart,
hcp.choiceTimeEnd,
hcp.activityGroupId,
hcp.cover,
hcp.activityGroupName,
hcp.healthCheckupType,
IF((select count(userId) from health_checkup_user hcu where hcu.projectId = hcp.id) > 0,true,false) AS isHasUserList,
IF(NOW() > hcp.choiceTimeStart AND NOW() < hcp.choiceTimeEnd, true, false) AS isWithinChoiceTime,
IF(NOW() < hcp.choiceTimeStart, true, false) AS canCreated
FROM
health_checkup_project hcp
$condition
""");
Cnd cnd = Cnd.NEW();
cnd.andEX("hcp.`year`", "=", year);
cnd.andEX("hcp.`name`", "=", name);
cnd.desc("hcp.choiceTimeStart");
sql.setCondition(cnd);
return Result.success(healthCheckupProjectService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql));
return applicationService.pageData(pageForm, year, name);
}
@At
@ApiOperation("体检名单确认")
@ApiOperation("体检项目确认")
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("healthCheckup.projectMange")
@SLog(type = "healthCheckup", tag = "体检管理", msg = "提交体检项目")
public Result doAdd(HealthCheckupProject healthCheckupProject,
@Param(value = "deleteRowIds") String[] deleteRowIds,
@Param(value = "deleteSubjectIds") String[] deleteSubjectIds) {
String projectId;
//如果是保存
if (StrUtil.isBlank(healthCheckupProject.getId())) {
healthCheckupProject.setYear(DateUtil.thisYear());
HealthCheckupProject checkupProject = healthCheckupProjectService.insertWith(healthCheckupProject, "healthCheckupProjectSubjects");
healthCheckupProject.getHealthCheckupProjectSubjects().forEach(v -> {
v.getSubjectMoneys().forEach(m -> {
m.setProjectId(checkupProject.getId());
m.setSubjectId(v.getId());
});
if (Lang.isNotEmpty(v.getSubjectMoneys())) {
healthCheckupProjectService.insert(v.getSubjectMoneys());
}
});
projectId = checkupProject.getId();
} else {
projectId = healthCheckupProject.getId();
//如果删除了医院
if (Lang.isNotEmpty(deleteSubjectIds)) {
healthCheckupProjectService.dao().clear(HealthCheckupProjectSubject.class, Cnd.where("id", "in", deleteSubjectIds));
healthCheckupProjectService.dao().clear(HealthCheckupProjectSubjectMoney.class, Cnd.where("subjectId", "in", deleteSubjectIds));
}
//如果删除了项目
if (Lang.isNotEmpty(deleteRowIds)) {
healthCheckupProjectService.dao().clear(HealthCheckupProjectSubjectMoney.class, Cnd.where("id", "in", deleteRowIds));
}
healthCheckupProjectService.dao().insertOrUpdate(healthCheckupProject.getHealthCheckupProjectSubjects());
healthCheckupProject.getHealthCheckupProjectSubjects().forEach(v -> {
v.setProjectId(projectId);
v.getSubjectMoneys().forEach(m -> {
m.setProjectId(projectId);
m.setSubjectId(v.getId());
});
if (Lang.isNotEmpty(v.getSubjectMoneys())) {
healthCheckupProjectService.insertOrUpdate(v.getSubjectMoneys());
}
});
healthCheckupProjectService.dao().insertOrUpdate(healthCheckupProject.getHealthCheckupProjectSubjects());
healthCheckupProjectService.update(healthCheckupProject);
}
healthCheckupProjectService.dao().clear(HealthCheckupUnionConfirm.class, Cnd.where("projectId", "=", projectId));
List<Sys_union> unionList = healthCheckupProjectService.dao().query(Sys_union.class, Cnd.NEW());
List<HealthCheckupUnionConfirm> confirmList = new ArrayList<>();
unionList.forEach(item -> {
HealthCheckupUnionConfirm c = new HealthCheckupUnionConfirm();
c.setProjectId(projectId);
c.setUnionId(item.getId());
c.setIsAudit(false);
confirmList.add(c);
});
healthCheckupProjectService.dao().insert(confirmList);
return Result.success();
return applicationService.doAdd(healthCheckupProject, deleteRowIds, deleteSubjectIds);
}
@At
@ApiOperation("查询体检项目内容")
@SLog(type = "healthCheckup", tag = "体检管理", msg = "查询体检项目内容")
public Result findOne(String id) {
return Result.success(healthCheckupProjectService.findOne(id));
return applicationService.findOne(id);
}
@At
@@ -162,18 +78,14 @@ public class HealthCheckupProjectMangeController {
@SaCheckPermission("healthCheckup.projectMange")
@SLog(type = "healthCheckup", tag = "体检管理", msg = "删除了一条体检项目")
public Result doDelete(String id) {
healthCheckupProjectService.delete(id);
healthCheckupProjectService.dao().clear(HealthCheckupProjectSubject.class, Cnd.where("projectId", "=", id));
healthCheckupProjectService.dao().clear(HealthCheckupProjectSubjectMoney.class, Cnd.where("projectId", "=", id));
return Result.success();
return applicationService.doDelete(id);
}
@At
@ApiOperation("按年份获取体检项目")
@SLog(type = "healthCheckup", tag = "体检管理", msg = "查询了所有的体检项目")
public Result getHealthCheckupProject(Integer year) {
List<HealthCheckupProject> projectList = healthCheckupProjectService.query(Cnd.NEW().andEX("year", "=", year).desc("choiceTimeStart"));
return Result.success(projectList);
return applicationService.getHealthCheckupProject(year);
}
@@ -189,8 +101,7 @@ public class HealthCheckupProjectMangeController {
@SaCheckPermission("healthCheckup.projectMange")
@SLog(type = "healthCheckup", tag = "体检管理", msg = "创建体检名单")
public Result createUserList(HealthCheckupProject healthCheckupProject) {
healthCheckupProjectService.addHealthCheckupUser(healthCheckupProject);
return Result.success();
return applicationService.createUserList(healthCheckupProject);
}
@@ -206,8 +117,7 @@ public class HealthCheckupProjectMangeController {
@SaCheckPermission("healthCheckup.projectMange")
@SLog(type = "healthCheckup", tag = "体检管理", msg = "更新体检名单")
public Result renewUserList(HealthCheckupProject healthCheckupProject) {
healthCheckupProjectService.renewHealthCheckupUser(healthCheckupProject);
return Result.success();
return applicationService.renewUserList(healthCheckupProject);
}
@@ -223,16 +133,6 @@ public class HealthCheckupProjectMangeController {
@SaCheckPermission("healthCheckup.projectMange")
@SLog(type = "healthCheckup", tag = "体检管理", msg = "重置体检名单")
public Result resetUserList(HealthCheckupProject healthCheckupProject) {
//重置名单,清空已选择的用户记录
Sql sql = Sqls.create("select id,selectUserId from health_checkup_user_selection where projectId = @projectId").setParam("projectId", healthCheckupProject.getId());
List<NutMap> list = (List<NutMap>) Daos.query(healthCheckupProjectService.dao(), sql.toString(), Sqls.callback.maps());
List<String> userSelectionIds = list.stream().map(v -> v.getString("id")).collect(Collectors.toList());
List<String> userIds = list.stream().map(v -> v.getString("selectUserId")).collect(Collectors.toList());
healthCheckupProjectService.dao().clear(HealthCheckupUserSelection.class, Cnd.where("selectUserId", "in", userIds));
healthCheckupProjectService.dao().clear(HealthCheckupUserCompanion.class, Cnd.where("userSelectionId", "in", userSelectionIds));
healthCheckupProjectService.dao().clear(HealthCheckupUser.class, Cnd.where("projectId", "=", healthCheckupProject.getId()));
healthCheckupProjectService.addHealthCheckupUser(healthCheckupProject);
return Result.success();
return applicationService.resetUserList(healthCheckupProject);
}
}
@@ -1,46 +1,22 @@
package com.budwk.app.zhgh.dayofficework.healthCheckup.controller;
import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import com.budwk.app.zhgh.dayofficework.healthCheckup.service.HealthCheckupSingleApplicationService;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.date.DateUtil;
import com.budwk.app.base.annotation.SLog;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.utils.CommonDownloadUtil;
import com.budwk.app.base.utils.ConditionGroupUtil;
import com.budwk.app.base.utils.ManyAddOrRenewUtil;
import com.budwk.app.sys.views.View_user;
import com.budwk.app.zhgh.dayofficework.healthCheckup.model.HealthCheckupProject;
import com.budwk.app.zhgh.dayofficework.healthCheckup.model.HealthCheckupProjectSubject;
import com.budwk.app.zhgh.dayofficework.healthCheckup.model.HealthCheckupProjectSubjectMoney;
import com.budwk.app.zhgh.dayofficework.healthCheckup.model.HealthCheckupUserSelection;
import com.budwk.app.zhgh.dayofficework.healthCheckup.service.HealthCheckupProjectService;
import com.budwk.app.zhgh.dayofficework.healthCheckup.service.HealthCheckupSingleService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.poi.ss.usermodel.Workbook;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Strings;
import org.nutz.lang.util.NutMap;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
/**
* @ClassName HealthCheckupSingleController
@@ -54,11 +30,14 @@ import java.util.stream.Collectors;
@At("/platform/healthCheckup/statistics/single")
public class HealthCheckupSingleController {
@Inject
private HealthCheckupSingleApplicationService applicationService;
@Inject
@Inject
private HealthCheckupSingleService healthCheckupSingleService;
@Inject
private ManyAddOrRenewUtil manyAddOrRenewUtil;
@Inject
private HealthCheckupProjectService healthCheckupProjectService;
@At("")
@Ok("beetl:/platform/zhgh/dayofficework/healthCheckup/statisticsSingle/index.html")
@@ -66,14 +45,32 @@ public class HealthCheckupSingleController {
public void index() {
}
@At
@ApiOperation("首页查询")
@SaCheckPermission("healthCheckup.statisticsSingle")
public Result pageData(String projectId,
String unionId) {
NutMap nutMap = healthCheckupSingleService.pageData(projectId, unionId);
return Result.success(nutMap);
return applicationService.pageData(projectId, unionId);
}
/**
* projectId传体检项目ID;返回Result.data为人数映射,__total__为总人数,其余键为医院ID。
*/
@At
@SaCheckPermission("healthCheckup.statisticsSingle")
public Result actualStatistics(String projectId) {
return applicationService.actualStatistics(projectId);
}
/**
* projectId传体检项目ID,counts传整行人数JSON(__total__及医院ID映射非负整数)。
* 返回Result,data为已保存人数映射;非零code表示校验或权限失败,msg说明原因。
*/
@At
@SaCheckPermission("healthCheckup.statisticsSingle")
@org.nutz.ioc.aop.Aop(org.nutz.aop.interceptor.ioc.TransAop.READ_COMMITTED)
public Result saveActualStatistics(String projectId, String counts) {
return applicationService.saveActualStatistics(projectId, counts);
}
/**
@@ -91,12 +88,22 @@ public class HealthCheckupSingleController {
public Result receivePageData(PageForm page, String isSelected,
String projectId,
String unionId) {
List<NutMap> labelList = healthCheckupSingleService.receivePageDataColumns(projectId);
Pagination list = healthCheckupSingleService.receivePageData(page.getPageNumber(), page.getPageSize(), page.getSearchName(), page.getSearchKeyword(), projectId, unionId, isSelected);
return Result.success(NutMap.NEW().addv("list", list).addv("label", labelList));
return applicationService.receivePageData(page, isSelected, projectId, unionId);
}
/**
* @param projectId 当前教职工体检项目ID;不接收分工会筛选,权限在service中校验
* @param response 返回ZIP附件,其中每个xlsx为一家医院的已选名单,不返回JSON
*/
@At
@Ok("void")
@ApiOperation("按医院导出已选名单ZIP")
@SaCheckPermission("healthCheckup.statisticsSingle")
public void exportSelectedZip(String projectId, HttpServletResponse response) {
applicationService.exportSelectedZip(projectId, response);
}
/**
* 导出已选/未选人员名单
*
@@ -114,48 +121,7 @@ public class HealthCheckupSingleController {
String unionId,
Integer year,
boolean flag, HttpServletResponse response) {
List<ExcelExportEntity> entityList = new ArrayList<>() {{
add(new ExcelExportEntity("职工号", "loginName", 20));
add(new ExcelExportEntity("姓名", "userName", 20));
add(new ExcelExportEntity("性别", "sex", 20));
// add(new ExcelExportEntity("出生年月", "birthday", 20));
add(new ExcelExportEntity("年龄", "age", 20));
add(new ExcelExportEntity("婚姻状况", "marriage", 20));
add(new ExcelExportEntity("身份证号", "idCard", 20));
add(new ExcelExportEntity("部门", "unitName", 20));
// add(new ExcelExportEntity("部门编码", "unitCode", 20));
add(new ExcelExportEntity("医院", "subjectName", 20));
add(new ExcelExportEntity("邮寄地址", "receiveAddress", 50));
}};
List<NutMap> list;
if (flag) {
//已选人员
list = healthCheckupSingleService.receivePageData(projectId, unionId, "0");
} else {
//未选人员
list = healthCheckupSingleService.receivePageData(projectId, unionId, "1");
}
//获取当前年
Calendar calendar = Calendar.getInstance();
int thisYear = calendar.get(Calendar.YEAR);
list.forEach(v -> {
int substring = 0;
if (Strings.isNotBlank(v.getString("birthday"))) {
substring = Integer.parseInt(v.getString("birthday").substring(0, 4));
} else if (Strings.isNotBlank(v.getString("idCard")) && v.getString("idCard").length() >= 10) {
substring = Integer.parseInt(v.getString("idCard").substring(6, 10));
}
v.put("age", thisYear - substring);
});
List<NutMap> list2 = list.stream().filter(v -> Strings.isNotBlank(v.getString("unionName"))).collect(Collectors.toList());
ExportParams exportParams = new ExportParams();
exportParams.setType(ExcelType.XSSF);
exportParams.setSheetName(year + "年教职工体检项目及地点统计表");
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entityList, list2);
CommonDownloadUtil.download((flag ? year + "年教职工体检项目及地点统计表" : "未选名单") + ".xlsx", workbook, response);
applicationService.exportReceiveDetail(projectId, unionId, year, flag, response);
}
@@ -166,69 +132,7 @@ public class HealthCheckupSingleController {
public void exportUserCompanion(String projectId,
String unionId,
Integer year, HttpServletResponse response) {
Sql sql = Sqls.create("""
SELECT
u.username,
u.unitname unitName,
u.mobile,
hcuc.userName familyName,
hcuc.sex,
hcuc.marry isMarried,
hcuc.idCard,
hcc.campusName,
(
SELECT
GROUP_CONCAT( hcps.optionName )
FROM
health_checkup_user_selection hcus
LEFT JOIN `health_checkup_project_subject` hcps ON hcps.id = hcus.subjectId
WHERE
hcus.projectId = @projectId
AND hcus.selectUserId = u.id
) optionName
FROM
`health_checkup_user_companion` hcuc
LEFT JOIN `health_checkup_user_selection` hcus ON hcus.id = hcuc.userSelectionId
LEFT JOIN `health_checkup_campus` hcc ON hcc.id = hcus.campus
LEFT JOIN `vw_user` u ON u.id = hcus.selectUserId
LEFT JOIN `health_checkup_project` hcp ON hcp.id = hcus.projectId
$condition
""").setParam("projectId", projectId);
Cnd cnd = Cnd.NEW();
cnd.andEX("hcus.projectId", "=", projectId);
cnd.andEX("u.unionid", "=", unionId);
cnd.andEX("hcp.year", "=", year);
cnd.asc("u.unioncode");
sql.setCondition(cnd);
//家属信息表
List<NutMap> companionList = healthCheckupSingleService.listMap(sql);
for (int i = 0; i < companionList.size(); i++) {
companionList.get(i).put("index", i + 1);
}
List<ExcelExportEntity> entityList = new ArrayList<>() {{
add(new ExcelExportEntity("序号", "index", 20));
add(new ExcelExportEntity("教职工姓名", "username", 20));
add(new ExcelExportEntity("学院(部门)", "unitName", 20));
add(new ExcelExportEntity("手机", "mobile", 20));
add(new ExcelExportEntity("体检人姓名", "familyName", 20));
add(new ExcelExportEntity("性别", "sex", 20));
add(new ExcelExportEntity("婚否", "isMarried", 20));
add(new ExcelExportEntity("体检人身份证号", "idCard", 50));
add(new ExcelExportEntity("自选项目", "optionName", 50));
add(new ExcelExportEntity("自选院区", "campusName", 50));
}};
try {
ExportParams exportParams = new ExportParams();
exportParams.setType(ExcelType.XSSF);
exportParams.setTitle(year + "教职工家属体检登记表");
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entityList, companionList);
CommonDownloadUtil.download(year + "教职工家属体检登记表.xlsx", workbook, response);
} catch (Exception e) {
e.printStackTrace();
}
applicationService.exportUserCompanion(projectId, unionId, year, response);
}
@@ -237,132 +141,20 @@ public class HealthCheckupSingleController {
@Ok("json:full")
@SaCheckPermission("healthCheckup.statisticsSingle")
@SLog(type = "healthCheckup", tag = "体检管理", msg = "管理员一键代选")
@org.nutz.ioc.aop.Aop(org.nutz.aop.interceptor.ioc.TransAop.READ_COMMITTED)
public Result doSelectByAdmin(String projectId, String optionId, String campus) {
Dao dao = healthCheckupSingleService.dao();
HealthCheckupProject project = dao.fetch(HealthCheckupProject.class, projectId);
Cnd cnd = Cnd.NEW();
Sql sql = Sqls.create("""
SELECT
u.id,
u.loginname loginName,
u.username userName,
u.sex,
u.birthday,
u.unitname unitName,
u.unionname unionName,
u.idcard idCard,
u.mobile
FROM
`activity_user_scope` aus
LEFT JOIN `vw_user` u ON u.id = aus.userId
$projectCnd $condition
""");
cnd.and("aus.groupId", "=", project.getActivityGroupId());
sql.setVar("projectCnd", "AND u.id NOT IN ( SELECT selectUserId FROM `health_checkup_user_selection` WHERE projectId = '" + projectId + "' AND selectUserId IS NOT NULL )");
cnd.groupBy("u.loginname");
sql.setCondition(cnd);
List<NutMap> mapList = healthCheckupSingleService.listMap(sql);
List<String> userIds = mapList.stream().map(v -> v.getString("id")).collect(Collectors.toList());
//题目id
List<HealthCheckupUserSelection> selections = userIds.stream().map(v -> {
HealthCheckupUserSelection selection = new HealthCheckupUserSelection();
selection.setProjectId(projectId);
selection.setSelectUserId(v);
selection.setCampus(campus);
selection.setSubjectId(optionId);
selection.setSelectTime(new Date());
return selection;
}).collect(Collectors.toList());
manyAddOrRenewUtil.asyncExecuteFastInsert(selections, null);
return Result.success();
return applicationService.doSelectByAdmin(projectId, optionId, campus);
}
@At
@ApiOperation("管理员一键选择去年数据")
@ApiOperation("按上年补齐未选人员")
@Ok("json:full")
@SaCheckPermission("healthCheckup.statisticsSingle")
@SLog(type = "healthCheckup", tag = "体检管理", msg = "管理员给没选的人统一选择去年的数据")
public Result selectOptionByLastYear(String projectId) {
Dao dao = healthCheckupSingleService.dao();
//获取去年项目
HealthCheckupProject lastYearProject = dao.fetch(HealthCheckupProject.class, Cnd.where("YEAR(year)", "=", DateUtil.thisYear() - 1));
if (lastYearProject == null) {
return Result.error("请先创建去年的体检项目");
}
Cnd cnd = Cnd.NEW();
Sql sql = Sqls.create("""
SELECT
u.id
FROM
health_checkup_user hcu
LEFT JOIN `vw_user` u ON u.id = hcu.userId
$projectCnd $condition
""");
cnd.and("hcu.projectId", "=", projectId);
sql.setVar("projectCnd", "AND u.id NOT IN ( SELECT selectUserId FROM `health_checkup_user_selection` WHERE projectId = '" + projectId + "' AND selectUserId IS NOT NULL )");
cnd.groupBy("u.loginname");
sql.setCondition(cnd);
List<NutMap> mapList = healthCheckupSingleService.listMap(sql);
List<String> userIds = mapList.stream().map(v -> v.getString("id")).toList();
//去年项目的人
List<HealthCheckupUserSelection> lastYearSelectionList = dao.query(HealthCheckupUserSelection.class, Cnd.where(HealthCheckupUserSelection::getProjectId, "=", lastYearProject.getId())
.and(HealthCheckupUserSelection::getSelectUserId, "in", userIds));
//去年项目下的医院
List<HealthCheckupProjectSubject> lastYearSubjectList = dao.query(HealthCheckupProjectSubject.class,
Cnd.where(HealthCheckupProjectSubject::getProjectId, "=", lastYearProject.getId()));
//今年项目下的医院
List<HealthCheckupProjectSubject> thisYearSubjectList = dao.query(HealthCheckupProjectSubject.class,
Cnd.where(HealthCheckupProjectSubject::getProjectId, "=", projectId));
List<HealthCheckupUserSelection> selections = new ArrayList<>();
userIds.forEach(v -> {
//获取去年这个人选择的记录
HealthCheckupUserSelection userSelection = lastYearSelectionList.stream().filter(item -> item.getSelectUserId().equals(v)).findFirst().orElse(null);
//去年这个人选择的选项是什么
HealthCheckupProjectSubject lastYearSubject = lastYearSubjectList.stream().filter(last -> last.getId().equals(userSelection.getSubjectId())).findFirst().orElse(null);
//今年项目下的选项
HealthCheckupProjectSubject thisYearSubject = thisYearSubjectList.stream().filter(thisYear -> thisYear.getOptionName().equals(lastYearSubject.getOptionName())).findFirst().orElse(null);
if (userSelection != null && thisYearSubject != null) {
//获取今年这个选项的收费
List<HealthCheckupProjectSubjectMoney> list = dao.query(HealthCheckupProjectSubjectMoney.class,
Cnd.where(HealthCheckupProjectSubjectMoney::getSubjectId, "=", thisYearSubject.getId()));
BigDecimal money = BigDecimal.valueOf(0.00);
String subjectMoneyId = null;
for (HealthCheckupProjectSubjectMoney subject : list) {
//找出这个人符合哪个项目
Cnd cndUser = Cnd.where("id", "=", v);
ConditionGroupUtil.applyConditionGroup(cndUser, subject.getMatchCnd());
int count = dao.count(View_user.class, cndUser);
if (count > 0) {
money = subject.getMoney();
subjectMoneyId = subject.getId();
break;
}
}
//如果有符合的就添加
if (subjectMoneyId != null) {
HealthCheckupUserSelection selection = new HealthCheckupUserSelection();
selection.setProjectId(projectId);
selection.setSelectUserId(v);
selection.setSubjectId(thisYearSubject.getId());
selection.setSubjectMoneyId(subjectMoneyId);
selection.setMoney(money);
selection.setSelectTime(new Date());
selections.add(selection);
}
}
});
return Result.success();
@org.nutz.ioc.aop.Aop(org.nutz.aop.interceptor.ioc.TransAop.READ_COMMITTED)
public Result selectOptionByLastYear(String projectId, String lastYearProjectId) {
return applicationService.selectOptionByLastYear(projectId, lastYearProjectId);
}
@@ -372,7 +164,7 @@ public class HealthCheckupSingleController {
@SaCheckPermission("healthCheckup.statisticsSingle")
@SLog(type = "healthCheckup", tag = "体检管理", msg = "管理员给未选的人发送提醒")
public Result msgNotSelect(@Valid String projectId) {
List<NutMap> userList = healthCheckupSingleService.receivePageData(projectId, null, "1");
return Result.success("暂未开启消息!");
return applicationService.msgNotSelect(projectId);
}
}
@@ -1,41 +1,23 @@
package com.budwk.app.zhgh.dayofficework.healthCheckup.h5controller;
import com.budwk.app.zhgh.dayofficework.healthCheckup.service.H5HealthCheckupApplicationService;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaMode;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.base.utils.ConditionGroupUtil;
import com.budwk.app.sys.views.View_user;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.dayofficework.healthCheckup.model.HealthCheckupCampus;
import com.budwk.app.zhgh.dayofficework.healthCheckup.model.HealthCheckupProject;
import com.budwk.app.zhgh.dayofficework.healthCheckup.model.HealthCheckupProjectSubjectMoney;
import com.budwk.app.zhgh.dayofficework.healthCheckup.model.HealthCheckupUserSelection;
import com.budwk.app.zhgh.dayofficework.healthCheckup.service.HealthCheckupProjectService;
import com.budwk.app.zhgh.dayofficework.healthCheckup.model.*;
import io.swagger.annotations.ApiOperation;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.cri.Static;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Lang;
import org.nutz.lang.util.NutMap;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* @author : hongqiwei
@@ -49,14 +31,13 @@ import java.util.Map;
public class H5HealthCheckupController {
@Inject
private Dao dao;
@Inject
private H5HealthCheckupApplicationService applicationService;
@Inject
private BaseService baseService;
@Inject
private HealthCheckupProjectService healthCheckupProjectService;
@At("/list")
@At("/list")
@Ok("beetl:/platform/zhghh5/dayofficework/healthCheckup/list/index.html")
@SaCheckPermission("h5.healthCheckup.list")
public void list() {
@@ -81,125 +62,42 @@ public class H5HealthCheckupController {
public Object pageData(PageForm pageForm,
String status,
String name) {
Sql sql = Sqls.create("""
select
*,
(select count(*) from health_checkup_user_selection s where s.projectId=p.id and s.selectUserId=@userId) as selectCount
from
health_checkup_project p
$condition
""");
sql.setParam("userId", SecurityUtil.getUserId());
Cnd cnd = Cnd.NEW();
// 根据 status 参数筛选进行中或已结束的项目
if ("ongoing".equals(status)) {
// 进行中的项目:结束时间大于等于当前时间
cnd.and("p.choiceTimeEnd", ">=", DateUtil.now());
} else if ("finished".equals(status)) {
// 已结束的项目:结束时间小于当前时间
cnd.and("p.choiceTimeEnd", "<", DateUtil.now());
}
cnd.and(new Static("(select count(*) from health_checkup_user where projectId=p.id and userId='%s')>0".formatted(SecurityUtil.getUserId())));
// 只有当name参数不为空时才添加名称查询条件
if (StrUtil.isNotBlank(name)) {
cnd.and("p.name", "like", "%" + name + "%");
}
cnd.asc("selectCount");
sql.setCondition(cnd);
return Result.success(healthCheckupProjectService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql));
return applicationService.pageData(pageForm, status, name);
}
@At
@SaCheckPermission(value = {"h5.healthCheckup.list", "h5.healthCheckup.mine"}, mode = SaMode.OR)
public Object mineData(PageForm pageForm, String status) {
Sql sql = Sqls.create("""
SELECT
us.*,
p.name,
p.choiceTimeStart,
p.choiceTimeEnd,
p.cover,
sub.optionName
FROM
health_checkup_user_selection us
LEFT JOIN health_checkup_project p ON us.projectId = p.id
LEFT JOIN health_checkup_project_subject sub on sub.id=us.subjectId
$condition
""");
Cnd cnd = Cnd.NEW();
cnd.and("us.selectUserId", "=", SecurityUtil.getUserId());
// 根据 status 参数筛选进行中或已结束的项目
if ("ongoing".equals(status)) {
// 进行中的项目:结束时间大于等于当前时间
cnd.and("p.choiceTimeEnd", ">=", DateUtil.now());
} else if ("finished".equals(status)) {
// 已结束的项目:结束时间小于当前时间
cnd.and("p.choiceTimeEnd", "<", DateUtil.now());
}
sql.setCondition(cnd);
Pagination pagination = baseService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
return Result.success(pagination);
public Object mineData(PageForm pageForm, String status, String projectId) {
return applicationService.mineData(pageForm, status, projectId);
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission(value = {"h5.healthCheckup.list", "h5.healthCheckup.mine"}, mode = SaMode.OR)
public Result doSubmit(@Param("userSelection") HealthCheckupUserSelection userSelection) {
BigDecimal money = userSelection.getMoney();
if (money.compareTo(BigDecimal.ZERO) <= 0) { // 小于等于0
return Result.error("金额必须大于0,当前医院没有符合您的选项!");
}
if (StrUtil.isNotBlank(userSelection.getId())) {
baseService.dao().clearLinks(userSelection, "companionList");
baseService.dao().delete(HealthCheckupUserSelection.class, userSelection.getId());
userSelection.setId(null);
}
userSelection.setSelectUserId(SecurityUtil.getUserId());
userSelection.setSelectTime(new Date());
baseService.dao().insertWith(userSelection, "companionList");
return Result.success("提交成功");
public Result doSubmit(@Param("userSelection") HealthCheckupUserSelection userSelection,
@Param("baseInfo") HealthCheckupUser baseInfo) {
// userSelection 为医院选择JSON,baseInfo 为可选个人信息JSON;返回 Result 表示整体保存结果。
return applicationService.submitWithBaseInfo(userSelection, baseInfo);
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission(value = {"h5.healthCheckup.list", "h5.healthCheckup.mine"}, mode = SaMode.OR)
public Result cancel(String id, String projectId) {
HealthCheckupProject checkupProject = baseService.dao().fetch(HealthCheckupProject.class, projectId);
if (DateUtil.compare(new Date(), checkupProject.getChoiceTimeEnd()) > 0) {
return Result.error("抱歉,当前时间已经不能取消");
}
HealthCheckupUserSelection userSelection = baseService.dao().fetch(HealthCheckupUserSelection.class, id);
baseService.dao().clearLinks(userSelection, "companionList");
baseService.dao().delete(HealthCheckupUserSelection.class, userSelection.getId());
return Result.success("取消成功");
return applicationService.cancel(id, projectId);
}
@At
@SaCheckPermission(value = {"h5.healthCheckup.list", "h5.healthCheckup.mine"}, mode = SaMode.OR)
public Object getCampus() {
return Result.success(baseService.dao().query(HealthCheckupCampus.class, Cnd.NEW().asc("campusCode")));
return applicationService.getCampus();
}
@At
@SaCheckPermission(value = {"h5.healthCheckup.list", "h5.healthCheckup.mine"}, mode = SaMode.OR)
public Object selectInfo(String projectId) {
HealthCheckupUserSelection userSelection = healthCheckupProjectService.dao().fetch(HealthCheckupUserSelection.class,
Cnd.where("projectId", "=", projectId).and("selectUserId", "=", SecurityUtil.getUserId()));
if (userSelection != null) {
baseService.dao().fetchLinks(userSelection, "companionList");
NutMap nutMap = Lang.obj2map(userSelection, NutMap.class);
if (userSelection.getCompanionList().size() > 0) {
nutMap.put("isFamily", "是");
} else {
nutMap.put("isFamily", "否");
}
return Result.success(nutMap);
}
return Result.success();
return applicationService.selectInfo(projectId);
}
// @At
@@ -217,25 +115,53 @@ public class H5HealthCheckupController {
@At
@SaCheckPermission(value = {"h5.healthCheckup.list", "h5.healthCheckup.mine"}, mode = SaMode.OR)
public Result getSubjectMoney(String id, String marriage) {
List<HealthCheckupProjectSubjectMoney> list = healthCheckupProjectService.dao().query(HealthCheckupProjectSubjectMoney.class, Cnd.where(HealthCheckupProjectSubjectMoney::getSubjectId, "=", id));
BigDecimal money = BigDecimal.valueOf(0.00);
String subjectMoneyId = "";
for (HealthCheckupProjectSubjectMoney subject : list) {
// 是否满足条件
Cnd cnd = Cnd.where("id", "=", SecurityUtil.getUserId());
ConditionGroupUtil.applyConditionGroup(cnd, subject.getMatchCnd());
int count = dao.count(View_user.class, cnd);
if (count > 0) {
money = subject.getMoney();
subjectMoneyId = subject.getId();
break;
}
public Result getSubjectMoney(String id, String marriage, Integer age) {
// id 为医院选项ID,age/marriage 为当前表单值;返回 data.money 和 data.subjectMoneyId,仅用于预览。
return applicationService.getSubjectMoney(id, marriage, age);
}
return Result.success().addData(Map.of("money", money, "subjectMoneyId", subjectMoneyId));
@At("/listParticulars")
@Ok("beetl:/platform/zhghh5/dayofficework/healthCheckup/listParticulars/index.html")
@SaCheckPermission("h5.healthCheckup.list")
@ApiOperation("体检项目报名详情页面")
public void listParticulars(){}
@At("/userBaseMessage")
@Ok("beetl:/platform/zhghh5/dayofficework/healthCheckup/userBaseMessage/index.html")
@SaCheckPermission("h5.healthCheckup.list")
@ApiOperation("基础信息确认")
public void userBaseMessage(){}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("h5.healthCheckup.list")
@ApiOperation("获取基础信息数据")
public Result findUserBaseMessageData(HealthCheckupUser checkupUser) {
return applicationService.findUserBaseMessageData(checkupUser);
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("h5.healthCheckup.list")
@ApiOperation("基础信息提交")
public Result doSubmitUserBaseMessage(HealthCheckupUser checkupUser) {
return applicationService.doSubmitUserBaseMessage(checkupUser);
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("h5.healthCheckup.list")
@ApiOperation("上年度体检医院")
public Result findLastYearHospital(HealthCheckupUserSelection checkupUserSelection) {
return applicationService.findLastYearHospital(checkupUserSelection);
}
@At("/physicalExaminationList")
@Ok("beetl:/platform/zhghh5/dayofficework/healthCheckup/physicalExaminationList/index.html")
@SaCheckPermission("h5.healthCheckup.list")
public void physicalExaminationList() {
}
}
@@ -0,0 +1,36 @@
package com.budwk.app.zhgh.dayofficework.healthCheckup.model;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.*;
import org.nutz.dao.interceptor.annotation.PrevInsert;
/** 项目级实际体检统计,与教职工选择记录独立保存。 */
@Data
@EqualsAndHashCode(callSuper = true)
@Table("health_checkup_actual_statistics")
@TableMeta("{'mysql-charset':'utf8mb4'}")
@TableIndexes({@Index(name = "IDX_CHECKUP_ACTUAL_ITEM", fields = {"projectId", "subjectId"}, unique = true)})
@Comment("实际体检人数统计")
public class HealthCheckupActualStatistics extends BaseModel {
@Name
@PrevInsert(uu32 = true)
@ColDefine(type = ColType.VARCHAR, width = 32)
@Comment("主键")
private String id;
@Column
@ColDefine(type = ColType.VARCHAR, width = 32)
@Comment("体检项目ID")
private String projectId;
@Column
@ColDefine(type = ColType.VARCHAR, width = 32)
@Comment("医院选项ID;__total__表示本次体检总人数")
private String subjectId;
@Column
@Comment("人工录入的实际体检人数,非负整数")
private Integer actualCount;
}
@@ -0,0 +1,52 @@
package com.budwk.app.zhgh.dayofficework.healthCheckup.model;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.ColDefine;
import org.nutz.dao.entity.annotation.ColType;
import org.nutz.dao.entity.annotation.Column;
import org.nutz.dao.entity.annotation.Comment;
import org.nutz.dao.entity.annotation.Name;
import org.nutz.dao.entity.annotation.Table;
import org.nutz.dao.entity.annotation.TableMeta;
import org.nutz.dao.interceptor.annotation.PrevInsert;
import java.io.Serializable;
/**
* Health checkup feedback record.
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Table("health_checkup_feedback")
@TableMeta("{'mysql-charset':'utf8mb4'}")
@Comment("health checkup feedback")
public class HealthCheckupFeedback extends BaseModel implements Serializable {
@Name
@Comment("id")
@ColDefine(type = ColType.VARCHAR, width = 32)
@PrevInsert(uu32 = true)
private String id;
@Column
@Comment("year")
@ColDefine(type = ColType.VARCHAR, width = 4)
private String year;
@Column
@Comment("project id")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String projectId;
@Column
@Comment("project name")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String projectName;
@Column
@Comment("union id")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String unionId;
@Column
@Comment("union name")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String unionName;
@Column
@Comment("feedback info")
@ColDefine(type = ColType.TEXT)
private String feedbackInfo;
}
@@ -2,10 +2,13 @@ package com.budwk.app.zhgh.dayofficework.healthCheckup.model;
import cn.hutool.json.JSONObject;
import com.budwk.app.base.model.BaseModel;
import com.budwk.app.sys.models.Sys_home_activity;
import com.budwk.app.sys.services.SysHomeConvert;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.*;
import org.nutz.dao.interceptor.annotation.PrevInsert;
import org.nutz.lang.Lang;
import java.io.Serializable;
import java.util.Date;
@@ -22,7 +25,7 @@ import java.util.List;
@Table("health_checkup_project")
@TableMeta("{'mysql-charset':'utf8mb4'}")
@Comment("体检项目表")
public class HealthCheckupProject extends BaseModel implements Serializable {
public class HealthCheckupProject extends BaseModel implements Serializable, SysHomeConvert {
@Column
@Name
@@ -72,11 +75,33 @@ public class HealthCheckupProject extends BaseModel implements Serializable {
@ColDefine(type = ColType.VARCHAR, width = 200)
private String cover;
@Column
@Comment("温馨提示")
@ColDefine(type = ColType.TEXT)
private String friendlyReminderText;
@Many(field = "projectId")
private List<HealthCheckupProjectSubject> healthCheckupProjectSubjects;
@Column
@Comment("短信信息")
@ColDefine(type = ColType.TEXT)
private String smsMessage;
@Override
public Sys_home_activity covertToSysHomeActivity() {
Sys_home_activity sysHomeActivity = new Sys_home_activity();
sysHomeActivity.setId(this.getId());
sysHomeActivity.setName(this.getName());
sysHomeActivity.setCover(this.getCover());
sysHomeActivity.setH5Url("/platform/healthCheckup/h5/list?id=" + this.getId());
if (Lang.isNotEmpty(this.getChoiceTimeStart())) {
sysHomeActivity.setStartDate(this.getChoiceTimeStart());
sysHomeActivity.setEndDate(this.getChoiceTimeEnd());
}
sysHomeActivity.setEnable(true);
sysHomeActivity.setAllowUserSql("select userId from health_checkup_user where projectId = '" + this.getId() + "' and userId = @userId");
sysHomeActivity.setClassPath(HealthCheckupProject.class.getName());
return sysHomeActivity;
}
}
@@ -62,4 +62,10 @@ public class HealthCheckupProjectSubjectMoney extends BaseModel implements Seria
@Comment("选项排序")
@ColDefine(type = ColType.VARCHAR, width = 50)
private String sort;
@Column
@Comment("序号")
@ColDefine(type = ColType.INT)
private Integer serialNumber;
}
@@ -1,6 +1,7 @@
package com.budwk.app.zhgh.dayofficework.healthCheckup.model;
import com.budwk.app.base.model.BaseModel;
import com.budwk.app.sys.annotation.DataCenterColumn;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.*;
@@ -78,4 +79,58 @@ public class HealthCheckupUser extends BaseModel implements Serializable {
@ColDefine(type = ColType.VARCHAR, width = 30)
private String userState;
@Column
@Comment("手机号码")
@ColDefine(type = ColType.VARCHAR, width = 11)
private String phone;
/** 本项目本人填写的年龄;未填写时从系统出生日期计算。 */
@Column
@Comment("体检年龄")
@ColDefine(type = ColType.INT)
private Integer age;
@Column
@Comment("身份证号")
@ColDefine(type = ColType.VARCHAR, width = 18)
private String card;
@Column
@Comment("婚姻状况")
@ColDefine(type = ColType.VARCHAR, width = 20)
@DataCenterColumn(name = "婚姻状况", key = "hyztm", dict = "USER_MARRIAGE")
private String marriage;
@Column
@Comment("教职工类别")
@ColDefine(type = ColType.VARCHAR)
private String personType;
@Column
@Comment("确认状态(用户选择体检医院确认)")
// 确认状态保存单字符值,显式限定长度,避免自动迁移生成 CHAR(0) 导致默认值无效。
@ColDefine(type = ColType.CHAR, width = 1)
@Default("0")
private String confirmStatus;
@Column
@Comment("体检报告")
@ColDefine(type = ColType.TEXT)
private String medicalExamination;
@Column
@Comment("是否自聘")
@ColDefine(type = ColType.VARCHAR, width = 10)
@Default("否")
private String isEmployed;
@Column
@Comment("备注")
@ColDefine(type = ColType.TEXT)
private String bz;
@Column
@Comment("是否确认")
@ColDefine(type = ColType.BOOLEAN)
private Boolean isAudit;
}
@@ -0,0 +1,61 @@
package com.budwk.app.zhgh.dayofficework.healthCheckup.param;
import com.budwk.app.base.param.PageForm;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
@EqualsAndHashCode(callSuper = true)
@Data
@ApiModel("用户生成体检名单分页查询参数")
public class HealthCheckupFilterUserPageForm extends PageForm {
@ApiModelProperty("体检项目id")
private String projectId;
@ApiModelProperty("姓名")
private String userName;
@ApiModelProperty("工号")
private String loginName;
@ApiModelProperty("性别")
private String sex;
@ApiModelProperty("出生日期")
private Date birthday;
@ApiModelProperty("出生月份")
private String[] birthMonths;
@ApiModelProperty("工会id")
private String unionId;
@ApiModelProperty("工会名称")
private String unionName;
@ApiModelProperty("单位id")
private String[] unitIds;
@ApiModelProperty("单位名称")
private String unitName;
@ApiModelProperty("人员类型")
private String[] personTypes;
@ApiModelProperty("聘用方式")
private String[] preparedBys;
@ApiModelProperty("在职状态")
private String[] userStates;
@ApiModelProperty("是否会员")
private Boolean isMember;
@ApiModelProperty("是否缴费会员")
private Boolean memberFeePaid;
}
@@ -0,0 +1,396 @@
package com.budwk.app.zhgh.dayofficework.healthCheckup.service;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.base.utils.ConditionGroupUtil;
import com.budwk.app.sys.views.View_user;
import com.budwk.app.sys.services.SysUserService;
import org.nutz.dao.Chain;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.dayofficework.healthCheckup.model.*;
import com.budwk.app.zhgh.dayofficework.healthCheckup.service.HealthCheckupProjectService;
import org.apache.commons.lang3.StringUtils;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.dao.util.cri.Static;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Lang;
import org.nutz.lang.util.NutMap;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
/** 体检接口业务服务;HTTP 路由和权限校验保留在 controller。 */
@IocBean
public class H5HealthCheckupApplicationService {
@Inject
private Dao dao;
@Inject
private BaseService baseService;
@Inject
private HealthCheckupProjectService healthCheckupProjectService;
@Inject
private SysUserService sysUserService;
/**
* 获取项目列表;业务筛选、数据读写和结果组装在本服务内完成。
* @param pageForm 分页、搜索及排序参数
* @param status 进行中 ongoing 或已结束 finished
* @param name 体检项目名称筛选值
* @return Object;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
public Object pageData(PageForm pageForm,
String status,
String name) {
Sql sql = Sqls.create("""
select
*,
(select count(*) from health_checkup_user_selection s where s.projectId=p.id and s.selectUserId=@userId) as selectCount
from
health_checkup_project p
$condition
""");
sql.setParam("userId", SecurityUtil.getUserId());
Cnd cnd = Cnd.NEW();
// 根据 status 参数筛选进行中或已结束的项目
if ("ongoing".equals(status)) {
// 进行中的项目:结束时间大于等于当前时间
SqlExpressionGroup group = Cnd.NEW().where();
group.and("p.choiceTimeStart", "<=", DateUtil.now());
group.and("p.choiceTimeEnd", ">=", DateUtil.now());
cnd.and(group);
} else if ("finished".equals(status)) {
// 已结束的项目:结束时间小于当前时间
cnd.and("p.choiceTimeEnd", "<", DateUtil.now());
}
cnd.and(new Static("(select count(*) from health_checkup_user where projectId=p.id and userId='%s')>0".formatted(SecurityUtil.getUserId())));
// 只有当name参数不为空时才添加名称查询条件
if (StrUtil.isNotBlank(name)) {
cnd.and("p.name", "like", "%" + name + "%");
}
cnd.asc("selectCount");
sql.setCondition(cnd);
return Result.success(healthCheckupProjectService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql));
}
/**
* 读取体检业务数据;业务筛选、数据读写和结果组装在本服务内完成。
* @param pageForm 分页、搜索及排序参数
* @param status 进行中 ongoing 或已结束 finished
* @param projectId 当前体检项目主键
* @return Object;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
public Object mineData(PageForm pageForm, String status, String projectId) {
Sql sql = Sqls.create("""
SELECT
us.*,
p.name,
p.choiceTimeStart,
p.choiceTimeEnd,
p.cover,
sub.optionName
FROM
health_checkup_user_selection us
LEFT JOIN health_checkup_project p ON us.projectId = p.id
LEFT JOIN health_checkup_project_subject sub on sub.id=us.subjectId
$condition
""");
Cnd cnd = Cnd.NEW();
cnd.and("us.selectUserId", "=", SecurityUtil.getUserId());
cnd.andEX("us.projectId", "=", projectId);
cnd.orderBy("us.selectTime","desc");
// 根据 status 参数筛选进行中或已结束的项目
// if ("ongoing".equals(status)) {
// // 进行中的项目:结束时间大于等于当前时间
// cnd.and("p.choiceTimeEnd", ">=", DateUtil.now());
// } else if ("finished".equals(status)) {
// // 已结束的项目:结束时间小于当前时间
// cnd.and("p.choiceTimeEnd", "<", DateUtil.now());
// }
sql.setCondition(cnd);
Pagination pagination = baseService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
return Result.success(pagination);
}
/**
* 提交本人的体检医院及家属选择;业务筛选、数据读写和结果组装在本服务内完成。
* @param userSelection 本人选择的医院、套餐及家属信息
* @return Result;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
@org.nutz.ioc.aop.Aop(org.nutz.aop.interceptor.ioc.TransAop.READ_COMMITTED)
public Result doSubmit(HealthCheckupUserSelection userSelection) {
// 手机端只允许操作本人在当前项目的选择;时间窗口和金额不能仅依赖页面校验。
HealthCheckupProject project = dao.fetch(HealthCheckupProject.class, userSelection.getProjectId());
Date now = new Date();
if (project == null || project.getChoiceTimeStart() == null || project.getChoiceTimeEnd() == null
|| now.before(project.getChoiceTimeStart()) || now.after(project.getChoiceTimeEnd())) {
return Result.error("当前项目不在报名时间内");
}
HealthCheckupProjectSubject selectedSubject = dao.fetch(HealthCheckupProjectSubject.class, userSelection.getSubjectId());
if (selectedSubject == null || !project.getId().equals(selectedSubject.getProjectId())) {
return Result.error("请选择当前项目的医院");
}
BigDecimal money = BigDecimal.ZERO;
for (HealthCheckupProjectSubjectMoney option : dao.query(HealthCheckupProjectSubjectMoney.class,
Cnd.where("subjectId", "=", selectedSubject.getId()).asc("serialNumber"))) {
Cnd eligible = Cnd.where("id", "=", SecurityUtil.getUserId());
ConditionGroupUtil.applyConditionGroup(eligible, option.getMatchCnd());
if (matchesSubject(userSelection.getProjectId(), option)) {
money = option.getMoney();
userSelection.setSubjectMoneyId(option.getId());
userSelection.setMoney(money);
break;
}
}
if (money == null || money.compareTo(BigDecimal.ZERO) <= 0) { // 小于等于0
return Result.error("金额必须大于0,当前医院没有符合您的选项!");
}
HealthCheckupUser healthCheckupUser = baseService.dao().fetch(HealthCheckupUser.class,
Cnd.NEW().and("projectId", "=", userSelection.getProjectId())
.and("userId", "=", SecurityUtil.getUserId()));
if (ObjectUtil.isEmpty(healthCheckupUser)) {
return Result.error("请确认您的身份信息是否无误!");
} else {
if (StringUtils.isBlank(healthCheckupUser.getPhone())) {
return Result.error("基础信息中,\"手机号\" 不能为空!");
} else if (StringUtils.isBlank(healthCheckupUser.getCard())) {
return Result.error("基础信息中,\"身份证号码\" 不能为空!");
}
}
HealthCheckupUserSelection existing = dao.fetch(HealthCheckupUserSelection.class,
Cnd.where("projectId", "=", project.getId()).and("selectUserId", "=", SecurityUtil.getUserId()));
if (StrUtil.isNotBlank(userSelection.getId()) && (existing == null || !existing.getId().equals(userSelection.getId()))) {
return Result.error("选择记录不属于当前用户");
}
userSelection.setId(existing == null ? null : existing.getId());
if (StrUtil.isNotBlank(userSelection.getId())) {
baseService.dao().clearLinks(userSelection, "companionList");
baseService.dao().delete(HealthCheckupUserSelection.class, userSelection.getId());
userSelection.setId(null);
}
userSelection.setSelectUserId(SecurityUtil.getUserId());
userSelection.setSelectTime(new Date());
baseService.dao().insertWith(userSelection, "companionList");
return Result.success("提交成功");
}
/**
* 在同一事务内保存个人信息、同步档案并提交医院选择。
* @param userSelection 当前项目及医院、家属信息
* @param baseInfo 可选个人信息,仅 age、phone、card、marriage 生效;项目归属取选择记录
* @return Result,code=0 表示全部保存成功;失败抛异常回滚,避免部分保存
*/
@Aop(TransAop.READ_COMMITTED)
public Result submitWithBaseInfo(HealthCheckupUserSelection userSelection, HealthCheckupUser baseInfo) {
if (baseInfo != null) {
baseInfo.setProjectId(userSelection.getProjectId());
Result saved = doSubmitUserBaseMessage(baseInfo);
if (!saved.isSuccess()) throw new com.budwk.app.base.exception.BaseException(saved.getMsg());
}
Result submitted = doSubmit(userSelection);
if (!submitted.isSuccess()) throw new com.budwk.app.base.exception.BaseException(submitted.getMsg());
return submitted;
}
/**
* 取消本人当前项目的体检选择;业务筛选、数据读写和结果组装在本服务内完成。
* @param id 当前业务记录主键
* @param projectId 当前体检项目主键
* @return Result;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
@org.nutz.ioc.aop.Aop(org.nutz.aop.interceptor.ioc.TransAop.READ_COMMITTED)
public Result cancel(String id, String projectId) {
HealthCheckupProject checkupProject = baseService.dao().fetch(HealthCheckupProject.class, projectId);
if (checkupProject == null || checkupProject.getChoiceTimeEnd() == null || DateUtil.compare(new Date(), checkupProject.getChoiceTimeEnd()) > 0) {
return Result.error("抱歉,当前时间已经不能取消");
}
HealthCheckupUserSelection userSelection = baseService.dao().fetch(HealthCheckupUserSelection.class, id);
if (userSelection == null || !SecurityUtil.getUserId().equals(userSelection.getSelectUserId())
|| !projectId.equals(userSelection.getProjectId())) {
return Result.error("选择记录不存在或不属于当前用户");
}
baseService.dao().clearLinks(userSelection, "companionList");
baseService.dao().delete(HealthCheckupUserSelection.class, userSelection.getId());
return Result.success("取消成功");
}
/**
* 读取可选体检院区;业务筛选、数据读写和结果组装在本服务内完成。
* @return Object;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
public Object getCampus() {
return Result.success(baseService.dao().query(HealthCheckupCampus.class, Cnd.NEW().asc("campusCode")));
}
/**
* 读取本人当前项目的选择记录和家属明细;业务筛选、数据读写和结果组装在本服务内完成。
* @param projectId 当前体检项目主键
* @return Object;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
public Object selectInfo(String projectId) {
HealthCheckupUserSelection userSelection = healthCheckupProjectService.dao().fetch(HealthCheckupUserSelection.class,
Cnd.where("projectId", "=", projectId).and("selectUserId", "=", SecurityUtil.getUserId()));
if (userSelection != null) {
baseService.dao().fetchLinks(userSelection, "companionList");
NutMap nutMap = Lang.obj2map(userSelection, NutMap.class);
if (userSelection.getCompanionList().size() > 0) {
nutMap.put("isFamily", "是");
} else {
nutMap.put("isFamily", "否");
}
return Result.success(nutMap);
}
return Result.success();
}
/**
* 按本人档案条件匹配医院套餐额度;业务筛选、数据读写和结果组装在本服务内完成。
* @param id 当前业务记录主键
* @param marriage 当前表单婚姻状态;为空时使用已保存值
* @param age 当前表单年龄;为空时使用已保存值,仅预览不写入数据库
* @return Result;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
public Result getSubjectMoney(String id, String marriage, Integer age) {
if (age != null && (age < 0 || age > 120)) return Result.error("请输入0至120的整数年龄");
HealthCheckupProjectSubject hospital = dao.fetch(HealthCheckupProjectSubject.class, id);
if (hospital == null) return Result.error("请选择体检医院");
List<HealthCheckupProjectSubjectMoney> list = healthCheckupProjectService.dao().query(HealthCheckupProjectSubjectMoney.class, Cnd.where(HealthCheckupProjectSubjectMoney::getSubjectId, "=", id).asc("serialNumber"));
BigDecimal money = BigDecimal.valueOf(0.00);
String subjectMoneyId = "";
for (HealthCheckupProjectSubjectMoney subject : list) {
// 是否满足条件
Cnd cnd = Cnd.where("id", "=", SecurityUtil.getUserId());
ConditionGroupUtil.applyConditionGroup(cnd, subject.getMatchCnd());
int count = matchesSubject(hospital.getProjectId(), subject, age, marriage) ? 1 : 0;
if (count > 0) {
money = subject.getMoney();
subjectMoneyId = subject.getId();
break;
}
}
return Result.success().addData(Map.of("money", money, "subjectMoneyId", subjectMoneyId));
}
/**
* 获取基础信息数据;业务筛选、数据读写和结果组装在本服务内完成。
* @param checkupUser 体检人员基本信息
* @return Result;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
public Result findUserBaseMessageData(HealthCheckupUser checkupUser) {
checkupUser.setUserId(SecurityUtil.getUserId());
if(StringUtils.isNotBlank(checkupUser.getProjectId()) && StringUtils.isNotBlank(checkupUser.getUserId())){
Sql sql = Sqls.create("""
select cu.id,cu.projectId,cu.userId,cu.userName,cu.sex,
cu.loginName,cu.phone,cu.card,cu.marriage,
cu.personType, COALESCE(cu.age, TIMESTAMPDIFF(YEAR,u.birthday,CURDATE())) AS age
from health_checkup_user cu
left join sys_user u on cu.userId=u.id
$condition
""");
Cnd cnd = Cnd.NEW().and("cu.projectId", "=", checkupUser.getProjectId())
.and("cu.userId", "=", checkupUser.getUserId());
sql.setCondition(cnd);
NutMap nutMap = baseService.fetchMap(sql);
return Result.success(nutMap);
}
return Result.success();
}
/**
* 保存本人体检信息并同步系统档案;两处更新由同一事务提交。
* @param checkupUser projectId 为当前项目;age 为手填体检年龄,phone 为手机号,
* card 为身份证号,marriage 为已婚或未婚;其余客户端字段不用于更新
* @return Result;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
@org.nutz.ioc.aop.Aop(org.nutz.aop.interceptor.ioc.TransAop.READ_COMMITTED)
public Result doSubmitUserBaseMessage(HealthCheckupUser checkupUser) {
// 以登录身份定位本人记录,不使用客户端传入的用户ID或记录ID。
HealthCheckupUser own = dao.fetch(HealthCheckupUser.class,
Cnd.where("projectId", "=", checkupUser.getProjectId()).and("userId", "=", SecurityUtil.getUserId()));
if (own == null) return Result.error("当前项目中没有您的体检记录");
// 仅接受本人当前项目的四个可编辑字段,禁止覆盖姓名、性别、人员类别和归属。
if (checkupUser.getAge() == null || checkupUser.getAge() < 0 || checkupUser.getAge() > 120)
return Result.error("请输入0至120的整数年龄");
String phone = StrUtil.trim(checkupUser.getPhone());
String card = StrUtil.upperFirst(StrUtil.trim(checkupUser.getCard()));
if (phone == null || !phone.matches("1[0-9]{10}")) return Result.error("请输入正确的手机号码");
if (card == null || !cn.hutool.core.util.IdcardUtil.isValidCard(card)) return Result.error("请输入正确的身份证号");
if (!"已婚".equals(checkupUser.getMarriage()) && !"未婚".equals(checkupUser.getMarriage()))
return Result.error("请选择婚姻状态");
if (sysUserService.fetch(SecurityUtil.getUserId()) == null)
return Result.error("当前用户档案不存在");
own.setAge(checkupUser.getAge());
own.setPhone(phone);
own.setCard(card.toUpperCase(java.util.Locale.ROOT));
own.setMarriage(checkupUser.getMarriage());
own.setConfirmStatus("1");
dao.update(own);
// 系统年龄由出生日期计算,使用身份证解析日期,不以手填年龄反推生日。
// 仅同步允许修改的档案列,保留姓名、性别、人员类型及其他系统字段。
sysUserService.update(Chain.make("mobile", phone)
.add("idCard", own.getCard())
.add("marriage", own.getMarriage())
.add("birthday", cn.hutool.core.util.IdcardUtil.getBirthDate(own.getCard())),
Cnd.where("id", "=", SecurityUtil.getUserId()));
sysUserService.deleteCache(SecurityUtil.getUserId());
return Result.success();
}
/**
* 上年度体检医院;业务筛选、数据读写和结果组装在本服务内完成。
* @param checkupUserSelection 查询上一年度医院的人员条件
* @return Result;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
public Result findLastYearHospital(HealthCheckupUserSelection checkupUserSelection) {
checkupUserSelection.setSelectUserId(SecurityUtil.getUserId());
return Result.success("成功",healthCheckupProjectService.findLastYearHospital(checkupUserSelection));
}
/**
* 用当前项目本人信息匹配套餐条件,未填写年龄时沿用系统出生日期。
* @param projectId 体检项目ID
* @param option 当前医院套餐金额选项及条件组
* @return true 表示本人满足该选项条件;名单不存在时为 false
*/
private boolean matchesSubject(String projectId, HealthCheckupProjectSubjectMoney option) {
return matchesSubject(projectId, option, null, null);
}
/** 用表单年龄和婚姻状态预览匹配;仅这两个字段可覆盖,其余信息取本人档案。 */
private boolean matchesSubject(String projectId, HealthCheckupProjectSubjectMoney option, Integer age, String marriage) {
Sql sql = Sqls.create("""
SELECT COUNT(*) AS matched FROM (
SELECT u.id, cu.userName AS username, cu.loginName AS loginname, cu.sex,
COALESCE(@age,cu.age,TIMESTAMPDIFF(YEAR,u.birthday,CURDATE())) AS age,
COALESCE(@marriage,cu.marriage) AS marriage, u.userState, u.preparedBy, cu.personType
FROM health_checkup_user cu JOIN vw_user u ON u.id=cu.userId
WHERE cu.projectId=@projectId AND cu.userId=@userId
) candidate $condition
""");
sql.setParam("age", age);
sql.setParam("marriage", StrUtil.blankToDefault(marriage, null));
sql.setParam("projectId", projectId);
sql.setParam("userId", SecurityUtil.getUserId());
Cnd condition = Cnd.NEW();
ConditionGroupUtil.applyConditionGroup(condition, option.getMatchCnd());
sql.setCondition(condition);
return baseService.fetchMap(sql).getInt("matched") > 0;
}
}
@@ -0,0 +1,108 @@
package com.budwk.app.zhgh.dayofficework.healthCheckup.service;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.dayofficework.healthCheckup.model.HealthCheckupCampus;
import org.nutz.dao.Cnd;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import java.util.List;
/** 体检接口业务服务;HTTP 路由和权限校验保留在 controller。 */
@IocBean
public class HealthCheckupCampusApplicationService {
@Inject
private BaseService baseService;
/**
* 分页查询;业务筛选、数据读写和结果组装在本服务内完成。
* @param pageForm 分页、搜索及排序参数
* @param campusName 院区名称筛选值
* @return Result;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
public Result pageData(PageForm pageForm, String campusName) {
Cnd cnd = Cnd.NEW();
Sql sql = Sqls.create("select * from health_checkup_campus $condition");
cnd.and(Cnd.likeEX("campusName", campusName));
cnd.asc("campusCode");
sql.setCondition(cnd);
return Result.success(baseService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql));
}
/**
* 添加院区;业务筛选、数据读写和结果组装在本服务内完成。
* @param campus 体检院区主键
* @return Result;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
@org.nutz.ioc.aop.Aop(org.nutz.aop.interceptor.ioc.TransAop.READ_COMMITTED)
public Result doAdd(HealthCheckupCampus campus) {
try {
int count = baseService.dao().count(HealthCheckupCampus.class, Cnd.where(HealthCheckupCampus::getCampusCode, "=", campus.getCampusCode()));
if (count > 0) {
return Result.error("编码重复");
}
baseService.dao().insert(campus);
return Result.success();
} catch (Exception e) {
e.printStackTrace();
return Result.error();
}
}
/**
* 编辑院区;业务筛选、数据读写和结果组装在本服务内完成。
* @param campus 体检院区主键
* @return Result;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
@org.nutz.ioc.aop.Aop(org.nutz.aop.interceptor.ioc.TransAop.READ_COMMITTED)
public Result doEdit(HealthCheckupCampus campus) {
try {
int count = baseService.dao().count(HealthCheckupCampus.class,
Cnd.where(HealthCheckupCampus::getCampusCode, "=", campus.getCampusCode())
.and(HealthCheckupCampus::getId, "!=", campus.getId()));
if (count > 0) {
return Result.error("编码重复");
}
baseService.dao().update(campus);
return Result.success();
} catch (Exception e) {
e.printStackTrace();
return Result.error();
}
}
/**
* 编辑院区;业务筛选、数据读写和结果组装在本服务内完成。
* @param id 当前业务记录主键
* @return Result;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
@org.nutz.ioc.aop.Aop(org.nutz.aop.interceptor.ioc.TransAop.READ_COMMITTED)
public Result doDelete(String id) {
try {
baseService.dao().clear(HealthCheckupCampus.class, Cnd.where(HealthCheckupCampus::getId, "=", id));
return Result.success();
} catch (Exception e) {
e.printStackTrace();
return Result.error();
}
}
/**
* 查询所有院区;业务筛选、数据读写和结果组装在本服务内完成。
* @return Result;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
public Result queryCampus() {
try {
List<HealthCheckupCampus> campusList = baseService.dao().query(HealthCheckupCampus.class, Cnd.NEW().asc("campusCode"));
return Result.success(campusList);
} catch (Exception e) {
e.printStackTrace();
return Result.error();
}
}
}
@@ -0,0 +1,27 @@
package com.budwk.app.zhgh.dayofficework.healthCheckup.service;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.dayofficework.healthCheckup.model.HealthCheckupFeedback;
import org.nutz.lang.util.NutMap;
import java.util.List;
public interface HealthCheckupFeedbackService extends BaseService<HealthCheckupFeedback> {
/**
* Query feedback records by the selected filters on the list page.
*/
List<NutMap> listFeedback(String year, String projectId, String unionId);
/**
* Save a feedback record and bind it to the selected year, project and union.
*/
NutMap saveFeedback(String year, String projectId, String unionId, String feedbackInfo);
/**
* 删除单条体检反馈。
*/
NutMap deleteFeedback(String id);
NutMap deleteFeedbackBatch(String[] ids);
/**
* 查询问题反馈列表页数据。
*/
Pagination listFeedbackPage(Integer pageNumber, Integer pageSize, String year, String projectId, String unionId);
}
@@ -0,0 +1,835 @@
package com.budwk.app.zhgh.dayofficework.healthCheckup.service;
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.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.model.ExcelImportRes;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.utils.CommonDownloadUtil;
import com.budwk.app.base.utils.ConditionGroupUtil;
import com.budwk.app.base.utils.easyexcel.EasyExcelUtil;
import com.budwk.app.sys.views.View_user;
import com.budwk.app.web.commons.auth.utils.AuthUtil;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.activity.basic.models.ActivityUserScope;
import com.budwk.app.zhgh.dayofficework.healthCheckup.model.*;
import com.budwk.app.zhgh.dayofficework.healthCheckup.param.HealthCheckupFilterUserPageForm;
import java.util.Set;
import java.util.HashSet;
import com.budwk.app.zhgh.dayofficework.healthCheckup.service.HealthCheckupFeedbackService;
import com.budwk.app.zhgh.dayofficework.healthCheckup.service.HealthCheckupProjectService;
import com.budwk.app.zhgh.dayofficework.healthCheckup.template.HealthCheckupImportTemp;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.formula.functions.T;
import org.apache.poi.ss.usermodel.Workbook;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Lang;
import org.nutz.lang.Strings;
import org.nutz.lang.util.NutMap;
import org.nutz.mvc.upload.TempFile;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
/** 体检接口业务服务;HTTP 路由和权限校验保留在 controller。 */
@IocBean
public class HealthCheckupListApplicationService {
@Inject
private Dao dao;
@Inject
private HealthCheckupProjectService healthCheckupProjectService;
@Inject
private HealthCheckupFeedbackService healthCheckupFeedbackService;
/**
* 查询尚未加入当前体检项目的人员;查询与提交共用筛选条件。
* @param pageForm 项目 ID、姓名/工号、人员属性、工会单位和分页排序参数,空筛选项表示不限
* @return Result,code=0 时 data.list 为人员列表,data.totalCount 为匹配总人数
*/
public Result candidatePageData(HealthCheckupFilterUserPageForm pageForm) {
Result invalid = validateAddUsers(pageForm);
if (invalid != null) return invalid;
return Result.success(healthCheckupProjectService.listPageMap(
pageForm.getPageNumber(), pageForm.getPageSize(), candidateSql(pageForm)));
}
/**
* 将全部匹配人员加入体检名单,并补齐项目参与范围;两个写入操作必须一起提交。
* @param pageForm 项目 ID 和人员筛选条件,分页参数仅用于预览,不限制实际添加数量
* @return Result,code=0 表示成功,msg 返回实际新增人数;已有名单和范围均不重复插入
*/
@Aop(TransAop.READ_COMMITTED)
public Result addUsers(HealthCheckupFilterUserPageForm pageForm) {
Result invalid = validateAddUsers(pageForm);
if (invalid != null) return invalid;
HealthCheckupProject project = dao.fetch(HealthCheckupProject.class, pageForm.getProjectId());
// 同一范围的添加请求锁定相同范围记录,避免并发重复写入名单及范围。
ActivityUserScope group = dao.fetch(ActivityUserScope.class,
Cnd.where("groupId", "=", project.getActivityGroupId()).asc("id"));
if (group == null) return Result.error("参与人员范围已变更,请刷新后重试");
List<NutMap> locked = healthCheckupProjectService.listMap(Sqls.create(
"select id from activity_user_scope where id=@id for update").setParam("id", group.getId()));
if (locked.isEmpty()) return Result.error("参与人员范围已变更,请刷新后重试");
List<NutMap> users = healthCheckupProjectService.listMap(candidateSql(pageForm));
Set<String> scopeUserIds = dao.query(ActivityUserScope.class,
Cnd.where("groupId", "=", project.getActivityGroupId())).stream()
.map(ActivityUserScope::getUserId).collect(Collectors.toSet());
Set<String> added = new HashSet<>();
for (NutMap user : users) {
String userId = user.getString("id");
if (!added.add(userId)) continue;
HealthCheckupUser entry = new HealthCheckupUser();
entry.setProjectId(project.getId());
entry.setUserId(userId);
entry.setLoginName(user.getString("loginname"));
entry.setUserName(user.getString("username"));
entry.setSex(user.getString("sex"));
entry.setPhone(user.getString("mobile"));
entry.setCard(user.getString("idCard"));
entry.setMarriage(user.getString("marriage"));
entry.setPersonType(user.getString("personType"));
entry.setUserState(user.getString("userState"));
entry.setUnionId(user.getString("unionId"));
entry.setUnionName(user.getString("unionName"));
entry.setUnitId(user.getString("unitId"));
entry.setUnitName(user.getString("unitName"));
dao.insert(entry);
// 范围已有人时只加名单;缺失时补入,避免后续更新名单将新增人员移除。
if (scopeUserIds.add(userId)) {
ActivityUserScope scope = new ActivityUserScope();
scope.setGroupId(group.getGroupId());
scope.setGroupName(group.getGroupName());
scope.setUserId(userId);
scope.setCreator(SecurityUtil.getUserId());
dao.insert(scope);
}
}
return Result.success("成功添加" + added.size() + "人,参与人员范围已同步");
}
/** 校验导入名单同等角色、目标项目和实际存在的参与范围;返回 null 表示通过。 */
private Result validateAddUsers(HealthCheckupFilterUserPageForm pageForm) {
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name(),
RoleConstant.SCHOOL_UNION_CHAIRMAN.name())) {
return Result.error("您没有【添加人员】的权限");
}
if (pageForm == null || StrUtil.isBlank(pageForm.getProjectId())) return Result.error("请先选择体检项目");
HealthCheckupProject project = dao.fetch(HealthCheckupProject.class, pageForm.getProjectId());
if (project == null) return Result.error("体检项目不存在");
if (project.getActivityGroupId() == null || dao.count(ActivityUserScope.class,
Cnd.where("groupId", "=", project.getActivityGroupId()).and("groupName", "is not", null)) == 0) {
return Result.error("项目未关联有效参与人员范围,请先设置参与人员范围");
}
return null;
}
/** 统一预览与提交的候选条件;项目限定和名单排除不能随可选筛选项省略。 */
private Sql candidateSql(HealthCheckupFilterUserPageForm form) {
Sql sql = Sqls.create("""
select u.* from vw_user u
left join sys_user su on su.id=u.id
$condition
""");
Cnd cnd = Cnd.NEW();
cnd.and("u.id", "not in", Sqls.create(
"select userId from health_checkup_user where projectId=@projectId and userId is not null")
.setParam("projectId", form.getProjectId()));
if (StrUtil.isNotBlank(form.getSearchKeyword())) {
SqlExpressionGroup search = new SqlExpressionGroup();
search.orLike("u.username", form.getSearchKeyword());
search.orLike("u.loginname", form.getSearchKeyword());
cnd.and(search);
}
cnd.andEX("u.sex", "=", form.getSex());
cnd.andEX("u.birthday", "=", form.getBirthday());
cnd.andEX("MONTH(u.birthday)", "in", form.getBirthMonths());
cnd.andEX("u.unionId", "=", form.getUnionId());
cnd.andEX("u.unitId", "in", form.getUnitIds());
cnd.andEX("u.personType", "in", form.getPersonTypes());
cnd.andEX("u.preparedBy", "in", form.getPreparedBys());
cnd.andEX("u.userState", "in", form.getUserStates());
cnd.andEX("u.member", "=", form.getIsMember());
cnd.andEX("IFNULL(su.memberFeePaid, 0)", "=", form.getMemberFeePaid());
// 排序字段仅接受弹窗展示列,禁止将客户端任意表达式拼入 SQL。
Set<String> sortFields = Set.of("loginname", "username", "sex", "birthday", "personType",
"preparedBy", "userState", "unionName", "unitName");
if (form.getPageOrderName() != null && sortFields.contains(form.getPageOrderName())) {
cnd.orderBy("u." + form.getPageOrderName(), "ascending".equals(form.getPageOrderBy()) ? "ASC" : "DESC");
} else {
cnd.asc("u.unionCode").asc("u.unitCode");
}
cnd.asc("u.id");
sql.setCondition(cnd);
return sql;
}
/**
* 分页查询;业务筛选、数据读写和结果组装在本服务内完成。
* @param pageForm 分页、搜索及排序参数
* @param projectId 当前体检项目主键
* @param subjectId 体检医院选项主键
* @param unionId 所属分工会主键,可空时按接口范围处理
* @param unitId 所属单位主键筛选值
* @param physicalExaminationStatus 1 已选、0 未选、2 全部
* @param isAudit 确认状态:1 已确认,0 未确认
* @return Result;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
public Result pageData(PageForm pageForm, String projectId, String subjectId,
String unionId, String unitId, String physicalExaminationStatus, String isAudit) {
List<HealthCheckupUnionConfirm> confirms = dao.query(HealthCheckupUnionConfirm.class, Cnd.where("projectId", "=", projectId));
HealthCheckupProject project = healthCheckupProjectService.findOne(projectId);
Cnd cnd = Cnd.NEW();
Sql sql = Sqls.create("""
SELECT
hcu.id AS headId,
hcu.userId AS id,
hcu.loginName,
hcu.userName,
hcu.sex,
hcu.unionId,
hcu.unionName,
hcu.unitName,
hcus.selectTime,
hcus.campus,
hcus.subjectId,
hcu.bz,
hcu.isAudit,
hcus.receiveAddress,
ca.campusName
FROM
health_checkup_user hcu
LEFT JOIN health_checkup_user_selection hcus ON hcu.userId = hcus.selectUserId
AND hcus.projectId=@projectId
LEFT JOIN health_checkup_campus ca on ca.id=hcus.campus
$condition
""").setParam("projectId", projectId);
sql.setCondition(cnd);
cnd.andEX("hcu.projectId", "=", projectId);
cnd.andEX("hcu.unionId", "=", unionId);
cnd.andEX("hcu.unitId", "=", unitId);
cnd.andEX("hcus.subjectId", "=", subjectId);
if(StringUtils.isNotBlank(isAudit)){
if(isAudit.equals("1")){
cnd.and("hcu.isAudit", "=", "1");
} else if(isAudit.equals("0")){
SqlExpressionGroup group = Cnd.NEW().where();
group.andEX("hcu.isAudit", "=", "0");
group.orIsNull("hcu.isAudit");
cnd.and(group);
}
}
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) {
cnd.andEX("hcu.unionId", "=", SecurityUtil.getUnionId());
}
//体检状态
if(StringUtils.isNotBlank(physicalExaminationStatus) && !physicalExaminationStatus.equals("2")){
if(physicalExaminationStatus.equals("1")) cnd.where().andNotIsNull("hcus.selectTime");
if(physicalExaminationStatus.equals("0")) cnd.where().andIsNull("hcus.selectTime");
}
if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
SqlExpressionGroup group = new SqlExpressionGroup();
group.orLike("hcu.userName", pageForm.getSearchKeyword());
group.orLike("hcu.loginName", pageForm.getSearchKeyword());
cnd.and(group);
}
cnd.groupBy("hcu.userId");
if (StrUtil.isNotBlank(pageForm.getPageOrderName()) && Strings.isNotBlank(pageForm.getPageOrderBy())) {
cnd.orderBy(pageForm.getPageOrderName(), "ascending".equalsIgnoreCase(pageForm.getPageOrderBy()) ? "ASC" : "DESC");
} else {
cnd.desc("hcus.selectTime").asc("hcu.unitId");
}
cnd.desc("hcu.id");
Pagination pagination = healthCheckupProjectService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
List<NutMap> paginationList = pagination.getList();
paginationList.forEach(v -> {
if (StrUtil.isNotBlank(v.getString("subjectId"))) {
List<HealthCheckupProjectSubject> subjects = project.getHealthCheckupProjectSubjects();
HealthCheckupProjectSubject subject = subjects.stream().filter(s -> s.getId().equals(v.getString("subjectId"))).findFirst().orElse(null);
v.setv("optionName", subject.getOptionName());
}
if (StrUtil.isNotBlank(v.getString("unionId")) && ObjectUtil.isNotEmpty(v.getBoolean("isAudit")) && v.getBoolean("isAudit")) {
HealthCheckupUnionConfirm confirm = confirms.stream()
.filter(o -> o.getUnionId().equals(v.getString("unionId")))
.findFirst()
.orElse(null);
// v.setv("isAudit", confirm != null ? confirm.getIsAudit() : "");
v.setv("auditTime", confirm != null ? confirm.getAuditTime() : "");
}
});
return Result.success(pagination);
}
/**
* 体检名单确认;业务筛选、数据读写和结果组装在本服务内完成。
* @param projectId 当前体检项目主键
* @param unionId 所属分工会主键,可空时按接口范围处理
* @param flag 确认或取消、已选或未选的业务标志
* @return Result;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
@org.nutz.ioc.aop.Aop(org.nutz.aop.interceptor.ioc.TransAop.READ_COMMITTED)
public Result doAuditUser(String projectId,
String unionId,
Boolean flag) {
if (StrUtil.isBlank(unionId)) {
unionId = SecurityUtil.getUnionId();
}
if (flag) {
dao.update(HealthCheckupUnionConfirm.class, Chain.make("isAudit", true)
.add("auditTime", DateUtil.date())
.add("auditUser", SecurityUtil.getUserId()),
Cnd.where("projectId", "=", projectId).and("unionId", "=", unionId));
dao.update(HealthCheckupUser.class, Chain.make("isAudit", true),
Cnd.where("projectId", "=", projectId).and("unionId", "=", unionId));
} else {
dao.update(HealthCheckupUnionConfirm.class, Chain.make("isAudit", null)
.add("auditTime", null)
.add("auditUser", null),
Cnd.where("projectId", "=", projectId).and("unionId", "=", unionId));
dao.update(HealthCheckupUser.class, Chain.make("isAudit", null),
Cnd.where("projectId", "=", projectId).and("unionId", "=", unionId));
}
return Result.success();
}
/**
* 体检名单管理员编辑选择记录;业务筛选、数据读写和结果组装在本服务内完成。
* @param subjectId 体检医院选项主键
* @param campus 体检院区主键
* @param projectId 当前体检项目主键
* @param userId 教职工主键
* @param bz 选择记录备注
* @return Result;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
@org.nutz.ioc.aop.Aop(org.nutz.aop.interceptor.ioc.TransAop.READ_COMMITTED)
public Result doEditHealthCheckupData(String subjectId, String campus, String projectId, String userId, String bz) {
if(!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(),"HEALTH_ADMIN")) {
return Result.error("您没有操作此按钮的权限");
}
//查询用户符合哪个套餐
List<HealthCheckupProjectSubjectMoney> list = healthCheckupProjectService.dao().query(HealthCheckupProjectSubjectMoney.class,
Cnd.where(HealthCheckupProjectSubjectMoney::getSubjectId, "=", subjectId));
BigDecimal money = BigDecimal.valueOf(0.00);
String subjectMoneyId = "";
for (HealthCheckupProjectSubjectMoney subject : list) {
// 是否满足条件
Cnd cnd = Cnd.where("id", "=", userId);
ConditionGroupUtil.applyConditionGroup(cnd, subject.getMatchCnd());
int count = dao.count(View_user.class, cnd);
if (count > 0) {
money = subject.getMoney();
subjectMoneyId = subject.getId();
break;
}
}
//查询用户有没有选择过套餐
HealthCheckupUserSelection userSelection = healthCheckupProjectService.dao().fetch(HealthCheckupUserSelection.class,
Cnd.where("projectId", "=", projectId)
.and("selectUserId", "=", userId));
//如果选择过套餐就修改选择的套餐id
if (Lang.isNotEmpty(userSelection)) {
userSelection.setSubjectId(subjectId);
userSelection.setCampus(campus);
userSelection.setBz(bz);
userSelection.setSubjectMoneyId(subjectMoneyId);
userSelection.setMoney(money);
healthCheckupProjectService.update(userSelection);
} else {
//如果没有选择过套餐添加一条记录
HealthCheckupUserSelection checkupUserSelection = new HealthCheckupUserSelection();
checkupUserSelection.setSelectUserId(userId);
checkupUserSelection.setProjectId(projectId);
checkupUserSelection.setSubjectId(subjectId);
checkupUserSelection.setCampus(campus);
checkupUserSelection.setSelectTime(new Date());
checkupUserSelection.setBz(bz);
checkupUserSelection.setSubjectMoneyId(subjectMoneyId);
checkupUserSelection.setMoney(money);
healthCheckupProjectService.insert(checkupUserSelection);
}
return Result.success();
}
/**
* 体检名单导入名单;业务筛选、数据读写和结果组装在本服务内完成。
* @param file 上传的 Excel 临时文件
* @param healthProjectId 导入目标体检项目主键
* @return Result;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
@org.nutz.ioc.aop.Aop(org.nutz.aop.interceptor.ioc.TransAop.READ_COMMITTED)
public Result healthImport(TempFile file, String healthProjectId) {
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(),
RoleConstant.SCHOOL_UNION_ADMIN.name(),
RoleConstant.SCHOOL_UNION_CHAIRMAN.name())) {
return Result.error("您没有【导入名单】的权限");
} else {
// 读取数据
List<T> ts = EasyExcelUtil.syncReadModel(file.getFile(), HealthCheckupImportTemp.class, 0, 1);
List<HealthCheckupImportTemp> healthCheckupImportTemps = JSONUtil.parseArray(JSONUtil.toJsonStr(ts)).toList(HealthCheckupImportTemp.class);
List<String> loginNames = healthCheckupImportTemps.stream().filter(v -> Strings.isNotBlank(v.getLoginName())).map(v -> v.getLoginName()).collect(Collectors.toList());
//全部的体检名单
List<HealthCheckupUser> healthCheckupUserList = dao.query(HealthCheckupUser.class, Cnd.where(HealthCheckupUser::getProjectId, "=", healthProjectId));
List<View_user> userList = dao.query(View_user.class, Cnd.where(View_user::getLoginname, "in", loginNames));
HealthCheckupProject checkupProject = dao.fetch(HealthCheckupProject.class, healthProjectId);
ActivityUserScope userScope = dao.fetch(ActivityUserScope.class, Cnd.where("groupId", "=", checkupProject.getActivityGroupId()));
List<ActivityUserScope> userScopeList = new ArrayList<>();
List<HealthCheckupUser> healthCheckupUsersToAdd = new ArrayList<>();
int successCount = 0;
for (int i = 0; i < healthCheckupImportTemps.size(); i++) {
HealthCheckupImportTemp excel = healthCheckupImportTemps.get(i);
if (StrUtil.isBlank(excel.getLoginName())) {
excel.setErrInfo("工号为空", i + 1);
continue;
}
// 不能在excel里重复
if (healthCheckupImportTemps.stream().filter(s -> s.getLoginName().equals(excel.getLoginName())).count() > 1) {
excel.setErrInfo("重复数据", i + 1);
continue;
}
View_user user = userList.stream().filter(s -> s.getLoginname().equals(excel.getLoginName())).findFirst().orElse(null);
if (user == null) {
excel.setErrInfo("无此用户:" + excel.getLoginName(), i + 1);
continue;
}
String projectName = checkupProject.getName();
if(projectName.contains("在职教职工")) {
projectName = "在职教职工";
}
if(projectName.contains("校聘高知")) {
projectName = "校聘高知";
}
if (healthCheckupUserList.stream().anyMatch(s -> s.getUserId().equals(user.getId()))) {
Chain chain = Chain.make("personType", projectName);
if(StringUtils.isNotBlank(excel.getPhone())){
chain.add("phone", excel.getPhone());
}
dao.update(
HealthCheckupUser.class,chain,
Cnd.NEW().and(HealthCheckupUser::getUserId, "=", user.getId())
.and(HealthCheckupUser::getProjectId, "=", healthProjectId)
);
} else {
ActivityUserScope s = new ActivityUserScope();
s.setGroupId(userScope.getGroupId());
s.setGroupName(userScope.getGroupName());
s.setUserId(user.getId());
s.setCreator(SecurityUtil.getUserId());
userScopeList.add(s);
HealthCheckupUser newHealthUser = new HealthCheckupUser();
newHealthUser.setProjectId(healthProjectId);
newHealthUser.setUserId(user.getId());
newHealthUser.setUserName(user.getUsername());
newHealthUser.setSex(user.getSex());
newHealthUser.setLoginName(user.getLoginname());
newHealthUser.setUnionName(user.getUnionName());
newHealthUser.setUnionId(user.getUnionId());
newHealthUser.setUnitName(user.getUnitName());
newHealthUser.setCard(user.getIdCard());
newHealthUser.setPhone(excel.getPhone());
newHealthUser.setMarriage(user.getMarriage());
newHealthUser.setPersonType(projectName);
healthCheckupUsersToAdd.add(newHealthUser);
successCount++;
}
}
dao.insert(userScopeList);
dao.insert(healthCheckupUsersToAdd);
// 创建结果集
ExcelImportRes<HealthCheckupImportTemp> excelImportRes = new ExcelImportRes<>();
excelImportRes.setTotalRecords(healthCheckupImportTemps.size());
// 添加错误记录
excelImportRes.setErrorDetails(healthCheckupImportTemps.stream().filter(s -> StrUtil.isNotBlank(s.getErrMsg())).collect(Collectors.toList()));
excelImportRes.setFailedCount(excelImportRes.getErrorDetails().size());
excelImportRes.setSuccessCount(successCount);
return Result.success(excelImportRes);
}
}
/**
* 下载体检参加人员导入模版;业务筛选、数据读写和结果组装在本服务内完成。
* @param response 接收 Excel 文件流的 HTTP 响应
* 文件内容直接写入 response,不返回 JSON。
*/
@org.nutz.ioc.aop.Aop(org.nutz.aop.interceptor.ioc.TransAop.READ_COMMITTED)
public void downloadTem(HttpServletResponse response) {
List<ExcelExportEntity> entities = new ArrayList<>();
entities.add(new ExcelExportEntity("工号", "loginName", 20));
entities.add(new ExcelExportEntity("姓名", "userName", 20));
entities.add(new ExcelExportEntity("手机号", "phone", 20));
ExportParams exportParams = new ExportParams();
exportParams.setType(ExcelType.XSSF);
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entities, Collections.emptyList());
CommonDownloadUtil.download("参加人员导入模版.xlsx", workbook, response);
}
/**
* 体检名单删除;业务筛选、数据读写和结果组装在本服务内完成。
* @param id 当前业务记录主键
* @return Result;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
@org.nutz.ioc.aop.Aop(org.nutz.aop.interceptor.ioc.TransAop.READ_COMMITTED)
public Result doDelete(String id) {
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(),
RoleConstant.SCHOOL_UNION_ADMIN.name(),
RoleConstant.SCHOOL_UNION_CHAIRMAN.name(),
RoleConstant.BRANCH_UNION_ADMIN.name(),
RoleConstant.BRANCH_UNION_CHAIRMAN.name())) {
return Result.error("您没有【删除】的权限");
} else {
HealthCheckupUser fetch = dao.fetch(HealthCheckupUser.class, Cnd.where(HealthCheckupUser::getId, "=", id));
Cnd and = Cnd.NEW().and(HealthCheckupUserSelection::getProjectId, "=", fetch.getProjectId())
.and(HealthCheckupUserSelection::getSelectUserId, "=", fetch.getUserId());
dao.clear(HealthCheckupUserSelection.class, and);
dao.clear(HealthCheckupUser.class, Cnd.where(HealthCheckupUser::getId, "=", id));
return Result.success();
}
}
/**
* 下载体检报告导入模版;业务筛选、数据读写和结果组装在本服务内完成。
* @param response 接收 Excel 文件流的 HTTP 响应
* 文件内容直接写入 response,不返回 JSON。
*/
@org.nutz.ioc.aop.Aop(org.nutz.aop.interceptor.ioc.TransAop.READ_COMMITTED)
public void downloadTemMedicalExaminationReport(HttpServletResponse response) {
List<ExcelExportEntity> entities = new ArrayList<>();
entities.add(new ExcelExportEntity("工号", "loginName", 20));
entities.add(new ExcelExportEntity("体检报告", "medicalExamination", 20));
ExportParams exportParams = new ExportParams();
exportParams.setType(ExcelType.XSSF);
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entities, Collections.emptyList());
CommonDownloadUtil.download("体检报告导入模版.xlsx", workbook, response);
}
/**
* 体检名单导入名单;业务筛选、数据读写和结果组装在本服务内完成。
* @param file 上传的 Excel 临时文件
* @param healthProjectId 导入目标体检项目主键
* @return Result;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
@org.nutz.ioc.aop.Aop(org.nutz.aop.interceptor.ioc.TransAop.READ_COMMITTED)
public Result healthImportMedicalExaminationReport(TempFile file, String healthProjectId) {
// 读取数据
List<T> ts = EasyExcelUtil.syncReadModel(file.getFile(), HealthCheckupImportTemp.class, 0, 1);
List<HealthCheckupImportTemp> healthCheckupImportTemps = JSONUtil.parseArray(JSONUtil.toJsonStr(ts)).toList(HealthCheckupImportTemp.class);
List<String> loginNames = healthCheckupImportTemps.stream().filter(v -> Strings.isNotBlank(v.getLoginName())).map(v -> v.getLoginName()).collect(Collectors.toList());
List<View_user> userList = dao.query(View_user.class, Cnd.where(View_user::getLoginname, "in", loginNames));
int successCount = 0;
for (int i = 0; i < healthCheckupImportTemps.size(); i++) {
HealthCheckupImportTemp excel = healthCheckupImportTemps.get(i);
if (StrUtil.isBlank(excel.getLoginName())) {
excel.setErrInfo("工号为空", i + 1);
continue;
}
// 不能在excel里重复
if (healthCheckupImportTemps.stream().filter(s -> s.getLoginName().equals(excel.getLoginName())).count() > 1) {
excel.setErrInfo("重复数据", i + 1);
}
View_user user = userList.stream().filter(s -> s.getLoginname().equals(excel.getLoginName())).findFirst().orElse(null);
if (user == null) {
excel.setErrInfo("无此用户", i + 1);
continue;
}
dao.update(HealthCheckupUser.class,
Chain.make("medicalExamination", excel.getMedicalExamination()),
Cnd.where("loginName", "=", excel.getLoginName()).and("projectId", "=", healthProjectId));
successCount++;
}
// 创建结果集
ExcelImportRes<HealthCheckupImportTemp> excelImportRes = new ExcelImportRes<>();
excelImportRes.setTotalRecords(healthCheckupImportTemps.size());
// 添加错误记录
excelImportRes.setErrorDetails(healthCheckupImportTemps.stream().filter(s -> StrUtil.isNotBlank(s.getErrMsg())).collect(Collectors.toList()));
excelImportRes.setFailedCount(excelImportRes.getErrorDetails().size());
excelImportRes.setSuccessCount(successCount);
return Result.success(excelImportRes);
}
/**
* 套餐选择器查询;业务筛选、数据读写和结果组装在本服务内完成。
* @param projectId 当前体检项目主键
* @return Result;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
public Result listSubjects(String projectId) {
return Result.success(
dao.query(HealthCheckupProjectSubject.class,
Cnd.NEW().and("projectId","=",projectId))
);
}
/**
* 体检反馈列表;业务筛选、数据读写和结果组装在本服务内完成。
* @param year 四位体检年度
* @param projectId 当前体检项目主键
* @param unionId 所属分工会主键,可空时按接口范围处理
* @return Result;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
public Result feedbackList(String year, String projectId, String unionId) {
if (StrUtil.isBlank(unionId)) {
unionId = SecurityUtil.getUnionId();
}
if (StrUtil.hasBlank(year, projectId, unionId)) {
return Result.error("请选择年度、体检项目、分工会后再查看反馈");
}
return Result.success(healthCheckupFeedbackService.listFeedback(year, projectId, unionId));
}
/**
* 保存体检反馈;业务筛选、数据读写和结果组装在本服务内完成。
* @param year 四位体检年度
* @param projectId 当前体检项目主键
* @param unionId 所属分工会主键,可空时按接口范围处理
* @param feedbackInfo 反馈正文
* @return Result;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
@org.nutz.ioc.aop.Aop(org.nutz.aop.interceptor.ioc.TransAop.READ_COMMITTED)
public Result saveFeedback(String year, String projectId, String unionId, String feedbackInfo) {
if (StrUtil.isBlank(unionId)) {
unionId = SecurityUtil.getUnionId();
}
NutMap result = healthCheckupFeedbackService.saveFeedback(year, projectId, unionId, feedbackInfo);
if (result.getInt("code") == 0) {
return Result.success(result.getString("msg"));
}
return Result.error(result.getString("msg"));
}
/**
* 删除体检反馈;业务筛选、数据读写和结果组装在本服务内完成。
* @param id 当前业务记录主键
* @return Result;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
@org.nutz.ioc.aop.Aop(org.nutz.aop.interceptor.ioc.TransAop.READ_COMMITTED)
public Result deleteFeedback(String id) {
NutMap result = healthCheckupFeedbackService.deleteFeedback(id);
if (result.getInt("code") == 0) {
return Result.success(result.getString("msg"));
}
return Result.error(result.getString("msg"));
}
/**
* 批量删除体检反馈;业务筛选、数据读写和结果组装在本服务内完成。
* @param ids 逗号分隔或 JSON 数组形式的记录主键
* @return Result;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
@org.nutz.ioc.aop.Aop(org.nutz.aop.interceptor.ioc.TransAop.READ_COMMITTED)
public Result deleteFeedbackBatch(String ids) {
String[] idArray = null;
if (StrUtil.isNotBlank(ids)) {
// 兼容前端传逗号分隔字符串和JSON数组字符串两种格式。
if (ids.trim().startsWith("[")) {
idArray = JSONUtil.parseArray(ids).toList(String.class).toArray(new String[0]);
} else {
idArray = ids.split(",");
}
}
NutMap result = healthCheckupFeedbackService.deleteFeedbackBatch(idArray);
if (result.getInt("code") == 0) {
return Result.success(result.getString("msg"));
}
return Result.error(result.getString("msg"));
}
/**
* 问题反馈列表分页;业务筛选、数据读写和结果组装在本服务内完成。
* @param pageForm 分页、搜索及排序参数
* @param year 四位体检年度
* @param projectId 当前体检项目主键
* @param unionId 所属分工会主键,可空时按接口范围处理
* @return Result;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
public Result feedbackQueryPageData(PageForm pageForm, String year, String projectId, String unionId) {
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name()) && StrUtil.isBlank(unionId)) {
unionId = SecurityUtil.getUnionId();
}
return Result.success(healthCheckupFeedbackService.listFeedbackPage(
pageForm.getPageNumber(),
pageForm.getPageSize(),
year,
projectId,
unionId
));
}
/**
* 体检名单编辑备注;业务筛选、数据读写和结果组装在本服务内完成。
* @param userId 教职工主键
* @param projectId 当前体检项目主键
* @param remarks 人员备注内容
* @return Result;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
@org.nutz.ioc.aop.Aop(org.nutz.aop.interceptor.ioc.TransAop.READ_COMMITTED)
public Result doEditRemarks(String userId, String projectId,String remarks) {
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.BRANCH_UNION_ADMIN.name(), RoleConstant.BRANCH_UNION_CHAIRMAN.name())) {
return Result.error("您没有操作此按钮的权限");
}
dao.update(HealthCheckupUser.class,
Chain.make("bz", remarks),
Cnd.where("userId", "=", userId)
.and("projectId", "=", projectId));
return Result.success();
}
/**
* 导出;业务筛选、数据读写和结果组装在本服务内完成。
* @param projectId 当前体检项目主键
* @param unionId 所属分工会主键,可空时按接口范围处理
* @param searchKeyword 姓名或工号搜索文本
* @param subjectId 体检医院选项主键
* @param unitId 所属单位主键筛选值
* @param physicalExaminationStatus 1 已选、0 未选、2 全部
* @param pageOrderName 排序字段名
* @param pageOrderBy ascending 升序,否则降序
* @param response 接收 Excel 文件流的 HTTP 响应
* 文件内容直接写入 response,不返回 JSON。
*/
public void exportHealthUser(String projectId,
String unionId,
String searchKeyword,
String subjectId,
String unitId,
String physicalExaminationStatus,
String pageOrderName,
String pageOrderBy,
HttpServletResponse response) {
List<HealthCheckupUnionConfirm> confirms = dao.query(HealthCheckupUnionConfirm.class, Cnd.where("projectId", "=", projectId));
HealthCheckupProject project = healthCheckupProjectService.findOne(projectId);
Cnd cnd = Cnd.NEW();
Sql sql = Sqls.create("""
SELECT
hcu.id AS headId,
hcu.userId AS id,
hcu.loginName,
hcu.userName,
hcu.sex,
hcu.unionId,
hcu.unionName,
hcu.unitName,
hcus.selectTime,
hcus.campus,
hcus.subjectId,
hcu.bz,
hcus.receiveAddress,
ca.campusName
FROM health_checkup_user hcu
LEFT JOIN health_checkup_user_selection hcus ON hcu.userId = hcus.selectUserId
AND hcus.projectId=@projectId
LEFT JOIN health_checkup_campus ca on ca.id=hcus.campus
$condition
""").setParam("projectId", projectId);
sql.setCondition(cnd);
cnd.andEX("hcu.projectId", "=", projectId);
cnd.andEX("hcu.unionId", "=", unionId);
cnd.andEX("hcu.unitId", "=", unitId);
cnd.andEX("hcus.subjectId", "=", subjectId);
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) {
cnd.andEX("hcu.unionId", "=", SecurityUtil.getUnionId());
}
//体检状态
if(StringUtils.isNotBlank(physicalExaminationStatus) && !physicalExaminationStatus.equals("2")){
if(physicalExaminationStatus.equals("1")) cnd.where().andNotIsNull("hcus.selectTime");
if(physicalExaminationStatus.equals("0")) cnd.where().andIsNull("hcus.selectTime");
}
if (StrUtil.isNotBlank(searchKeyword)) {
SqlExpressionGroup group = new SqlExpressionGroup();
group.orLike("hcu.userName", searchKeyword);
group.orLike("hcu.loginName", searchKeyword);
cnd.and(group);
}
cnd.groupBy("hcu.userId");
if (StrUtil.isNotBlank(pageOrderName) && Strings.isNotBlank(pageOrderBy)) {
cnd.orderBy(pageOrderName, "ascending".equalsIgnoreCase(pageOrderBy) ? "ASC" : "DESC");
} else {
cnd.desc("hcus.selectTime").asc("hcu.unitId");
}
sql.setCondition(cnd);
List<NutMap> paginationList = healthCheckupProjectService.listMap(sql);
paginationList.forEach(v -> {
if (StrUtil.isNotBlank(v.getString("subjectId"))) {
List<HealthCheckupProjectSubject> subjects = project.getHealthCheckupProjectSubjects();
HealthCheckupProjectSubject subject = subjects.stream().filter(s -> s.getId().equals(v.getString("subjectId"))).findFirst().orElse(null);
v.setv("optionName", subject.getOptionName());
}
if (StrUtil.isNotBlank(v.getString("unionId"))) {
HealthCheckupUnionConfirm confirm = confirms.stream().filter(o -> o.getUnionId().equals(v.getString("unionId"))).findFirst().orElse(null);
v.setv("isAudit", confirm != null && confirm.getIsAudit() ? "已确认" : "未确认");
v.setv("auditTime", confirm != null && ObjectUtil.isNotEmpty(confirm.getAuditTime() )
? new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(confirm.getAuditTime()) : "");
}
});
for (int i = 0; i < paginationList.size(); i++) {
paginationList.get(i).put("index", i + 1);
}
List<ExcelExportEntity> entityList = new ArrayList<>() {{
add(new ExcelExportEntity("序号", "index", 20));
add(new ExcelExportEntity("工号", "loginName", 20));
add(new ExcelExportEntity("姓名", "userName", 20));
add(new ExcelExportEntity("性别", "sex", 20));
add(new ExcelExportEntity("所属工会", "unionName", 20));
add(new ExcelExportEntity("所属单位", "unitName", 20));
add(new ExcelExportEntity("所选套餐", "optionName", 20));
add(new ExcelExportEntity("选择时间", "selectTime", 50));
add(new ExcelExportEntity("是否确认", "isAudit", 50));
add(new ExcelExportEntity("确认时间", "auditTime", 50));
add(new ExcelExportEntity("备注", "bz", 50));
}};
try {
ExportParams exportParams = new ExportParams();
exportParams.setType(ExcelType.XSSF);
exportParams.setTitle("体检名单数据导出");
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entityList, paginationList);
CommonDownloadUtil.download("体检名单数据导出.xlsx", workbook, response);
} catch (Exception e) {
e.printStackTrace();
}
}
}
@@ -0,0 +1,26 @@
package com.budwk.app.zhgh.dayofficework.healthCheckup.service;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.dayofficework.healthCheckup.model.HealthCheckupUser;
/**
* @ClassName HealthCheckupPersonConfirmStatisticsService
* @Description 工会人员确认情况统计
* @Author Codex
* @Date 2026/4/28
*/
public interface HealthCheckupPersonConfirmStatisticsService extends BaseService<HealthCheckupUser> {
/**
* 工会确认情况分页统计
*
* @param pageNumber 页码
* @param pageSize 页大小
* @param year 年度
* @param projectId 体检项目
* @param unionId 工会
* @return 统计分页结果
*/
Pagination personConfirmPageData(Integer pageNumber, Integer pageSize, Integer year, String projectId, String unionId);
}
@@ -0,0 +1,224 @@
package com.budwk.app.zhgh.dayofficework.healthCheckup.service;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.sys.models.Sys_home_activity;
import com.budwk.app.sys.models.Sys_union;
import com.budwk.app.zhgh.activity.culture.service.ActivityCultureService;
import com.budwk.app.zhgh.dayofficework.healthCheckup.model.*;
import com.budwk.app.zhgh.dayofficework.healthCheckup.service.HealthCheckupProjectService;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Cnd;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.Daos;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Lang;
import org.nutz.lang.util.NutMap;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/** 体检接口业务服务;HTTP 路由和权限校验保留在 controller。 */
@IocBean
public class HealthCheckupProjectMangeApplicationService {
@Inject
private HealthCheckupProjectService healthCheckupProjectService;
@Inject
private ActivityCultureService activityCultureService;
/**
* 分页查询;业务筛选、数据读写和结果组装在本服务内完成。
* @param pageForm 分页、搜索及排序参数
* @param year 四位体检年度
* @param name 体检项目名称筛选值
* @return Result;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
public Result pageData(PageForm pageForm,
Integer year,
String name) {
Sql sql = Sqls.create("""
SELECT
hcp.id,
hcp.`year`,
hcp.`name`,
hcp.choiceTimeStart,
hcp.choiceTimeEnd,
hcp.activityGroupId,
hcp.cover,
hcp.activityGroupName,
hcp.healthCheckupType,
IF((select count(userId) from health_checkup_user hcu where hcu.projectId = hcp.id) > 0,true,false) AS isHasUserList,
IF(NOW() > hcp.choiceTimeStart AND NOW() < hcp.choiceTimeEnd, true, false) AS isWithinChoiceTime,
IF(NOW() < hcp.choiceTimeStart, true, false) AS canCreated
FROM
health_checkup_project hcp
$condition
""");
Cnd cnd = Cnd.NEW();
cnd.andEX("hcp.`year`", "=", year);
cnd.andEX("hcp.`name`", "=", name);
cnd.desc("hcp.choiceTimeStart");
sql.setCondition(cnd);
return Result.success(healthCheckupProjectService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql));
}
/**
* 体检项目确认;业务筛选、数据读写和结果组装在本服务内完成。
* @param healthCheckupProject 体检项目及医院套餐明细
* @param deleteRowIds 待删除的套餐金额记录主键数组
* @param deleteSubjectIds 待删除的医院选项主键数组
* @return Result;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
@org.nutz.ioc.aop.Aop(org.nutz.aop.interceptor.ioc.TransAop.READ_COMMITTED)
public Result doAdd(HealthCheckupProject healthCheckupProject,
String[] deleteRowIds,
String[] deleteSubjectIds) {
String projectId;
//如果是保存
if (StrUtil.isBlank(healthCheckupProject.getId())) {
healthCheckupProject.setYear(DateUtil.thisYear());
HealthCheckupProject checkupProject = healthCheckupProjectService.insertWith(healthCheckupProject, "healthCheckupProjectSubjects");
healthCheckupProject.getHealthCheckupProjectSubjects().forEach(v -> {
v.getSubjectMoneys().forEach(m -> {
m.setProjectId(checkupProject.getId());
m.setSubjectId(v.getId());
});
if (Lang.isNotEmpty(v.getSubjectMoneys())) {
healthCheckupProjectService.insert(v.getSubjectMoneys());
}
});
projectId = checkupProject.getId();
} else {
projectId = healthCheckupProject.getId();
//如果删除了医院
if (Lang.isNotEmpty(deleteSubjectIds)) {
healthCheckupProjectService.dao().clear(HealthCheckupProjectSubject.class, Cnd.where("id", "in", deleteSubjectIds));
healthCheckupProjectService.dao().clear(HealthCheckupProjectSubjectMoney.class, Cnd.where("subjectId", "in", deleteSubjectIds));
}
//如果删除了项目
if (Lang.isNotEmpty(deleteRowIds)) {
healthCheckupProjectService.dao().clear(HealthCheckupProjectSubjectMoney.class, Cnd.where("id", "in", deleteRowIds));
}
healthCheckupProjectService.dao().insertOrUpdate(healthCheckupProject.getHealthCheckupProjectSubjects());
healthCheckupProject.getHealthCheckupProjectSubjects().forEach(v -> {
v.setProjectId(projectId);
v.getSubjectMoneys().forEach(m -> {
m.setProjectId(projectId);
m.setSubjectId(v.getId());
});
if (Lang.isNotEmpty(v.getSubjectMoneys())) {
healthCheckupProjectService.insertOrUpdate(v.getSubjectMoneys());
}
});
healthCheckupProjectService.dao().insertOrUpdate(healthCheckupProject.getHealthCheckupProjectSubjects());
healthCheckupProjectService.update(healthCheckupProject);
}
// healthCheckupProjectService.dao().clear(HealthCheckupUnionConfirm.class, Cnd.where("projectId", "=", projectId));
// List<Sys_union> unionList = healthCheckupProjectService.dao().query(Sys_union.class, Cnd.NEW());
// List<HealthCheckupUnionConfirm> confirmList = new ArrayList<>();
// unionList.forEach(item -> {
// HealthCheckupUnionConfirm c = new HealthCheckupUnionConfirm();
// c.setProjectId(projectId);
// c.setUnionId(item.getId());
// c.setIsAudit(false);
// confirmList.add(c);
// });
// healthCheckupProjectService.dao().insert(confirmList);
Sys_home_activity sysHomeActivity = activityCultureService.dao().fetch(Sys_home_activity.class, Cnd.where("id", "=", healthCheckupProject.getId()));
if(ObjectUtil.isNotEmpty(sysHomeActivity)){
activityCultureService.dao().clear(Sys_home_activity.class, Cnd.where(Sys_home_activity::getId, "=", sysHomeActivity.getId()));
}
activityCultureService.insertOrUpdate(healthCheckupProject.covertToSysHomeActivity());
return Result.success();
}
/**
* 查询体检项目内容;业务筛选、数据读写和结果组装在本服务内完成。
* @param id 当前业务记录主键
* @return Result;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
public Result findOne(String id) {
return Result.success(healthCheckupProjectService.findOne(id));
}
/**
* 删除体检项目;业务筛选、数据读写和结果组装在本服务内完成。
* @param id 当前业务记录主键
* @return Result;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
@org.nutz.ioc.aop.Aop(org.nutz.aop.interceptor.ioc.TransAop.READ_COMMITTED)
public Result doDelete(String id) {
healthCheckupProjectService.delete(id);
healthCheckupProjectService.dao().clear(HealthCheckupProjectSubject.class, Cnd.where("projectId", "=", id));
healthCheckupProjectService.dao().clear(HealthCheckupProjectSubjectMoney.class, Cnd.where("projectId", "=", id));
activityCultureService.dao().clear(Sys_home_activity.class, Cnd.where(Sys_home_activity::getId, "=", id));
return Result.success();
}
/**
* 按年份获取体检项目;业务筛选、数据读写和结果组装在本服务内完成。
* @param year 四位体检年度
* @return Result;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
public Result getHealthCheckupProject(Integer year) {
List<HealthCheckupProject> projectList = healthCheckupProjectService.query(Cnd.NEW().andEX("year", "=", year).desc("choiceTimeStart"));
return Result.success(projectList);
}
/**
* 创建体检名单;业务筛选、数据读写和结果组装在本服务内完成。
* @param healthCheckupProject 体检项目及医院套餐明细
* @return Result;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
@org.nutz.ioc.aop.Aop(org.nutz.aop.interceptor.ioc.TransAop.READ_COMMITTED)
public Result createUserList(HealthCheckupProject healthCheckupProject) {
healthCheckupProjectService.addHealthCheckupUser(healthCheckupProject);
return Result.success();
}
/**
* 更新体检名单;业务筛选、数据读写和结果组装在本服务内完成。
* @param healthCheckupProject 体检项目及医院套餐明细
* @return Result;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
@org.nutz.ioc.aop.Aop(org.nutz.aop.interceptor.ioc.TransAop.READ_COMMITTED)
public Result renewUserList(HealthCheckupProject healthCheckupProject) {
healthCheckupProjectService.renewHealthCheckupUser(healthCheckupProject);
return Result.success();
}
/**
* 重置体检名单;业务筛选、数据读写和结果组装在本服务内完成。
* @param healthCheckupProject 体检项目及医院套餐明细
* @return Result;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
@org.nutz.ioc.aop.Aop(org.nutz.aop.interceptor.ioc.TransAop.READ_COMMITTED)
public Result resetUserList(HealthCheckupProject healthCheckupProject) {
//重置名单,清空已选择的用户记录
Sql sql = Sqls.create("select id,selectUserId from health_checkup_user_selection where projectId = @projectId").setParam("projectId", healthCheckupProject.getId());
List<NutMap> list = (List<NutMap>) Daos.query(healthCheckupProjectService.dao(), sql.toString(), Sqls.callback.maps());
List<String> userSelectionIds = list.stream().map(v -> v.getString("id")).collect(Collectors.toList());
List<String> userIds = list.stream().map(v -> v.getString("selectUserId")).collect(Collectors.toList());
healthCheckupProjectService.dao().clear(HealthCheckupUserSelection.class,
Cnd.NEW().and("selectUserId", "in", userIds)
.and("projectId", "=", healthCheckupProject.getId()));
healthCheckupProjectService.dao().clear(HealthCheckupUserCompanion.class, Cnd.where("userSelectionId", "in", userSelectionIds));
healthCheckupProjectService.dao().clear(HealthCheckupUser.class, Cnd.where("projectId", "=", healthCheckupProject.getId()));
healthCheckupProjectService.addHealthCheckupUser(healthCheckupProject);
return Result.success();
}
}
@@ -2,6 +2,7 @@ package com.budwk.app.zhgh.dayofficework.healthCheckup.service;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.dayofficework.healthCheckup.model.HealthCheckupProject;
import com.budwk.app.zhgh.dayofficework.healthCheckup.model.HealthCheckupUserSelection;
public interface HealthCheckupProjectService extends BaseService<HealthCheckupProject> {
@@ -20,4 +21,10 @@ public interface HealthCheckupProjectService extends BaseService<HealthCheckupPr
* @param healthCheckupProject
*/
void renewHealthCheckupUser(HealthCheckupProject healthCheckupProject);
/**
* 获取用户上年度体检医院
* @param checkupUserSelection
*/
String findLastYearHospital(HealthCheckupUserSelection checkupUserSelection);
}
@@ -0,0 +1,550 @@
package com.budwk.app.zhgh.dayofficework.healthCheckup.service;
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.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.sms.SmsService;
import com.budwk.app.base.utils.CommonDownloadUtil;
import com.budwk.app.base.utils.ConditionGroupUtil;
import com.budwk.app.base.utils.ManyAddOrRenewUtil;
import com.budwk.app.sys.views.View_user;
import com.budwk.app.web.commons.base.Globals;
import com.budwk.app.zhgh.dayofficework.healthCheckup.model.HealthCheckupProject;
import com.budwk.app.zhgh.dayofficework.healthCheckup.model.HealthCheckupProjectSubject;
import com.budwk.app.zhgh.dayofficework.healthCheckup.model.HealthCheckupProjectSubjectMoney;
import com.budwk.app.zhgh.dayofficework.healthCheckup.model.HealthCheckupUserSelection;
import com.budwk.app.zhgh.dayofficework.healthCheckup.service.HealthCheckupProjectService;
import com.budwk.app.zhgh.dayofficework.healthCheckup.service.HealthCheckupSingleService;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Strings;
import org.nutz.lang.util.NutMap;
import javax.servlet.http.HttpServletResponse;
import com.budwk.app.web.commons.auth.utils.AuthUtil;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.exception.BaseException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
/** 体检接口业务服务;HTTP 路由和权限校验保留在 controller。 */
@IocBean
public class HealthCheckupSingleApplicationService {
@Inject
private SmsService smsService;
@Inject
private HealthCheckupSingleService healthCheckupSingleService;
@Inject
private ManyAddOrRenewUtil manyAddOrRenewUtil;
@Inject
private HealthCheckupProjectService healthCheckupProjectService;
/**
* 首页查询;业务筛选、数据读写和结果组装在本服务内完成。
* @param projectId 当前体检项目主键
* @param unionId 所属分工会主键,可空时按接口范围处理
* @return Result;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
public Result pageData(String projectId,
String unionId) {
NutMap nutMap = healthCheckupSingleService.pageData(projectId, unionId);
return Result.success(nutMap);
}
/**
* 首页查询;业务筛选、数据读写和结果组装在本服务内完成。
* @param projectId 当前体检项目主键
* @return Result;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
public Result actualStatistics(String projectId) {
try {
return Result.success(healthCheckupSingleService.actualStatistics(projectId));
} catch (IllegalArgumentException e) {
return Result.error(e.getMessage());
}
}
/**
* 读取体检业务数据;业务筛选、数据读写和结果组装在本服务内完成。
* @param projectId 当前体检项目主键
* @param counts 总人数及各医院人数的 JSON 映射
* @return Result;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
@org.nutz.ioc.aop.Aop(org.nutz.aop.interceptor.ioc.TransAop.READ_COMMITTED)
public Result saveActualStatistics(String projectId, String counts) {
try {
return Result.success(healthCheckupSingleService.saveActualStatistics(projectId, counts));
} catch (IllegalArgumentException e) {
return Result.error(e.getMessage());
}
}
/**
* 每个分工会领取人员/未领取人员;业务筛选、数据读写和结果组装在本服务内完成。
* @param page 分页及搜索参数
* @param isSelected 0 已选择,1 未选择
* @param projectId 当前体检项目主键
* @param unionId 所属分工会主键,可空时按接口范围处理
* @return Result;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
public Result receivePageData(PageForm page, String isSelected,
String projectId,
String unionId) {
List<NutMap> labelList = healthCheckupSingleService.receivePageDataColumns(projectId);
Pagination list = healthCheckupSingleService.receivePageData(page.getPageNumber(), page.getPageSize(), page.getSearchName(), page.getSearchKeyword(), projectId, unionId, isSelected);
return Result.success(NutMap.NEW().addv("list", list).addv("label", labelList));
}
/**
* 按医院导出当前项目全部已选教职工;不受页面分工会筛选影响。
* @param projectId 当前教职工体检项目ID
* @param response 输出ZIP附件,每个有报名人员的医院对应一个xlsx文件;无JSON返回值
*/
public void exportSelectedZip(String projectId, HttpServletResponse response) {
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) {
throw new BaseException("仅系统管理员或校工会管理员可导出全部已选名单");
}
if (Strings.isBlank(projectId)) throw new BaseException("请选择体检项目");
HealthCheckupProject project = healthCheckupProjectService.fetch(projectId);
if (project == null || !"jzg".equals(project.getHealthCheckupType())) {
throw new BaseException("请选择教职工体检项目");
}
// 以已选记录为查询起点,不包含尚未报名人员,也不因分工会为空而丢失报名记录。
Sql sql = Sqls.create("""
SELECT s.subjectId, h.optionName AS subjectName,
c.loginName, c.userName, c.sex,
COALESCE(c.age,TIMESTAMPDIFF(YEAR,u.birthday,CURDATE())) AS age,
c.phone AS mobile, c.card AS idCard,
COALESCE(d.name,c.personType) AS personType,
c.marriage, s.money, m.settlementMoney,
c.unitName, c.unionName, s.receiveAddress, c.isEmployed
FROM health_checkup_user_selection s
JOIN health_checkup_project_subject h ON h.id=s.subjectId AND h.projectId=s.projectId
LEFT JOIN health_checkup_user c ON c.projectId=s.projectId AND c.userId=s.selectUserId
LEFT JOIN sys_user u ON u.id=s.selectUserId
LEFT JOIN health_checkup_project_subject_money m ON m.id=s.subjectMoneyId AND m.subjectId=s.subjectId
LEFT JOIN sys_dict p ON p.code='USER_PERSON_TYPE'
LEFT JOIN sys_dict d ON d.parentId=p.id AND d.code=c.personType
WHERE s.projectId=@projectId
ORDER BY h.optionSort,h.id,c.unionName,c.unitName,c.loginName,s.id
""");
sql.setParam("projectId", projectId);
List<NutMap> rows = healthCheckupSingleService.listMap(sql);
if (rows.isEmpty()) throw new BaseException("当前项目暂无已选名单");
Map<String, List<NutMap>> hospitals = new LinkedHashMap<>();
rows.forEach(row -> hospitals.computeIfAbsent(row.getString("subjectId"), key -> new ArrayList<>()).add(row));
List<ExcelExportEntity> columns = new ArrayList<>();
columns.add(new ExcelExportEntity("工号", "loginName", 20));
columns.add(new ExcelExportEntity("姓名", "userName", 20));
columns.add(new ExcelExportEntity("性别", "sex", 20));
columns.add(new ExcelExportEntity("年龄", "age", 20));
columns.add(new ExcelExportEntity("手机号码", "mobile", 20));
columns.add(new ExcelExportEntity("身份证号", "idCard", 20));
columns.add(new ExcelExportEntity("人员类别", "personType", 20));
columns.add(new ExcelExportEntity("婚姻状况", "marriage", 20));
columns.add(new ExcelExportEntity("医院", "subjectName", 20));
columns.add(new ExcelExportEntity("套餐金额", "money", 20));
columns.add(new ExcelExportEntity("结算金额", "settlementMoney", 20));
columns.add(new ExcelExportEntity("部门", "unitName", 20));
columns.add(new ExcelExportEntity("分工会", "unionName", 20));
columns.add(new ExcelExportEntity("邮寄地址", "receiveAddress", 20));
columns.add(new ExcelExportEntity("是否自聘", "isEmployed", 20));
// 先完整生成压缩包再写响应,生成失败时不返回半截附件;各医院使用独立工作簿。
try (ByteArrayOutputStream bytes = new ByteArrayOutputStream()) {
try (ZipOutputStream zip = new ZipOutputStream(bytes, StandardCharsets.UTF_8)) {
int index = 1;
for (List<NutMap> hospitalRows : hospitals.values()) {
String hospital = hospitalRows.get(0).getString("subjectName");
ExportParams exportParams = new ExportParams();
exportParams.setType(ExcelType.XSSF);
exportParams.setSheetName("已选名单");
exportParams.setTitle(hospital + "已选名单");
// 序号避免同名医院产生重复ZIP条目;替换路径分隔符及文件名非法字符。
String safeName = Strings.sNull(hospital).replaceAll("[\\\\/:*?\"<>|\\p{Cntrl}]", "_");
if (safeName.length() > 80) safeName = safeName.substring(0, 80);
zip.putNextEntry(new ZipEntry((index++) + "_" + safeName + ".xlsx"));
try (Workbook workbook = ExcelExportUtil.exportExcel(exportParams, columns, hospitalRows)) {
workbook.write(zip);
}
zip.closeEntry();
}
}
CommonDownloadUtil.download(project.getName() + "_已选名单.zip", bytes.toByteArray(), response);
} catch (IOException e) {
throw new BaseException("已选名单ZIP生成失败,请重试");
}
}
/**
* 导出已选/未选人员名单;业务筛选、数据读写和结果组装在本服务内完成。
* @param projectId 当前体检项目主键
* @param unionId 所属分工会主键,可空时按接口范围处理
* @param year 四位体检年度
* @param flag 确认或取消、已选或未选的业务标志
* @param response 接收 Excel 文件流的 HTTP 响应
* 文件内容直接写入 response,不返回 JSON。
*/
public void exportReceiveDetail(String projectId,
String unionId,
Integer year,
boolean flag, HttpServletResponse response) {
List<ExcelExportEntity> entityList = new ArrayList<>() {{
add(new ExcelExportEntity("工号", "loginName", 20));
add(new ExcelExportEntity("姓名", "userName", 20));
add(new ExcelExportEntity("性别", "sex", 20));
add(new ExcelExportEntity("年龄", "age", 20));
add(new ExcelExportEntity("手机号码", "mobile", 20));
add(new ExcelExportEntity("身份证号", "idCard", 20));
add(new ExcelExportEntity("用户类型", "personType", 20));
add(new ExcelExportEntity("婚姻状况", "marriage", 20));
add(new ExcelExportEntity("医院", "subjectName", 20));
add(new ExcelExportEntity("套餐金额", "money", 20));
add(new ExcelExportEntity("结算金额", "settlementMoney", 20));
add(new ExcelExportEntity("部门", "unitName", 20));
add(new ExcelExportEntity("分工会", "unionName", 20));
add(new ExcelExportEntity("邮寄地址", "receiveAddress", 50));
add(new ExcelExportEntity("是否自聘", "isEmployed", 50));
}};
List<NutMap> list;
if (flag) {
//已选人员
list = healthCheckupSingleService.receivePageData(projectId, unionId, "0");
} else {
//未选人员
list = healthCheckupSingleService.receivePageData(projectId, unionId, "1");
}
//获取当前年
Calendar calendar = Calendar.getInstance();
int thisYear = calendar.get(Calendar.YEAR);
list.forEach(v -> {
int substring = 0;
if (Strings.isNotBlank(v.getString("birthday"))) {
substring = Integer.parseInt(v.getString("birthday").substring(0, 4));
} else if (Strings.isNotBlank(v.getString("idCard")) && v.getString("idCard").length() >= 10) {
substring = Integer.parseInt(v.getString("idCard").substring(6, 10));
}
v.put("age", thisYear - substring);
if(v.getString("projectName").contains("在职教职工")){
v.put("personType", "在职教职工");
} else if(v.getString("projectName").contains("校聘高知")){
v.put("personType", "校聘高知保健对象");
}
});
List<NutMap> list2 = list.stream().filter(v -> Strings.isNotBlank(v.getString("unionName"))).collect(Collectors.toList());
ExportParams exportParams = new ExportParams();
exportParams.setType(ExcelType.XSSF);
exportParams.setSheetName(year + "年教职工体检项目及地点统计表");
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entityList, list2);
CommonDownloadUtil.download((flag ? year + "年教职工体检项目及地点统计表" : "未选名单") + ".xlsx", workbook, response);
}
/**
* 导出教职工家属体检登记表;业务筛选、数据读写和结果组装在本服务内完成。
* @param projectId 当前体检项目主键
* @param unionId 所属分工会主键,可空时按接口范围处理
* @param year 四位体检年度
* @param response 接收 Excel 文件流的 HTTP 响应
* 文件内容直接写入 response,不返回 JSON。
*/
public void exportUserCompanion(String projectId,
String unionId,
Integer year, HttpServletResponse response) {
Sql sql = Sqls.create("""
SELECT
u.username,
u.unitname unitName,
u.mobile,
hcuc.userName familyName,
hcuc.sex,
hcuc.marry isMarried,
hcuc.idCard,
hcc.campusName,
(
SELECT
GROUP_CONCAT( hcps.optionName )
FROM
health_checkup_user_selection hcus
LEFT JOIN `health_checkup_project_subject` hcps ON hcps.id = hcus.subjectId
WHERE
hcus.projectId = @projectId
AND hcus.selectUserId = u.id
) optionName
FROM
`health_checkup_user_companion` hcuc
LEFT JOIN `health_checkup_user_selection` hcus ON hcus.id = hcuc.userSelectionId
LEFT JOIN `health_checkup_campus` hcc ON hcc.id = hcus.campus
LEFT JOIN `vw_user` u ON u.id = hcus.selectUserId
LEFT JOIN `health_checkup_project` hcp ON hcp.id = hcus.projectId
$condition
""").setParam("projectId", projectId);
Cnd cnd = Cnd.NEW();
cnd.andEX("hcus.projectId", "=", projectId);
cnd.andEX("u.unionid", "=", unionId);
cnd.andEX("hcp.year", "=", year);
cnd.asc("u.unioncode");
sql.setCondition(cnd);
//家属信息表
List<NutMap> companionList = healthCheckupSingleService.listMap(sql);
for (int i = 0; i < companionList.size(); i++) {
companionList.get(i).put("index", i + 1);
}
List<ExcelExportEntity> entityList = new ArrayList<>() {{
add(new ExcelExportEntity("序号", "index", 20));
add(new ExcelExportEntity("教职工姓名", "username", 20));
add(new ExcelExportEntity("学院(部门)", "unitName", 20));
add(new ExcelExportEntity("手机", "mobile", 20));
add(new ExcelExportEntity("体检人姓名", "familyName", 20));
add(new ExcelExportEntity("性别", "sex", 20));
add(new ExcelExportEntity("婚否", "isMarried", 20));
add(new ExcelExportEntity("体检人身份证号", "idCard", 50));
add(new ExcelExportEntity("自选项目", "optionName", 50));
add(new ExcelExportEntity("自选院区", "campusName", 50));
}};
try {
ExportParams exportParams = new ExportParams();
exportParams.setType(ExcelType.XSSF);
exportParams.setTitle(year + "教职工家属体检登记表");
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entityList, companionList);
CommonDownloadUtil.download(year + "教职工家属体检登记表.xlsx", workbook, response);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 管理员一键代选;业务筛选、数据读写和结果组装在本服务内完成。
* @param projectId 当前体检项目主键
* @param optionId 代选的医院选项主键
* @param campus 体检院区主键
* @return Result;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
@org.nutz.ioc.aop.Aop(org.nutz.aop.interceptor.ioc.TransAop.READ_COMMITTED)
public Result doSelectByAdmin(String projectId, String optionId, String campus) {
Dao dao = healthCheckupSingleService.dao();
HealthCheckupProject project = dao.fetch(HealthCheckupProject.class, projectId);
Cnd cnd = Cnd.NEW();
Sql sql = Sqls.create("""
SELECT
u.id,
u.loginname loginName,
u.username userName,
u.sex,
u.birthday,
u.unitname unitName,
u.unionname unionName,
u.idcard idCard,
u.mobile
FROM
`activity_user_scope` aus
LEFT JOIN `vw_user` u ON u.id = aus.userId
$projectCnd $condition
""");
cnd.and("aus.groupId", "=", project.getActivityGroupId());
sql.setVar("projectCnd", "AND u.id NOT IN ( SELECT selectUserId FROM `health_checkup_user_selection` WHERE projectId = '" + projectId + "' AND selectUserId IS NOT NULL )");
cnd.groupBy("u.loginname");
sql.setCondition(cnd);
List<NutMap> mapList = healthCheckupSingleService.listMap(sql);
List<String> userIds = mapList.stream().map(v -> v.getString("id")).collect(Collectors.toList());
//题目id
List<HealthCheckupUserSelection> selections = userIds.stream().map(v -> {
HealthCheckupUserSelection selection = new HealthCheckupUserSelection();
selection.setProjectId(projectId);
selection.setSelectUserId(v);
selection.setCampus(campus);
selection.setSubjectId(optionId);
selection.setSelectTime(new Date());
return selection;
}).collect(Collectors.toList());
manyAddOrRenewUtil.asyncExecuteFastInsert(selections, null);
return Result.success();
}
/**
* 按上年补齐未选人员;业务筛选、数据读写和结果组装在本服务内完成。
* @param projectId 当前体检项目主键
* @param lastYearProjectId 上一年度体检项目主键
* @return Result;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
@org.nutz.ioc.aop.Aop(org.nutz.aop.interceptor.ioc.TransAop.READ_COMMITTED)
public Result selectOptionByLastYear(String projectId, String lastYearProjectId) {
Dao dao = healthCheckupSingleService.dao();
//获取去年项目
HealthCheckupProject lastYearProject = dao.fetch(HealthCheckupProject.class, Cnd.where("id", "=", lastYearProjectId));
if (lastYearProject == null) {
return Result.error("请先创建去年的体检项目");
}
Cnd cnd = Cnd.NEW();
Sql sql = Sqls.create("""
SELECT
u.id
FROM
health_checkup_user hcu
LEFT JOIN `vw_user` u ON u.id = hcu.userId
$projectCnd $condition
""");
cnd.and("hcu.projectId", "=", projectId);
sql.setVar("projectCnd",
"AND u.id NOT IN ( SELECT selectUserId FROM `health_checkup_user_selection` WHERE projectId = '" + projectId + "' AND selectUserId IS NOT NULL )");
cnd.groupBy("u.loginname");
sql.setCondition(cnd);
List<NutMap> mapList = healthCheckupSingleService.listMap(sql);
if(CollectionUtil.isEmpty(mapList)) return Result.success();
List<String> userIds = mapList.stream().map(v -> v.getString("id")).filter(Strings::isNotBlank).toList();
//去年项目的人
Cnd aNew = Cnd.NEW();
aNew.and(HealthCheckupUserSelection::getProjectId, "=", lastYearProject.getId())
.and(HealthCheckupUserSelection::getSelectUserId, "in", userIds);
List<HealthCheckupUserSelection> lastYearSelectionList = dao.query(HealthCheckupUserSelection.class, aNew);
//去年项目下的医院
List<HealthCheckupProjectSubject> lastYearSubjectList = dao.query(HealthCheckupProjectSubject.class,
Cnd.where(HealthCheckupProjectSubject::getProjectId, "=", lastYearProject.getId()));
//今年项目下的医院
List<HealthCheckupProjectSubject> thisYearSubjectList = dao.query(HealthCheckupProjectSubject.class,
Cnd.where(HealthCheckupProjectSubject::getProjectId, "=", projectId));
List<HealthCheckupProjectSubjectMoney> thisYearSubjectMoneyList = dao.query(HealthCheckupProjectSubjectMoney.class,
Cnd.where(HealthCheckupProjectSubject::getProjectId, "=", projectId));
List<HealthCheckupUserSelection> selections = new ArrayList<>();
userIds.forEach(v -> {
//获取去年这个人选择的记录
HealthCheckupUserSelection userSelection = lastYearSelectionList.stream().filter(item -> item.getSelectUserId().equals(v)).findFirst().orElse(null);
if(ObjectUtil.isNotEmpty(userSelection)){
//去年这个人选择的选项是什么
HealthCheckupProjectSubject lastYearSubject = lastYearSubjectList.stream().filter(last -> last.getId().equals(userSelection.getSubjectId())).findFirst().orElse(null);
//今年项目下的选项
HealthCheckupProjectSubject thisYearSubject = thisYearSubjectList.stream().filter(thisYear -> thisYear.getOptionName().equals(lastYearSubject.getOptionName())).findFirst().orElse(null);
if (userSelection != null && thisYearSubject != null) {
//获取今年这个选项的收费
List<HealthCheckupProjectSubjectMoney> list = dao.query(HealthCheckupProjectSubjectMoney.class,
Cnd.where(HealthCheckupProjectSubjectMoney::getSubjectId, "=", thisYearSubject.getId()));
BigDecimal money = BigDecimal.valueOf(0.00);
String subjectMoneyId = null;
for (HealthCheckupProjectSubjectMoney subject : list) {
//找出这个人符合哪个项目
Cnd cndUser = Cnd.where("id", "=", v);
ConditionGroupUtil.applyConditionGroup(cndUser, subject.getMatchCnd());
int count = dao.count(View_user.class, cndUser);
if (count > 0) {
money = subject.getMoney();
subjectMoneyId = subject.getId();
break;
}
}
//如果有符合的就添加
if (subjectMoneyId != null) {
HealthCheckupUserSelection selection = new HealthCheckupUserSelection();
selection.setProjectId(projectId);
selection.setSelectUserId(v);
selection.setSubjectId(thisYearSubject.getId());
selection.setSubjectMoneyId(subjectMoneyId);
selection.setMoney(money);
selection.setSelectTime(new Date());
selections.add(selection);
}
}
} else {
//若去年未体检,则去本年校医院体检
HealthCheckupProjectSubject thisYearProjectSubject = thisYearSubjectList.stream()
.filter(e -> e.getOptionName().contains("校医院"))
.findFirst()
.orElse(null);
if(ObjectUtil.isNotEmpty(thisYearProjectSubject)) {
List<HealthCheckupProjectSubjectMoney> healthCheckupProjectSubjectMonies = thisYearSubjectMoneyList.stream()
.filter(e -> e.getSubjectId().equals(thisYearProjectSubject.getId()))
.collect(Collectors.toList());
if(CollectionUtil.isNotEmpty(healthCheckupProjectSubjectMonies)){
BigDecimal money = BigDecimal.valueOf(0.00);
String subjectMoneyId = null;
for (HealthCheckupProjectSubjectMoney subject : healthCheckupProjectSubjectMonies) {
//找出这个人符合哪个项目
Cnd cndUser = Cnd.where("id", "=", v);
ConditionGroupUtil.applyConditionGroup(cndUser, subject.getMatchCnd());
int count = dao.count(View_user.class, cndUser);
if (count > 0) {
money = subject.getMoney();
subjectMoneyId = subject.getId();
break;
}
}
//如果有符合的就添加
if (subjectMoneyId != null) {
HealthCheckupUserSelection selection = new HealthCheckupUserSelection();
selection.setProjectId(projectId);
selection.setSelectUserId(v);
selection.setSubjectId(thisYearProjectSubject.getId());
selection.setSubjectMoneyId(subjectMoneyId);
selection.setMoney(money);
selection.setSelectTime(new Date());
selections.add(selection);
}
}
}
}
});
dao.insert(selections);
return Result.success();
}
/**
* 给未选的人发送提醒;业务筛选、数据读写和结果组装在本服务内完成。
* @param projectId 当前体检项目主键
* @return Result;Result 的 code 为 0 表示成功,msg 为说明,data 为本接口业务数据。
*/
public Result msgNotSelect(String projectId) {
List<NutMap> userList = healthCheckupSingleService.receivePageData(projectId, null, "1");
if(CollectionUtil.isNotEmpty(userList)){
HealthCheckupProject project = healthCheckupProjectService.dao().fetch(HealthCheckupProject.class, Cnd.where("id", "=", projectId));
String smsMessage = StringUtils.defaultIfBlank(
project.getSmsMessage(),
"系统显示您本年度体检医院信息尚未维护,请尽快登录小程序完成体检医院选择及个人信息确认,以免影响正常体检安排。");
List<String> loginNameList = userList.stream()
.map(e -> e.getString("loginName"))
.collect(Collectors.toList());
// 使用当前项目的消息服务,保留项目标题、提醒正文和手机端入口。
smsService.massSend(loginNameList, project.getName(), smsMessage,
Globals.AppDomain + "/platform/healthCheckup/h5/list");
return Result.success("提醒成功");
} else {
return Result.success("不存在未体检人员");
}
}
}
@@ -9,6 +9,21 @@ import java.util.List;
public interface HealthCheckupSingleService extends BaseService<HealthCheckupProject> {
/**
* 查询项目实际人数,不按工会筛选。
* @param projectId 体检项目ID
* @return NutMap:__total__为本次体检人数,其他键为医院选项ID,值为人数;未录入项不返回
*/
NutMap actualStatistics(String projectId);
/**
* 保存当前项目整行实际人数。
* @param projectId 体检项目ID
* @param counts JSON对象,__total__及当前项目全部医院ID为键,非负整数为值
* @return 保存后的实际人数映射
*/
NutMap saveActualStatistics(String projectId, String counts);
/**
* 分页查询
*
@@ -0,0 +1,129 @@
package com.budwk.app.zhgh.dayofficework.healthCheckup.service.impl;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.sys.models.Sys_union;
import com.budwk.app.zhgh.dayofficework.healthCheckup.model.HealthCheckupFeedback;
import com.budwk.app.zhgh.dayofficework.healthCheckup.model.HealthCheckupProject;
import com.budwk.app.zhgh.dayofficework.healthCheckup.service.HealthCheckupFeedbackService;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.util.NutMap;
import java.util.ArrayList;
import java.util.List;
@IocBean(args = {"refer:dao"})
public class HealthCheckupFeedbackServiceImpl extends BaseServiceImpl<HealthCheckupFeedback> implements HealthCheckupFeedbackService {
public HealthCheckupFeedbackServiceImpl(Dao dao) {
super(dao);
}
@Override
public List<NutMap> listFeedback(String year, String projectId, String unionId) {
Cnd cnd = Cnd.NEW();
cnd.and("year", "=", year)
.and("projectId", "=", projectId)
.and("unionId", "=", unionId)
.desc("createdAt");
List<HealthCheckupFeedback> feedbackList = query(cnd);
List<NutMap> result = new ArrayList<>();
for (HealthCheckupFeedback feedback : feedbackList) {
result.add(NutMap.NEW()
.addv("id", feedback.getId())
.addv("year", feedback.getYear())
.addv("projectName", feedback.getProjectName())
.addv("unionName", feedback.getUnionName())
.addv("feedbackInfo", feedback.getFeedbackInfo())
.addv("createdBy", feedback.getCreatedBy())
.addv("createdAt", feedback.getCreatedAt()));
}
return result;
}
@Override
@org.nutz.ioc.aop.Aop(org.nutz.aop.interceptor.ioc.TransAop.READ_COMMITTED)
public NutMap saveFeedback(String year, String projectId, String unionId, String feedbackInfo) {
if (StrUtil.isBlank(year) || StrUtil.isBlank(projectId) || StrUtil.isBlank(unionId)) {
return NutMap.NEW().addv("code", 1).addv("msg", "请选择年度、体检项目、分工会后再录入反馈");
}
if (StrUtil.isBlank(feedbackInfo)) {
return NutMap.NEW().addv("code", 1).addv("msg", "请输入反馈信息");
}
// 保存前先根据页面选中的条件反查项目和分工会,确保反馈和主列表条件强关联。
HealthCheckupProject project = dao().fetch(HealthCheckupProject.class, projectId);
if (project == null) {
return NutMap.NEW().addv("code", 1).addv("msg", "当前体检项目不存在,请重新选择");
}
Sys_union union = dao().fetch(Sys_union.class, unionId);
if (union == null) {
return NutMap.NEW().addv("code", 1).addv("msg", "当前分工会不存在,请重新选择");
}
HealthCheckupFeedback feedback = new HealthCheckupFeedback();
feedback.setYear(year);
feedback.setProjectId(projectId);
feedback.setProjectName(project.getName());
feedback.setUnionId(unionId);
feedback.setUnionName(union.getName());
feedback.setFeedbackInfo(feedbackInfo);
insert(feedback);
return NutMap.NEW().addv("code", 0).addv("msg", "保存成功");
}
@Override
@org.nutz.ioc.aop.Aop(org.nutz.aop.interceptor.ioc.TransAop.READ_COMMITTED)
public NutMap deleteFeedback(String id) {
if (StrUtil.isBlank(id)) {
return NutMap.NEW().addv("code", 1).addv("msg", "反馈ID不能为空");
}
HealthCheckupFeedback feedback = fetch(id);
if (feedback == null) {
return NutMap.NEW().addv("code", 1).addv("msg", "反馈记录不存在");
}
delete(id);
return NutMap.NEW().addv("code", 0).addv("msg", "删除成功");
}
@Override
@org.nutz.ioc.aop.Aop(org.nutz.aop.interceptor.ioc.TransAop.READ_COMMITTED)
public NutMap deleteFeedbackBatch(String[] ids) {
if (ids == null || ids.length == 0) {
return NutMap.NEW().addv("code", 1).addv("msg", "请选择需要删除的反馈");
}
Cnd cnd = Cnd.NEW();
cnd.and("id", "in", ids);
clear(cnd);
return NutMap.NEW().addv("code", 0).addv("msg", "批量删除成功");
}
@Override
public Pagination listFeedbackPage(Integer pageNumber, Integer pageSize, String year, String projectId, String unionId) {
Cnd cnd = Cnd.NEW();
cnd.andEX("hcf.year", "=", year);
cnd.andEX("hcf.projectId", "=", projectId);
cnd.andEX("hcf.unionId", "=", unionId);
cnd.desc("hcf.createdAt");
Sql sql = Sqls.create("""
SELECT
hcf.id,
hcf.year,
hcf.projectName,
hcf.unionName,
hcf.feedbackInfo,
hcf.createdAt,
su.username AS createdByName
FROM health_checkup_feedback hcf
LEFT JOIN sys_user su ON su.id = hcf.createdBy
$condition
""");
sql.setCondition(cnd);
return listPageMap(pageNumber, pageSize, sql);
}
}
@@ -0,0 +1,60 @@
package com.budwk.app.zhgh.dayofficework.healthCheckup.service.impl;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.zhgh.dayofficework.healthCheckup.model.HealthCheckupUser;
import com.budwk.app.zhgh.dayofficework.healthCheckup.service.HealthCheckupPersonConfirmStatisticsService;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.loader.annotation.IocBean;
/**
* @ClassName HealthCheckupPersonConfirmStatisticsServiceImpl
* @Description 工会人员确认情况统计实现
* @Author Codex
* @Date 2026/4/28
*/
@IocBean(args = {"refer:dao"})
public class HealthCheckupPersonConfirmStatisticsServiceImpl extends BaseServiceImpl<HealthCheckupUser> implements HealthCheckupPersonConfirmStatisticsService {
public HealthCheckupPersonConfirmStatisticsServiceImpl(Dao dao) {
super(dao);
}
@Override
public Pagination personConfirmPageData(Integer pageNumber, Integer pageSize, Integer year, String projectId, String unionId) {
// 统计以系统工会为主表,保证未报名的工会也会显示在列表中。
Sql sql = Sqls.create("""
SELECT
su.id AS unionId,
su.name AS unionName,
COUNT(CASE WHEN hcp.id IS NOT NULL THEN hcu.id END) AS totalCount,
SUM(CASE WHEN hcp.id IS NOT NULL AND hcu.isAudit = '1' THEN 1 ELSE 0 END) AS confirmedCount,
SUM(CASE WHEN hcp.id IS NOT NULL AND hcu.id IS NOT NULL AND (hcu.isAudit IS NULL OR hcu.isAudit <> '1') THEN 1 ELSE 0 END) AS unconfirmedCount,
CASE
WHEN COUNT(CASE WHEN hcp.id IS NOT NULL THEN hcu.id END) > 0
AND COUNT(CASE WHEN hcp.id IS NOT NULL THEN hcu.id END) = SUM(CASE WHEN hcp.id IS NOT NULL AND hcu.isAudit = '1' THEN 1 ELSE 0 END) THEN '已完成'
ELSE '未完成'
END AS completeStatus
FROM sys_union su
LEFT JOIN health_checkup_user hcu ON hcu.unionId = su.id
AND hcu.delFlag = 0
AND hcu.projectId = @projectId
LEFT JOIN health_checkup_project hcp ON hcp.id = hcu.projectId
AND hcp.delFlag = 0
AND hcp.year = @year
$condition
""").setParam("projectId", projectId).setParam("year", year);
Cnd cnd = Cnd.NEW();
cnd.andEX("su.id", "=", unionId);
cnd.groupBy("su.id,su.name");
cnd.asc("completeStatus");
cnd.asc("su.unionCode");
sql.setCondition(cnd);
// isAudit 存在于 health_checkup_user 中,只要还有未确认人员,该工会状态就显示未完成。
// 排序按报名总人数倒序,人数相同时再按工会编码升序,保证列表顺序稳定。
return listPageMap(pageNumber, pageSize, sql);
}
}
@@ -1,8 +1,12 @@
package com.budwk.app.zhgh.dayofficework.healthCheckup.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.base.utils.ConditionGroupUtil;
import com.budwk.app.base.utils.ManyAddOrRenewUtil;
import com.budwk.app.sys.views.View_user;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.activity.basic.models.ActivityUserScope;
import com.budwk.app.zhgh.dayofficework.healthCheckup.model.*;
import com.budwk.app.zhgh.dayofficework.healthCheckup.service.HealthCheckupProjectService;
@@ -12,6 +16,7 @@ import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.Daos;
import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
@@ -19,8 +24,8 @@ import org.nutz.lang.Strings;
import org.nutz.lang.random.R;
import org.nutz.lang.util.NutMap;
import java.util.List;
import java.util.Set;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;
/**
@@ -47,13 +52,14 @@ public class HealthCheckupProjectServiceImpl extends BaseServiceImpl<HealthCheck
}
HealthCheckupProject project = fetchLinks(fetch(id), "healthCheckupProjectSubjects", Cnd.NEW().asc("optionSort"));
project.getHealthCheckupProjectSubjects().forEach(v -> {
v.setSubjectMoneys(dao().query(HealthCheckupProjectSubjectMoney.class, Cnd.where("subjectId", "=", v.getId())));
v.setSubjectMoneys(dao().query(HealthCheckupProjectSubjectMoney.class, Cnd.where("subjectId", "=", v.getId()).asc("serialNumber")));
});
return project;
}
@Override
@org.nutz.ioc.aop.Aop(org.nutz.aop.interceptor.ioc.TransAop.READ_COMMITTED)
public void addHealthCheckupUser(HealthCheckupProject healthCheckupProject) {
List<HealthCheckupUser> needAddUsers = getHealthCheckupUserListByProject(healthCheckupProject);
manyAddOrRenewUtil.asyncExecuteFastInsert(needAddUsers, null);
@@ -86,12 +92,29 @@ public class HealthCheckupProjectServiceImpl extends BaseServiceImpl<HealthCheck
.filter(v -> !sourceSelectionUserIds.contains(v.getUserId()))
.collect(Collectors.toList());
List<String> userSelectionIds = userList.stream().filter(needDeleteUserIds::contains).map(HealthCheckupUser::getId).collect(Collectors.toList());
dao().clear(HealthCheckupUser.class, Cnd.where("userId", "in", needDeleteUserIds));
dao().clear(HealthCheckupUserSelection.class, Cnd.where("selectUserId", "in", needDeleteUserIds));
dao().clear(HealthCheckupUserCompanion.class, Cnd.where("userSelectionId", "in", userSelectionIds));
// 更新名单仅清理当前项目,并使用真实选择记录主键清理家属,不能影响其他年度。
if (!needDeleteUserIds.isEmpty()) {
List<HealthCheckupUserSelection> removed = dao().query(HealthCheckupUserSelection.class,
Cnd.where("projectId", "=", healthCheckupProject.getId()).and("selectUserId", "in", needDeleteUserIds));
List<String> selectionIds = removed.stream().map(HealthCheckupUserSelection::getId).collect(Collectors.toList());
if (!selectionIds.isEmpty()) {
dao().clear(HealthCheckupUserCompanion.class, Cnd.where("userSelectionId", "in", selectionIds));
}
dao().clear(HealthCheckupUserSelection.class, Cnd.where("projectId", "=", healthCheckupProject.getId()).and("selectUserId", "in", needDeleteUserIds));
dao().clear(HealthCheckupUser.class, Cnd.where("projectId", "=", healthCheckupProject.getId()).and("userId", "in", needDeleteUserIds));
}
manyAddOrRenewUtil.asyncExecuteFastInsert(needInsertSelectionUsers, null);
// Sql updateSql = Sqls.create("""
// update health_checkup_user cu
// left join health_checkup_project p on cu.projectId=p.id
// left join sys_user u on cu.userId=u.id
// set cu.phone=if(cu.phone is null,u.mobile,cu.phone),cu.card=u.idCard,cu.marriage=u.marriage
// ,cu.personType=(case when p.name like '%在职教职工%' then '在职教职工' when p.name like '%校聘高知%' then '校聘高知保健对象' else '' end)
// where cu.projectId=@projectId2 and cu.confirmStatus!='1'
// """);
// updateSql.setParam("projectId2", healthCheckupProject.getId());
// dao().execute(updateSql);
}
@@ -108,6 +131,11 @@ public class HealthCheckupProjectServiceImpl extends BaseServiceImpl<HealthCheck
user.setLoginName(v.getLoginname());
user.setUserName(v.getUsername());
user.setSex(v.getSex());
// 名单生成时带入当前人员档案,手机端只读回显并据此校验报名资格。
user.setPhone(v.getMobile());
user.setCard(v.getIdCard());
user.setMarriage(v.getMarriage());
user.setPersonType(v.getPersonType());
user.setUnitId(v.getUnitId());
user.setUnitName(v.getUnitName());
user.setUnionId(v.getUnionId());
@@ -115,4 +143,35 @@ public class HealthCheckupProjectServiceImpl extends BaseServiceImpl<HealthCheck
return user;
}).collect(Collectors.toList());
}
/**
* 获取上年度体检医院
* @param healthCheckupUserSelection
* @return
*/
public String findLastYearHospital(HealthCheckupUserSelection healthCheckupUserSelection){
Date selectTime = new Date();
if(ObjectUtil.isNotEmpty(selectTime)) {
Calendar instance = Calendar.getInstance();
instance.setTime(selectTime);
int year = instance.get(Calendar.YEAR);
LocalDate start = LocalDate.of(year - 1, 1, 1);
LocalDate end = LocalDate.of(year - 1, 12, 31);
Cnd lastYearCndSelection = Cnd.NEW();
lastYearCndSelection.and("selectUserId","=",healthCheckupUserSelection.getSelectUserId());
lastYearCndSelection.and("selectTime", ">=", start.toString()).and("selectTime", "<=", end.toString());
lastYearCndSelection.where().andNotIsNull("selectTime");
lastYearCndSelection.desc("selectTime");
List<HealthCheckupUserSelection> lastYearSelections = dao().query(HealthCheckupUserSelection.class, lastYearCndSelection);
if(CollectionUtil.isNotEmpty(lastYearSelections)) {
Cnd cnd = Cnd.NEW();
cnd.and("id","=",lastYearSelections.get(0).getSubjectId());
HealthCheckupProjectSubject projectSubject = dao().fetch(HealthCheckupProjectSubject.class, cnd);
if(ObjectUtil.isNotEmpty(projectSubject)) {
return projectSubject.getOptionName();
}
}
}
return null;
}
}
@@ -1,11 +1,21 @@
package com.budwk.app.zhgh.dayofficework.healthCheckup.service.impl;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.zhgh.dayofficework.healthCheckup.model.HealthCheckupActualStatistics;
import com.budwk.app.web.commons.auth.utils.AuthUtil;
import com.budwk.app.base.constant.RoleConstant;
import org.nutz.ioc.aop.Aop;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.json.Json;
import java.util.LinkedHashSet;
import java.util.Set;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.base.utils.StringUtil;
import com.budwk.app.zhgh.dayofficework.healthCheckup.model.HealthCheckupProject;
import com.budwk.app.zhgh.dayofficework.healthCheckup.model.HealthCheckupProjectSubject;
import com.budwk.app.zhgh.dayofficework.healthCheckup.service.HealthCheckupProjectService;
import com.budwk.app.zhgh.dayofficework.healthCheckup.service.HealthCheckupSingleService;
import org.apache.commons.lang3.StringUtils;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
@@ -29,6 +39,81 @@ public class HealthCheckupSingleServiceImpl extends BaseServiceImpl<HealthChecku
@Inject
private HealthCheckupProjectService healthCheckupProjectService;
/** {@inheritDoc} */
@Override
public NutMap actualStatistics(String projectId) {
if (Strings.isBlank(projectId) || dao().fetch(HealthCheckupProject.class, projectId) == null) {
throw new IllegalArgumentException("请选择有效的体检项目");
}
NutMap result = NutMap.NEW();
dao().query(HealthCheckupActualStatistics.class, Cnd.where("projectId", "=", projectId))
.forEach(item -> result.put(item.getSubjectId(), item.getActualCount()));
return result;
}
/** {@inheritDoc} */
@Override
@Aop(TransAop.READ_COMMITTED)
public NutMap saveActualStatistics(String projectId, String counts) {
// 实际人数属于整个项目,分工会查看权限不能用于覆盖项目级统计。
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) {
throw new IllegalArgumentException("仅系统管理员或校工会管理员可维护实际体检人数");
}
actualStatistics(projectId);
NutMap values;
try {
values = Json.fromJson(NutMap.class, counts);
} catch (Exception e) {
throw new IllegalArgumentException("实际人数格式错误");
}
Set<String> keys = new LinkedHashSet<>();
keys.add("__total__");
dao().query(HealthCheckupProjectSubject.class, Cnd.where("projectId", "=", projectId))
.forEach(subject -> keys.add(subject.getId()));
// 要求整行提交且医院仍属于当前项目,防止跨项目写入或医院变更后漏存。
if (values == null || !keys.equals(values.keySet())) {
throw new IllegalArgumentException("医院选项已变化或人数未填全,请刷新后重新录入");
}
for (String key : keys) {
Object value = values.get(key);
if (value == null || !value.toString().matches("[0-9]+")) {
throw new IllegalArgumentException("人数必须为非负整数");
}
try {
values.put(key, Integer.valueOf(value.toString()));
} catch (NumberFormatException e) {
throw new IllegalArgumentException("人数超出允许范围");
}
}
// 在任何写入之前校验医院合计;用long累加,避免多个合法整数相加溢出。
long hospitalTotal = 0L;
for (String key : keys) {
if (!"__total__".equals(key)) {
hospitalTotal += values.getInt(key);
}
}
if (hospitalTotal > values.getInt("__total__")) {
throw new IllegalArgumentException("各医院人数合计不能大于本次体检人数");
}
// 校验整行通过后统一保存,总人数允许大于医院合计。
for (String key : keys) {
HealthCheckupActualStatistics item = dao().fetch(HealthCheckupActualStatistics.class,
Cnd.where("projectId", "=", projectId).and("subjectId", "=", key));
if (item == null) {
item = new HealthCheckupActualStatistics();
item.setProjectId(projectId);
item.setSubjectId(key);
item.setActualCount(values.getInt(key));
dao().insert(item);
} else {
item.setActualCount(values.getInt(key));
dao().update(item);
}
}
return values;
}
@Override
public NutMap pageData(String projectId, String unionId) {
HealthCheckupProject project = healthCheckupProjectService.findOne(projectId);
@@ -47,6 +132,7 @@ public class HealthCheckupSingleServiceImpl extends BaseServiceImpl<HealthChecku
sql.setCondition(cnd);
//分工会参加体检人数汇总
List<NutMap> healthCheckupList = listMap(sql);
//该体检项目下的套餐
List<HealthCheckupProjectSubject> subjectList = project.getHealthCheckupProjectSubjects();
@@ -54,9 +140,12 @@ public class HealthCheckupSingleServiceImpl extends BaseServiceImpl<HealthChecku
List<NutMap> userSelectList = this.getUserSelectionByProjectId(projectId);
for (NutMap healthCheckup : healthCheckupList) {
List<NutMap> thisUnionSelectList = userSelectList.stream().filter(x ->
x.getString("unionId").equals(healthCheckup.getString("id")) &&
x.getString("projectId").equals(projectId)).collect(Collectors.toList());
List<NutMap> thisUnionSelectList = userSelectList.stream()
.filter(x ->
x.getString("unionId").equals(healthCheckup.getString("id"))
&& x.getString("projectId").equals(projectId)
)
.collect(Collectors.toList());
subjectList.forEach(o -> {
//每一项选择的人数
@@ -121,7 +210,6 @@ public class HealthCheckupSingleServiceImpl extends BaseServiceImpl<HealthChecku
return listMap(sql);
}
@Override
public List<NutMap> receivePageDataColumns(String projectId) {
List<NutMap> labelList = new ArrayList<>();
@@ -210,16 +298,17 @@ public class HealthCheckupSingleServiceImpl extends BaseServiceImpl<HealthChecku
u.username AS userName,
u.sex,
u.birthday,
u.personType,
hcu.personType,
u.unitName,
u.unitCode,
u.marriage,
u.unionName,
u.idCard,
u.mobile,
hcu.marriage,
hcu.unionName,
hcu.phone as mobile,
hcu.card as idCard,
hcps.optionName subjectName,
hcus.receiveAddress,
hcc.campusName,
hcpsm.money,
hcpsm.settlementMoney,
(
SELECT
GROUP_CONCAT( hcps.optionName )
@@ -229,13 +318,17 @@ public class HealthCheckupSingleServiceImpl extends BaseServiceImpl<HealthChecku
WHERE
hcus.projectId = @projectId
AND hcus.selectUserId = u.id
) optionName
) optionName,
hcus.receiveAddress,
hcp.name projectName
FROM
`health_checkup_user_selection` hcus
LEFT JOIN `vw_user` u ON u.id = hcus.selectUserId
LEFT JOIN `health_checkup_user` hcu ON hcu.userId = hcus.selectUserId and hcu.projectId=@projectId
LEFT JOIN `health_checkup_project_subject` hcps ON hcps.id = hcus.subjectId
LEFT JOIN `health_checkup_project_subject_money` hcpsm ON hcpsm.projectId = @projectId and hcpsm.subjectId=hcps.id and hcus.subjectMoneyId=hcpsm.id
LEFT JOIN `health_checkup_campus` hcc ON hcc.id = hcus.campus
LEFT JOIN `health_checkup_project` hcp ON hcp.id = hcu.projectId
$condition
""").setParam("projectId", projectId);
cnd.and("hcus.projectId", "=", projectId);
@@ -253,10 +346,12 @@ public class HealthCheckupSingleServiceImpl extends BaseServiceImpl<HealthChecku
hcu.unitName,
hcu.unionName,
u.idcard idCard,
u.mobile
u.mobile,
hcp.name projectName
FROM
`health_checkup_user` hcu
LEFT JOIN `vw_user` u ON u.id = hcu.userId
LEFT JOIN `health_checkup_project` hcp ON hcp.id = hcu.projectId
$condition
""");
sql.setVar("projectCnd", projectId);
@@ -22,4 +22,10 @@ public class HealthCheckupImportTemp extends ExcelImportError {
@ExcelProperty("姓名")
private String userName;
@ExcelProperty("手机号")
private String phone;
@ExcelProperty("体检报告")
private String medicalExamination;
}
@@ -70,12 +70,14 @@ const initTableMixins = {
pageData(data = null) {
const address = this.pageDataUrl ? this.pageDataUrl : loc() + "/pageData"
this.tableLoading = true
this.$axios.post(address, data ? data : this.pageForm).then((res) => {
this.tableLoading = false
return this.$axios.post(address, data ? data : this.pageForm).then((res) => {
if (res.code === 0) {
this.tableData = res.data.list
this.pageForm.totalCount = res.data.totalCount
}
}).finally(() => {
// 查询失败也释放表格遮罩,供体检等复用此 mixin 的页面统一处理。
this.tableLoading = false
})
},
notifySuccess(msg) {
@@ -0,0 +1,126 @@
/**
* H5 弹框历史管理:一层可见弹框对应一条历史;关闭和跳转均等待历史回退完成。
* 页面通过 healthCheckupHistory 显式接入,其他业务页保持现有导航行为。
*/
(() => {
const entries = [];
let pending = null;
let root = null;
let sequence = 0;
let syncing = false;
const marker = '__h5Overlay';
const session = Date.now() + '-' + Math.random();
const closeEntries = (depth) => {
syncing = true;
while (entries.length > depth) {
const entry = entries.pop();
entry.close();
}
syncing = false;
};
const backTo = (depth) => {
if (pending) return pending.promise.then(() => backTo(depth));
const count = entries.length - depth;
if (count <= 0) return Promise.resolve();
let resolve;
const promise = new Promise(done => { resolve = done; });
pending = {promise, resolve};
window.history.go(-count);
return promise;
};
const manager = {
// key 标识具体弹框;close 同步关闭其 UI;返回 Promise 在历史状态稳定后结束。
open(key, close) {
if (pending) return pending.promise.then(() => manager.open(key, close));
if (entries.some(entry => entry.key === key)) return Promise.resolve();
entries.push({key, close});
const state = Object.assign({}, window.history.state || {});
state[marker] = {session, depth: entries.length};
window.history.pushState(state, '', window.location.href);
return Promise.resolve();
},
close(key) {
if (syncing) return Promise.resolve();
const index = entries.findIndex(entry => entry.key === key);
// 下层弹框主动关闭时一并清理其上方弹框,防止留下孤立历史。
return index < 0 ? Promise.resolve() : backTo(index);
},
clear() { return backTo(0); },
back() {
return entries.length ? backTo(entries.length - 1) : Promise.resolve().then(() => historyBack());
}
};
window.H5PopupHistory = manager;
// 捕获阶段消费弹框历史,避免 jquery.pjax 把同页历史当成业务页面跳转。
window.addEventListener('popstate', event => {
const state = event.state && event.state[marker];
if (!entries.length && !pending && !state) return;
event.stopImmediatePropagation();
const depth = state && state.session === session ? Math.min(state.depth, entries.length) : 0;
closeEntries(depth);
const waiting = pending;
pending = null;
if (waiting) waiting.resolve();
// 前进到已关闭弹框的历史时不重新打开;去掉旧标记,保持页面状态一致。
if (state && !entries.length) {
const clean = Object.assign({}, event.state);
delete clean[marker];
window.history.replaceState(clean, '', window.location.href);
}
}, true);
Vue.mixin({
created() {
if (this.$options.healthCheckupHistory) root = this;
if (!this.$root.$options.healthCheckupHistory) return;
const name = (this.$options.name || '').toLowerCase().replace(/-/g, '');
if (!['vanpopup', 'vandialog'].includes(name)) return;
// 只登记最外层弹框,避免 Dialog 内部的 Popup 重复增加历史。
let parent = this.$parent;
while (parent && parent !== this.$root) {
const parentName = (parent.$options.name || '').toLowerCase().replace(/-/g, '');
if (['vanpopup', 'vandialog'].includes(parentName)) return;
parent = parent.$parent;
}
const key = 'component-' + (++sequence);
this._overlayKey = key;
this._unwatchOverlay = this.$watch('value', value => {
if (value) {
manager.open(key, () => this.$emit('input', false)).then(() => {
if (!this.value || this._isDestroyed) manager.close(key);
});
} else manager.close(key);
}, {immediate: true});
},
beforeDestroy() {
if (this._unwatchOverlay) this._unwatchOverlay();
if (this._overlayKey) manager.close(this._overlayKey);
if (root === this) {
root = null;
manager.clear();
}
}
});
// PJAX 替换 DOM 不会自动销毁 Vue 实例;主动注销页面及其弹框监听。
$(document).on('pjax:beforeReplace.healthCheckupHistory', () => {
if (root && !root._isDestroyed) root.$destroy();
});
window.healthCheckupHistoryMixin = {
methods: {
historyBack() { return manager.back(); },
navigateHealthCheckup(url, data = null) {
return manager.clear().then(() => pjaxReplace(url, data));
},
// options 是 Vant 确认框配置;只在用户确认且弹框历史已清理后 resolve。
$healthConfirm(options) {
const key = 'confirm-' + (++sequence);
return manager.open(key, () => vant.Dialog.close()).then(() => {
return vant.Dialog.confirm(options).then(value => {
return manager.close(key).then(() => value);
}, error => {
return manager.close(key).then(() => Promise.reject(error));
});
});
}
}
};
})();
@@ -76,6 +76,7 @@
></script>
<script src="https://map.qq.com/api/gljs?v=2.exp&key=MLLBZ-GQECI-ASXG7-5GNOZ-XW2OF-H5BVH"></script>
<script src="${base!}/assets/platform/js/util/h5PopupHistory.js?v=20261110-2"></script>
<script nonce="${cspNonce!}">
Vue.config.devtools = true
@@ -2080,7 +2080,7 @@ layout("/layouts/v4/baseLayout.html"){
// 获取活动列表
getActivity() {
$.get("/platform/home/listHomeActivity").then((res) => {
$.get("/platform/home/listHomeActivity", {platform: "PC"}).then((res) => {
if (res.code === 0) {
this.activityList = res.data
}
@@ -61,7 +61,7 @@ const act = {
methods: {
/*查询活动*/
listAct() {
this.$axios.post("/platform/home/listHomeActivity").then((res) => {
this.$axios.post("/platform/home/listHomeActivity", {platform: "PC"}).then((res) => {
if (res.code === 0) {
// for (let i = 0; i < 10; i++) {
// this.actList = this.actList.concat(res.data)
@@ -58,7 +58,7 @@ layout("/layouts/platform.html"){
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="addDialogVisible = false">取 消</el-button>
<el-button @click="cancelAddCampus">取 消</el-button>
<el-button type="primary" @click="doAdd">确 定</el-button>
</span>
</el-dialog>
@@ -73,70 +73,83 @@ layout("/layouts/platform.html"){
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="editDialogVisible = false">取 消</el-button>
<el-button @click="cancelEditCampus">取 消</el-button>
<el-button type="primary" @click="doEdit">确 定</el-button>
</span>
</el-dialog>
</div>
<script nonce="${cspNonce!}">
new Vue({
new Vue({
el: '#app',
mixins: [initTableMixins],
data() {
return {
addDialogVisible: false,
editDialogVisible: false,
formData: {},
rules: {
campusName: [{required: true, message: '请输入院区名称', trigger: ['change', 'blur']}],
campusCode: [{required: true, message: '请输入院区编码', trigger: ['change', 'blur']}]
campusName: [{
required: true,
message: '请输入院区名称',
trigger: ['change', 'blur']
}],
campusCode: [{
required: true,
message: '请输入院区编码',
trigger: ['change', 'blur']
}]
},
tableData: []
}
};
},
methods: {
cancelAddCampus() {
// 仅关闭当前弹框,不执行提交或保存操作。
this.$set(this, 'addDialogVisible', false);
},
cancelEditCampus() {
// 仅关闭当前弹框,不执行提交或保存操作。
this.$set(this, 'editDialogVisible', false);
},
openAdd() {
this.formData = {}
this.addDialogVisible = true
if (this.$refs["addForm"])
this.$refs["addForm"].resetFields()
this.formData = {};
this.addDialogVisible = true;
if (this.$refs["addForm"]) this.$refs["addForm"].resetFields();
},
doAdd() {
this.$refs['addForm'].validate(valid => {
if (valid) {
$.post('/platform/healthCheckup/campus/doAdd', this.formData).then(res => {
this.$axios.post('/platform/healthCheckup/campus/doAdd', this.formData).then(res => {
if (res.code === 0) {
this.addDialogVisible = false
this.pageData()
this.$message.success(res.msg)
this.addDialogVisible = false;
this.pageData();
this.$message.success(res.msg);
} else {
this.$message.error(res.msg)
this.$message.error(res.msg);
}
})
});
}
})
});
},
openEdit(obj) {
this.formData = JSON.parse(JSON.stringify(obj))
this.editDialogVisible = true
this.formData = JSON.parse(JSON.stringify(obj));
this.editDialogVisible = true;
},
doEdit() {
this.$refs['editForm'].validate(valid => {
if (valid) {
$.post('/platform/healthCheckup/campus/doEdit', this.formData).then(res => {
this.$axios.post('/platform/healthCheckup/campus/doEdit', this.formData).then(res => {
if (res.code === 0) {
this.editDialogVisible = false
this.pageData()
this.$message.success(res.msg)
this.editDialogVisible = false;
this.pageData();
this.$message.success(res.msg);
} else {
this.$message.error(res.msg)
this.$message.error(res.msg);
}
})
});
}
})
});
},
doDelete(id) {
this.$confirm('此操作将删除院区,确认删除吗?', '提示', {
@@ -144,20 +157,21 @@ layout("/layouts/platform.html"){
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
$.post('/platform/healthCheckup/campus/doDelete', {id: id}).then(res => {
this.$axios.post('/platform/healthCheckup/campus/doDelete', {
id: id
}).then(res => {
if (res.code === 0) {
this.doSearch()
this.$message.success(res.msg)
this.doSearch();
this.$message.success(res.msg);
}
})
}).catch(() => {
})
});
}).catch(() => {});
}
},
created() {
this.pageData()
this.pageData();
}
})
});
</script>
<!--#
@@ -1,5 +1,6 @@
const CONDITION_STRUCTURE_DIALOG = {
template: /*language=HTML*/ `
template: /*language=HTML*/`
<el-dialog
:close-on-click-modal="false"
:visible.sync="dialogVisible"
@@ -76,7 +77,7 @@ const CONDITION_STRUCTURE_DIALOG = {
</el-col>
</el-row>
<span slot="footer">
<el-button @click="dialogVisible = false">关 闭</el-button>
<el-button @click="closeConditionDialog">关 闭</el-button>
<el-button type="primary" @click="doSubmit">确 认</el-button>
</span>
</el-dialog>
@@ -89,70 +90,92 @@ const CONDITION_STRUCTURE_DIALOG = {
method: "",
conditions: []
},
fields: [
{
fields: [{
"label": "性别",
"value": "sex",
"type": "select",
"options": [{"value": "男", "label": "男"}, {"value": "女", "label": "女"}]
},
{
"options": [{
"value": "男",
"label": "男"
}, {
"value": "女",
"label": "女"
}]
}, {
"label": "出生日期",
"value": "birthday",
"type": "date",
},
{
"type": "date"
}, {
"label": "婚姻状况",
"value": "marriage",
"type": "select",
"options": [{"value": "未婚", "label": "未婚"}, {"value": "已婚", "label": "已婚"}]
}
],
operationalOption: [
{"label": "等于(=)", "value": "="},
{"label": "不等于(!=)", "value": "!="},
{"label": "小于(<)", "value": "<"},
{"label": "小于等于(<=)", "value": "<="},
{"label": "大于(>)", "value": ">"},
{"label": "大于等于(>=)", "value": ">="},
]
}
"options": [{
"value": "未婚",
"label": "未婚"
}, {
"value": "已婚",
"label": "已婚"
}]
}],
operationalOption: [{
"label": "等于(=)",
"value": "="
}, {
"label": "不等于(!=)",
"value": "!="
}, {
"label": "小于(<)",
"value": "<"
}, {
"label": "小于等于(<=)",
"value": "<="
}, {
"label": "大于(>)",
"value": ">"
}, {
"label": "大于等于(>=)",
"value": ">="
}]
};
},
methods: {
async onOpen(formData) {
console.log(formData)
closeConditionDialog() {
// 仅关闭当前弹框,不执行提交或保存操作。
this.$set(this, 'dialogVisible', false);
},
onOpen(formData) {
console.log(formData);
if (formData) {
this.formData = formData
}else{
this.formData = formData;
} else {
this.formData = {
method: "",
conditions: []
};
}
}
this.dialogVisible = true
this.$forceUpdate()
this.dialogVisible = true;
this.$forceUpdate();
},
fieldChange(val, cnd) {
this.$set(cnd, "fieldObj", this.fields.find(v => v.value === val))
this.$set(cnd, "value", null)
this.$forceUpdate()
this.$set(cnd, "fieldObj", this.fields.find(v => v.value === val));
this.$set(cnd, "value", null);
this.$forceUpdate();
},
doSubmit() {
if (!this.formData.method) {
this.$message.error("请选择匹配方式")
return
this.$message.error("请选择匹配方式");
return;
}
if (this.formData.conditions.length === 0) {
this.$message.error("请添加条件")
return
this.$message.error("请添加条件");
return;
}
if (this.formData.conditions.some(v => !v.field || !v.operational || !v.value)) {
this.$message.error("请填写完整条件")
return
this.$message.error("请填写完整条件");
return;
}
this.$emit("confirm", this.formData)
this.dialogVisible = false
this.$emit("confirm", this.formData);
this.dialogVisible = false;
}
}
}
};
@@ -0,0 +1,201 @@
<!--#
layout("/layouts/platform.html"){
#-->
<div id="app" class="platform" v-cloak>
<guava ref="guava" padding="0 5%">
<template>
<el-card shadow="never">
<search @search="doSearch">
<search-item label="年度">
<el-date-picker
v-model="pageForm.year"
type="year"
style="width: 100%;"
value-format="yyyy"
placeholder="请选择年度"
@change="yearChange">
</el-date-picker>
</search-item>
<search-item label="体检项目">
<el-select v-model="pageForm.projectId"
style="width: 100%;"
clearable
@change="doSearch">
<el-option v-for="item in projectOptions"
:key="item.id"
:label="item.name"
:value="item.id"></el-option>
</el-select>
</search-item>
<search-item label="分工会" v-if="$auth.hasRoleOr(['SYSADMIN', 'SCHOOL_UNION_ADMIN'])">
<el-select v-model="pageForm.unionId"
style="width: 100%;"
clearable
filterable
@change="doSearch">
<el-option v-for="item in unions"
:key="item.id"
:label="item.name"
:value="item.id"></el-option>
</el-select>
</search-item>
</search>
</el-card>
<el-card shadow="never">
<table-tool label="问题反馈列表">
<el-button type="danger"
size="small"
:disabled="selectedFeedbackIds.length === 0"
@click="deleteFeedbackBatch">批量删除</el-button>
</table-tool>
<el-table :data="tableData"
style="width: 100%"
row-key="id"
:size="tableSize"
v-loading="tableLoading"
@selection-change="handleSelectionChange"
ref="table"
class="vi-table">
<el-table-column type="selection"
width="55"
align="center"
header-align="center"></el-table-column>
<el-table-column type="index"
:index="indexMethod"
label="序号"
width="80px"
align="center"
header-align="center"></el-table-column>
<el-table-column prop="year"
label="年度"
min-width="120px"
align="center"
header-align="center"
show-overflow-tooltip></el-table-column>
<el-table-column prop="projectName"
label="体检项目"
min-width="160px"
align="center"
header-align="center"
show-overflow-tooltip></el-table-column>
<el-table-column prop="unionName"
label="分工会"
min-width="160px"
align="center"
header-align="center"
show-overflow-tooltip></el-table-column>
<el-table-column prop="feedbackInfo"
label="反馈信息"
min-width="240px"
align="center"
header-align="center"
show-overflow-tooltip>
<template slot-scope="{row}">
<div v-html="row.feedbackInfo" style="white-space: pre-line !important;"></div>
</template>
</el-table-column>
<el-table-column prop="createdByName"
label="创建人"
min-width="120px"
align="center"
header-align="center"
show-overflow-tooltip></el-table-column>
<el-table-column prop="createdAt" label="创建时间"
width="180px"
align="center"
header-align="center">
<template slot-scope="{row}">
{{ row.createdAt ? $moment(row.createdAt).format('YYYY-MM-DD HH:mm:ss') : '' }}
</template>
</el-table-column>
</el-table>
<!--#include("/layouts/pagination.html"){}#-->
</el-card>
</template>
</guava>
</div>
<script nonce="${cspNonce!}">
const vue = new Vue({
el: '#app',
mixins: [initTableMixins],
data() {
return {
tableData: [],
selectedFeedbackIds: [],
unions: [],
projectOptions: []
};
},
methods: {
handleSelectionChange(rows) {
this.selectedFeedbackIds = rows.map(item => {
return item.id;
});
},
deleteFeedbackBatch() {
if (this.selectedFeedbackIds.length === 0) {
this.notifyWarning("请选择需要删除的反馈");
return;
}
return Promise.resolve(this.$confirm("确认批量删除选中的反馈吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
})).then(confirm => {
if (confirm === "confirm") {
return this.$axios.post("/platform/healthCheckup/list/mange/deleteFeedbackBatch", {
ids: this.selectedFeedbackIds.join(",")
}).then(resp => {
if (resp.code === 0) {
this.notifySuccess(resp.msg);
return Promise.resolve(this.pageData()).then(() => {});
} else {
this.notifyError(resp.msg);
}
});
} else {}
});
},
yearChange() {
return Promise.resolve($.get("/platform/healthCheckup/project/mange/getHealthCheckupProject", {
year: this.pageForm.year
})).then(resp => {
if (resp.code === 0) {
this.projectOptions = resp.data;
if (resp.data && resp.data.length > 0) {
const currentProjectId = this.pageForm.projectId;
if (!currentProjectId || !resp.data.some(item => {
return item.id === currentProjectId;
})) {
this.pageForm.projectId = resp.data[0].id;
}
} else {
this.pageForm.projectId = "";
}
return Promise.resolve(this.doSearch()).then(() => {});
} else {
this.notifyError(resp.msg);
}
});
}
},
created() {
this.pageForm.year = this.$moment().format('YYYY');
return Promise.resolve(this.$businessTool.listUnion()).then(resolvedValue => {
this.unions = resolvedValue;
if (!this.pageForm.unionId && this.unions && this.unions.length === 1) {
this.pageForm.unionId = this.unions[0].id;
}
return Promise.resolve(this.yearChange()).then(() => {});
});
}
});
</script>
<!--#
}
#-->
@@ -0,0 +1,91 @@
const addHealthCheckupUser = {
template: "#health-checkup-add-user-template",
mixins: [initTableMixins],
data() {
return {
visible: false,
pageDataUrl: "/platform/healthCheckup/list/mange/candidatePageData",
pageForm: {
searchKeyword: null,
sex: null,
birthday: null,
birthMonths: [],
unionId: null,
unitIds: [],
personTypes: [],
preparedBys: [],
userStates: [],
isMember: null,
memberFeePaid: null
},
sexOptions: ["男", "女"],
unionOptions: [],
unitOptions: [],
tableColumns: [
{ prop: "loginname", label: "工号" },
{ prop: "username", label: "姓名" },
{ prop: "sex", label: "性别", sortable: true, checked: 0 },
{ prop: "birthday", label: "出生日期", sortable: true, checked: 0 },
{ prop: "personType", label: "人员类型", sortable: true },
{ prop: "preparedBy", label: "编制类别", sortable: true },
{ prop: "userState", label: "在职状态", sortable: true },
{ prop: "unionName", label: "所属工会", sortable: true },
{ prop: "unitName", label: "所属单位", sortable: true }
]
}
},
methods: {
onCancel() {
// 关闭新增人员弹框,保留现有提交中禁用取消按钮的规则。
this.$set(this, "visible", false);
},
// 每次打开绑定目标项目并清空业务筛选,查询和分页由 mixin 负责。
onOpen(id) {
if (!id) {
this.notifyWarning("请先选择体检项目");
return;
}
this.$set(this, "visible", true);
this.$set(this, "tableData", []);
const defaults = addHealthCheckupUser.data().pageForm;
Object.keys(defaults).forEach(key => this.$set(this.pageForm, key, defaults[key]));
this.$set(this.pageForm, "projectId", id);
this.$businessTool.listUnion().then(rows => { this.unionOptions = rows; });
this.unionIdChange(null);
this.doSearch();
},
// 工会切换后清空旧单位,防止保留不属于当前工会的筛选条件。
unionIdChange(id) {
this.$set(this.pageForm, "unitIds", []);
return this.$businessTool.listUnit(id).then(rows => { this.unitOptions = rows; });
},
onSubmit() {
if (this.formLoading || this.tableLoading) return;
this.formLoading = true;
// 先按最新输入刷新预览,冻结本次筛选,确认后添加全部匹配人员而非当前页。
const params = JSON.parse(JSON.stringify(this.pageForm));
this.$set(this.pageForm, "totalCount", 0);
return this.pageData(params).then(() => {
if (!this.pageForm.totalCount) {
this.notifyWarning("当前筛选没有可添加人员");
return;
}
return this.$confirm("当前筛选出" + this.pageForm.totalCount + "人,确认全部添加到体检名单并补入参与人员范围吗?", "提示", {
confirmButtonText: "确定", cancelButtonText: "取消", type: "warning"
}).then(() => {
return this.$axios.post("/platform/healthCheckup/list/mange/addUsers", params).then(res => {
if (res.code === 0) {
this.notifySuccess(res.msg);
this.$set(this, "visible", false);
this.$emit("refresh");
} else {
this.notifyError(res.msg);
}
});
});
}).catch(error => {
if (error !== "cancel" && error !== "close") this.notifyError("添加未完成,请刷新后重试");
}).finally(() => { this.formLoading = false; });
}
}
};
@@ -1,7 +1,12 @@
<!--#
<!--#
layout("/layouts/platform.html"){
#-->
<style>
.el-table .cell.el-tooltip {
white-space: pre-line !important;
min-width: 50px !important;
}
</style>
<div id="app" class="platform" v-cloak>
<guava ref="guava" padding="0 5%">
@@ -20,6 +25,9 @@ layout("/layouts/platform.html"){
value-format="yyyy">
</el-date-picker>
</search-item>
<search-item label="姓名/工号">
<el-input placeholder="请输入姓名或工号" clearable v-model="pageForm.searchKeyword"></el-input>
</search-item>
<search-item label="体检项目">
<el-select @change="doSearch();getHealthCheckupSubject();"
style="width: 100%;"
@@ -28,8 +36,16 @@ layout("/layouts/platform.html"){
v-for="item in projectOptions"></el-option>
</el-select>
</search-item>
<search-item label="姓名/工号">
<el-input placeholder="请输入姓名或工号" clearable v-model="pageForm.searchKeyword"></el-input>
<search-item label="所选套餐">
<el-select placeholder="请选择套餐" v-model="pageForm.subjectId"
style="width: 100%;"
clearable
filterable>
<el-option v-for="item in subjects"
:label="item.optionName"
:value="item.id"
:key="item.id"></el-option>
</el-select>
</search-item>
<search-item label="所属工会" v-if="$auth.hasRoleOr(['SYSADMIN', 'SCHOOL_UNION_ADMIN'])">
<el-select placeholder="请选择所属工会" v-model="pageForm.unionId"
@@ -54,12 +70,40 @@ layout("/layouts/platform.html"){
:key="item.id"></el-option>
</el-select>
</search-item>
<search-item label="确认状态">
<el-select v-model="pageForm.isAudit" placeholder="请选择" clearable>
<el-option
v-for="item in isAuditOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</search-item>
</search>
</el-card>
<el-card shadow="never" class="mt10">
<table-tool label="体检项目">
<el-button type="primary" size="small" @click="openImport">导入名单
<el-radio-group v-model="pageForm.physicalExaminationStatus" size="medium" @change="doSearch" class="pr10">
<el-radio-button :label="2">全部</el-radio-button>
<el-radio-button :label="1">已选择</el-radio-button>
<el-radio-button :label="0">未选择</el-radio-button>
</el-radio-group>
<el-button type="primary" size="small" @click="openHealthFeedback"
v-if="$auth.hasRoleOr(['BRANCH_UNION_CHAIRMAN'])">问题反馈</el-button>
<el-button type="primary" size="small" @click="$refs.addUserRef.onOpen(pageForm.projectId)"
:disabled="!pageForm.projectId"
v-if="$auth.hasPermissionOr(['healthCheckup.list.import']) && $auth.hasRoleOr(['SYSADMIN','SCHOOL_UNION_ADMIN','SCHOOL_UNION_CHAIRMAN'])">添加人员
</el-button>
<el-button type="primary" size="small" @click="openImport"
v-if="$auth.hasPermissionOr(['healthCheckup.list.import'])">导入名单
</el-button>
<el-button type="primary" size="small" @click="openImportMedicalExaminationReport"
v-if="$auth.hasPermissionOr(['healthCheckup.list.import.medicalExamination'])">导入体检报告
</el-button>
<el-button type="primary" size="small" @click="exportData"
v-if="$auth.hasRoleOr(['SYSADMIN','BRANCH_UNION_ADMIN','BRANCH_UNION_CHAIRMAN'])">导出
</el-button>
<el-button :disabled="!pageForm.projectId" type="primary" size="small"
@click="doAudit(true)">确认名单
@@ -68,6 +112,12 @@ layout("/layouts/platform.html"){
<el-button :disabled="!pageForm.projectId" type="danger" size="small"
@click="doAudit(false)">取消确认
</el-button>
<!-- <el-button @click="msgNotSelect"
icon="el-icon-printer"
size="small"
type="primary"
v-if="$auth.hasRoleOr(['SYSADMIN'])">给未选发送消息
</el-button> -->
</table-tool>
@@ -106,13 +156,24 @@ layout("/layouts/platform.html"){
</template>
</el-table-column>
<el-table-column prop="userOnline" align="center" header-align="center"
label="操作"
label="操作" width="250px"
fixed="right">
<template slot-scope="{row}">
<el-button @click="openEditRemarks(row)" size="mini"
v-if="$auth.hasRoleOr(['SYSADMIN','BRANCH_UNION_ADMIN','BRANCH_UNION_CHAIRMAN'])"
type="primary">
编辑备注
</el-button>
<el-button @click="openEditHealthCheckup(row)" size="mini"
:disabled="row.isAudit" type="primary">
v-if="$auth.hasRoleOr(['SYSADMIN', 'HEALTH_ADMIN'])"
type="primary">
编辑
</el-button>
<el-button @click="doDelete(row.headId)" size="mini"
v-if="$auth.hasPermissionOr(['healthCheckup.list.delete']) && $auth.hasRoleOr(['SYSADMIN'])"
type="danger">
删除
</el-button>
</template>
</el-table-column>
</el-table>
@@ -126,10 +187,10 @@ layout("/layouts/platform.html"){
:close-on-click-modal="false">
<el-form :model="editHealthCheckupData" label-width="120px" ref="editHealthCheckupForm">
<el-form-item label="姓名">
<el-input :value="editHealthCheckupData.userName"></el-input>
<el-input :value="editHealthCheckupData.userName" readonly></el-input>
</el-form-item>
<el-form-item label="工号">
<el-input :value="editHealthCheckupData.loginName"></el-input>
<el-input :value="editHealthCheckupData.loginName" readonly></el-input>
</el-form-item>
<el-form-item label="套餐"
prop="subjectId"
@@ -154,12 +215,48 @@ layout("/layouts/platform.html"){
</el-form-item>-->
</el-form>
<el-row type="flex" justify="end">
<el-button @click="exportHealthCheckupDialog=false" type="primary" plain>取消</el-button>
<el-button @click="cancelHealthCheckupEdit" type="primary" plain>取消</el-button>
<el-button @click="doEditHealthCheckupData" type="primary" v-loading="loading">确定
</el-button>
</el-row>
</el-dialog>
<el-dialog title="修改备注" :visible.sync="doEditRemarksDialog" width="50%"
:close-on-click-modal="false">
<el-form label-width="120px" ref="editRemarksForm" :model="doEditRemarks">
<el-form-item label="备注" prop="bz">
<el-input v-model="doEditRemarks.remarks" type="textarea" rows="5"
placeholder="请输入备注"></el-input>
</el-form-item>
</el-form>
<el-row type="flex" justify="end">
<el-button @click="cancelRemarksEdit" type="primary" plain>取消</el-button>
<el-button @click="doEditRemarksFun" type="primary" v-loading="loading">确定</el-button>
</el-row>
</el-dialog>
<el-dialog title="问题反馈" :visible.sync="healthFeedbackDialog" width="60%"
:close-on-click-modal="false">
<el-form :model="healthFeedbackForm" label-width="100px">
<el-form-item label="反馈信息">
<el-input v-model="healthFeedbackForm.feedbackInfo" placeholder="请输入反馈信息"></el-input>
</el-form-item>
</el-form>
<el-row type="flex" justify="end" class="mb10">
<el-button @click="closeHealthFeedback" type="primary" plain>取消</el-button>
<el-button @click="saveHealthFeedback" type="primary" v-loading="healthFeedbackLoading">保存</el-button>
</el-row>
<el-table :data="healthFeedbackList" border >
<el-table-column align="center" label="问题反馈" prop="feedbackInfo" show-overflow-tooltip="false"></el-table-column>
<el-table-column align="center" label="操作" width="100px">
<template slot-scope="{row}">
<el-button @click="deleteHealthFeedback(row)" type="danger" size="mini">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-dialog>
<add-health-checkup-user ref="addUserRef" @refresh="doSearch"></add-health-checkup-user>
<excel-import
ref="excelImportRef"
@@ -171,164 +268,593 @@ layout("/layouts/platform.html"){
:extra_params="{healthProjectId:pageForm.projectId}"
></excel-import>
<excel-import
ref="excelImportExaminationReportRef"
url="/platform/healthCheckup/list/mange/healthImportMedicalExaminationReport"
template_url="/platform/healthCheckup/list/mange/downloadTemMedicalExaminationReport"
:visible.sync="importVisibleExaminationReport"
title="导入名单数据"
width="700px"
:extra_params="{healthProjectId:pageForm.projectId}"
></excel-import>
</div>
<script type="text/x-template" id="health-checkup-add-user-template">
<el-dialog title="添加人员" :close-on-click-modal="false" :visible.sync="visible" width="80%">
<el-card shadow="never">
<search @search="doSearch">
<search-item label="姓名/工号">
<el-input @keyup.enter.native="doSearch" clearable placeholder="请输入姓名/工号"
style="width: 100%" v-model="pageForm.searchKeyword">
</el-input>
</search-item>
<search-item label="性别">
<el-select clearable placeholder="请选择性别" style="width: 100%" v-model="pageForm.sex">
<el-option :key="sex" :label="sex" :value="sex"
v-for="sex in sexOptions"></el-option>
</el-select>
</search-item>
<search-item label="出生日期">
<el-date-picker
v-model="pageForm.birthday"
type="date"
placeholder="选择出生日期"
value-format="yyyy-MM-dd"
style="width: 100%">
</el-date-picker>
</search-item>
<search-item label="出生月份">
<el-select
v-model="pageForm.birthMonths"
multiple
collapse-tags
placeholder="请选择出生月份"
style="width: 100%">
<el-option
v-for="month in 12"
:key="month"
:label="month + '月'"
:value="month">
</el-option>
</el-select>
</search-item>
<search-item label="所属工会">
<el-select clearable filterable placeholder="请选择所属工会"
style="width: 100%"
@change="unionIdChange"
v-model="pageForm.unionId">
<el-option
:key="item.id"
:label="item.name"
:value="item.id"
v-for="item in unionOptions">
</el-option>
</el-select>
</search-item>
<search-item label="所属单位">
<el-select clearable
filterable
multiple
collapse-tags
placeholder="请选择所属单位" style="width: 100%"
v-model="pageForm.unitIds">
<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 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_PREPARED_BY_TYPE"
multiple
collapse-tags
placeholder="请选择编制类别"
v-model="pageForm.preparedBys"></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 placeholder="请选择是否会员" style="width: 100%"
v-model="pageForm.isMember">
<el-option label="全部" :value="null"></el-option>
<el-option label="是" :value="true"></el-option>
<el-option label="否" :value="false"></el-option>
</el-select>
</search-item>
<search-item label="是否缴费会员">
<el-select clearable placeholder="请选择是否缴费会员" style="width: 100%"
v-model="pageForm.memberFeePaid">
<el-option label="是" :value="true"></el-option>
<el-option label="否" :value="false"></el-option>
</el-select>
</search-item>
</search>
</el-card>
<el-card shadow="never">
<el-table :data="tableData"
:size="tableSize"
@sort-change="pageOrder"
ref="tableRef"
row-key="id"
v-loading="tableLoading"
style="width: 100%">
<el-table-column
:index="indexMethod"
label="序号"
type="index"
width="80px"
></el-table-column>
<el-table-column
:key="column.prop"
:label="column.label"
:prop="column.prop"
:sortable="column.sortable"
:width="column.width"
align="center"
header-align="center"
show-overflow-tooltip
v-for="column in tableColumns"
>
</el-table-column>
</el-table>
<!--#include("/layouts/pagination.html"){}#-->
</el-card>
<template #footer>
<el-button @click="onCancel" :disabled="formLoading">取消</el-button>
<el-button type="primary" @click="onSubmit" :loading="formLoading" :disabled="tableLoading">确定</el-button>
</template>
<slot></slot>
</el-dialog>
</script>
<script nonce="${cspNonce!}">
const vue = new Vue({
<!--#include("addUser.js"){}#-->
const vue = new Vue({
el: '#app',
mixins: [initTableMixins],
data() {
return {
pageForm: {
year: moment().format('YYYY'),
physicalExaminationStatus: '2'
},
unions: [],
subjects: [],
units: [],
projectOptions: [],
projectSubjectOptions: [],
tableColumns: [
{prop: 'loginName', label: '工号'},
{prop: 'userName', label: '姓名'},
{prop: 'sex', label: '性别', sortable: true},
{prop: 'unionName', label: '所属工会', sortable: true},
{prop: 'unitName', label: '所属单位', sortable: true},
{prop: 'optionName', label: '所选套餐'},
{prop: 'selectTime', label: '选择时间', width: "160px"},
{prop: 'receiveAddress', label: '邮寄地址', width: "160px"},
{prop: 'isAudit', label: '是否确认'},
{prop: 'auditTime', label: '确认时间'},
{prop: 'bz', label: '备注',width: "200px"},
],
tableColumns: [{
prop: 'loginName',
label: '工号'
}, {
prop: 'userName',
label: '姓名'
}, {
prop: 'sex',
label: '性别',
sortable: true
}, {
prop: 'unionName',
label: '所属工会',
sortable: true
}, {
prop: 'unitName',
label: '所属单位',
sortable: true
}, {
prop: 'optionName',
label: '所选套餐'
}, {
prop: 'selectTime',
label: '选择时间',
width: "160px"
}, {
prop: 'isAudit',
label: '是否确认',
sortable: true
}, {
prop: 'auditTime',
label: '确认时间'
}, {
prop: 'bz',
label: '备注',
width: "200px"
}],
exportHealthCheckupDialog: false,
editHealthCheckupData: {},
loading: false,
//导入名单
importVisible: false,
importVisibleExaminationReport: false,
doEditRemarksDialog: false,
doEditRemarks: {
userId: "",
projectId: "",
remarks: ""
},
healthFeedbackDialog: false,
healthFeedbackLoading: false,
healthFeedbackList: [],
healthFeedbackForm: {
year: "",
projectId: "",
unionId: "",
feedbackInfo: ""
},
campusOptions: [],
}
isAuditOptions: [{
label: "已确认",
value: "1"
}, {
label: "未确认",
value: "0"
}]
};
},
components: {
"add-health-checkup-user": addHealthCheckupUser,
"file-import": httpVueLoader("/components/plugins/sysImport/index.vue?v=" + new Date().getTime())
},
methods: {
async doAudit(flag) {
cancelHealthCheckupEdit() {
// 仅关闭当前弹框,不执行提交或保存操作。
this.$set(this, 'exportHealthCheckupDialog', false);
},
cancelRemarksEdit() {
// 仅关闭当前弹框,不执行提交或保存操作。
this.$set(this, 'doEditRemarksDialog', false);
},
closeHealthFeedback() {
// 仅关闭当前弹框,不执行提交或保存操作。
this.$set(this, 'healthFeedbackDialog', false);
},
doAudit(flag) {
// flag true,确认 false,取消
if (this.$auth.hasRoleOr(["SYSADMIN", "SCHOOL_UNION_ADMIN"]) && !this.pageForm.unionId) {
this.notifyWarning("请选择需要提交的分工会")
return
this.notifyWarning("请选择需要提交的分工会");
return;
}
if (this.pageForm.totalCount === 0) {
this.notifyWarning("当前分工会没有名单,暂不需要提交")
return
this.notifyWarning("当前分工会没有名单,暂不需要提交");
return;
}
const confirm = await this.$confirm(flag ? '您确定名单都已核实,准确无误?' : '您确定取消确认?', '提示', {
return Promise.resolve(this.$confirm(flag ? '您确定名单都已核实,准确无误?' : '您确定取消确认?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
})).then(confirm => {
if (confirm === "confirm") {
const resp = await $.post("/platform/healthCheckup/list/mange/doAuditUser", {
return this.$axios.post("/platform/healthCheckup/list/mange/doAuditUser", {
projectId: this.pageForm.projectId,
unionId: this.pageForm.unionId,
flag: flag
})
}).then(resp => {
if (resp.code === 0) {
this.notifySuccess(resp.msg)
this.doSearch()
this.notifySuccess(resp.msg);
this.doSearch();
} else {
this.notifyError(resp.msg)
}
this.notifyError(resp.msg);
}
});
} else {}
});
},
doEditHealthCheckupData() {
this.$refs['editHealthCheckupForm'].validate(async valid => {
this.$refs['editHealthCheckupForm'].validate(valid => {
if (valid) {
this.loading = true
const resp = await $.post(loc() + "/doEditHealthCheckupData", this.editHealthCheckupData)
this.exportHealthCheckupDialog = false
this.loading = true;
return this.$axios.post(loc() + "/doEditHealthCheckupData", this.editHealthCheckupData).then(resp => {
this.exportHealthCheckupDialog = false;
if (resp.code === 0) {
this.notifySuccess(resp.msg)
this.doSearch()
this.notifySuccess(resp.msg);
this.doSearch();
} else {
this.notifyWarning(resp.msg)
this.notifyWarning(resp.msg);
}
this.loading = false
}
})
this.loading = false;
}).finally(() => {
this.loading = false;
});
} else {}
});
},
async openEditHealthCheckup(row) {
openEditHealthCheckup(row) {
this.editHealthCheckupData = {
userName: row.userName,
loginName: row.loginName,
subjectId: row.subjectId,
campus: row.campus,
projectId: this.pageForm.projectId,
userId: row.id
}
this.exportHealthCheckupDialog = true
userId: row.id,
isEmployed: row.isEmployed
};
this.exportHealthCheckupDialog = true;
},
async yearChange() {
const resp = await $.get("/platform/healthCheckup/project/mange/getHealthCheckupProject", {year: this.pageForm.year})
yearChange() {
return Promise.resolve($.get("/platform/healthCheckup/project/mange/getHealthCheckupProject", {
year: this.pageForm.year
})).then(resp => {
if (resp.code === 0) {
this.projectOptions = resp.data
this.projectOptions = resp.data;
if (resp.data && resp.data.length > 0) {
this.$set(this.pageForm, "projectId", resp.data[0].id)
this.$set(this.pageForm, "projectId", resp.data[0].id);
} else {
this.$set(this.pageForm, "projectId", null)
this.$set(this.pageForm, "projectId", null);
}
await this.getHealthCheckupSubject()
return Promise.resolve(this.getHealthCheckupSubject()).then(() => {});
} else {
this.notifyError(resp.msg)
this.notifyError(resp.msg);
}
});
},
async getHealthCheckupSubject() {
const resp = await $.get("/platform/healthCheckup/project/mange/findOne", {id: this.pageForm.projectId})
getHealthCheckupSubject() {
return Promise.resolve($.get("/platform/healthCheckup/project/mange/findOne", {
id: this.pageForm.projectId
})).then(resp => {
if (resp.code === 0) {
this.projectSubjectOptions = resp.data.healthCheckupProjectSubjects
this.projectSubjectOptions = resp.data.healthCheckupProjectSubjects;
} else {
this.notifyError(resp.msg)
this.notifyError(resp.msg);
}
this.$set(this.pageForm, "subjectId", "");
return Promise.resolve(this.listSubjects()).then(() => {
this.doSearch();
});
});
},
async queryCampus() {
const resp = await $.get("/platform/healthCheckup/campus/queryCampus")
queryCampus() {
return Promise.resolve($.get("/platform/healthCheckup/campus/queryCampus")).then(resp => {
if (resp.code === 0) {
this.campusOptions = resp.data
this.campusOptions = resp.data;
} else {
this.notifyError(resp.msg)
this.notifyError(resp.msg);
}
});
},
async flushUnits() {
this.$set(this.pageForm, "unitId", "")
flushUnits() {
this.$set(this.pageForm, "unitId", "");
if (this.$auth.hasRoleOr(["SYSADMIN", "SCHOOL_UNION_ADMIN"])) {
this.units = await this.$businessTool.listUnit(this.pageForm.unionId)
return Promise.resolve(this.$businessTool.listUnit(this.pageForm.unionId)).then(resolvedValue => {
this.units = resolvedValue;
});
} else {
this.units = await this.$businessTool.listUnit()
return Promise.resolve(this.$businessTool.listUnit()).then(resolvedValue => {
this.units = resolvedValue;
});
}
},
listSubjects() {
return Promise.resolve($.get(loc() + "/listSubjects", {
projectId: this.pageForm.projectId,
unionId: this.pageForm.unionId
})).then(resp => {
if (resp.code === 0) {
this.subjects = resp.data;
} else {
this.notifyError(resp.msg);
}
});
},
validateHealthFeedbackCondition() {
// 反馈必须绑定当前筛选的年度和体检项目;分工会未选时由后端自动回落到当前登录人所属分工会。
if (!this.pageForm.year || !this.pageForm.projectId) {
this.notifyWarning("请先选择年度、体检项目后再打开问题反馈");
return false;
}
return true;
},
openHealthFeedback() {
if (!this.validateHealthFeedbackCondition()) {
return;
}
this.healthFeedbackForm.year = this.pageForm.year;
this.healthFeedbackForm.projectId = this.pageForm.projectId;
this.healthFeedbackForm.unionId = this.pageForm.unionId;
this.healthFeedbackForm.feedbackInfo = "";
return Promise.resolve(this.queryHealthFeedbackList()).then(() => {
this.healthFeedbackDialog = true;
});
},
queryHealthFeedbackList() {
return Promise.resolve($.get(loc() + "/feedbackList", {
year: this.pageForm.year,
projectId: this.pageForm.projectId,
unionId: this.pageForm.unionId
})).then(resp => {
if (resp.code === 0) {
this.healthFeedbackList = resp.data || [];
} else {
this.notifyError(resp.msg);
}
});
},
saveHealthFeedback() {
if (!this.validateHealthFeedbackCondition()) {
return;
}
if (!this.healthFeedbackForm.feedbackInfo) {
this.notifyWarning("请输入反馈信息");
return;
}
this.healthFeedbackLoading = true;
return this.$axios.post(loc() + "/saveFeedback", this.healthFeedbackForm).then(resp => {
if (resp.code === 0) {
this.notifySuccess(resp.msg);
this.healthFeedbackForm.feedbackInfo = "";
return Promise.resolve(this.queryHealthFeedbackList()).then(() => {
this.healthFeedbackLoading = false;
});
} else {
this.notifyError(resp.msg);
this.healthFeedbackLoading = false;
}
}).finally(() => {
this.healthFeedbackLoading = false;
});
},
deleteHealthFeedback(row) {
return Promise.resolve(this.$confirm("确认删除这条反馈吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
})).then(confirm => {
if (confirm === "confirm") {
return this.$axios.post(loc() + "/deleteFeedback", {
id: row.id
}).then(resp => {
if (resp.code === 0) {
this.notifySuccess(resp.msg);
return Promise.resolve(this.queryHealthFeedbackList()).then(() => {});
} else {
this.notifyError(resp.msg);
}
});
} else {}
});
},
openImport() {
// importUser
this.importVisible = true;
},
openImportMedicalExaminationReport() {
// importUser
this.importVisibleExaminationReport = true;
},
successImport() {
this.importLoading = false
this.doSearch()
this.importLoading = false;
this.doSearch();
},
},
async created() {
this.unions = await this.$businessTool.listUnion()
this.flushUnits()
await this.yearChange()
this.pageData()
await this.queryCampus()
doDelete(id) {
this.$confirm("您确定要删除吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "info"
}).then(() => {
return this.$axios.post(loc() + '/doDelete', {
id: id
}).then(resp => {
if (resp.code === 0) {
this.notifySuccess(resp.msg);
this.doSearch();
} else {
this.notifyError(resp.msg);
}
})
});
});
},
msgNotSelect() {
return Promise.resolve(this.$confirm('是否给未体检的教职工发送提醒短信?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})).then(confirm => {
if (confirm === "confirm") {
this.tableLoading = true;
return Promise.resolve($.get('/platform/healthCheckup/statistics/single/msgNotSelect', {
projectId: this.pageForm.projectId
})).then(resp => {
if (resp.code === 0) {
setTimeout(() => {
this.doSearch();
this.tableLoading = false;
this.notifySuccess(resp.msg);
}, 2000);
} else {
this.notifyWarning(resp.msg);
}
}).finally(() => {
this.tableLoading = false;
});
} else {}
});
},
openEditRemarks(row) {
this.doEditRemarks.userId = row.id;
this.doEditRemarks.remarks = row.bz;
this.doEditRemarks.projectId = this.pageForm.projectId;
this.doEditRemarksDialog = true;
},
doEditRemarksFun() {
this.$refs['editRemarksForm'].validate(valid => {
if (valid) {
this.loading = true;
return this.$axios.post(loc() + "/doEditRemarks", this.doEditRemarks).then(resp => {
this.doEditRemarksDialog = false;
if (resp.code === 0) {
this.notifySuccess(resp.msg);
this.doSearch();
this.doEditRemarks = {
userId: '',
projectId: '',
remarks: ''
};
} else {
this.notifyWarning(resp.msg);
}
this.loading = false;
}).finally(() => {
this.loading = false;
});
} else {}
});
},
exportData() {
const {
year,
searchKeyword,
projectId,
subjectId,
unionId,
unitId,
physicalExaminationStatus,
pageOrderName,
pageOrderBy
} = this.pageForm;
window.open(loc() + '/exportHealthUser?year=' + year + '&projectId=' + projectId + '&unionId=' + (unionId ? unionId : '') + '&searchKeyword=' + (searchKeyword ? searchKeyword : '') + '&subjectId=' + (subjectId ? subjectId : '') + '&unitId=' + (unitId ? unitId : '') + '&physicalExaminationStatus=' + (physicalExaminationStatus ? physicalExaminationStatus : '') + '&pageOrderName=' + (pageOrderName ? pageOrderName : '') + '&pageOrderBy=' + (pageOrderBy ? pageOrderBy : ''));
}
},
created() {
return Promise.resolve(this.$businessTool.listUnion()).then(resolvedValue => {
this.unions = resolvedValue;
if (!this.pageForm.unionId && this.unions && this.unions.length === 1) {
this.pageForm.unionId = this.unions[0].id;
}
this.flushUnits();
return Promise.resolve(this.yearChange()).then(() => {
this.pageData();
return Promise.resolve(this.queryCampus()).then(() => {});
});
});
}
});
</script>
<!--#
@@ -0,0 +1,150 @@
<!--#
layout("/layouts/platform.html"){
#-->
<div class="platform" id="app" v-cloak>
<guava ref="guava">
<el-card shadow="never">
<search @search="doSearch">
<search-item label="年度">
<el-date-picker
clearable
@change="yearChange"
placeholder="选择年"
style="width: 100%;"
type="year"
v-model="pageForm.year"
value-format="yyyy">
</el-date-picker>
</search-item>
<search-item label="体检项目">
<el-select @change="doSearch"
clearable
style="width: 100%;"
v-model="pageForm.projectId">
<el-option :key="item.id" :label="item.name" :value="item.id"
v-for="item in projectOptions"></el-option>
</el-select>
</search-item>
<search-item label="所属工会">
<el-select placeholder="请选择所属工会"
v-model="pageForm.unionId"
style="width: 100%;"
:clearable="clearable"
filterable
@change="doSearch">
<el-option v-for="item in unions"
:label="item.name"
:value="item.id"
:key="item.id"></el-option>
</el-select>
</search-item>
</search>
</el-card>
<el-card class="mt10" shadow="never">
<table-tool label="工会确认情况统计"></table-tool>
<el-table :data="tableData"
:size="tableSize"
@sort-change="pageOrder"
class="vi-table"
ref="table"
row-key="unionId"
style="width: 100%"
v-loading="tableLoading">
<el-table-column label="序号" type="index" width="80px" :index="indexMethod" align="center" header-align="center"></el-table-column>
<el-table-column
v-for="column in tableColumns"
:key="column.prop"
:label="column.label"
:prop="column.prop"
:sortable="column.sortable"
align="center"
header-align="center"
min-width="120px"
show-overflow-tooltip>
</el-table-column>
</el-table>
<!--#include("/layouts/pagination.html"){}#-->
</el-card>
</guava>
</div>
<script nonce="${cspNonce!}">
new Vue({
el: '#app',
mixins: [initTableMixins],
data() {
return {
clearable: false,
tableData: [],
unions: [],
projectOptions: [],
pageForm: {
year: moment().format('YYYY'),
projectId: '',
unionId: ''
},
tableColumns: [{
prop: 'unionName',
label: '工会名称'
}, {
prop: 'totalCount',
label: '体检总人数'
}, {
prop: 'confirmedCount',
label: '已确认总人数'
}, {
prop: 'unconfirmedCount',
label: '未确认总人数'
}, {
prop: 'completeStatus',
label: '确认完成状态'
}]
};
},
methods: {
yearChange() {
// 年度切换后重新加载该年度下的体检项目,并默认定位到第一条可用项目。
return this.$axios.post('/platform/healthCheckup/project/mange/getHealthCheckupProject', {
year: this.pageForm.year
}).then(resp => {
if (resp.code === 0) {
this.projectOptions = resp.data || [];
if (this.projectOptions.length > 0) {
this.$set(this.pageForm, 'projectId', this.projectOptions[0].id);
} else {
this.$set(this.pageForm, 'projectId', '');
}
this.doSearch();
} else {
this.notifyError(resp.msg);
}
});
},
init() {
if (this.$auth.hasRoleOr(['SYSADMIN', 'SCHOOL_UNION_ADMIN'])) {
return Promise.resolve(this.$businessTool.listUnion()).then(resolvedValue => {
this.unions = resolvedValue;
this.clearable = true;
return Promise.resolve(this.yearChange()).then(() => {});
});
} else {
return Promise.resolve(this.$businessTool.listUnion(this.$store.state.user.union.id)).then(resolvedValue => {
this.unions = resolvedValue;
if (this.unions && this.unions.length > 0) {
this.$set(this.pageForm, 'unionId', this.unions[0].id);
}
return Promise.resolve(this.yearChange()).then(() => {});
});
}
}
},
created() {
this.init();
}
});
</script>
<!--#
}
#-->
@@ -1,8 +1,6 @@
<!--#
layout("/layouts/platform.html"){
#-->
<div id="app" class="platform" v-cloak>
<guava ref="guava" padding="0 5%">
<template>
@@ -85,11 +83,11 @@ layout("/layouts/platform.html"){
:command="{type:'renew',data:row}">
更新名单
</el-dropdown-item>
<el-dropdown-item
v-if="( row.isWithinChoiceTime || row.canCreated ) && row.isHasUserList>0"
:command="{type:'reset',data:row}">
重置名单
</el-dropdown-item>
<!-- <el-dropdown-item-->
<!-- v-if="( row.isWithinChoiceTime || row.canCreated ) && row.isHasUserList>0"-->
<!-- :command="{type:'reset',data:row}">-->
<!-- 重置名单-->
<!-- </el-dropdown-item>-->
<el-dropdown-item :command="{type:'edit',data:row}">
编辑
</el-dropdown-item>
@@ -142,10 +140,21 @@ layout("/layouts/platform.html"){
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="温馨提示">
<el-input type="textarea" v-model="formData.friendlyReminderText" :rows="4"></el-input>
</el-form-item>
</el-col>
<!-- <el-col :span="24">
<el-form-item label="短信信息">
<el-input type="textarea" v-model="formData.smsMessage" :rows="4"></el-input>
</el-form-item>
</el-col> -->
<el-col :span="24">
<el-form-item label="体检套餐" prop="healthCheckupProjectSubjects">
<el-card shadow="hover" v-for="(item,index) in formData.healthCheckupProjectSubjects"
<el-collapse shadow="hover" v-for="(item,index) in formData.healthCheckupProjectSubjects"
:key="index">
<el-collapse-item :title="item.optionName" name="1">
<el-row :gutter="20">
<el-col :span="4">
医院名称:
@@ -171,7 +180,7 @@ layout("/layouts/platform.html"){
</div>
</el-col>
<el-col :span="2">
<el-col :span="4">
操作:
<div>
<el-button type="danger" size="small"
@@ -180,6 +189,15 @@ layout("/layouts/platform.html"){
</el-button>
</div>
</el-col>
<el-col :span="4">
复用:
<div>
<el-button type="primary" size="small"
:disabled="formData.healthCheckupProjectSubjects.length < 2"
@click="openReuseSubject(item)">复用套餐
</el-button>
</div>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
@@ -193,9 +211,18 @@ layout("/layouts/platform.html"){
</div>
</el-col>
</el-row>
<div class="s-options-list" style="margin-top: 10px;">
<el-table :data="item.subjectMoneys" border size="small">
<el-table-column label="序号" width="100px">
<template slot-scope="{row}">
<el-input v-model="row.serialNumber"
:value="row.serialNumber"
autosize
class="text-input"
data-type="option"
></el-input>
</template>
</el-table-column>
<el-table-column label="名称">
<template slot-scope="{row}">
<el-input v-model="row.subjectMoneyName" autosize
@@ -225,6 +252,7 @@ layout("/layouts/platform.html"){
<template slot-scope="{row,$index}" slot="header">
<el-button size="mini" type="primary"
@click="item.subjectMoneys.push({
serialNumber: item.subjectMoneys.length + 1,
money:'',
settlementMoney:'',
subjectMoneyName:'',
@@ -246,7 +274,8 @@ layout("/layouts/platform.html"){
</el-table-column>
</el-table>
</div>
</el-card>
</el-collapse-item>
</el-collapse>
<div class="s-operation">
<el-button size="small" type="primary"
@click="formData.healthCheckupProjectSubjects.push({optionNameId:'',
@@ -327,7 +356,7 @@ layout("/layouts/platform.html"){
<qrcode :options="{ width: 400 }" :value="activityUrl"></qrcode>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="codeDialogVisible = false" type="primary">关 闭</el-button>
<el-button @click="closeCodeDialog" type="primary">关 闭</el-button>
</span>
</el-dialog>
@@ -343,11 +372,11 @@ layout("/layouts/platform.html"){
</template>
<span slot="footer" class="dialog-footer">
<template v-if="subjectIndex!==null&&subjectIndex>=0">
<el-button @click="richTextDialog = false">关 闭</el-button>
<el-button @click="closeRichTextDialog">关 闭</el-button>
<el-button type="primary" @click="getRichText">确 定</el-button>
</template>
<template v-else>
<el-button @click="richTextDialog = false" type="primary">关 闭</el-button>
<el-button @click="closeRichTextDialog" type="primary">关 闭</el-button>
</template>
</span>
</el-dialog>
@@ -364,11 +393,27 @@ layout("/layouts/platform.html"){
<condition-structure-dialog ref="conditionStructureDialog" @confirm="addCondition"></condition-structure-dialog>
<el-dialog :visible.sync="reuseSubjectDialogVisible" title="复用套餐" width="500px"
:close-on-click-modal="false" @closed="resetReuseSubject">
<el-alert title="复用会覆盖当前套餐的名称、说明、材料和套餐明细,保留当前位置的排序;提交项目后才会保存。"
type="warning" :closable="false" style="margin-bottom: 16px;"></el-alert>
<el-select v-model="reuseSubjectSourceIndex" placeholder="请选择当前项目中的其他套餐"
filterable style="width: 100%">
<el-option v-for="option in reuseSubjectOptions" :key="option.index"
:label="option.label" :value="option.index"></el-option>
</el-select>
<div slot="footer">
<el-button @click="cancelReuseSubject">取消</el-button>
<el-button type="primary" :disabled="reuseSubjectSourceIndex === null"
@click="confirmReuseSubject">确认复用</el-button>
</div>
</el-dialog>
<el-dialog :visible.sync="conditionStructureDialogVisible" title="条件构造器">
<condition-group :group="conditionGroup" :field_options="fieldOptions"
@remove="removeRootGroup"></condition-group>
<div slot="footer">
<el-button @click="conditionStructureDialogVisible = false">取消</el-button>
<el-button @click="cancelCnd">取消</el-button>
<el-button type="primary" @click="confirmCnd">确定</el-button>
</div>
</el-dialog>
@@ -376,8 +421,8 @@ layout("/layouts/platform.html"){
</div>
<script nonce="${cspNonce!}">
<!--#include("../common/ConditionStructure.js"){}#-->
const vue = new Vue({
<!--#include("../common/ConditionStructure.js"){}#-->
const vue = new Vue({
el: '#app',
mixins: [initTableMixins],
data() {
@@ -385,7 +430,7 @@ layout("/layouts/platform.html"){
showSubmit: true,
conditions: {
method: "",
conditions: [{}],
conditions: [{}]
},
viewData: {},
subjectIndex: null,
@@ -397,234 +442,375 @@ layout("/layouts/platform.html"){
formData: {
healthCheckupProjectSubjects: []
},
tableColumns: [
{prop: 'year', label: '年度'},
{prop: 'name', label: '项目名称'},
{prop: 'choiceTime', label: '选择时间'},
],
tableColumns: [{
prop: 'year',
label: '年度'
}, {
prop: 'name',
label: '项目名称'
}, {
prop: 'choiceTime',
label: '选择时间'
}],
formRules: {
name: [{required: true, message: '必填', trigger: ['blur', 'change']}],
choiceTime: [{required: true, message: '必填', trigger: ['blur', 'change']}],
activityGroupId: [{required: true, message: '必填', trigger: ['blur', 'change']}],
// cover: [{required: true, message: '必填', trigger: ['blur', 'change']}],
healthCheckupType: [{required: true, message: '必填', trigger: ['blur', 'change']}],
healthCheckupProjectSubjects: [{
name: [{
required: true,
message: '必填',
trigger: ['blur', 'change']
}],
choiceTime: [{
required: true,
message: '必填',
trigger: ['blur', 'change']
}],
activityGroupId: [{
required: true,
message: '必填',
trigger: ['blur', 'change']
}],
// cover: [{required: true, message: '必填', trigger: ['blur', 'change']}],
healthCheckupType: [{
required: true,
message: '必填',
trigger: ['blur', 'change']
}],
healthCheckupProjectSubjects: [{
required: true,
message: '必填',
trigger: ['blur', 'change']
}]
},
deleteRowIds: [],
description: "",
codeDialogVisible: false,
activityUrl: '',
rowData: {},
subjectRowData: {},
subjectMoneyDialog: false,
subjectMoneyIndex: null,
hospitalIndex: null,
deleteSubjectIds: {},
deleteSubjectIds: [],
reuseSubjectDialogVisible: false,
reuseSubjectTarget: null,
reuseSubjectSourceIndex: null,
// 条件构造器相关
conditionStructureDialogVisible: false,
// 可选字段列表
fieldOptions: [
{label: "姓名", value: "username"},
{label: "工号", value: "loginname"},
{label: "性别", value: "sex"},
{label: "年龄", value: "age"},
{label: "婚姻状况", value: "marriage"},
{label: "在职状态", value: "userState"},
{label: "编制类别", value: "preparedBy"},
{label: "教职工类别", value: "personType"},
],
fieldOptions: [{
label: "姓名",
value: "username"
}, {
label: "工号",
value: "loginname"
}, {
label: "性别",
value: "sex"
}, {
label: "年龄",
value: "age"
}, {
label: "婚姻状况",
value: "marriage"
}, {
label: "在职状态",
value: "userState"
}, {
label: "编制类别",
value: "preparedBy"
}, {
label: "教职工类别",
value: "personType"
}],
conditionGroup: {
logic: "AND",
conditions: [],
groups: []
}
}
};
},
components: {
"drawer-user-scope": httpVueLoader("/components/module/activity/DrawerUserScope.vue"),
"condition-structure-dialog": CONDITION_STRUCTURE_DIALOG
},
computed: {
reuseSubjectOptions() {
// 使用列表位置区分同名套餐,并排除正在接收复用内容的套餐。
return this.formData.healthCheckupProjectSubjects.map((item, index) => ({
item,
index,
label: (index + 1) + ' / ' + (item.optionName || '未命名套餐')
})).filter(option => option.item !== this.reuseSubjectTarget);
}
},
methods: {
closeCodeDialog() {
// 仅关闭当前弹框,不执行提交或保存操作。
this.$set(this, 'codeDialogVisible', false);
},
closeRichTextDialog() {
// 仅关闭当前弹框,不执行提交或保存操作。
this.$set(this, 'richTextDialog', false);
},
openReuseSubject(item) {
// 新建和编辑项目均可复用当前表单内的其他套餐,不读取历史项目。
if (this.formData.healthCheckupProjectSubjects.length < 2) return;
this.$set(this, 'reuseSubjectTarget', item);
this.$set(this, 'reuseSubjectSourceIndex', null);
this.$set(this, 'reuseSubjectDialogVisible', true);
},
cancelReuseSubject() {
// 取消只关闭选择窗口,确认复用前不改变套餐内容。
this.$set(this, 'reuseSubjectDialogVisible', false);
},
resetReuseSubject() {
// 关闭后释放当前套餐引用,避免下次打开沿用上次的选择。
this.$set(this, 'reuseSubjectTarget', null);
this.$set(this, 'reuseSubjectSourceIndex', null);
},
confirmReuseSubject() {
const subjects = this.formData.healthCheckupProjectSubjects;
const target = this.reuseSubjectTarget;
const source = subjects[this.reuseSubjectSourceIndex];
// 检查来源和目标仍在当前项目中,禁止将自身作为复制来源。
if (!target || !source || source === target || !subjects.includes(target)) {
this.$message.warning('请选择当前项目中的其他套餐');
return;
}
// 只复制业务字段并深拷贝材料、条件,保留目标排序且不带入主键或关联字段。
const copied = JSON.parse(JSON.stringify({
optionName: source.optionName || '',
description: source.description || '',
imgUrl: source.imgUrl || '',
files: source.files || [],
subjectMoneys: (source.subjectMoneys || []).map(row => ({
serialNumber: row.serialNumber,
subjectMoneyName: row.subjectMoneyName,
money: row.money,
settlementMoney: row.settlementMoney,
sort: row.sort,
matchCnd: row.matchCnd
}))
}));
// 编辑时记录被替换的已保存明细,提交时沿用现有删除流程,避免新旧明细同时保留。
(target.subjectMoneys || []).forEach(row => {
if (row.id && !this.deleteRowIds.includes(row.id)) {
this.deleteRowIds.push(row.id);
}
});
Object.keys(copied).forEach(key => {
this.$set(target, key, copied[key]);
});
this.$set(this, 'reuseSubjectDialogVisible', false);
this.$message.success('已复用套餐设置,请提交项目保存');
},
removeRootGroup() {
// 重置根条件组
this.updateFromData.conditionGroup = {
logic: "AND",
conditions: [],
groups: []
}
};
},
dropdownCommand(command) {
const {type, data} = command
const {
type,
data
} = command;
if (type === 'view') {
this.openView(data)
this.openView(data);
} else if (type === 'created') {
this.createUserList(data)
this.createUserList(data);
} else if (type === 'renew') {
this.renewUserList(data)
this.renewUserList(data);
} else if (type === 'reset') {
this.resetUserList(data)
this.resetUserList(data);
} else if (type === 'edit') {
this.openEdit(data)
this.openEdit(data);
} else if (type === 'delete') {
this.doDelete(data.id)
this.doDelete(data.id);
} else if (type === 'code') {
this.openCode(data.id)
this.openCode(data.id);
}
},
openAddSubjectMoney(row, index) {
row.index = index
this.subjectRowData = row
this.subjectMoneyDialog = true
row.index = index;
this.subjectRowData = row;
this.subjectMoneyDialog = true;
},
addCondition(val) {
this.$set(this.formData.healthCheckupProjectSubjects[this.subjectMoneyIndex].subjectMoneys[this.rowData.index], 'matchCnd', val)
this.$set(this.formData.healthCheckupProjectSubjects[this.subjectMoneyIndex].subjectMoneys[this.rowData.index], 'matchCnd', val);
},
openCnd(row, subjectIndex, hospitalIndex) {
this.rowData = row
this.subjectMoneyIndex = subjectIndex
this.hospitalIndex = hospitalIndex
this.conditionStructureDialogVisible = true
this.rowData = row;
this.subjectMoneyIndex = subjectIndex;
this.hospitalIndex = hospitalIndex;
this.conditionStructureDialogVisible = true;
if (row.matchCnd) {
this.conditionGroup = row.matchCnd
this.conditionGroup = row.matchCnd;
} else {
this.conditionGroup = {
logic: "AND",
conditions: [],
groups: []
}
};
}
// this.$refs.conditionStructureDialog.onOpen(row.matchCnd ? row.matchCnd : null)
},
cancelCnd() {
// 通过 Vue 实例方法关闭可选条件弹框,避免内联事件中的 this 指向错误。
this.$set(this, 'conditionStructureDialogVisible', false);
},
// 条件构造器确认
confirmCnd() {
this.conditionStructureDialogVisible = false
this.$set(this.formData.healthCheckupProjectSubjects[this.hospitalIndex].subjectMoneys[this.subjectMoneyIndex], 'matchCnd', this.conditionGroup)
this.conditionStructureDialogVisible = false;
this.$set(this.formData.healthCheckupProjectSubjects[this.hospitalIndex].subjectMoneys[this.subjectMoneyIndex], 'matchCnd', this.conditionGroup);
},
async createUserList(row) {
const msg = '您确定要生成【' + row.name + '】体检名单吗?(该名单生成是按照创建时选择的可报名人员范围来生成)'
const confirm = await this.$confirm(msg, '提示', {
createUserList(row) {
const msg = '您确定要生成【' + row.name + '】体检名单吗?(该名单生成是按照创建时选择的可报名人员范围来生成)';
return Promise.resolve(this.$confirm(msg, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
if ('confirm' !== confirm) return
})).then(confirm => {
if ('confirm' !== confirm) return;
const loading = this.$loading({
lock: true,
text: '数据更新中,请稍后...',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
const resp = await this.$axios.post(loc() + '/createUserList', row)
return this.$axios.post(loc() + '/createUserList', row).then(resp => {
if (resp.code === 0) {
setTimeout(() => {
loading.close()
this.$message.success('操作成功')
this.pageData()
}, 1000)
loading.close();
this.$message.success('操作成功');
this.pageData();
}, 1000);
} else {
this.$message.warning(resp.msg)
this.$message.warning(resp.msg);
}
}).finally(() => {
loading.close();
});
});
},
async renewUserList(row) {
const msg = '您确定要更新【' + row.name + '】的体检名单吗?(如可报名人员范围发生变化会删除已选择的人员信息)'
const confirm = await this.$confirm(msg, '提示', {
renewUserList(row) {
const msg = '您确定要更新【' + row.name + '】的体检名单吗?(如可报名人员范围发生变化会删除已选择的人员信息)';
return Promise.resolve(this.$confirm(msg, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
if ('confirm' !== confirm) return
})).then(confirm => {
if ('confirm' !== confirm) return;
const loading = this.$loading({
lock: true,
text: '数据更新中,请稍后...',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
const resp = await this.$axios.post(loc() + '/renewUserList', row)
loading.close()
return this.$axios.post(loc() + '/renewUserList', row).then(resp => {
loading.close();
if (resp.code === 0) {
this.$message.success('操作成功')
this.pageData()
this.$message.success('操作成功');
this.pageData();
} else {
this.$message.warning(resp.msg)
this.$message.warning(resp.msg);
}
}).finally(() => {
loading.close();
});
});
},
async resetUserList(row) {
const msg = '您确定要重置【' + row.name + '】的体检名单吗?(该操作会将所有已选择的人员清空后,在进行生成)'
const confirm = await this.$confirm(msg, '提示', {
resetUserList(row) {
const msg = '您确定要重置【' + row.name + '】的体检名单吗?(该操作会将所有已选择的人员清空后,在进行生成)';
return Promise.resolve(this.$confirm(msg, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
if ('confirm' !== confirm) return
})).then(confirm => {
if ('confirm' !== confirm) return;
const loading = this.$loading({
lock: true,
text: '数据更新中,请稍后...',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
const resp = await this.$axios.post(loc() + '/resetUserList', row)
loading.close()
return this.$axios.post(loc() + '/resetUserList', row).then(resp => {
loading.close();
if (resp.code === 0) {
this.$message.success('操作成功')
this.pageData()
this.$message.success('操作成功');
this.pageData();
} else {
this.$message.warning(resp.msg)
this.$message.warning(resp.msg);
}
}).finally(() => {
loading.close();
});
});
},
openCode(id) {
this.activityUrl = location.origin + "/platform/healthCheckup/project/mange"
this.codeDialogVisible = true
this.activityUrl = location.origin + "/platform/healthCheckup/h5/listParticulars?projectId=" + encodeURIComponent(id);
this.codeDialogVisible = true;
},
deleteSubject(row, index) {
this.formData.healthCheckupProjectSubjects.splice(index, 1)
if (row.id) this.deleteSubjectIds.push(row.id)
this.formData.healthCheckupProjectSubjects.splice(index, 1);
if (row.id) this.deleteSubjectIds.push(row.id);
},
deleteRow(row, index, subjectMoneyIndex) {
this.formData.healthCheckupProjectSubjects[subjectMoneyIndex].subjectMoneys.splice(index, 1)
if (row.id) this.deleteRowIds.push(row.id)
this.formData.healthCheckupProjectSubjects[subjectMoneyIndex].subjectMoneys.splice(index, 1);
if (row.id) this.deleteRowIds.push(row.id);
this.reloadSubjectMoneySerialNumber(this.formData.healthCheckupProjectSubjects[subjectMoneyIndex].subjectMoneys);
},
async doDelete(id) {
const confirm = await this.$confirm('确定要删除该项目吗?', '提示', {type: 'warning'})
reloadSubjectMoneySerialNumber(rowDetail) {
//重新加载体检套餐序号
for (let i = 0; i < rowDetail.length; i++) {
rowDetail[i].serialNumber = i + 1;
}
},
doDelete(id) {
return Promise.resolve(this.$confirm('确定要删除该项目吗?', '提示', {
type: 'warning'
})).then(confirm => {
if (confirm) {
const resp = await this.$axios.post(loc() + "/doDelete", {id})
return this.$axios.post(loc() + "/doDelete", {
id
}).then(resp => {
if (resp.code === 0) {
this.pageData()
this.notifySuccess(resp.msg)
this.pageData();
this.notifySuccess(resp.msg);
} else {
this.notifyWarning(resp.msg)
}
this.notifyWarning(resp.msg);
}
});
} else {}
});
},
async openEdit(row) {
this.formData = await this.findOne(row.id)
this.$refs.guava.edit()
openEdit(row) {
return Promise.resolve(this.findOne(row.id)).then(resolvedValue => {
this.formData = resolvedValue;
this.$refs.guava.edit();
});
},
async openView(row) {
this.viewData = await this.findOne(row.id)
this.$refs.guava.view()
openView(row) {
return Promise.resolve(this.findOne(row.id)).then(resolvedValue => {
this.viewData = resolvedValue;
this.$refs.guava.view();
});
},
async findOne(id) {
const resp = await this.$axios.post(loc() + "/findOne", {id: id})
findOne(id) {
return this.$axios.post(loc() + "/findOne", {
id: id
}).then(resp => {
if (resp.code === 0) {
resp.data.choiceTime = [resp.data.choiceTimeStart, resp.data.choiceTimeEnd]
return resp.data
resp.data.choiceTime = [resp.data.choiceTimeStart, resp.data.choiceTimeEnd];
return resp.data;
} else {
this.notifyError("查询失败")
this.notifyError("查询失败");
}
});
},
async doAdd() {
const formValid = await this.$refs["form"].validate()
doAdd() {
return Promise.resolve(this.$refs["form"].validate()).then(formValid => {
if (formValid) {
const loading = this.$loading({
lock: true,
@@ -632,72 +818,76 @@ layout("/layouts/platform.html"){
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
const formData = clone(this.formData)
formData.choiceTimeStart = formData.choiceTime[0]
formData.choiceTimeEnd = formData.choiceTime[1]
formData.healthCheckupProjectSubjects = JSON.stringify(this.formData.healthCheckupProjectSubjects)
formData.activityGroupName = this.activityGroupList.find(v => v.groupId === this.formData.activityGroupId).groupName
formData.deleteRowIds = JSON.stringify(this.deleteRowIds)
formData.deleteSubjectIds = JSON.stringify(this.deleteSubjectIds)
const resp = await this.$axios.post("/platform/healthCheckup/project/mange/doAdd", formData)
loading.close()
const formData = clone(this.formData);
formData.choiceTimeStart = formData.choiceTime[0];
formData.choiceTimeEnd = formData.choiceTime[1];
formData.healthCheckupProjectSubjects = JSON.stringify(this.formData.healthCheckupProjectSubjects);
formData.activityGroupName = this.activityGroupList.find(v => v.groupId === this.formData.activityGroupId)?.groupName;
formData.deleteRowIds = JSON.stringify(this.deleteRowIds);
formData.deleteSubjectIds = JSON.stringify(this.deleteSubjectIds);
formData.friendlyReminderText = formData.friendlyReminderText || '';
formData.smsMessage = formData.smsMessage || '';
return this.$axios.post("/platform/healthCheckup/project/mange/doAdd", formData).then(resp => {
loading.close();
if (resp.code === 0) {
this.pageData()
this.$refs.guava.index()
this.deleteRowIds = []
this.deleteSubjectIds = []
this.pageData();
this.$refs.guava.index();
this.deleteRowIds = [];
this.deleteSubjectIds = [];
} else {
this.notifyWarning(resp.msg)
}
this.notifyWarning(resp.msg);
}
}).finally(() => {
loading.close();
});
} else {}
});
},
openAdd() {
this.formData = {
name: null,
activityGroupId: null,
choiceTime: null,
healthCheckupProjectSubjects: [
{
healthCheckupProjectSubjects: [{
optionName: '套餐1',
optionNameId: '',
optionSort: '1',
imgUrl: '',
subjectMoneys: []
}
]
}
}]
};
if (this.$refs.form) this.$refs.form.resetFields();
this.$refs.guava.edit()
this.$refs.guava.edit();
},
openDescRichText(richText, sIdx) {
this.subjectIndex = sIdx
this.description = richText
this.richTextDialog = true
this.subjectIndex = sIdx;
this.description = richText;
this.richTextDialog = true;
},
openViewDescRichText(richText) {
this.subjectIndex = null
this.description = richText
this.richTextDialog = true
this.subjectIndex = null;
this.description = richText;
this.richTextDialog = true;
},
getRichText() {
this.formData.healthCheckupProjectSubjects[this.subjectIndex].description = this.description
this.richTextDialog = false
this.formData.healthCheckupProjectSubjects[this.subjectIndex].description = this.description;
this.richTextDialog = false;
},
async getActivityGroup() {
const {data} = await this.$axios.post('/platform/activity/basic/scope/getActivityUserScopeGroup')
this.activityGroupList = data
},
},
async created() {
this.getActivityGroup()
this.pageData()
getActivityGroup() {
return this.$axios.post('/platform/activity/basic/scope/getActivityUserScopeGroup').then(({
data
}) => {
this.activityGroupList = data;
});
}
})
},
created() {
this.getActivityGroup();
this.pageData();
}
});
</script>
<!--#
}
#-->
@@ -3,6 +3,54 @@ layout("/layouts/platform.html"){
#-->
<style>
.health-statistics-region { overflow: hidden; }
.health-statistics-region .actual-statistics-row { display: flex; width: 100%; }
.health-statistics-region .actual-statistics-row .actual-statistics-table { flex-shrink: 0; }
/* 延续主表滚动条占位的背景及外边框,保留统计表列宽和操作按钮位置。 */
.health-statistics-region .actual-statistics-gutter {
flex-shrink: 0;
align-self: stretch;
box-sizing: border-box;
background: #f5f7fa;
border-top: 1px solid #ebeef5;
border-right: 1px solid #ebeef5;
border-bottom: 1px solid #ebeef5;
}
.health-statistics-region .actual-statistics-gutter {
border-left: 1px solid #ebeef5;
}
/* 分隔线绘制在可见固定汇总层内,避免被固定列遮挡;2px与两行统计相接的双边框一致。 */
.health-statistics-region > .el-table--scrollable-y > .el-table__fixed-right > .el-table__fixed-footer-wrapper::after {
content: '';
position: absolute;
top: 0;
right: 0;
bottom: 0;
width: 2px;
background: #ebeef5;
z-index: 4;
pointer-events: none;
}
.health-statistics-region .actual-statistics-table { border-top: 1px solid #ebeef5; }
.health-statistics-region .actual-statistics-table .el-table__body-wrapper { overflow-x: hidden; }
/* 单行统计隐藏横向滚动条后,固定列铺满实际行高,避免组件预留滚动条高度造成截断。 */
.health-statistics-region .actual-statistics-table .el-table__fixed-right {
top: 0;
bottom: 0;
height: auto !important;
box-shadow: none;
}
.health-statistics-region .actual-statistics-table .el-table__fixed-body-wrapper {
top: 0 !important;
}
.health-statistics-region .actual-statistics-table .el-table__fixed-right::before { display: none; }
.health-statistics-region .el-table__footer-wrapper td,
.health-statistics-region .actual-statistics-table td { background: #f5f7fa; }
</style>
<div class="platform" id="app" v-cloak>
<guava padding="0 5%" ref="guava">
<template>
@@ -54,10 +102,13 @@ layout("/layouts/platform.html"){
<template
v-if="$auth.hasRoleOr(['SYSADMIN', 'SCHOOL_UNION_ADMIN', 'BRANCH_UNION_CHAIRMAN'])">
<template v-if="projectInfo.healthCheckupType==='jzg'">
<el-button v-if="$auth.hasRoleOr(['SYSADMIN', 'SCHOOL_UNION_ADMIN'])"
:disabled="!pageForm.projectId" @click="exportSelectedZip"
icon="el-icon-download" size="small" type="primary">导出已选名单Zip
</el-button>
<el-button :disabled="!pageForm.projectId"
@click="exportReceiveDetail(true)"
icon="el-icon-printer"
size="small"
type="primary">导出教职工已选名单
</el-button>
@@ -74,18 +125,18 @@ layout("/layouts/platform.html"){
type="primary"
v-if="$auth.hasRoleOr(['SYSADMIN', 'SCHOOL_UNION_ADMIN'])">管理员代选择
</el-button>-->
<el-button @click="selectOptionByLastYear"
<el-button @click="openLastYear"
icon="el-icon-printer"
size="small"
type="primary"
v-if="$auth.hasRoleOr(['SYSADMIN', 'SCHOOL_UNION_ADMIN'])">一键选择去年数据
v-if="$auth.hasRoleOr(['SYSADMIN', 'SCHOOL_UNION_ADMIN'])">按上年补齐未选人员
</el-button>
<el-button @click="msgNotSelect"
<!-- <el-button @click="msgNotSelect"
icon="el-icon-printer"
size="small"
type="primary"
v-if="$auth.hasRoleOr(['SYSADMIN', 'SCHOOL_UNION_ADMIN'])">给未选发送消息
</el-button>
</el-button> -->
</template>
<el-button :disabled="!pageForm.projectId"
@@ -98,13 +149,14 @@ layout("/layouts/platform.html"){
</template>
</table-tool>
<el-table :data="tableData" :size="tableSize" :summary-method="getSummaries"
<div ref="statisticsRegion" class="health-statistics-region">
<el-table :height="statisticsHeight" :data="tableData" :size="tableSize" :summary-method="getSummaries"
@sort-change="pageOrder" class="vi-table"
ref="table" row-key="id" show-summary
style="width: 100%" v-loading="tableLoading">
<el-table-column :index="indexMethod" align="center" header-align="center"
label="序号" type="index"
width="80px"></el-table-column>
width="150px"></el-table-column>
<el-table-column
:label="column.label"
:prop="column.prop"
@@ -135,6 +187,41 @@ layout("/layouts/platform.html"){
</template>
</el-table-column>
</el-table>
<!-- 实际统计按整个项目维护,与上方按工会筛选的选择统计独立。 -->
<div v-if="actualProjectId" class="actual-statistics-row">
<el-table ref="actualTable" :data="[actualCounts]" :show-header="false"
:size="tableSize" :fit="false" class="vi-table actual-statistics-table"
v-loading="formLoading" :style="{width: actualTableWidth ? actualTableWidth + 'px' : '100%'}">
<el-table-column width="150px" align="center">
<template slot-scope="scope">实际体检统计</template>
</el-table-column>
<el-table-column v-for="(column, columnIndex) in tableColumns" :key="column.prop"
:width="statisticsColumnWidths[columnIndex + 1] || column.width || 100" align="center">
<template slot-scope="scope">
<template v-if="column.actualKey">
<el-input-number v-if="actualEditing" :value="actualDraft[column.actualKey]"
@input="setActualCount(column.actualKey, $event)"
:min="0" :max="2147483647" :precision="0" :controls="false"
size="mini" style="width: 100%"></el-input-number>
<span v-else>{{actualCounts[column.actualKey] == null ? '未录入' : actualCounts[column.actualKey] + ' 人'}}</span>
</template>
<span v-else>{{column.prop === 'unionName' ? '' : '—'}}</span>
</template>
</el-table-column>
<el-table-column width="200px" align="center" fixed="right">
<template slot-scope="scope" v-if="$auth.hasRoleOr(['SYSADMIN', 'SCHOOL_UNION_ADMIN'])">
<template v-if="actualEditing">
<el-button size="mini" type="primary" :loading="formLoading" @click="saveActualCounts">保存</el-button>
<el-button size="mini" :disabled="formLoading" @click="cancelActualEdit">取消</el-button>
</template>
<el-button v-else size="mini" type="primary" :disabled="tableLoading" @click="editActualCounts">编辑</el-button>
</template>
</el-table-column>
</el-table>
<div v-if="actualGutterWidth" class="actual-statistics-gutter" aria-hidden="true"
:style="{width: actualGutterWidth + 'px'}"></div>
</div>
</div>
</el-card>
</template>
@@ -222,20 +309,43 @@ layout("/layouts/platform.html"){
</el-form-item>
</el-form>
<el-row justify="end" type="flex">
<el-button @click="selectByAdminDialogVisible=false">取消</el-button>
<el-button @click="cancelAdminSelection">取消</el-button>
<el-button @click="doSelectByAdmin" type="primary" :loading="loading">提交</el-button>
</el-row>
</el-dialog>
<el-dialog :visible.sync="selectLastYearProject" title="选择去年项目"
width="35%">
<el-form label-position="right" label-width="120px">
<el-form-item label="体检项目">
<el-select style="width: 100%" v-model="lastYearProjectId">
<el-option :key="item.id" :label="item.name" :value="item.id"
v-for="item in lastYearProjectSubjectOptions"></el-option>
</el-select>
</el-form-item>
</el-form>
<el-row>
<span style="color: red; padding-left: 52px;">温馨提示:系统默认选择去年的体检项目,新入职教职工选用本年校医院套餐</span>
</el-row>
<el-row justify="end" type="flex" style="margin-top: 15px">
<el-button @click="cancelLastYearSelection">取消</el-button>
<el-button @click="selectOptionByLastYear" type="primary" :loading="loading">提交</el-button>
</el-row>
</el-dialog>
</div>
<script nonce="${cspNonce!}">
const vue = new Vue({
const vue = new Vue({
el: '#app',
mixins: [initTableMixins],
data() {
return {
pageForm: {
year: moment().format('YYYY'),
year: moment().format('YYYY')
},
pageFormUnion: {
pageNumber: 1,
@@ -243,7 +353,7 @@ layout("/layouts/platform.html"){
totalCount: null,
searchName: 'u.username',
//0:已选人员页面 1:未选人员页面
isSelected: '',
isSelected: ''
},
units: [],
unions: [],
@@ -251,163 +361,357 @@ layout("/layouts/platform.html"){
receive_tableColumns: [],
receive_tableData: [],
clearable: false,
tableColumns: [
{prop: 'unionName', label: '所属工会'},
{prop: 'sumCount', label: '本次体检人数'},
],
tableColumns: [{
prop: 'unionName',
label: '所属工会'
}, {
prop: 'sumCount',
label: '本次体检人数'
}],
projectInfo: {},
statisticsHeight: 360,
actualTableWidth: 0,
actualGutterWidth: 0,
statisticsColumnWidths: [],
actualCounts: {},
actualDraft: {},
actualProjectId: null,
actualEditing: false,
statisticsRequest: 0,
selectByAdminDialogVisible: false,
projectSubjectOptions: [],
campusOptions: [],
noSelectOptionId: null,
campus: null,
loading: false,
selectLastYearProject: false,
lastYearProjectId: null,
lastYearProjectSubjectOptions: [],
isEmployedOptions: [{
value: '是',
label: '是'
}, {
value: '否',
label: '否'
}]
};
},
watch: {
tableData() {
this.queueStatisticsLayout();
},
tableColumns() {
this.queueStatisticsLayout();
},
actualProjectId() {
this.queueStatisticsLayout();
},
actualEditing() {
this.queueStatisticsLayout();
}
},
methods: {
async msgNotSelect() {
const confirm = await this.$confirm('请确定未选择的教职工发送提醒短信?', '提示', {
exportSelectedZip() {
// 当前项目全部已选人员按医院打包,不带入页面分工会筛选条件。
if (!this.pageForm.projectId) { this.$message.warning('请选择体检项目'); return; }
window.open(loc() + '/exportSelectedZip?projectId=' + encodeURIComponent(this.pageForm.projectId));
},
cancelAdminSelection() {
// 仅关闭当前弹框,不执行提交或保存操作。
this.$set(this, 'selectByAdminDialogVisible', false);
},
cancelLastYearSelection() {
// 仅关闭当前弹框,不执行提交或保存操作。
this.$set(this, 'selectLastYearProject', false);
},
// 动态医院列和输入控件渲染后,按可用视口高度计算主表高度。
queueStatisticsLayout() {
this.$nextTick(() => {
this.layoutStatistics();
});
},
layoutStatistics() {
const region = this.$refs.statisticsRegion;
const table = this.$refs.table;
if (!region || !table || !region.offsetHeight) return;
const actual = this.$refs.actualTable;
const footerHeight = actual ? actual.$el.offsetHeight : 0;
const height = Math.max(180, Math.floor(window.innerHeight - region.getBoundingClientRect().top - footerHeight - 24));
if (height !== this.statisticsHeight) this.statisticsHeight = height;
this.$nextTick(() => {
if (!this.$refs.table) return;
table.doLayout();
this.$nextTick(() => {
if (!this.$refs.table) return;
// 扣除主表纵向滚动条占位,底部表使用主表实际列宽,避免统计错列。
const widths = table.columns.map(column => column.realWidth || column.width || 100);
if (JSON.stringify(widths) !== JSON.stringify(this.statisticsColumnWidths)) this.statisticsColumnWidths = widths;
// 底部补齐与主表一致的滚动条占位;无纵向滚动条时占位归零。
const gutterWidth = table.layout.scrollY ? table.layout.gutterWidth : 0;
if (gutterWidth !== this.actualGutterWidth) this.actualGutterWidth = gutterWidth;
const width = table.$el.clientWidth - gutterWidth;
if (width !== this.actualTableWidth) this.actualTableWidth = width;
this.bindStatisticsScroll();
this.$nextTick(() => {
if (this.$refs.actualTable) this.$refs.actualTable.doLayout();
this.syncStatisticsScroll();
});
});
});
},
// 仅主表数据区滚动;其横向滚动条同时带动底部实际统计行。
bindStatisticsScroll() {
const body = this.$refs.table && this.$refs.table.bodyWrapper;
if (this.statisticsScrollBody === body) return;
if (this.statisticsScrollBody) this.statisticsScrollBody.removeEventListener('scroll', this.syncStatisticsScroll);
this.statisticsScrollBody = body;
if (body) body.addEventListener('scroll', this.syncStatisticsScroll, {
passive: true
});
},
syncStatisticsScroll() {
const actual = this.$refs.actualTable;
if (actual && this.statisticsScrollBody) actual.bodyWrapper.scrollLeft = this.statisticsScrollBody.scrollLeft;
},
msgNotSelect() {
return Promise.resolve(this.$confirm('请确定未选择的教职工发送提醒短信?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
})).then(confirm => {
if (confirm === "confirm") {
this.tableLoading = true
const resp = await $.get(loc() + '/msgNotSelect', {
this.tableLoading = true;
return Promise.resolve($.get(loc() + '/msgNotSelect', {
projectId: this.pageForm.projectId
})
})).then(resp => {
if (resp.code === 0) {
setTimeout(() => {
this.doSearch()
this.tableLoading = false
this.notifySuccess(resp.msg)
}, 2000)
this.doSearch();
this.tableLoading = false;
this.notifySuccess(resp.msg);
}, 2000);
} else {
this.notifyWarning(resp.msg)
}
this.notifyWarning(resp.msg);
}
}).finally(() => {
this.tableLoading = false;
});
} else {}
});
},
async doSelectByAdmin() {
doSelectByAdmin() {
if (!this.noSelectOptionId || !this.campus) {
this.notifyWarning('请选择套餐/院区')
return
this.notifyWarning('请选择套餐/院区');
return;
}
const {optionName} = this.projectSubjectOptions.find(v => v.id === this.noSelectOptionId)
const confirm = await this.$confirm('请确定是否把【' + optionName + '】套餐赋给未选择的教职工?', '提示', {
const {
optionName
} = this.projectSubjectOptions.find(v => v.id === this.noSelectOptionId);
return Promise.resolve(this.$confirm('请确定是否把【' + optionName + '】套餐赋给未选择的教职工?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
})).then(confirm => {
if (confirm === "confirm") {
this.loading = true
const resp = await $.get(loc() + '/doSelectByAdmin', {
this.loading = true;
return Promise.resolve($.get(loc() + '/doSelectByAdmin', {
optionId: this.noSelectOptionId,
campus: this.campus,
projectId: this.pageForm.projectId
})
})).then(resp => {
if (resp.code === 0) {
setTimeout(() => {
this.doSearch()
this.loading = false
this.selectByAdminDialogVisible = false
this.notifySuccess(resp.msg)
}, 2000)
this.doSearch();
this.loading = false;
this.selectByAdminDialogVisible = false;
this.notifySuccess(resp.msg);
}, 2000);
} else {
this.notifyWarning(resp.msg)
this.notifyWarning(resp.msg);
}
}
}).finally(() => {
this.loading = false;
});
} else {}
});
},
async selectOptionByLastYear() {
const confirm = await this.$confirm('请确定是否把没有选择的教职工统一选择去年的数据?', '提示', {
selectOptionByLastYear() {
return Promise.resolve(this.$confirm('请确定是否把没有选择的教职工统一选择去年的数据?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
})).then(confirm => {
if (!this.lastYearProjectId) {
this.$message().error('请选择上年度体检项目');
}
if (confirm === "confirm") {
const resp = await this.$axios.post(loc() + '/selectOptionByLastYear', {
projectId: this.pageForm.projectId
})
return this.$axios.post(loc() + '/selectOptionByLastYear', {
projectId: this.pageForm.projectId,
lastYearProjectId: this.lastYearProjectId
}).then(resp => {
if (resp.code === 0) {
this.doSearch()
this.notifySuccess(resp.msg)
this.lastYearProjectId = '';
this.selectLastYearProject = false;
this.doSearch();
this.notifySuccess(resp.msg);
} else {
this.notifyWarning(resp.msg)
}
this.notifyWarning(resp.msg);
}
});
} else {}
});
},
selectOptionByAdmin() {
this.noSelectOptionId = null
this.selectByAdminDialogVisible = true
this.noSelectOptionId = null;
this.selectByAdminDialogVisible = true;
},
async yearChange() {
const resp = await this.$axios.post("/platform/healthCheckup/project/mange/getHealthCheckupProject", {year: this.pageForm.year})
yearChange() {
const year = this.pageForm.year;
// 清空旧项目状态,年度无项目时不能继续显示或编辑上一次统计。
this.$set(this.pageForm, 'projectId', null);
this.projectSubjectOptions = [];
this.projectInfo = {};
this.pageData();
return this.$axios.post('/platform/healthCheckup/project/mange/getHealthCheckupProject', {
year: year
}).then(resp => {
if (year !== this.pageForm.year) return;
if (resp.code === 0) {
this.projectOptions = resp.data
if (resp.data && resp.data.length > 0) {
this.$set(this.pageForm, "projectId", resp.data[0].id)
} else {
this.$set(this.pageForm, "projectId", null)
this.projectOptions = resp.data || [];
this.$set(this.pageForm, 'projectId', this.projectOptions.length ? this.projectOptions[0].id : null);
return this.getHealthCheckupSubject();
}
await this.getHealthCheckupSubject()
} else {
this.notifyError(resp.msg)
}
},
async getHealthCheckupSubject() {
this.projectSubjectOptions = []
const resp = await this.$axios.post("/platform/healthCheckup/project/mange/findOne", {id: this.pageForm.projectId})
if (resp.code === 0) {
if (resp.data) {
this.projectInfo = resp.data
this.projectSubjectOptions = resp.data.healthCheckupProjectSubjects
}
} else {
this.notifyError(resp.msg)
}
this.doSearch()
},
async queryCampus() {
const resp = await this.$axios.post("/platform/healthCheckup/campus/queryCampus")
if (resp.code === 0) {
this.campusOptions = resp.data
} else {
this.notifyError(resp.msg)
}
},
async pageData() {
this.tableColumns = []
const loading = this.$loading({
lock: true,
text: '努力加载中。。。',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
this.notifyError(resp.msg);
});
this.tableColumns.push(
{prop: 'unionName', label: '所属工会'},
{prop: 'sumCount', label: '本次体检人数'}
)
if (this.projectSubjectOptions && this.projectSubjectOptions.length > 0) {
this.projectSubjectOptions.forEach(v => {
this.tableColumns.push({prop: v.optionName, label: v.optionName})
})
},
getHealthCheckupSubject() {
const projectId = this.pageForm.projectId;
this.statisticsRequest++;
this.actualProjectId = null;
this.actualEditing = false;
this.tableData = [];
this.projectSubjectOptions = [];
this.projectInfo = {};
if (!projectId) {
this.tableLoading = false;
return Promise.resolve();
}
this.tableColumns.push(
{prop: "totalCount", label: "选取人数"},
{prop: "unselectedTeacherSum", label: "未选人数"}
)
if (!this.pageForm.projectId) {
this.tableData = []
}
const resp = await this.$axios.post(loc() + '/pageData', this.pageForm)
return this.$axios.post('/platform/healthCheckup/project/mange/findOne', {
id: projectId
}).then(resp => {
if (projectId !== this.pageForm.projectId) return;
if (resp.code === 0 && resp.data) {
this.projectInfo = resp.data;
this.projectSubjectOptions = resp.data.healthCheckupProjectSubjects || [];
this.doSearch();
} else this.notifyError(resp.msg);
});
},
queryCampus() {
return this.$axios.post("/platform/healthCheckup/campus/queryCampus").then(resp => {
if (resp.code === 0) {
this.tableData = resp.data.tableList
this.campusOptions = resp.data;
} else {
this.notifyError(resp.msg);
}
loading.close()
});
},
// 同时加载选择统计和项目实际统计;请求序号防止快速切换项目时旧响应覆盖新数据。
pageData() {
const projectId = this.pageForm.projectId;
const request = ++this.statisticsRequest;
this.actualEditing = false;
this.actualProjectId = null;
this.tableData = [];
this.tableColumns = [{
prop: 'unionName',
label: '所属工会'
}, {
prop: 'sumCount',
label: '本次体检人数',
actualKey: '__total__'
}];
(this.projectSubjectOptions || []).forEach(item => {
this.tableColumns.push({
prop: item.optionName,
label: item.optionName,
actualKey: item.id
});
});
this.tableColumns.push({
prop: 'totalCount',
label: '选取人数'
}, {
prop: 'unselectedTeacherSum',
label: '未选人数'
});
if (!projectId) {
this.tableLoading = false;
return;
}
this.tableLoading = true;
Promise.all([this.$axios.post(loc() + '/pageData', this.pageForm), this.$axios.post(loc() + '/actualStatistics', {
projectId: projectId
})]).then(([selection, actual]) => {
if (request !== this.statisticsRequest || projectId !== this.pageForm.projectId) return;
if (selection.code === 0) this.tableData = selection.data.tableList;else this.notifyError(selection.msg);
if (actual.code === 0) {
this.actualCounts = actual.data;
this.actualProjectId = projectId;
} else this.notifyError(actual.msg);
}).finally(() => {
if (request === this.statisticsRequest) this.tableLoading = false;
}).finally(() => {
this.tableLoading = false;
});
},
// 仅编辑已加载项目的快照,未录入项需明确填写,避免自动将空值记为零。
editActualCounts() {
if (this.actualProjectId !== this.pageForm.projectId) return;
this.actualDraft = Object.assign({}, this.actualCounts);
this.actualEditing = true;
},
setActualCount(key, value) {
this.$set(this.actualDraft, key, value);
},
cancelActualEdit() {
this.actualEditing = false;
this.actualDraft = {};
},
// 整行保存项目总人数和各医院人数;零有效,空值、负数和小数不能提交。
saveActualCounts() {
const projectId = this.actualProjectId;
if (!projectId || projectId !== this.pageForm.projectId || this.formLoading) return;
const counts = {};
const columns = this.tableColumns.filter(column => column.actualKey);
if (columns.some(column => !Number.isInteger(this.actualDraft[column.actualKey]) || this.actualDraft[column.actualKey] < 0 || this.actualDraft[column.actualKey] > 2147483647)) {
this.notifyWarning('请完整填写本次体检人数及各医院人数,人数必须为非负整数');
return;
}
// 医院合计只能包含医院选项,不能将本次体检总人数重复累加。
const hospitalTotal = columns.filter(column => column.actualKey !== '__total__').reduce((total, column) => total + this.actualDraft[column.actualKey], 0);
if (hospitalTotal > this.actualDraft.__total__) {
this.notifyWarning('各医院人数合计不能大于本次体检人数');
return;
}
columns.forEach(column => {
counts[column.actualKey] = this.actualDraft[column.actualKey];
});
this.formLoading = true;
this.$axios.post(loc() + '/saveActualStatistics', {
projectId: projectId,
counts: JSON.stringify(counts)
}).then(res => {
if (res.code === 0) {
if (projectId === this.actualProjectId && projectId === this.pageForm.projectId) {
this.actualCounts = res.data;
this.actualEditing = false;
}
this.notifySuccess('实际体检统计已保存');
} else this.notifyError(res.msg);
}).finally(() => {
this.formLoading = false;
});
},
pageNumberChange(val) {
this.pageFormUnion.pageNumber = val;
@@ -417,69 +721,92 @@ layout("/layouts/platform.html"){
this.pageFormUnion.pageSize = val;
this.pageUnionData();
},
async flushUnits() {
this.$set(this.pageForm, "unitId", "")
flushUnits() {
this.$set(this.pageForm, "unitId", "");
if (this.$auth.hasRoleOr(["SYSADMIN", "SCHOOL_UNION_ADMIN"])) {
this.units = await this.$businessTool.listUnit(this.pageForm.unionId)
return Promise.resolve(this.$businessTool.listUnit(this.pageForm.unionId)).then(resolvedValue => {
this.units = resolvedValue;
});
} else {
this.units = await this.$businessTool.listUnit()
return Promise.resolve(this.$businessTool.listUnit()).then(resolvedValue => {
this.units = resolvedValue;
});
}
},
async init() {
init() {
if (this.$auth.hasRoleOr(["SYSADMIN", "SCHOOL_UNION_ADMIN"])) {
this.unions = await this.$businessTool.listUnion()
this.clearable = true
return Promise.resolve(this.$businessTool.listUnion()).then(resolvedValue => {
this.unions = resolvedValue;
this.clearable = true;
return Promise.resolve(this.yearChange()).then(() => {
return Promise.resolve(this.queryCampus()).then(() => {});
});
});
} else {
this.unions = await this.$businessTool.listUnion(this.$store.state.user.union.id)
this.$set(this.pageForm, "unionId", this.unions[0].id)
return Promise.resolve(this.$businessTool.listUnion(this.$store.state.user.union.id)).then(resolvedValue => {
this.unions = resolvedValue;
this.$set(this.pageForm, "unionId", this.unions[0].id);
return Promise.resolve(this.yearChange()).then(() => {
return Promise.resolve(this.queryCampus()).then(() => {});
});
});
}
await this.yearChange()
await this.queryCampus()
},
//查看未选择人数
async openUnclaimed(unionId) {
this.pageFormUnion.isSelected = '1'
this.pageFormUnion.unionId = unionId
this.pageFormUnion.projectId = this.pageForm.projectId
await this.pageUnionData();
openUnclaimed(unionId) {
this.pageFormUnion.isSelected = '1';
this.pageFormUnion.unionId = unionId;
this.pageFormUnion.projectId = this.pageForm.projectId;
return Promise.resolve(this.pageUnionData()).then(() => {});
},
//查看已选人员
async openView(unionId) {
this.pageFormUnion.isSelected = '0'
this.pageFormUnion.unionId = unionId
this.pageFormUnion.projectId = this.pageForm.projectId
await this.pageUnionData();
openView(unionId) {
this.pageFormUnion.isSelected = '0';
this.pageFormUnion.unionId = unionId;
this.pageFormUnion.projectId = this.pageForm.projectId;
return Promise.resolve(this.pageUnionData()).then(() => {});
},
async pageUnionData() {
const resp = await this.$axios.post(loc() + '/receivePageData', this.pageFormUnion)
pageUnionData() {
return this.$axios.post(loc() + '/receivePageData', this.pageFormUnion).then(resp => {
if (resp.code === 0) {
this.$refs.guava.view()
this.receive_tableColumns = resp.data.label
this.receive_tableData = resp.data.list.list
this.pageFormUnion.totalCount = resp.data.list.totalCount
this.$refs.guava.view();
this.receive_tableColumns = resp.data.label;
this.receive_tableData = resp.data.list.list;
this.pageFormUnion.totalCount = resp.data.list.totalCount;
}
});
},
doUnionSearch() {
this.pageFormUnion.pageNumber = 1
this.pageFormUnion.pageNumber = 1;
this.pageUnionData();
},
//按分工会导出已选名单
exportReceiveDetail(flag) {
const {year, projectId, unionId} = this.pageForm
window.open(loc() + '/exportReceiveDetail?year=' + year + '&projectId=' + projectId + '&unionId=' + (unionId ? unionId : '') + '&flag=' + flag)
const {
year,
projectId,
unionId
} = this.pageForm;
window.open(loc() + '/exportReceiveDetail?year=' + year + '&projectId=' + projectId + '&unionId=' + (unionId ? unionId : '') + '&flag=' + flag);
},
//导出家属信息
exportUserCompanion() {
const {year, projectId, unionId} = this.pageForm
window.open(loc() + '/exportUserCompanion?year=' + year + '&projectId=' + projectId + '&unionId=' + (unionId ? unionId : ''))
const {
year,
projectId,
unionId
} = this.pageForm;
window.open(loc() + '/exportUserCompanion?year=' + year + '&projectId=' + projectId + '&unionId=' + (unionId ? unionId : ''));
},
getSummaries(param) {
const {columns, data} = param;
const {
columns,
data
} = param;
const sums = [];
columns.forEach((column, index) => {
if (index === 0) {
sums[index] = '总计';
sums[index] = '院区选择统计';
return;
}
const values = data.map(item => Number(item[column.property]));
@@ -498,12 +825,40 @@ layout("/layouts/platform.html"){
}
});
return sums;
},
openLastYear() {
return this.$axios.post("/platform/healthCheckup/project/mange/getHealthCheckupProject", {
year: this.pageForm.year - 1
}).then(resp => {
if (resp.code === 0) {
if (resp.data.length > 0) {
this.lastYearProjectSubjectOptions = resp.data;
this.selectLastYearProject = true;
} else {
this.$message.warning("无上一年度体检项目");
}
}
});
}
},
async created() {
this.init()
mounted() {
this.queueStatisticsLayout();
window.addEventListener('resize', this.queueStatisticsLayout);
// 侧栏宽度变化、筛选区换行和详情返回时同步重新布局。
this.statisticsResizeObserver = new ResizeObserver(() => {
this.queueStatisticsLayout();
});
this.statisticsResizeObserver.observe(this.$el);
},
beforeDestroy() {
window.removeEventListener('resize', this.queueStatisticsLayout);
if (this.statisticsResizeObserver) this.statisticsResizeObserver.disconnect();
if (this.statisticsScrollBody) this.statisticsScrollBody.removeEventListener('scroll', this.syncStatisticsScroll);
},
created() {
this.init();
}
})
});
</script>
@@ -29,6 +29,25 @@ layout("/layouts/platform_h5.html"){
<table-list api="/platform/healthCheckup/h5/pageData" :page_form.sync="pageForm" ref="tableListRef"
@ready="doSearch" img="cover">
<template v-slot="{index,row}">
<table-column label="项目名称">
<div style="display: flex;
justify-content: space-between;
align-items: flex-start; /* 关键:改成顶部对齐 */
width: 100%;">
<span style="word-break: break-all;
white-space: normal;
flex: 1;
padding-right: 5px;">{{ row.name }}</span>
<span style="margin-left: 8px; font-size: 14px; white-space: nowrap;">
<span v-if="row.selectCount > 0" style="color: rgb(29 179 76);">
已报名
</span>
<span v-if="row.selectCount == 0" style="color: #ff0000;">
未报名
</span>
</span>
</div>
</table-column>
<table-column label="开始时间">
{{$moment(row.choiceTimeStart).format('MM/DD HH:mm')}}
</table-column>
@@ -37,23 +56,30 @@ layout("/layouts/platform_h5.html"){
</table-column>
</template>
<template #actions="{index,row}">
<template v-if="$moment().isBefore($moment(row.choiceTimeEnd))">
<div class="action-btn" @click="onView(row)">
<template v-if="$moment(row.choiceTimeStart).isBefore($moment()) && $moment().isBefore($moment(row.choiceTimeEnd))">
<div class="action-btn" style="text-align: right" @click="onView(row)">
<i class="fa fa-edit"></i>
<span>去报名</span>
</div>
</template>
<template v-else>
<div class="action-btn" @click="look(row)">
<van-icon name="eye-o" />
<span>查看</span>
</div>
</template>
</template>
</table-list>
</div>
<script nonce="${cspNonce!}">
const vue = new Vue({
const vue = new Vue({
healthCheckupHistory: true,
mixins: [healthCheckupHistoryMixin],
el: "#app",
store,
components: {
},
components: {},
data() {
return {
yearList: [],
@@ -62,10 +88,11 @@ layout("/layouts/platform_h5.html"){
pageSize: 10,
totalCount: 0,
year: "",
status: "ongoing", // 默认为进行中
status: "ongoing",
// 默认为进行中
name: "" // 添加搜索名称字段
},
}
};
},
methods: {
// 查看项目详情
@@ -73,7 +100,6 @@ layout("/layouts/platform_h5.html"){
// 这里可以添加查看项目详情的逻辑
console.log('查看项目详情:', row);
},
onView(row) {
/* if(row.selectCount > 0) {
vant.Dialog.alert({
@@ -90,14 +116,16 @@ layout("/layouts/platform_h5.html"){
return;
}
this.$pjaxReplace("/platform/healthCheckup/h5/projectManageForm?projectId=" + row.id)
// this.navigateHealthCheckup("/platform/healthCheckup/h5/projectManageForm?projectId=" + row.id)
this.navigateHealthCheckup("/platform/healthCheckup/h5/listParticulars?projectId=" + row.id);
sessionStorage.setItem("friendlyReminderStatus", 0);
},
doSearch() {
this.$nextTick(() => {
this.pageForm.pageNumber = 1
this.pageForm.totalCount = 0
this.$refs.tableListRef.doSearch()
})
this.pageForm.pageNumber = 1;
this.pageForm.totalCount = 0;
this.$refs.tableListRef.doSearch();
});
},
// 新增状态切换处理方法
onStatusChange() {
@@ -105,17 +133,23 @@ layout("/layouts/platform_h5.html"){
},
initData() {
for (let i = new Date().getFullYear() - 10; i <= new Date().getFullYear(); i++) {
this.yearList.unshift({value: i, text: i + "年"})
this.yearList.unshift({
value: i,
text: i + "年"
});
}
this.$set(this.pageForm, "year", this.yearList[0].value)
this.$set(this.pageForm, "year", this.yearList[0].value);
// 初始化状态
this.$set(this.pageForm, "status", "ongoing")
this.$set(this.pageForm, "status", "ongoing");
},
look(row) {
this.navigateHealthCheckup("/platform/healthCheckup/h5/physicalExaminationList?projectId=" + row.id);
}
},
created() {
this.initData()
this.initData();
}
})
});
</script>
<!--#
@@ -0,0 +1,116 @@
<!--#
layout("/layouts/platform_h5.html"){
#-->
<style scoped>
.buttonClass {
height: 93px;
margin: 8px 0px;
border-radius: 20px
}
.van-dialog__header {
color: red;
font-size: 20px;
}
.dialog-content > div {
padding: 8px 20px;
}
</style>
<div id="app">
<van-nav-bar @click-left="historyBack" left-arrow left-text="返回" title="报名详情" placeholder
fixed></van-nav-bar>
<div style="padding: 15px 20px">
<van-button round type="primary" size="large" color="#0db776" class="buttonClass" @click="userMessage"><van-icon name="user-o" size="27px"/> 基础信息查询</van-button>
<van-button round type="primary" size="large" color="#2cabdd" class="buttonClass" @click="selectPhysicalExaminationHospital"><van-icon name="records-o" size="27px"/> 选择体检医院</van-button>
<van-button round type="primary" size="large" color="#eca807" class="buttonClass" @click="minePhysicalExaminationHospital"><van-icon name="idcard" size="27px"/> 我的体检医院</van-button>
</div>
<van-dialog v-model="show" title="温馨提醒" @confirm="confirmClick"
:confirm-button-text="confirmButtonText"
:before-close="confirmStatusFun"
:confirm-button-disabled="confirmDisabled">
<div class="dialog-content">
<div style="white-space: pre-line">{{project.friendlyReminderText}}</div>
</div>
</van-dialog>
</div>
<script nonce="${cspNonce!}">
const vue = new Vue({
healthCheckupHistory: true,
mixins: [healthCheckupHistoryMixin],
el: "#app",
store,
components: {},
data() {
return {
show: false,
confirmButtonText: "",
amount: 0,
project: {
id: "",
friendlyReminderText: ""
},
baseLoc: "",
count: 5,
confirmDisabled: true
};
},
methods: {
confirmStatusFun(action, done) {
if (action === 'confirm' && this.count > 0) {
done(false);
} else {
done();
}
},
userMessage() {
this.navigateHealthCheckup("/platform/healthCheckup/h5/userBaseMessage?projectId=" + this.project.id);
},
selectPhysicalExaminationHospital() {
this.navigateHealthCheckup("/platform/healthCheckup/h5/projectManageForm?projectId=" + this.project.id);
},
minePhysicalExaminationHospital() {
this.navigateHealthCheckup("/platform/healthCheckup/h5/mine?projectId=" + this.project.id);
},
confirmClick() {
sessionStorage.setItem("friendlyReminderStatus", 1);
},
// 查询体检套餐
findSubject(id) {
return Promise.resolve().then(() => {
return this.$axios.post('/platform/healthCheckup/project/mange/findOne', {
id
}).then(resp => {
this.project = resp.data || {};
});
}).catch(error => {
vant.Toast('获取体检项目失败');
});
}
},
beforeDestroy() {
// PJAX 离开页面时释放提示倒计时,避免旧页面继续更新状态。
clearInterval(this.reminderTimer);
},
created() {
let projectId = GetQueryString("projectId");
this.findSubject(projectId);
let friendlyReminderStatus = sessionStorage.getItem("friendlyReminderStatus");
if (friendlyReminderStatus == "0") {
this.show = true;
this.confirmButtonText = '已知悉(' + this.count + ')';
this.reminderTimer = setInterval(() => {
this.count--;
this.confirmButtonText = '已知悉(' + this.count + ')';
if (this.count <= 0) {
this.confirmDisabled = false;
this.confirmButtonText = '已知悉';
clearInterval(this.reminderTimer);
}
}, 1000);
}
}
});
</script>
<!--#
}
#-->
@@ -4,15 +4,6 @@ layout("/layouts/platform_h5.html"){
<div id="app">
<van-nav-bar @click-left="historyBack" left-arrow left-text="返回" title="我的体检" placeholder fixed></van-nav-bar>
<van-sticky offset-top="46px">
<van-tabs v-model="pageForm.status"
@change="onStatusChange">
<van-tab title="进行中" name="ongoing"></van-tab>
<van-tab title="已结束" name="finished"></van-tab>
</van-tabs>
</van-sticky>
<table-list api="/platform/healthCheckup/h5/mineData" :page_form.sync="pageForm" ref="tableListRef"
@ready="doSearch" title="healthCheckupName">
<template v-slot="{index,row}">
@@ -20,9 +11,13 @@ layout("/layouts/platform_h5.html"){
<table-column label="开始时间">{{row.choiceTimeStart}}</table-column>
<table-column label="结束时间">{{row.choiceTimeEnd}}</table-column>
<table-column label="选择医院">{{row.optionName}}</table-column>
<table-column label="邮寄地址">{{row.receiveAddress}}</table-column>
<!-- <table-column label="邮寄地址">{{row.receiveAddress}}</table-column>-->
</template>
<template #actions="{index,row}">
<div class="action-btn" @click="look(row)">
<van-icon name="eye-o" />
<span>查看</span>
</div>
<div class="action-btn" @click="edit(row)" v-if="new Date().getTime() < new Date(row.choiceTimeEnd).getTime()">
<i class="fa fa-edit"></i>
<span>修改</span>
@@ -33,15 +28,14 @@ layout("/layouts/platform_h5.html"){
</div>
</template>
</table-list>
</div>
<script nonce="${cspNonce!}">
const vue = new Vue({
const vue = new Vue({
healthCheckupHistory: true,
mixins: [healthCheckupHistoryMixin],
el: "#app",
store,
components: {
},
components: {},
data() {
return {
viewShow: false,
@@ -51,69 +45,77 @@ layout("/layouts/platform_h5.html"){
pageSize: 10,
totalCount: 0,
year: "",
status: "ongoing", // 默认为进行中
name: "" // 添加搜索名称字段
},
name: "",
// 添加搜索名称字段
projectId: ""
}
};
},
methods: {
edit(row) {
this.$pjaxReplace("/platform/healthCheckup/h5/projectManageForm?projectId=" + row.projectId)
this.navigateHealthCheckup("/platform/healthCheckup/h5/projectManageForm?projectId=" + row.projectId);
},
async cancel(o) {
vant.Dialog.confirm({
cancel(o) {
this.$healthConfirm({
title: '温馨提醒',
message: '您确定要取消此选择吗?',
}).then(async () => {
const res = await $.post('/platform/healthCheckup/h5/cancel', {id: o.id, projectId: o.projectId})
message: '您确定要取消此选择吗?'
}).then(() => {
return this.$axios.post('/platform/healthCheckup/h5/cancel', {
id: o.id,
projectId: o.projectId
}).then(res => {
if (res.code === 0) {
vant.Toast.success('取消成功')
this.doSearch()
vant.Toast.success('取消成功');
this.doSearch();
} else {
vant.Toast(res.msg)
vant.Toast(res.msg);
}
}).catch(() => {
})
});
}).catch(() => {});
},
tabChange(val) {
this.tabName = val
this.startLoading()
this.tableData = []
this.pageForm.pageNumber = 1
this.pageData()
this.closeLoading()
this.tabName = val;
this.startLoading();
this.tableData = [];
this.pageForm.pageNumber = 1;
this.pageData();
this.closeLoading();
},
doSearch() {
this.$nextTick(() => {
this.pageForm.pageNumber = 1
this.pageForm.totalCount = 0
this.$refs.tableListRef.doSearch()
})
this.pageForm.pageNumber = 1;
this.pageForm.totalCount = 0;
this.$refs.tableListRef.doSearch();
});
},
// 新增状态切换处理方法
onStatusChange() {
this.doSearch();
},
init() {
this.startLoading()
this.pageForm.tabName = 'ing'
this.pageData()
this.closeLoading()
this.startLoading();
this.pageForm.tabName = 'ing';
this.pageData();
this.closeLoading();
},
initData() {
for (let i = new Date().getFullYear() - 10; i <= new Date().getFullYear(); i++) {
this.yearList.unshift({value: i, text: i + "年"})
this.yearList.unshift({
value: i,
text: i + "年"
});
}
this.$set(this.pageForm, "year", this.yearList[0].value)
// 初始化状态
this.$set(this.pageForm, "status", "ongoing")
this.$set(this.pageForm, "year", this.yearList[0].value);
this.pageForm.projectId = GetQueryString("projectId");
},
look(row) {
this.navigateHealthCheckup("/platform/healthCheckup/h5/physicalExaminationList?projectId=" + row.projectId);
}
},
created() {
this.initData()
this.initData();
}
})
});
</script>
@@ -0,0 +1,531 @@
<!--#
layout("/layouts/platform_h5.html"){
#-->
<div id="app">
<van-nav-bar @click-left="historyBack" left-arrow left-text="返回" title="体检套餐选择" placeholder
fixed></van-nav-bar>
<div class="form-container">
<van-form ref="formRef" @submit="doSubmit">
<van-cell-group title="基础信息">
<!-- 与填写页使用相同的项目个人信息,查看页仅展示已保存值。 -->
<van-field label="姓名" :value="userBaseMessage.userName" input-align="right" readonly></van-field>
<van-field label="性别" :value="userBaseMessage.sex" input-align="right" readonly></van-field>
<van-field label="年龄" :value="userBaseMessage.age" input-align="right" readonly></van-field>
<van-field label="手机号码" :value="userBaseMessage.phone" input-align="right" readonly></van-field>
<van-field label="身份证号" :value="userBaseMessage.card" input-align="right" readonly></van-field>
<van-field label="人员类别" :value="personTypeLabel" input-align="right" readonly></van-field>
<van-field label="婚姻状态" :value="userBaseMessage.marriage" input-align="right" readonly></van-field>
</van-cell-group>
<van-cell-group title="本年度预约体检医院">
<div class="van-card-body">
<van-radio-group v-model="formData.subjectId" class="basic" disabled >
<van-cell-group>
<van-cell
v-for="(item,index) in project.healthCheckupProjectSubjects"
:key="item.id"
:title="(index + 1) + '、' +item.optionName">
<template #label>
<div style="display: flex; justify-content: space-between; margin-left: 8px">
<div @click.stop="viewDesc(item)" style="color: #0e78c5; padding-left: 15px">查看说明</div>
</div>
</template>
<template #right-icon>
<van-radio :name="item.id" ></van-radio>
</template>
</van-cell>
</van-cell-group>
</van-radio-group>
</div>
<van-field
input-align="right"
readonly
required
label="所选医院额度"
name="marriage"
placeholder="请选择医院"
v-model="formData.money"
:rules="[{ required: true, message: '请选择医院' }]"></van-field>
</van-cell-group>
<van-cell-group title="上年度预约体检医院">
<div class="van-card-body">
<van-cell title="上年度体检医院" :value="formData.lastYearHospital" />
</div>
</van-cell-group>
<!-- <van-cell-group title="体检报告邮寄地址">-->
<!-- <van-radio-group v-model="formData.postMethod" disabled>-->
<!-- <van-cell-group>-->
<!-- <van-cell title="单位" >-->
<!-- <template #right-icon>-->
<!-- <van-radio name="1"></van-radio>-->
<!-- </template>-->
<!-- </van-cell>-->
<!-- <van-cell title="自选" >-->
<!-- <template #right-icon>-->
<!-- <van-radio name="2"></van-radio>-->
<!-- </template>-->
<!-- </van-cell>-->
<!-- </van-cell-group>-->
<!-- </van-radio-group>-->
<!-- <template>-->
<!-- <van-field-->
<!-- v-if="formData.postMethod === '2'"-->
<!-- v-model="formData.receiveAddress"-->
<!-- label="邮寄地址"-->
<!-- placeholder="请选择邮寄地址"-->
<!-- readonly-->
<!-- type="textarea"-->
<!-- autosize-->
<!-- rows="2"-->
<!-- is-link-->
<!-- @click="this.$set(this, 'editAddressPopup', true)"-->
<!-- required-->
<!-- ></van-field>-->
<!-- <van-cell title="邮寄地址" v-else>{{formData.receiveAddress}}</van-cell>-->
<!-- <van-cell title="体检报告">-->
<!-- <div v-html="formData.medicalExamination"></div>-->
<!-- </van-cell>-->
<!-- </template>-->
<!-- </van-cell-group>-->
<van-cell-group title="家属信息" v-if="formData.isFamily === '是'">
<div style="padding: 10px;">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
<span>家属信息</span>
<div>
<van-tag @click="delCompanion" size="large" type="primary" color="lightgrey">
删除家属
</van-tag>
<van-tag @click="addCompanion" size="large" type="primary" color="#1867b0">
添加家属
</van-tag>
</div>
</div>
<div v-if="formData.companionList && formData.companionList.length > 0">
<van-tabs v-model="active" type="card" color="#0e78c5" animated>
<van-tab
v-for="(item, index) in formData.companionList"
:key="index"
:title="'家属' + (index + 1)">
<van-field
label="姓名"
name="userName"
placeholder="请填写姓名"
v-model="item.userName"
:rules="[{ required: true, message: '请填写姓名' }]">
</van-field>
<van-field
:rules="[{ required: true, message: '请选择性别' }]"
label="性别"
name="sex">
<template #input>
<van-radio-group direction="horizontal" v-model="item.sex">
<van-radio name="男" shape="square">男</van-radio>
<van-radio name="女" shape="square">女</van-radio>
</van-radio-group>
</template>
</van-field>
<van-field
:rules="[{ required: true, message: '请选择婚否' }]"
label="婚否"
name="marry">
<template #input>
<van-radio-group direction="horizontal" v-model="item.marry">
<van-radio name="未婚" shape="square">未婚</van-radio>
<van-radio name="已婚" shape="square">已婚</van-radio>
</van-radio-group>
</template>
</van-field>
<van-field
label="年龄"
name="age"
placeholder="请填写年龄"
type="digit"
:rules="[{ required: true, message: '请填写年龄' }]"
v-model="item.age">
</van-field>
<van-field
label="身份证号"
name="idCard"
placeholder="请填写身份证号"
type="digit"
:rules="[{ required: true, message: '请填写身份证号' }]"
v-model="item.idCard">
</van-field>
<van-field
label="手机号"
name="mobile"
placeholder="请填写手机号"
type="digit"
:rules="[{ required: true, message: '请填写手机号' }]"
v-model="item.mobile">
</van-field>
</van-tab>
</van-tabs>
</div>
<div v-else class="empty_text">
<span>如有家属,请添加家属</span>
</div>
</div>
</van-cell-group>
</van-form>
</div>
<!--体检院区弹出框-->
<van-action-sheet title="体检院区" v-model="campusVisible">
<van-picker
title="院区"
show-toolbar
:columns="campusList"
value-key="campusName"
@confirm="onCampusConfirm"
@cancel="cancelCampusSelection">
</van-picker>
</van-action-sheet>
<!--查看说明-->
<van-action-sheet title="医院说明" v-model="descVisible" :style="{ height: '60%' }">
<div class="form-container" style="margin: 10px;">
<van-cell-group title="说明"></van-cell-group>
<div v-html="desc" class="pre_text"></div>
<div style="margin-top: 10px">
<van-cell-group title="以下内容由医院方提供"></van-cell-group>
<file-preview :files="files" complete_result></file-preview>
</div>
</div>
</van-action-sheet>
<!-- 收货地址选择弹窗 -->
<van-action-sheet v-model="editAddressPopup" title="选择收货地址" :round="true">
<div style="padding: 16px 0 0">
<div
v-for="address in addressOptions"
:key="address.id"
class="address-item"
@click="selectAddress(address)"
style="padding: 12px 16px; border-bottom: 1px solid #f0f0f0; cursor: pointer"
>
<div style="display: flex; justify-content: space-between; align-items: flex-start">
<div style="flex: 1">
<div style="font-size: 16px; font-weight: 500; color: #323233; margin-bottom: 4px">
{{ address.userName }} {{ address.tel }}
</div>
<div style="font-size: 14px; color: #646566; line-height: 1.4">
{{ address.province }}{{ address.city }}{{ address.county }}{{ address.addressDetail
}}
</div>
</div>
<van-tag v-if="address.isDefault" type="primary" size="mini" style="margin-left: 8px">默认
</van-tag>
</div>
</div>
<div v-if="addressOptions.length === 0"
style="text-align: center; padding: 40px 16px; color: #969799">暂无收货地址
</div>
</div>
<div style="padding: 16px; border-top: 1px solid #f0f0f0; background: #fafafa">
<van-button type="info" block round @click="goToAddressManage">收货地址管理</van-button>
</div>
</van-action-sheet>
</div>
<script nonce="${cspNonce!}">
const vue = new Vue({
healthCheckupHistory: true,
dicts: ["USER_PERSON_TYPE"],
mixins: [healthCheckupHistoryMixin],
el: "#app",
store,
dicts: ["ASSET_USAGE_STATE"],
data() {
return {
formLoading: false,
viewData: {},
formData: {
subjectId: '',
campus: '',
campusName: '',
isFamily: '否',
companionList: [],
projectId: '',
postMethod: ''
},
assetUsageStateNameOption: [],
showAssetUsageStateNamePopup: false,
assetUseUserOption: [],
assetUseNameShow: false,
searchKeyword: "",
active: 0,
campusList: [],
campusVisible: false,
descVisible: false,
desc: '',
files: [],
project: {},
marriageList: ["已婚", "未婚"],
marriageVisible: false,
editAddressPopup: false,
addressOptions: [],
userBaseMessage: {}
};
},
computed: {
personTypeLabel() {
// 人员类别编码使用系统字典展示;兼容已有中文值,避免历史记录显示为空。
const value = this.userBaseMessage.personType;
const options = this.dict.type.USER_PERSON_TYPE || [];
const option = options.find(item => String(item.code) === String(value));
return option ? option.name : (value || '');
},
// 根据 subjectId 自动匹配出 optionName
getSelectedOptionName() {
if (!this.formData.subjectId) return '';
if (!this.project?.healthCheckupProjectSubjects) return '';
const item = this.project.healthCheckupProjectSubjects.find(item => item.id == this.formData.subjectId);
return item ? item.optionName : '';
}
},
methods: {
cancelCampusSelection() {
// 关闭院区选择弹框;已有全局弹框监听会同步回退对应历史记录。
this.$set(this, 'campusVisible', false);
},
// 选择收货地址
selectAddress(address) {
this.formData.receiveAddress = address.userName + " " + address.tel + " " + address.province + address.city + address.county + address.addressDetail;
this.editAddressPopup = false;
},
// 跳转到地址管理页面
goToAddressManage() {
this.navigateHealthCheckup("/platform/h5/welfare/address/list");
},
// 获取收货地址
getAddress() {
this.$axios.post("/platform/welfare/addressManage/selectUserAddress").then(resp => {
if (resp.code === 0) {
this.addressOptions = resp.data;
}
});
},
isEmptyObject(obj) {
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
return false;
}
}
return true;
},
addressClick() {},
// 打开表单
initData() {
return Promise.resolve(this.getCampus()).then(() => {
this.getAddress();
let projectId = GetQueryString("projectId");
return Promise.resolve(this.findSubject(projectId)).then(() => {
if (projectId) {
const {
id,
userState,
marriage,
birthday,
unit
} = this.$store.state.user;
return Promise.resolve(this.selectInfo(projectId)).then(() => {
return Promise.resolve(this.findUserBaseMessage(projectId, id)).then(() => {
return Promise.resolve(this.findLastYearHospital(projectId, id)).then(() => {
this.$set(this.formData, 'projectId', projectId);
this.$set(this.formData, 'idCard', this.userBaseMessage?.card);
this.$set(this.formData, 'userState', userState);
this.$set(this.formData, 'marriage', this.userBaseMessage?.marriage);
this.$set(this.formData, 'birthday', birthday);
this.$set(this.formData, 'unitName', (unit ? unit.name : ''));
this.$set(this.formData, 'postMethod', this.formData?.postMethod ? this.formData.postMethod : "1");
this.$set(this.formData, 'receiveAddress', this.formData?.receiveAddress ? this.formData.receiveAddress : (unit ? unit.name : ''));
this.$set(this.formData, 'medicalExamination', this.formData?.medicalExamination);
});
});
});
} else {}
});
});
},
clickRadio(item) {
const toast = this.$toast.loading({
duration: 0,
// 持续展示 toast
forbidClick: true,
message: '查询中....'
});
this.$axios.post("/platform/healthCheckup/h5/getSubjectMoney", {
id: item.id,
marriage: this.formData.marriage
}).then(resp => {
if (resp.code === 0) {
this.$set(this.formData, 'subjectId', item.id);
this.$set(this.formData, 'money', resp.data.money);
this.$set(this.formData, 'subjectMoneyId', resp.data.subjectMoneyId);
} else {
this.$set(this.formData, 'money', 0);
}
toast.clear();
}).finally(() => {
toast.clear();
});
},
selectInfo(projectId) {
return this.$axios.post('/platform/healthCheckup/h5/selectInfo', {
projectId: projectId
}).then(resp => {
if (resp.data !== null) {
this.formData = resp.data;
}
});
},
// 查看说明
viewDesc(item) {
if (item.description || item.files) {
this.files = item.files;
this.desc = item.description;
this.descVisible = true;
} else {
vant.Toast('暂无详细说明');
}
},
// 执行提交
doSubmit() {
if (this.formLoading) return;
const formData = clone(this.formData);
if (!formData.receiveAddress) {
this.$toast.fail('请填写邮寄地址!');
return;
}
const subject = this.project.healthCheckupProjectSubjects.find(v => v.id === formData.subjectId);
if (!subject) { this.$toast.fail('请选择体检医院'); return; }
this.$healthConfirm({
title: '温馨提示',
message: '您确定要提交选择的【' + subject.optionName + '】吗?'
}).then(() => {
const toast = this.$toast.loading({
duration: 0,
// 持续展示 toast
forbidClick: true,
message: '提交中....'
});
this.formLoading = true;
/* if (formData.isFamily === '否') {
formData.companionList = [];
} else {
// 过滤空的家属信息
formData.companionList = formData.companionList.filter(item => item.userName && item.userName.trim() !== '');
}*/
this.$axios.post('/platform/healthCheckup/h5/doSubmit', {
userSelection: JSON.stringify(formData)
}).then(resp => {
if (resp.code === 0) {
toast.clear();
this.$toast.success(resp.msg);
// 提交成功后展示当前项目的医院选择,避免按来源历史退回基础信息页。
return this.navigateHealthCheckup('/platform/healthCheckup/h5/mine?projectId=' + encodeURIComponent(formData.projectId));
}
}).finally(() => {
toast.clear();
this.formLoading = false;
});
}).catch(() => {});
},
// 添加家属
addCompanion() {
// 检查是否已达到最大数量限制
if (this.formData.companionList && this.formData.companionList.length >= 5) {
vant.Toast('最多只能添加5个家属');
return;
}
if (!this.formData.companionList) {
this.$set(this.formData, 'companionList', []);
}
this.formData.companionList.push({
userName: '',
sex: '',
marry: '',
age: '',
idCard: '',
mobile: ''
});
},
// 删除家属
delCompanion() {
if (this.formData.companionList && this.formData.companionList.length > 0) {
this.formData.companionList.splice(this.active, 1);
// 确保active索引有效
if (this.active >= this.formData.companionList.length && this.active > 0) {
this.active = this.formData.companionList.length - 1;
}
}
},
// 院区选择确认
onCampusConfirm(value) {
this.formData.campusName = value.campusName;
this.formData.campus = value.id;
this.campusVisible = false;
},
// 婚姻状况确认
onMarriageConfirm(value) {
this.$set(this.formData, 'marriage', value);
this.$set(this.formData, 'subjectId', null);
this.marriageVisible = false;
},
// 查询体检套餐
findSubject(id) {
return Promise.resolve().then(() => {
return this.$axios.post('/platform/healthCheckup/project/mange/findOne', {
id: id
}).then(resp => {
this.project = resp.data || {};
});
}).catch(error => {
vant.Toast('获取体检套餐失败');
});
},
// 获取院区
getCampus() {
return Promise.resolve().then(() => {
return this.$axios.post('/platform/healthCheckup/h5/getCampus').then(resp => {
this.campusList = resp.data || [];
});
}).catch(error => {
vant.Toast('获取院区失败');
});
},
findUserBaseMessage(projectId, userId) {
return this.$axios.post('/platform/healthCheckup/h5/findUserBaseMessageData', {
projectId,
userId
}).then(resp => {
if (resp.data !== null) {
this.userBaseMessage = resp.data;
}
});
},
findLastYearHospital(projectId, userId, subjectId) {
return this.$axios.post('/platform/healthCheckup/h5/findLastYearHospital', {
projectId,
selectUserId: userId
}).then(resp => {
if (resp.data !== null) {
this.$set(this.formData, 'lastYearHospital', resp.data);
}
});
}
},
created() {
this.initData();
}
});
</script>
<!--#
}
#-->
@@ -1,7 +1,6 @@
<!--#
layout("/layouts/platform_h5.html"){
#-->
<div id="app">
<van-nav-bar @click-left="historyBack" left-arrow left-text="返回" title="体检套餐选择" placeholder
fixed></van-nav-bar>
@@ -11,41 +10,61 @@ layout("/layouts/platform_h5.html"){
<van-cell-group title="基础信息">
<van-field
input-align="right"
label="身份证号"
name="idCard"
label="姓名"
name="userName"
readonly
required
placeholder="请联系校工会补充身份证号"
v-model="formData.idCard"
:rules="[{ required: true, message: '请输入身份证号' }]">
v-model="userBaseMessage.userName"
:rules="[{ required: true, message: '请输入姓名' }]">
</van-field>
<van-cell title="人员类型">{{formData.userState}}</van-cell>
<van-cell title="性别">
<template #right-icon>
<van-radio-group v-model="userBaseMessage.sex" direction="horizontal" disabled >
<van-radio name="男">男</van-radio>
<van-radio name="女">女</van-radio>
</van-radio-group>
</template>
</van-cell>
<van-field label="年龄" input-align="right" v-model="userBaseMessage.age" type="digit" required :rules="[{ required: true, message: '请输入年龄' }]"></van-field>
<van-field
input-align="right"
readonly
label="手机号码"
name="phone"
required
label="婚姻状况"
name="marriage"
placeholder="请联系校工会补充婚姻状况"
v-model="formData.marriage"
:rules="[{ required: true, message: '请选择婚姻状况' }]"></van-field>
<van-cell title="出生年月">{{$moment(formData.birthday).format('YYYY-MM-DD')}}</van-cell>
<van-cell title="单位">{{formData.unitName}}</van-cell>
v-model="userBaseMessage.phone"
:rules="[{ required: true, message: '请输入手机号码' }, { pattern: /^1[0-9]{10}$/, message: '请输入正确的手机号码' }]">
</van-field>
<van-field
input-align="right"
label="身份证号"
name="card"
required
placeholder="请输入身份证号"
v-model="userBaseMessage.card"
:rules="[{ required: true, message: '请输入身份证号' }]">
</van-field>
<van-cell title="人员类别">{{personTypeLabel}}</van-cell>
<van-cell title="婚姻状态">
<template #right-icon>
<van-radio-group v-model="userBaseMessage.marriage" direction="horizontal" >
<van-radio name="已婚">已婚</van-radio>
<van-radio name="未婚">未婚</van-radio>
</van-radio-group>
</template>
</van-cell>
</van-cell-group>
<van-cell-group title="本年度预约体检医院">
<div class="van-card-body">
<van-radio-group v-model="formData.subjectId" class="basic">
<van-cell-group>
<van-cell
v-for="item in project.healthCheckupProjectSubjects"
v-for="(item,index) in project.healthCheckupProjectSubjects"
:key="item.id"
:title="item.optionName">
:title="(index + 1) + '、' +item.optionName">
<template #label>
<div style="display: flex; justify-content: space-between; margin-left: 8px">
<!-- <div>{{ item.optionName }}</div>-->
<div></div>
<div @click.stop="viewDesc(item)" style="color: #0e78c5">查看说明</div>
<div @click.stop="viewDesc(item)" style="color: #0e78c5; padding-left: 15px">查看说明</div>
</div>
</template>
<template #right-icon>
@@ -60,50 +79,55 @@ layout("/layouts/platform_h5.html"){
readonly
required
label="所选医院额度"
name="marriage"
name="money"
placeholder="请选择医院"
v-model="formData.money"
:rules="[{ required: true, message: '请选择医院' }]"></van-field>
</van-cell-group>
<van-cell-group title="体检报告邮寄地址">
<van-radio-group v-model="formData.postMethod">
<van-cell-group>
<van-cell title="单位" clickable
@click="formData.postMethod = '1';formData.receiveAddress=formData.unitName">
<template #right-icon>
<van-radio name="1"></van-radio>
</template>
</van-cell>
<van-cell title="自选" clickable
@click="formData.postMethod = '2';formData.receiveAddress=''">
<template #right-icon>
<van-radio name="2"></van-radio>
</template>
</van-cell>
</van-cell-group>
</van-radio-group>
<template>
<van-field
v-if="formData.postMethod === '2'"
v-model="formData.receiveAddress"
label="邮寄地址"
placeholder="请选择邮寄地址"
readonly
type="textarea"
autosize
rows="2"
is-link
@click="editAddressPopup = true"
required
></van-field>
<van-cell title="邮寄地址" v-else>{{formData.receiveAddress}}</van-cell>
</template>
<van-cell-group title="上年度预约体检医院">
<div class="van-card-body">
<van-cell title="上年度体检医院" :value="formData.lastYearHospital" />
</div>
</van-cell-group>
<!-- <van-cell-group title="体检报告邮寄地址">-->
<!-- <van-radio-group v-model="formData.postMethod">-->
<!-- <van-cell-group>-->
<!-- <van-cell title="单位" clickable-->
<!-- @click="this.$set(formData, 'postMethod', '1');this.$set(formData, 'receiveAddress', formData.unitName)">-->
<!-- <template #right-icon>-->
<!-- <van-radio name="1"></van-radio>-->
<!-- </template>-->
<!-- </van-cell>-->
<!-- <van-cell title="自选" clickable-->
<!-- @click="this.$set(formData, 'postMethod', '2');this.$set(formData, 'receiveAddress', '')">-->
<!-- <template #right-icon>-->
<!-- <van-radio name="2"></van-radio>-->
<!-- </template>-->
<!-- </van-cell>-->
<!-- </van-cell-group>-->
<!-- </van-radio-group>-->
<!-- <template>-->
<!-- <van-field-->
<!-- v-if="formData.postMethod === '2'"-->
<!-- v-model="formData.receiveAddress"-->
<!-- label="邮寄地址"-->
<!-- placeholder="请选择邮寄地址"-->
<!-- readonly-->
<!-- type="textarea"-->
<!-- autosize-->
<!-- rows="2"-->
<!-- is-link-->
<!-- @click="this.$set(this, 'editAddressPopup', true)"-->
<!-- required-->
<!-- ></van-field>-->
<!-- <van-cell title="邮寄地址" v-else>{{formData.receiveAddress}}</van-cell>-->
<!-- </template>-->
<!-- </van-cell-group>-->
<!-- <van-cell-group title="基础信息">
<div class="van-card-body">
<van-field
@click="campusVisible = true"
@click="this.$set(this, 'campusVisible', true)"
readonly
is-link
label="院区"
@@ -210,7 +234,7 @@ layout("/layouts/platform_h5.html"){
<!-- 按钮区域 -->
<div class="form-actions">
<van-button type="primary" native-type="submit">提交</van-button>
<van-button type="primary" native-type="submit" :loading="formLoading" :disabled="formLoading">提交</van-button>
</div>
</van-form>
</div>
@@ -223,7 +247,7 @@ layout("/layouts/platform_h5.html"){
:columns="campusList"
value-key="campusName"
@confirm="onCampusConfirm"
@cancel="campusVisible = false">
@cancel="cancelCampusSelection">
</van-picker>
</van-action-sheet>
@@ -276,12 +300,16 @@ layout("/layouts/platform_h5.html"){
<script nonce="${cspNonce!}">
const vue = new Vue({
const vue = new Vue({
healthCheckupHistory: true,
dicts: ["USER_PERSON_TYPE"],
mixins: [healthCheckupHistoryMixin],
el: "#app",
store,
dicts: ["ASSET_USAGE_STATE"],
data() {
return {
formLoading: false,
viewData: {},
formData: {
subjectId: '',
@@ -304,93 +332,140 @@ layout("/layouts/platform_h5.html"){
desc: '',
files: [],
project: {},
marriageList: ["已婚", "未婚"],
marriageVisible: false,
editAddressPopup: false,
addressOptions: []
addressOptions: [],
userBaseMessage: {}
};
},
computed: {
personTypeLabel() {
// 人员类别编码使用系统字典展示;兼容已有中文值,避免历史记录显示为空。
const value = this.userBaseMessage.personType;
const options = this.dict.type.USER_PERSON_TYPE || [];
const option = options.find(item => String(item.code) === String(value));
return option ? option.name : (value || '');
}
},
watch: {
'userBaseMessage.age'(value, previous) {
if (previous !== undefined && value !== previous) this.resetSubjectForBaseInfo();
},
'userBaseMessage.marriage'(value, previous) {
if (previous !== undefined && value !== previous) this.resetSubjectForBaseInfo();
}
},
methods: {
resetSubjectForBaseInfo() {
// 年龄或婚姻状态影响套餐资格,修改后需重新选择医院计算额度。
this.$set(this.formData, 'subjectId', null);
this.$set(this.formData, 'subjectMoneyId', null);
this.$set(this.formData, 'money', null);
},
cancelCampusSelection() {
// 关闭院区选择弹框;已有全局弹框监听会同步回退对应历史记录。
this.$set(this, 'campusVisible', false);
},
// 选择收货地址
selectAddress(address) {
this.formData.receiveAddress =
address.userName + " " + address.tel + " " + address.province + address.city + address.county + address.addressDetail
this.editAddressPopup = false
this.formData.receiveAddress = address.userName + " " + address.tel + " " + address.province + address.city + address.county + address.addressDetail;
this.editAddressPopup = false;
},
// 跳转到地址管理页面
goToAddressManage() {
this.$pjaxReplace("/platform/h5/welfare/address/list")
this.navigateHealthCheckup("/platform/h5/welfare/address/list");
},
// 获取收货地址
getAddress() {
this.$axios.post("/platform/welfare/addressManage/selectUserAddress").then(resp => {
if (resp.code === 0) {
this.addressOptions = resp.data
this.addressOptions = resp.data;
}
})
});
},
isEmptyObject(obj) {
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
return false
return false;
}
}
return true
},
addressClick() {
return true;
},
addressClick() {},
// 打开表单
async initData() {
await this.getCampus()
this.getAddress()
await this.findSubject(GetQueryString("projectId"))
if (GetQueryString("projectId")) {
await this.selectInfo(GetQueryString("projectId"));
const {idCard, userState, marriage, birthday, unit} = this.$store.state.user
this.$set(this.formData, 'projectId', GetQueryString("projectId"))
this.$set(this.formData, 'idCard', idCard)
this.$set(this.formData, 'userState', userState)
this.$set(this.formData, 'marriage', marriage)
this.$set(this.formData, 'birthday', birthday)
this.$set(this.formData, 'unitName', unit.name)
this.$set(this.formData, 'postMethod', this.formData?.postMethod ? this.formData.postMethod : "1")
this.$set(this.formData, 'receiveAddress', this.formData?.receiveAddress ? this.formData.receiveAddress : unit.name)
}
initData() {
return Promise.resolve(this.getCampus()).then(() => {
this.getAddress();
let projectId = GetQueryString("projectId");
return Promise.resolve(this.findSubject(projectId)).then(() => {
if (projectId) {
const {
id,
birthday,
unit
} = this.$store.state.user;
return Promise.resolve(this.selectInfo(projectId)).then(() => {
return Promise.resolve(this.findUserBaseMessage(projectId, id)).then(() => {
return Promise.resolve(this.findLastYearHospital(projectId, id)).then(() => {
this.$set(this.formData, 'projectId', projectId);
this.$set(this.formData, 'idCard', this.userBaseMessage?.card);
this.$set(this.formData, 'marriage', this.userBaseMessage?.marriage);
this.$set(this.formData, 'birthday', birthday);
this.$set(this.formData, 'unitName', (unit ? unit.name : ''));
this.$set(this.formData, 'postMethod', this.formData?.postMethod ? this.formData.postMethod : "1");
this.$set(this.formData, 'receiveAddress', this.formData?.receiveAddress ? this.formData.receiveAddress : (unit ? unit.name : ''));
});
});
});
} else {}
});
});
},
clickRadio(item) {
if (this.userBaseMessage.age == null || this.userBaseMessage.age === '' || !this.userBaseMessage.marriage) {
this.$set(this.formData, 'subjectId', null);
this.$toast.fail('请填写年龄并选择婚姻状态');
return;
}
const previewAge = this.userBaseMessage.age;
const previewMarriage = this.userBaseMessage.marriage;
const toast = this.$toast.loading({
duration: 0, // 持续展示 toast
duration: 0,
// 持续展示 toast
forbidClick: true,
message: '查询中....',
message: '查询中....'
});
$.post("/platform/healthCheckup/h5/getSubjectMoney", {
this.$axios.post("/platform/healthCheckup/h5/getSubjectMoney", {
id: item.id,
marriage: this.formData.marriage
marriage: previewMarriage,
age: previewAge
}).then(resp => {
// 输入在请求期间变化时丢弃旧额度,避免旧条件覆盖新表单。
if (previewAge !== this.userBaseMessage.age || previewMarriage !== this.userBaseMessage.marriage) return;
if (resp.code === 0) {
this.$set(this.formData, 'subjectId', item.id)
this.$set(this.formData, 'money', resp.data.money)
this.$set(this.formData, 'subjectMoneyId', resp.data.subjectMoneyId)
this.$set(this.formData, 'subjectId', item.id);
this.$set(this.formData, 'money', resp.data.money);
this.$set(this.formData, 'subjectMoneyId', resp.data.subjectMoneyId);
} else {
this.$set(this.formData, 'money', 0)
this.$set(this.formData, 'money', 0);
}
toast.clear();
})
}).finally(() => {
toast.clear();
});
},
async selectInfo(projectId) {
const resp = await this.$axios.post('/platform/healthCheckup/h5/selectInfo', {projectId: projectId});
selectInfo(projectId) {
return this.$axios.post('/platform/healthCheckup/h5/selectInfo', {
projectId: projectId
}).then(resp => {
if (resp.data !== null) {
this.formData = resp.data;
/* const o = this.campusList.find(o => o.id === this.formData.campus)
this.formData.campusName = o.campusName*/
}
});
},
// 查看说明
viewDesc(item) {
if (item.description || item.files) {
@@ -401,51 +476,62 @@ layout("/layouts/platform_h5.html"){
vant.Toast('暂无详细说明');
}
},
// 执行提交
async doSubmit() {
doSubmit() {
if (this.formLoading) return;
const age = Number(this.userBaseMessage.age);
if (this.userBaseMessage.age == null || this.userBaseMessage.age === '' || !Number.isInteger(age) || age < 0 || age > 120) {
this.$toast.fail('请输入0至120的整数年龄');
return;
}
if (!this.userBaseMessage.marriage) { this.$toast.fail('请选择婚姻状态'); return; }
const baseInfo = {age, phone: this.userBaseMessage.phone, card: this.userBaseMessage.card, marriage: this.userBaseMessage.marriage};
const formData = clone(this.formData);
if (!formData.receiveAddress) {
this.$toast.fail('请填写邮寄地址!')
this.$toast.fail('请填写邮寄地址!');
return;
}
const subject = this.project.healthCheckupProjectSubjects.find(v => v.id === formData.subjectId)
this.$dialog.confirm({
const subject = this.project.healthCheckupProjectSubjects.find(v => v.id === formData.subjectId);
if (!subject) { this.$toast.fail('请选择体检医院'); return; }
this.$healthConfirm({
title: '温馨提示',
message: '您确定要提交选择的【' + subject.optionName + '】吗?',
message: '您确定要提交选择的【' + subject.optionName + '】吗?'
}).then(() => {
const toast = this.$toast.loading({
duration: 0, // 持续展示 toast
duration: 0,
// 持续展示 toast
forbidClick: true,
message: '提交中....',
message: '提交中....'
});
this.formLoading = true;
/* if (formData.isFamily === '否') {
formData.companionList = [];
} else {
// 过滤空的家属信息
formData.companionList = formData.companionList.filter(item => item.userName && item.userName.trim() !== '');
}*/
this.$axios.post('/platform/healthCheckup/h5/doSubmit', {
userSelection: JSON.stringify(formData),
return this.$axios.post('/platform/healthCheckup/h5/doSubmit', {
baseInfo: JSON.stringify(baseInfo),
userSelection: JSON.stringify(formData)
}).then(resp => {
if (resp.code === 0) {
toast.clear();
this.$toast.success(resp.msg)
this.historyBack()
this.$toast.success(resp.msg);
// 提交成功后展示当前项目的医院选择,避免按来源历史退回基础信息页。
return this.navigateHealthCheckup('/platform/healthCheckup/h5/mine?projectId=' + encodeURIComponent(formData.projectId));
}
})
}).catch(() => {
toast.clear();
}).finally(() => {
toast.clear();
this.formLoading = false;
});
}).catch(() => {});
},
// 添加家属
addCompanion() {
// 检查是否已达到最大数量限制
if (this.companionList && this.formData.companionList.length >= 5) {
if (this.formData.companionList && this.formData.companionList.length >= 5) {
vant.Toast('最多只能添加5个家属');
return;
}
@@ -453,10 +539,14 @@ layout("/layouts/platform_h5.html"){
this.$set(this.formData, 'companionList', []);
}
this.formData.companionList.push({
userName: '', sex: '', marry: '', age: '', idCard: '', mobile: ''
userName: '',
sex: '',
marry: '',
age: '',
idCard: '',
mobile: ''
});
},
// 删除家属
delCompanion() {
if (this.formData.companionList && this.formData.companionList.length > 0) {
@@ -467,50 +557,66 @@ layout("/layouts/platform_h5.html"){
}
}
},
// 院区选择确认
onCampusConfirm(value) {
this.formData.campusName = value.campusName;
this.formData.campus = value.id;
this.campusVisible = false;
},
// 婚姻状况确认
onMarriageConfirm(value) {
this.$set(this.formData, 'marriage', value)
this.$set(this.formData, 'subjectId', null)
this.$set(this.formData, 'marriage', value);
this.$set(this.formData, 'subjectId', null);
this.marriageVisible = false;
},
// 查询体检套餐
async findSubject(id) {
try {
const resp = await this.$axios.post('/platform/healthCheckup/project/mange/findOne', {
findSubject(id) {
return Promise.resolve().then(() => {
return this.$axios.post('/platform/healthCheckup/project/mange/findOne', {
id: id
});
}).then(resp => {
this.project = resp.data || {};
} catch (error) {
});
}).catch(error => {
vant.Toast('获取体检套餐失败');
}
});
},
// 获取院区
async getCampus() {
try {
const resp = await this.$axios.post('/platform/healthCheckup/h5/getCampus');
getCampus() {
return Promise.resolve().then(() => {
return this.$axios.post('/platform/healthCheckup/h5/getCampus').then(resp => {
this.campusList = resp.data || [];
} catch (error) {
});
}).catch(error => {
vant.Toast('获取院区失败');
});
},
findUserBaseMessage(projectId, userId) {
return this.$axios.post('/platform/healthCheckup/h5/findUserBaseMessageData', {
projectId,
userId
}).then(resp => {
if (resp.data !== null) {
this.userBaseMessage = resp.data;
}
});
},
findLastYearHospital(projectId, userId, subjectId) {
return this.$axios.post('/platform/healthCheckup/h5/findLastYearHospital', {
projectId,
selectUserId: userId
}).then(resp => {
if (resp.data !== null) {
this.$set(this.formData, 'lastYearHospital', resp.data);
}
});
}
},
created() {
this.initData()
this.initData();
}
})
});
</script>
<!--#
}
#-->
@@ -0,0 +1,182 @@
<!--#
layout("/layouts/platform_h5.html"){
#-->
<div id="app">
<van-nav-bar @click-left="historyBack" left-arrow left-text="返回" title="基础信息" placeholder
style="background-color: #ccc5c5;"
fixed></van-nav-bar>
<div class="form-container" v-cloak>
<van-form ref="formRef" @submit="doSubmit">
<van-cell-group>
<van-field
input-align="right"
label="姓名"
name="userName"
readonly
required
v-model="formData.userName"
:rules="[{ required: true, message: '请输入姓名' }]">
</van-field>
<van-cell title="性别">
<template #right-icon>
<van-radio-group v-model="formData.sex" direction="horizontal" disabled >
<van-radio name="男">男</van-radio>
<van-radio name="女">女</van-radio>
</van-radio-group>
</template>
</van-cell>
<van-field label="年龄" input-align="right" v-model="formData.age" type="digit" required :rules="[{ required: true, message: '请输入年龄' }]"></van-field>
<van-field
input-align="right"
label="手机号码"
name="phone"
required
v-model="formData.phone"
:rules="[{ required: true, message: '请输入手机号码' }, { pattern: /^1[0-9]{10}$/, message: '请输入正确的手机号码' }]">
</van-field>
<van-field
input-align="right"
label="身份证号"
name="card"
required
placeholder="请输入身份证号"
v-model="formData.card"
:rules="[{ required: true, message: '请输入身份证号' }]">
</van-field>
<van-cell title="人员类别">{{personTypeLabel}}</van-cell>
<van-cell title="婚姻状态">
<template #right-icon>
<van-radio-group v-model="formData.marriage" direction="horizontal" >
<van-radio name="已婚">已婚</van-radio>
<van-radio name="未婚">未婚</van-radio>
</van-radio-group>
</template>
</van-cell>
</van-cell-group>
<!-- 按钮区域 -->
<div class="form-actions">
<van-button native-type="button" @click="historyBack" :disabled="formLoading">返回</van-button>
<van-button native-type="submit" type="primary" :loading="formLoading">保存并继续体检</van-button>
</div>
</van-form>
</div>
</div>
<script nonce="${cspNonce!}">
const vue = new Vue({
healthCheckupHistory: true,
dicts: ["USER_PERSON_TYPE"],
mixins: [healthCheckupHistoryMixin],
el: "#app",
store,
data() {
return {
project: {},
formLoading: false,
formData: {
sex: "",
age: 0,
marriage: ""
}
};
},
computed: {
personTypeLabel() {
// 人员类别编码使用系统字典展示;兼容已有中文值,避免历史记录显示为空。
const value = this.formData.personType;
const options = this.dict.type.USER_PERSON_TYPE || [];
const option = options.find(item => String(item.code) === String(value));
return option ? option.name : (value || '');
}
},
methods: {
// 打开表单
initData() {
return Promise.resolve(this.findSubject(GetQueryString("projectId"))).then(() => {
if (GetQueryString("projectId")) {
const {
id,
birthday
} = this.$store.state.user;
return Promise.resolve(this.selectInfo(GetQueryString("projectId"), id)).then(() => {
this.$set(this.formData, 'projectId', GetQueryString("projectId"));
// 1. 转成日期对象
// 1. 转成日期对象
if (this.formData.age == null && birthday) {
const birth = new Date(birthday);
const now = new Date();
let age = now.getFullYear() - birth.getFullYear();
const birthMonth = birth.getMonth();
const nowMonth = now.getMonth();
const birthDate = birth.getDate();
const nowDate = now.getDate();
if (nowMonth < birthMonth || nowMonth === birthMonth && nowDate < birthDate) {
age--;
}
this.$set(this.formData, 'age', age);
}
});
} else {}
});
},
findSubject(id) {
return Promise.resolve().then(() => {
return this.$axios.post('/platform/healthCheckup/project/mange/findOne', {
id
}).then(resp => {
this.project = resp.data || {};
});
}).catch(error => {
vant.Toast('获取体检项目失败');
});
},
// 仅提交当前项目允许修改的字段,保存成功后继续选择医院。
doSubmit() {
if (this.formLoading) return;
const age = Number(this.formData.age);
if (this.formData.age == null || this.formData.age === '' || !Number.isInteger(age) || age < 0 || age > 120) {
vant.Toast('请输入0至120的整数年龄');
return;
}
if (!this.formData.marriage) {
vant.Toast('请选择婚姻状态');
return;
}
this.formLoading = true;
return this.$axios.post('/platform/healthCheckup/h5/doSubmitUserBaseMessage', {
projectId: GetQueryString('projectId'), age,
phone: this.formData.phone, card: this.formData.card, marriage: this.formData.marriage
}).then(resp => {
if (resp.code === 0) {
return this.navigateHealthCheckup('/platform/healthCheckup/h5/projectManageForm?projectId=' + encodeURIComponent(GetQueryString('projectId')));
}
vant.Toast(resp.msg || '保存失败');
}).catch(error => {
// 全局拦截器会拒绝业务失败响应;捕获后保留输入,供用户修正并重试。
vant.Toast((error && (error.msg || error.message)) || '保存失败,请重试');
}).finally(() => { this.formLoading = false; });
},
selectInfo(projectId, userId) {
return this.$axios.post('/platform/healthCheckup/h5/findUserBaseMessageData', {
projectId,
userId
}).then(resp => {
if (resp.data !== null) {
this.formData = resp.data;
}
});
}
},
created() {
this.initData().catch(error => {
// 初始化失败时给出可读提示,避免未捕获的 Promise 异常。
vant.Toast((error && (error.msg || error.message)) || '个人信息加载失败,请重试');
});
}
});
</script>
<!--#
}
#-->