Compare commits

...
10 Commits
Author SHA1 Message Date
= e68e71b883 基金会员 2026-07-27 11:29:13 +08:00
c-zhangr1 26646395e0 医疗互助补助模块 2026-07-23 18:26:44 +08:00
c-zhangr1 c34dd44bb4 Merge remote-tracking branch 'origin/main' 2026-07-23 13:58:17 +08:00
c-zhangr1 076f17905d 医疗互助补助模块 2026-07-23 13:57:28 +08:00
c-zhangr1 a17140fbd2 医疗互助补助模块 2026-07-23 11:57:08 +08:00
= 4e9d55bfc0 基金会员 2026-07-23 10:40:17 +08:00
= b45f194355 commit 2026-06-16 15:49:36 +08:00
= ec015a66df commit 2026-06-15 15:47:34 +08:00
dd3s_206 5e4e27023f (人员管理)离退休-离退休人员管理:新增手机h5端查询页; 2026-06-15 09:23:04 +08:00
dd3s_206 38441b30f6 (人员管理)离退休-离退休人员管理:新增手机h5端查询页; 2026-06-13 10:37:01 +08:00
82 changed files with 8492 additions and 276 deletions
@@ -27,14 +27,13 @@ import java.util.Map;
@IocBean
public class SmsJshvcServiceImpl implements SmsService {
private static final String APPID = "1430576717768122368";
private static final String APP_SECRET = "19A0ACB03FBQHL4R4XQD";
private static final String TOKEN_URL = "https://gateway.jshvc.edu.cn/token/gateway/accessToken";
private static final String MSG_URL = "https://gateway.jshvc.edu.cn/mp/restful/v2/message/send";
private static final String REDIS_KEY_MSG_ACCESS_TOKEN = "msg:token:";
private static final String APPID = "";
private static final String APP_SECRET = "";
private static final String TOKEN_URL = "";
private static final String MSG_URL = "";
private static final String REDIS_KEY_MSG_ACCESS_TOKEN = "";
@Inject
@Inject
private RedisService redisService;
@Override
@@ -1,6 +1,7 @@
package com.budwk.app.sys.listener;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.event.user.UserChangeEventListener;
import com.budwk.app.base.event.user.UserChangeMsg;
import com.budwk.app.sys.models.Sys_user_role;
@@ -31,9 +32,17 @@ public class SysUserChangeListener implements UserChangeEventListener {
@Aop(TransAop.READ_COMMITTED)
public void receive(UserChangeMsg message) {
if (ObjectUtil.equals(message.getOperationType(), UserChangeMsg.UNIT_CHANGE_OPERATION)) {
if (StrUtil.isBlank(message.getSourceUnitId())) {
return;
}
// 单位变动,删除原单位的角色
dao.clear(Sys_user_role.class, Cnd.where("userId", "in", message.getUserIds()).and("unitId", "=", message.getSourceUnitId()));
if (StrUtil.isBlank(message.getTargetUnitId())) {
return;
}
// 判断这两个单位是不是在一个工会,如果不是,删除原来工会的所有角色
Sql sql = Sqls.create("""
SELECT
@@ -51,8 +60,9 @@ public class SysUserChangeListener implements UserChangeEventListener {
dao.execute(sql);
NutMap result = (NutMap) sql.getResult();
if (!result.getBoolean("result")) {
dao.clear(Sys_user_role.class, Cnd.where("userId", "in", message.getUserIds()).and("unionId", "=", result.getString("sourceUnionId")));
String sourceUnionId = result.getString("sourceUnionId");
if (!result.getBoolean("result") && StrUtil.isNotBlank(sourceUnionId)) {
dao.clear(Sys_user_role.class, Cnd.where("userId", "in", message.getUserIds()).and("unionId", "=", sourceUnionId));
}
}
@@ -294,6 +294,11 @@ public class Sys_user extends BaseModel implements Serializable {
@Default(value = "0")
private Boolean aidFundMember;
@Column
@Comment("大病基金会员类别")
@ColDefine(type = ColType.VARCHAR, width = 50)
private String aidFundMemberType;
@Column
@Comment("家庭主要成员及其工作单位")
@ColDefine(type = ColType.MYSQL_JSON)
@@ -5,6 +5,7 @@ import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HtmlUtil;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.event.role.RoleEventMsg;
@@ -26,13 +27,14 @@ import lombok.extern.slf4j.Slf4j;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.FieldFilter;
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.random.R;
import org.nutz.lang.util.NutMap;
import org.springframework.beans.BeanUtils;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.lang.reflect.Field;
@@ -64,6 +66,11 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
private static Map<String, String> unitIdNameMap;
/**
* 数据中心允许更新的用户字段表达式。
*/
private static String dataCenterUpdateFields;
/**
* 字段映射类,用于缓存反射结果
*/
@@ -110,6 +117,24 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
return fieldMappings;
}
/**
* 获取数据中心字段白名单表达式,供批量更新时过滤本地业务字段。
*
* @return 字段过滤表达式
*/
private static String getDataCenterUpdateFields() {
if (dataCenterUpdateFields == null) {
synchronized (SysDataUserAllUpdateServiceImpl.class) {
if (dataCenterUpdateFields == null) {
dataCenterUpdateFields = getFieldMappings().stream()
.map(mapping -> mapping.field.getName())
.collect(Collectors.joining("|", "^(id|", ")$"));
}
}
}
return dataCenterUpdateFields;
}
/**
* 检查用户是否符合会员条件
*
@@ -182,12 +207,8 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
for (Sys_user_source source : sources) {
Sys_user user = userMap.get(source.getLoginname());
// 创建用户对象
Sys_user u = new Sys_user();
BeanUtils.copyProperties(source, u);
// 数据中心可能返回三级单位编码,系统用户统一挂到二级单位。
u.setUnitId(normalizeSecondLevelUnitId(u.getUnitId()));
// 创建只包含数据中心字段的用户对象,避免覆盖本地维护的会员、登录、积分等业务字段。
Sys_user u = buildDataCenterUser(source);
if (user == null) {
// 新增用户,初始化数据
@@ -199,8 +220,6 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
} else {
// 修改现有用户
u.setId(user.getId());
// 全量更新只同步数据中心基础资料,会员状态由会员业务流程维护。
u.setMember(null);
needDoUpdateList.add(u);
}
@@ -244,7 +263,8 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
List<List<Sys_user>> batches = ListUtil.split(needDoUpdateList, 500);
batches.forEach(batch -> {
try {
dao.updateIgnoreNull(batch);
Dao extDao = Daos.ext(dao, FieldFilter.create(Sys_user.class, getDataCenterUpdateFields()));
extDao.updateIgnoreNull(batch);
} catch (Exception e) {
log.error("批量更新用户异常", e);
}
@@ -369,6 +389,53 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
return unitId;
}
/**
* 构建数据中心同步对象,只写入带有DataCenterColumn注解且来源非空的字段。
*
* @param source 数据中心来源用户
* @return 待新增或更新的系统用户
*/
private Sys_user buildDataCenterUser(Sys_user_source source) {
Sys_user user = new Sys_user();
for (FieldMapping mapping : getFieldMappings()) {
try {
Object sourceValue = mapping.field.get(source);
Object updateValue = normalizeDataCenterValue(mapping.field.getName(), sourceValue);
if (updateValue != null) {
mapping.field.set(user, updateValue);
}
} catch (IllegalAccessException e) {
log.error("数据中心字段赋值失败: {}", mapping.field.getName(), e);
}
}
return user;
}
/**
* 数据中心字段更新值归一化,空字符串和空值不参与更新,单位编码统一归一到二级单位。
*
* @param fieldName 字段名
* @param value 来源值
* @return 可更新值
*/
private Object normalizeDataCenterValue(String fieldName, Object value) {
if (value == null) {
return null;
}
if (value instanceof String) {
String strValue = (String) value;
String trimmedValue = StrUtil.trimToNull(strValue);
if (trimmedValue == null) {
return null;
}
if ("unitId".equals(fieldName)) {
return normalizeSecondLevelUnitId(trimmedValue);
}
return trimmedValue;
}
return value;
}
/**
* 构建变更的历史数据
*
@@ -403,14 +470,18 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
}
Object sourceValue = mapping.field.get(source);
Object userValue = mapping.field.get(user);
Object updateValue = normalizeDataCenterValue(mapping.field.getName(), sourceValue);
if (updateValue == null) {
continue;
}
// 如果值不相等,记录变更
if (!ObjectUtil.equals(sourceValue, userValue)) {
if (!ObjectUtil.equals(updateValue, userValue)) {
NutMap change = NutMap.NEW();
change.put("fieldName", mapping.name);
change.put("field", mapping.field.getName());
change.put("sourceValue", userValue == null ? "" : userValue.toString());
change.put("newValue", sourceValue == null ? "" : sourceValue.toString());
change.put("newValue", updateValue.toString());
changeList.add(change);
}
} catch (IllegalAccessException e) {
@@ -436,18 +507,21 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
// 2. 单位变更
String oldUnitId = normalizeSecondLevelUnitId(user.getUnitId());
String newUnitId = normalizeSecondLevelUnitId(source.getUnitId());
if (!ObjectUtil.equals(oldUnitId, newUnitId)) {
if (StrUtil.isNotBlank(newUnitId) && !ObjectUtil.equals(oldUnitId, newUnitId)) {
changeTypes.add(MemberChangeType.UNIT_CHANGE.name());
NutMap change = NutMap.NEW();
change.put("fieldName", "单位");
change.put("field", "unitId");
change.put("sourceValue", unitIdNameMap.get(oldUnitId));
change.put("newValue", unitIdNameMap.get(newUnitId));
change.put("sourceValue", getUnitChangeDisplayValue(oldUnitId));
change.put("newValue", getUnitChangeDisplayValue(newUnitId));
changeList.add(change);
// 单位异动发送订阅消息,清空原单位的所有角色
UserChangePublisher.broadcast(new UserChangeMsg(List.of(user.getId()),
UserChangeMsg.UNIT_CHANGE_OPERATION, oldUnitId, newUnitId));
// 旧单位为空时没有明确的来源单位角色范围,避免清理到全局角色。
if (StrUtil.isNotBlank(oldUnitId)) {
// 单位异动发送订阅消息,清空原单位的所有角色
UserChangePublisher.broadcast(new UserChangeMsg(List.of(user.getId()),
UserChangeMsg.UNIT_CHANGE_OPERATION, oldUnitId, newUnitId));
}
}
// 如果没有任何变更,返回null
@@ -458,8 +532,8 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
// 生成变更信息描述
String changeInfos = changeList.stream()
.map(v -> v.getString("fieldName") + ":" +
HtmlUtil.cleanHtmlTag(v.getString("sourceValue")) + "→" +
HtmlUtil.cleanHtmlTag(v.getString("newValue")))
cleanChangeValue(v.get("sourceValue")) + "→" +
cleanChangeValue(v.get("newValue")))
.collect(Collectors.joining(";"));
// 设置历史记录信息
@@ -469,4 +543,31 @@ public class SysDataUserAllUpdateServiceImpl implements SysDataUserUpdateService
return history;
}
/**
* 单位变更历史展示值,优先显示单位名称,单位表缺失时显示单位编码,空单位显示空字符串。
*
* @param unitId 单位编码
* @return 历史记录展示值
*/
private String getUnitChangeDisplayValue(String unitId) {
if (StrUtil.isBlank(unitId)) {
return "";
}
String unitName = unitIdNameMap.get(unitId);
return StrUtil.blankToDefault(unitName, unitId);
}
/**
* 生成变更描述前统一处理空值和HTML标签,避免来源数据为空时生成历史记录失败。
*
* @param value 变更前后值
* @return 清洗后的变更展示值
*/
private String cleanChangeValue(Object value) {
if (value == null) {
return "";
}
return HtmlUtil.cleanHtmlTag(value.toString());
}
}
@@ -210,4 +210,7 @@ public class View_user {
@Column
private Boolean aidFundMember;
@Column
private String aidFundMemberType;
}
@@ -5,7 +5,7 @@ import cn.hutool.core.date.DateUtil;
import com.budwk.app.base.result.Result;
import com.budwk.app.sys.models.Sys_user;
import com.budwk.app.zhgh.dayofficework.fund.member.service.FundMemberService;
import com.budwk.app.zhgh.dayofficework.fund.subsidy.models.FundSubsidy;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.apply.model.MutualApply;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.nutz.dao.Cnd;
@@ -14,10 +14,10 @@ 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.util.NutMap;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
@@ -61,11 +61,11 @@ public class FundDashBoardController {
dao.execute(sql);
int newCount = sql.getInt();
// 医疗补助数
int subsidyCount = dao.count(FundSubsidy.class, Cnd.NEW());
// 医疗补助数:按新的互助金申请主表统计申请数量。
int subsidyCount = dao.count(MutualApply.class, Cnd.NEW());
// 总补助金额
int totalSubsidyCost = dao.func(FundSubsidy.class, "sum", "subsidyCost");
// 医疗补助总金额:按互助金申请主表实际补助金额汇总,保持和审核最终金额一致。
BigDecimal totalSubsidyCost = sumMutualApplyActualSubsidyAmount();
HashMap<String, Object> map = new HashMap<>();
map.put("count", count);
@@ -76,6 +76,30 @@ public class FundDashBoardController {
return Result.success(map);
}
/**
* 汇总互助金申请主表中的实际补助金额,避免按动态分段明细重复或偏差统计。
*/
private BigDecimal sumMutualApplyActualSubsidyAmount() {
Sql sql = Sqls.create("SELECT IFNULL(SUM(actual_subsidy_amount), 0) AS totalSubsidyCost FROM tb_mutual_apply");
sql.setCallback(Sqls.callback.maps());
dao.execute(sql);
List<HashMap> list = sql.getList(HashMap.class);
if (list == null || list.isEmpty()) {
return BigDecimal.ZERO;
}
Object value = list.get(0).get("totalSubsidyCost");
if (value == null) {
return BigDecimal.ZERO;
}
if (value instanceof BigDecimal) {
return (BigDecimal) value;
}
if (value instanceof Number) {
return BigDecimal.valueOf(((Number) value).doubleValue());
}
return new BigDecimal(value.toString());
}
@At
@SaCheckPermission("fund.dashBoard")
@ApiOperation("各分工会基金会员数")
@@ -1,6 +1,7 @@
package com.budwk.app.zhgh.dayofficework.fund.member.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaMode;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.page.Pagination;
@@ -39,8 +40,14 @@ public class FundMemberBranchUnionApprovalController {
}
@At("/h5")
@Ok("beetl:/platform/zhghh5/dayofficework/fund/member/branchUnionApproval/index.html")
@SaCheckPermission("h5.fundMember.branchUnionApproval")
public void h5() {
}
@At
@SaCheckPermission("fundMember.branchUnionApproval")
@SaCheckPermission(value = {"fundMember.branchUnionApproval", "h5.fundMember.branchUnionApproval"}, mode = SaMode.OR)
public Result pageData(PageForm pageForm, String taskId, String searchKeyword, Integer year, boolean approval) {
Sql sql = Sqls.create("""
SELECT
@@ -71,7 +78,7 @@ public class FundMemberBranchUnionApprovalController {
$condition
""");
Cnd cnd = Cnd.NEW();
cnd.and("t.taskName", "in", List.of("fgh", "ltx"));
cnd.and("t.taskName", "=", "fgh");
if (!AuthUtil.hasRole(RoleConstant.SYSADMIN.name())) {
cnd.and("ta.actorId", "in", List.of(SecurityUtil.getUserId()));
}
@@ -4,6 +4,7 @@ import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.utils.CommonDownloadUtil;
import com.budwk.app.base.utils.SysOfficeTemplateUtil;
@@ -31,9 +32,10 @@ import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@IocBean
@At("/platform/fundMember/common")
@@ -72,9 +74,12 @@ public class FundMemberCommonController {
FundMemberApply fundMember = dao.fetch(FundMemberApply.class, id);
Map<String, Object> docData = BeanUtil.beanToMap(fundMember);
docData.put("birthday", DateUtil.format(fundMember.getBirthday(), "yyyy年MM月dd日"));
docData.put("joinWorkTime", DateUtil.format(fundMember.getJoinWorkTime(), "yyyy年MM月dd日"));
docData.put("retireTime", DateUtil.format(fundMember.getRetireTime(), "yyyy年MM月dd日"));
docData.put("birthday", formatDate(fundMember.getBirthday(), "yyyy年MM月"));
docData.put("joinWorkTime", formatDate(fundMember.getJoinWorkTime(), "yyyy年MM月dd日"));
docData.put("retireTime", formatDate(fundMember.getRetireTime(), "yyyy年MM月dd日"));
docData.put("submitTime", formatDate(fundMember.getSubmitTime(), "yyyy年MM月dd日"));
docData.put("unionName", StrUtil.nullToEmpty(fundMember.getUnionName()));
docData.put("familyMembers", buildFamilyMembers(fundMember.getFamilies()));
if (StrUtil.isNotBlank(fundMember.getAvatar())) {
docData.put("avatar", sysOfficeTemplateUtil.createPictureRenderData(fundMember.getAvatar()));
}
@@ -88,19 +93,87 @@ public class FundMemberCommonController {
doneTaskVos.add(taskVO);
}
// 按任务节点分组
Map<String, List<ProcessTaskVO>> taskGroups = doneTaskVos.stream().collect(Collectors.groupingBy(ProcessTaskVO::getDisplayName));
docData.putAll(taskGroups);
// 按模板约定输出分工会、校工会审核信息,fgh、xgh 可在模板中直接使用 date 和 opinion 子字段。
putApprovalData(docData, doneTaskVos, List.of("fgh", "ltx"), "fgh");
putApprovalData(docData, doneTaskVos, List.of("xgh"), "xgh");
Configure configure = Configure.builder().build();
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
XWPFTemplate.compile(sysOfficeTemplateUtil.getTemplate("fundMember"), configure).render(docData).writeAndClose(byteArrayOutputStream);
XWPFTemplate.compile(sysOfficeTemplateUtil.getTemplate("fundMemberApply"), configure).render(docData).writeAndClose(byteArrayOutputStream);
CommonDownloadUtil.download(fundMember.getUserName() + "的医疗互助“爱心”基金入会申请表.docx", byteArrayOutputStream.toByteArray(), response);
} catch (Exception e) {
log.error("导出word异常", e);
}
}
/**
* 格式化导出日期,空日期以空字符串输出,避免 Word 模板显示 null。
*
* @param date 日期
* @param pattern Word 模板展示格式
* @return 格式化后的日期文本
*/
private String formatDate(Date date, String pattern) {
return date == null ? "" : DateUtil.format(date, pattern);
}
/**
* 将家庭成员列表转换为 Word 模板单元格可直接展示的多行文本。
*
* @param families 家庭成员列表
* @return 序号、姓名、年龄、与本人关系组成的多行文本
*/
private String buildFamilyMembers(List<JSONObject> families) {
if (families == null || families.isEmpty()) {
return "";
}
StringBuilder content = new StringBuilder();
for (int index = 0; index < families.size(); index++) {
JSONObject family = families.get(index);
if (family == null) {
continue;
}
if (content.length() > 0) {
content.append(System.lineSeparator());
}
String sequence = StrUtil.blankToDefault(family.getStr("sequence"), String.valueOf(index + 1));
content.append(sequence).append("、")
.append(StrUtil.nullToEmpty(family.getStr("name"))).append(",")
.append(StrUtil.nullToEmpty(family.getStr("age"))).append("岁,")
.append(StrUtil.nullToEmpty(family.getStr("relation")));
}
return content.toString();
}
/**
* 从指定流程节点中取最后一次已办记录,组装 Word 模板所需的审核日期与意见。
*
* @param docData 模板数据
* @param doneTasks 已办流程任务
* @param taskNames 对应的流程任务名称
* @param templateKey 模板对象名称,例如 fgh、xgh
*/
private void putApprovalData(Map<String, Object> docData, List<ProcessTaskVO> doneTasks, List<String> taskNames, String templateKey) {
Map<String, Object> approval = new HashMap<>();
approval.put("date", "");
approval.put("opinion", "");
ProcessTaskVO latestTask = null;
for (ProcessTaskVO task : doneTasks) {
if (!taskNames.contains(task.getTaskName())) {
continue;
}
if (latestTask == null || (task.getCreatedAt() != null
&& (latestTask.getCreatedAt() == null || task.getCreatedAt() > latestTask.getCreatedAt()))) {
latestTask = task;
}
}
if (latestTask != null) {
approval.put("date", formatDate(latestTask.getFinishTime(), "yyyy年MM月dd日"));
approval.put("opinion", StrUtil.nullToEmpty(latestTask.getTaskFormData().getStr("tf_opinion")));
}
docData.put(templateKey, approval);
}
}
@@ -35,10 +35,11 @@ public class FundMemberHisController {
@At
@SaCheckPermission("fundMember.his")
public Result pageData(PageForm pageForm, Integer year, String unionId) {
// 同一会员可能存在多次入会记录,加入时间只取最新一条,避免子查询返回多行。
Sql sql = Sqls.create("""
SELECT
his.*,
(SELECT changeTime FROM fund_member_change_record WHERE loginName = his.loginName AND isJoin = 1 ORDER BY changeTime DESC) joinTime
(SELECT changeTime FROM fund_member_change_record WHERE loginName = his.loginName AND isJoin = 1 ORDER BY changeTime DESC LIMIT 1) joinTime
FROM
fund_member_his his
$condition
@@ -46,8 +47,8 @@ public class FundMemberHisController {
Cnd cnd = Cnd.NEW();
if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
SqlExpressionGroup seg = new SqlExpressionGroup();
seg.orLike("username", pageForm.getSearchKeyword());
seg.orLike("loginname", pageForm.getSearchKeyword());
seg.orLike("userName", pageForm.getSearchKeyword());
seg.orLike("loginName", pageForm.getSearchKeyword());
cnd.and(seg);
}
cnd.andEX("year", "=", year);
@@ -1,6 +1,7 @@
package com.budwk.app.zhgh.dayofficework.fund.member.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaMode;
import com.budwk.app.base.annotation.SLog;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.result.Result;
@@ -44,8 +45,14 @@ public class FundMemberMineController {
}
@At("/h5")
@Ok("beetl:/platform/zhghh5/dayofficework/fund/member/mine/index.html")
@SaCheckPermission("h5.fundMember.mine")
public void h5() {
}
@At
@SaCheckPermission("fundMember.mine")
@SaCheckPermission(value = {"fundMember.mine", "h5.fundMember.mine"}, mode = SaMode.OR)
public Result pageData(Integer pageNumber, Integer pageSize, String searchKeyword, Integer year) {
Sql sql = Sqls.create("""
SELECT
@@ -76,13 +83,15 @@ public class FundMemberMineController {
Cnd cnd = Cnd.NEW();
cnd.andEX("YEAR(info.submitTime)", "=", year);
cnd.and("info.createdBy", "=", SecurityUtil.getUserId());
// 同一流程任务存在多个参与人时,按申请主表主键合并,避免我的申请列表出现重复记录。
cnd.groupBy("info.id");
sql.setCondition(cnd);
Pagination<NutMap> pagination = fundMemberService.listPageMap(pageNumber, pageSize, sql);
return Result.success(pagination);
}
@At
@SaCheckPermission("fundMember.mine")
@SaCheckPermission(value = {"fundMember.mine", "h5.fundMember.mine"}, mode = SaMode.OR)
@Aop(TransAop.READ_COMMITTED)
@ApiOperation("删除")
@SLog(tag = "基金会员-我的申请", msg = "删除")
@@ -0,0 +1,199 @@
package com.budwk.app.zhgh.dayofficework.fund.member.controller;
import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.ObjectUtil;
import com.budwk.app.base.annotation.SLog;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.utils.CommonDownloadUtil;
import com.budwk.app.zhgh.dayofficework.fund.member.mode.FundMemberPayImportTemp;
import com.budwk.app.zhgh.dayofficework.fund.member.models.FundMemberPay;
import com.budwk.app.zhgh.dayofficework.fund.member.service.FundMemberPayService;
import com.budwk.app.zhgh.dayofficework.fund.member.vo.FundMemberPayPageForm;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.config.model.MutualMemberConfig;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.config.service.MedicalMutualAidConfigService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.poi.ss.usermodel.Workbook;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.util.NutMap;
import org.nutz.mvc.annotation.AdaptBy;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.upload.TempFile;
import org.nutz.mvc.upload.UploadAdaptor;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* 基金会员缴费管理。
*/
@IocBean
@At("/platform/fundMember/payRecord")
@Ok("json:full")
@Api(tags = "基金会员缴费管理")
public class FundMemberPayController {
@Inject
private FundMemberPayService fundMemberPayService;
@Inject
private MedicalMutualAidConfigService medicalMutualAidConfigService;
@At("")
@Ok("beetl:/platform/zhgh/dayofficework/fund/member/payRecord/index.html")
@SaCheckPermission("fundMember.payRecord")
public void index() {
}
@At
@ApiOperation("缴费记录分页查询")
@SaCheckPermission("fundMember.payRecord")
public Result pageData(FundMemberPayPageForm pageForm) {
Sql sql = fundMemberPayService.buildPageSql(pageForm);
Pagination pagination = fundMemberPayService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
return Result.success(pagination);
}
/**
* 查询生成指定年度缴费名单时实际采用的月缴费金额,供前端确认提示展示。
*
* @param year 缴费年度
* @return 实际匹配到的会员缴费配置年度和每月缴费金额
*/
@At
@ApiOperation("查询年度缴费配置")
@SaCheckPermission("fundMember.payRecord")
public Result memberConfig(Integer year) {
MutualMemberConfig memberConfig = medicalMutualAidConfigService.getMemberConfigForPay(year);
if (memberConfig == null || memberConfig.getMonthAmount() == null
|| memberConfig.getMonthAmount().signum() <= 0) {
return Result.error("未维护有效的会员缴费配置,请先在医疗互助配置中设置每月缴费金额");
}
return Result.success(NutMap.NEW()
.addv("year", memberConfig.getYear())
.addv("monthAmount", memberConfig.getMonthAmount()));
}
@At
@ApiOperation("生成年度缴费名单")
@SLog(tag = "基金会员缴费管理", msg = "生成年度缴费名单")
@SaCheckPermission("fundMember.payRecord")
public Result createPayList(Integer year) {
int count = fundMemberPayService.createPayList(year);
return Result.success("成功生成" + count + "条缴费记录");
}
@At
@ApiOperation("设置缴费状态")
@SLog(tag = "基金会员缴费管理", msg = "设置缴费状态")
@SaCheckPermission("fundMember.payRecord")
public Result updatePayed(String id, Boolean isPayed) {
if (ObjectUtil.isEmpty(id) || isPayed == null) {
return Result.error("参数错误");
}
fundMemberPayService.updatePayed(id, isPayed);
return Result.success();
}
@At
@ApiOperation("清空当年缴费名单")
@SLog(tag = "基金会员缴费管理", msg = "清空年度缴费名单")
@SaCheckPermission("fundMember.payRecord")
public Result clearPayList(Integer year) {
fundMemberPayService.clearCurrentYearPayList(year);
return Result.success();
}
@At
@Ok("void")
@ApiOperation("下载缴费导入模板")
@SaCheckPermission("fundMember.payRecord")
public void downloadImportTemp(HttpServletResponse response) {
List<ExcelExportEntity> exportEntities = new ArrayList<>();
exportEntities.add(new ExcelExportEntity("年度", "year", 12));
exportEntities.add(new ExcelExportEntity("工号", "loginName", 20));
exportEntities.add(new ExcelExportEntity("姓名", "userName", 20));
ExportParams exportParams = new ExportParams();
exportParams.setType(ExcelType.XSSF);
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, exportEntities, new ArrayList<>());
CommonDownloadUtil.download("基金会员缴费导入模板.xlsx", workbook, response);
}
@At
@Ok("void")
@ApiOperation("导出缴费记录")
@SaCheckPermission("fundMember.payRecord")
public void exportExcel(FundMemberPayPageForm pageForm, HttpServletResponse response) {
Sql sql = fundMemberPayService.buildPageSql(pageForm);
List<NutMap> records = fundMemberPayService.listMap(sql);
records.forEach(record -> record.put("payState", record.getBoolean("isPayed") ? "已缴费" : "未缴费"));
List<ExcelExportEntity> exportEntities = new ArrayList<>();
exportEntities.add(new ExcelExportEntity("年度", "year", 12));
exportEntities.add(new ExcelExportEntity("工号", "loginName", 20));
exportEntities.add(new ExcelExportEntity("姓名", "userName", 20));
exportEntities.add(new ExcelExportEntity("性别", "sex", 10));
exportEntities.add(new ExcelExportEntity("会员类别", "aidFundMemberType", 20));
exportEntities.add(new ExcelExportEntity("所属单位", "unitName", 30));
exportEntities.add(new ExcelExportEntity("所属工会", "unionName", 30));
exportEntities.add(new ExcelExportEntity("缴费金额", "money", 15));
exportEntities.add(new ExcelExportEntity("缴费状态", "payState", 15));
ExportParams exportParams = new ExportParams();
exportParams.setType(ExcelType.XSSF);
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, exportEntities, records);
CommonDownloadUtil.download(pageForm.getYear() + "年基金会员缴费记录.xlsx", workbook, response);
}
@At
@ApiOperation("导入缴费记录")
@SLog(tag = "基金会员缴费管理", msg = "导入缴费记录")
@SaCheckPermission("fundMember.payRecord")
@AdaptBy(type = UploadAdaptor.class, args = {"ioc:fileUpload"})
public Result doImport(TempFile file, Integer year) {
if (file == null || year == null) {
return Result.error("文件和缴费年度不能为空");
}
List<FundMemberPayImportTemp> importRows = ExcelImportUtil.importExcel(
file.getFile(), FundMemberPayImportTemp.class, new ImportParams());
if (importRows.isEmpty()) {
return Result.error("导入文件没有缴费记录");
}
for (int i = 0; i < importRows.size(); i++) {
FundMemberPayImportTemp importRow = importRows.get(i);
int rowNumber = i + 2;
// 页面选择的年度与模板年度必须一致,防止将缴费状态写入错误年度。
if (importRow.getYear() == null) {
return Result.error("第" + rowNumber + "行年度不能为空");
}
if (!year.equals(importRow.getYear())) {
return Result.error("第" + rowNumber + "行年度与当前选择的缴费年度不一致");
}
if (cn.hutool.core.util.StrUtil.isBlank(importRow.getLoginName())) {
return Result.error("第" + rowNumber + "行工号不能为空");
}
}
List<String> loginNames = importRows.stream()
.map(FundMemberPayImportTemp::getLoginName)
.collect(Collectors.toList());
List<String> errorLoginNames = fundMemberPayService.markPaidByLoginNames(year, loginNames);
// 无论是否存在异常都返回导入统计,前端据 errorCount 决定提示类型。
NutMap result = NutMap.NEW();
result.setv("totalCount", importRows.size());
result.setv("successCount", importRows.size() - errorLoginNames.size());
result.setv("errorCount", errorLoginNames.size());
result.setv("errorList", errorLoginNames.stream()
.map(loginName -> NutMap.NEW().addv("工号", loginName).addv("错误原因", "未找到该年度缴费记录"))
.collect(Collectors.toList()));
return Result.success(result);
}
}
@@ -39,6 +39,7 @@ import org.nutz.ioc.loader.annotation.IocBean;
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 javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
@@ -78,9 +79,16 @@ public class FundMemberQueryController {
u.userState,
u.preparedBy,
u.personType,
COALESCE(NULLIF(u.aidFundMemberType, ''),
(SELECT applyInfo.aidFundMemberType
FROM fund_member_apply applyInfo
WHERE applyInfo.loginName = u.loginName
AND applyInfo.isJoin = 1
ORDER BY applyInfo.submitTime DESC
LIMIT 1)) AS aidFundMemberType,
u.unitName,
u.unionName,
(SELECT changeTime FROM fund_member_change_record WHERE loginName = u.loginName AND isJoin = 1 ORDER BY changeTime DESC limit 1) joinTime
(SELECT changeTime FROM fund_member_change_record WHERE loginName = u.loginName AND isJoin = 1 ORDER BY changeTime DESC LIMIT 1) AS joinTime
FROM
vw_user u
$condition
@@ -93,13 +101,20 @@ public class FundMemberQueryController {
return Result.success(pagination);
}
@At
@SaCheckPermission("fundMember.query")
@ApiOperation("修改大病基金会员类别")
public Result updateAidFundMemberType(@Param("loginName") String loginName, @Param("aidFundMemberType") String aidFundMemberType) {
fundMemberService.updateAidFundMemberType(loginName, aidFundMemberType);
return Result.success();
}
@At
@SaCheckPermission("fundMember.query")
@Ok("void")
@ApiOperation("导出登记汇总Excel")
public void exportExcel(Integer year, String unionId, HttpServletResponse response) {
public void exportExcel(String unionId, HttpServletResponse response) {
Cnd cnd = Cnd.NEW();
cnd.and("YEAR(submitTime)", "=", year);
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) {
cnd.and("unionId", "=", SecurityUtil.getUnionId());
} else {
@@ -0,0 +1,104 @@
package com.budwk.app.zhgh.dayofficework.fund.member.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaMode;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.constant.RoleConstant;
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.flow.enums.ProcessTaskStateEnum;
import com.budwk.app.web.commons.auth.utils.AuthUtil;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.dayofficework.fund.member.service.FundMemberService;
import io.swagger.annotations.Api;
import org.nutz.dao.Cnd;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.util.NutMap;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import java.util.List;
/**
* 基金会员离退休审核。
*/
@IocBean
@At("/platform/fundMember/retiredApproval")
@Ok("json:full")
@Api("基金会员-离退休审核")
public class FundMemberRetiredApprovalController {
@Inject
private FundMemberService fundMemberService;
@At("")
@Ok("beetl:/platform/zhgh/dayofficework/fund/member/retiredApproval/index.html")
@SaCheckPermission("fundMember.retiredApproval")
public void index() {
}
@At("/h5")
@Ok("beetl:/platform/zhghh5/dayofficework/fund/member/retiredApproval/index.html")
@SaCheckPermission("h5.fundMember.retiredApproval")
public void h5() {
}
@At
@SaCheckPermission(value = {"fundMember.retiredApproval", "h5.fundMember.retiredApproval"}, mode = SaMode.OR)
public Result pageData(PageForm pageForm, boolean approval) {
Sql sql = Sqls.create("""
SELECT
info.*,
ins.id AS instanceId,
ins.businessNo,
ins.state instanceState,
ins.variable instanceVariable,
ins.processDefineId instanceProcessDefineId,
t.id taskId,
t.taskName AS taskKey,
t.displayName taskName,
t.taskType,
t.performType taskPerformType,
t.taskState,
t.finishTime,
t.taskParentId,
t.variable taskVariable,
IFNULL(GROUP_CONCAT(DISTINCT nt.displayName),'结束') curTaskName,
IF(rt.id IS NOT NULL, 1, 0) AS canRevoke
FROM
wf_process_task t
LEFT JOIN wf_process_instance ins ON ins.id = t.processInstanceId
LEFT JOIN fund_member_apply info ON info.id = ins.businessNo
LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = t.id
LEFT JOIN wf_process_task nt ON nt.processInstanceId = ins.id AND nt.taskState = 10
LEFT JOIN wf_process_task rt ON rt.processInstanceId = ins.id AND rt.taskParentId = t.id AND rt.taskState = 10
$condition
""");
Cnd cnd = Cnd.NEW();
cnd.and("t.taskName", "=", "ltx");
if (!AuthUtil.hasRole(RoleConstant.SYSADMIN.name())) {
cnd.and("ta.actorId", "in", List.of(SecurityUtil.getUserId()));
}
if (approval) {
cnd.and("t.taskState", "in", List.of(ProcessTaskStateEnum.FINISHED.getCode(), ProcessTaskStateEnum.INTERRUPT.getCode()));
} else {
cnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode());
}
if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
SqlExpressionGroup searchGroup = new SqlExpressionGroup();
searchGroup.orLike("info.loginName", pageForm.getSearchKeyword());
searchGroup.orLike("info.userName", pageForm.getSearchKeyword());
cnd.and(searchGroup);
}
cnd.groupBy("t.id");
cnd.desc("t.createdAt");
sql.setCondition(cnd);
Pagination<NutMap> pagination = fundMemberService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
return Result.success(pagination);
}
}
@@ -1,6 +1,7 @@
package com.budwk.app.zhgh.dayofficework.fund.member.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaMode;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.page.Pagination;
@@ -39,8 +40,14 @@ public class FundMemberSchoolUnionApprovalController {
}
@At("/h5")
@Ok("beetl:/platform/zhghh5/dayofficework/fund/member/schoolUnionApproval/index.html")
@SaCheckPermission("h5.fundMember.schoolUnionApproval")
public void h5() {
}
@At
@SaCheckPermission("fundMember.schoolUnionApproval")
@SaCheckPermission(value = {"fundMember.schoolUnionApproval", "h5.fundMember.schoolUnionApproval"}, mode = SaMode.OR)
public Result pageData(PageForm pageForm, String unionId, Integer year, boolean approval) {
Sql sql = Sqls.create("""
SELECT
@@ -1,6 +1,7 @@
package com.budwk.app.zhgh.dayofficework.fund.member.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaMode;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.annotation.SLog;
@@ -50,8 +51,14 @@ public class FundMemberWriteController {
}
@At("/h5")
@Ok("beetl:/platform/zhghh5/dayofficework/fund/member/write/index.html")
@SaCheckPermission("h5.fundMember.write")
public void h5() {
}
@At
@SaCheckPermission("fundMember.write")
@SaCheckPermission(value = {"fundMember.write", "h5.fundMember.write"}, mode = SaMode.OR)
@ApiOperation("保存申请")
@SLog(tag = "建言献策-填写申请", msg = "保存申请")
public Result save(@Param("data") FundMemberApply fundMember) {
@@ -64,11 +71,13 @@ public class FundMemberWriteController {
@At
@SaCheckPermission("fundMember.write")
@SaCheckPermission(value = {"fundMember.write", "h5.fundMember.write"}, mode = SaMode.OR)
@ApiOperation("提交申请")
@Aop(TransAop.READ_COMMITTED)
@SLog(tag = "基金会员-填写申请", msg = "提交申请")
public Result submit(@Param("data") FundMemberApply fundMember) {
fundMemberService.validateCurrentUserNotFundMember();
fundMemberService.validateApplyForm(fundMember);
if (StrUtil.isBlank(fundMember.getId())) {
fundMemberService.validateApply();
}
@@ -80,7 +89,9 @@ public class FundMemberWriteController {
args.set(FlowConst.SUBMIT_TYPE, ProcessSubmitTypeEnum.APPLY.getCode());
args.set(FlowConst.FORM_DATA, fundMember);
args.set(FlowConst.TASK_FORM_DATA_PREFIX + "userState", fundMember.getUserState());
ProcessInstance instance = flowEngine.startProcessInstanceByKey("FUND_MEMBER", fundMember.getId(), SecurityUtil.getUserId(), args);
// 会员类别作为流程条件变量,供 JJHY 的离退休、分工会审核分支使用。
args.set(FlowConst.TASK_FORM_DATA_PREFIX + "aidFundMemberType", fundMember.getAidFundMemberType());
ProcessInstance instance = flowEngine.startProcessInstanceByKey("JJHY", fundMember.getId(), SecurityUtil.getUserId(), args);
// 自动完成第一个申请任务
List<ProcessTask> doingTaskList = flowEngine.processTaskService().getDoingTaskList(instance.getId(), null);
@@ -91,11 +102,13 @@ public class FundMemberWriteController {
}
@At
@SaCheckPermission("fundMember.write")
@SaCheckPermission(value = {"fundMember.write", "h5.fundMember.write"}, mode = SaMode.OR)
@ApiOperation("重新提交申请")
@Aop(TransAop.READ_COMMITTED)
@SLog(tag = "基金会员-填写申请", msg = "重新提交申请")
public Result submitAgain(@Param("data") FundMemberApply fundMember, @Param("taskId") Long taskId) {
fundMemberService.validateCurrentUserNotFundMember();
fundMemberService.validateApplyForm(fundMember);
if (StrUtil.isBlank(fundMember.getId())) {
fundMemberService.validateApply();
}
@@ -104,12 +117,16 @@ public class FundMemberWriteController {
Dict dict = Dict.create();
dict.set(FlowConst.PROCESS_TASK_ID_KEY, taskId);
dict.set(FlowConst.SUBMIT_TYPE, ProcessSubmitTypeEnum.RE_APPLY.getCode());
// 退回后重新提交时同步表单与条件变量,避免分支仍使用首次提交的会员类别。
dict.set(FlowConst.FORM_DATA, fundMember);
dict.set(FlowConst.TASK_FORM_DATA_PREFIX + "userState", fundMember.getUserState());
dict.set(FlowConst.TASK_FORM_DATA_PREFIX + "aidFundMemberType", fundMember.getAidFundMemberType());
flowCommonService.executeTask(dict);
return Result.success();
}
@At
@SaCheckPermission("fundMember.write")
@SaCheckPermission(value = {"fundMember.write", "fundMember.mine", "fundMember.branchUnionApproval", "fundMember.retiredApproval", "fundMember.schoolUnionApproval", "h5.fundMember.write", "h5.fundMember.mine", "h5.fundMember.branchUnionApproval", "h5.fundMember.retiredApproval", "h5.fundMember.schoolUnionApproval"}, mode = SaMode.OR)
@ApiOperation("获取申请信息")
public Result info(@Param("id") String id) {
FundMemberApply box = dao.fetch(FundMemberApply.class, id);
@@ -118,10 +135,20 @@ public class FundMemberWriteController {
@At
@SaCheckPermission("fundMember.write")
@SaCheckPermission(value = {"fundMember.write", "h5.fundMember.write"}, mode = SaMode.OR)
@ApiOperation("获取当前用户基金会员状态")
public Result memberStatus() {
return Result.success(fundMemberService.isCurrentUserFundMember());
}
@At
@SaCheckPermission(value = {"fundMember.write", "h5.fundMember.write"}, mode = SaMode.OR)
@ApiOperation("校验是否有申请在流程中")
public Result validateApply(){
fundMemberService.validateApply();
return Result.success();
boolean isFundMember = fundMemberService.isCurrentUserFundMember();
if (!isFundMember) {
fundMemberService.validateApply();
}
return Result.success(isFundMember);
}
}
@@ -25,11 +25,15 @@ public class FundMemberPassInterceptor implements FlowInterceptor {
public void intercept(Execution execution) {
Dao dao = ServiceContext.find(Dao.class);
FundMemberApply fundMemberApply = Json.fromJson(FundMemberApply.class, execution.getArgs().getStr("f_data"));
// 修改基金会员状态
dao.update(Sys_user.class, Chain.make("aidFundMember", 1), Cnd.where(Sys_user::getLoginname, "=", fundMemberApply.getLoginName()));
// 修改基金会员状态,并同步申请时选择的大病基金会员类别。
dao.update(Sys_user.class,
Chain.make("aidFundMember", 1).add("aidFundMemberType", fundMemberApply.getAidFundMemberType()),
Cnd.where(Sys_user::getLoginname, "=", fundMemberApply.getLoginName()));
// 添加到变更记录
FundMemberChangeRecord record = BeanUtil.copyProperties(fundMemberApply, FundMemberChangeRecord.class, "id");
record.setId(null);
// 后置拦截器只在入会申请审核通过后执行,明确标记为加入记录,供台账展示加入时间。
record.setIsJoin(true);
record.setChangeOrigin("流程申请");
record.setChangeTime(new Date());
dao.insert(record);
@@ -0,0 +1,20 @@
package com.budwk.app.zhgh.dayofficework.fund.member.mode;
import cn.afterturn.easypoi.excel.annotation.Excel;
import lombok.Data;
/**
* 基金会员缴费导入模板。
*/
@Data
public class FundMemberPayImportTemp {
@Excel(name = "年度")
private Integer year;
@Excel(name = "工号")
private String loginName;
@Excel(name = "姓名")
private String userName;
}
@@ -1,6 +1,7 @@
package com.budwk.app.zhgh.dayofficework.fund.member.models;
import cn.afterturn.easypoi.excel.annotation.Excel;
import cn.hutool.json.JSONObject;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -8,6 +9,7 @@ import org.nutz.dao.entity.annotation.*;
import org.nutz.dao.interceptor.annotation.PrevInsert;
import java.util.Date;
import java.util.List;
@Data
@Table("fund_member_apply")
@@ -48,7 +50,7 @@ public class FundMemberApply extends BaseModel {
private String idCard;
@Column
@Comment("参加工作时间")
@Comment("入职时间")
@ColDefine(type = ColType.DATE)
private Date joinWorkTime;
@@ -57,11 +59,31 @@ public class FundMemberApply extends BaseModel {
@ColDefine(type = ColType.DATE)
private Date retireTime;
@Column
@Comment("大病基金会员类别")
@ColDefine(type = ColType.VARCHAR, width = 50)
private String aidFundMemberType;
@Column
@Comment("家庭住址")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String address;
@Column
@Comment("籍贯")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String nativePlace;
@Column
@Comment("学历")
@ColDefine(type = ColType.VARCHAR, width = 50)
private String education;
@Column
@Comment("健康状况")
@ColDefine(type = ColType.VARCHAR, width = 200)
private String healthStatus;
@Column
@Comment("手机号码")
@ColDefine(type = ColType.VARCHAR, width = 20)
@@ -72,6 +94,16 @@ public class FundMemberApply extends BaseModel {
@ColDefine(type = ColType.VARCHAR, width = 20)
private String homePhone;
@Column
@Comment("家庭主要成员")
@ColDefine(type = ColType.MYSQL_JSON)
private List<JSONObject> families;
@Column
@Comment("备注")
@ColDefine(type = ColType.VARCHAR, customType = "text")
private String remark;
@Column
@Comment("照片")
@ColDefine(type = ColType.VARCHAR, width = 100)
@@ -0,0 +1,65 @@
package com.budwk.app.zhgh.dayofficework.fund.member.models;
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.Default;
import org.nutz.dao.entity.annotation.Index;
import org.nutz.dao.entity.annotation.Name;
import org.nutz.dao.entity.annotation.Table;
import org.nutz.dao.entity.annotation.TableIndexes;
import org.nutz.dao.entity.annotation.TableMeta;
import org.nutz.dao.interceptor.annotation.PrevInsert;
import java.math.BigDecimal;
/**
* 基金会员年度缴费记录。
*/
@Data
@Table("fund_member_pay")
@TableMeta("{'mysql-charset':'utf8mb4'}")
@TableIndexes({
@Index(name = "UK_FUND_MEMBER_PAY_USER_YEAR", fields = {"userId", "year"}, unique = true),
@Index(name = "IDX_FUND_MEMBER_PAY_YEAR", fields = {"year"}, unique = false)
})
@EqualsAndHashCode(callSuper = true)
@Comment("基金会员年度缴费记录")
public class FundMemberPay extends BaseModel {
@Name
@Comment("ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
@PrevInsert(uu32 = true)
private String id;
@Column
@Comment("用户ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String userId;
@Column
@Comment("年度")
@ColDefine(type = ColType.INT)
private Integer year;
@Column
@Comment("缴费金额")
@ColDefine(customType = "decimal(10,2)")
private BigDecimal money;
@Column
@Comment("是否已缴费")
@Default("0")
@ColDefine(type = ColType.BOOLEAN)
private Boolean isPayed;
@Column
@Comment("基金会员类别")
@ColDefine(type = ColType.VARCHAR, width = 50)
private String aidFundMemberType;
}
@@ -0,0 +1,54 @@
package com.budwk.app.zhgh.dayofficework.fund.member.service;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.dayofficework.fund.member.models.FundMemberPay;
import com.budwk.app.zhgh.dayofficework.fund.member.vo.FundMemberPayPageForm;
import org.nutz.dao.sql.Sql;
import java.util.List;
/**
* 基金会员缴费管理服务。
*/
public interface FundMemberPayService extends BaseService<FundMemberPay> {
/**
* 生成指定年度的基金会员缴费名单。
*
* @param year 缴费年度
* @return 新增的缴费记录数量
*/
int createPayList(Integer year);
/**
* 构建缴费记录分页及导出共用查询。
*
* @param pageForm 查询条件
* @return 缴费记录查询 SQL
*/
Sql buildPageSql(FundMemberPayPageForm pageForm);
/**
* 修改单条缴费记录的缴费状态。
*
* @param id 缴费记录 ID
* @param isPayed 是否已缴费
*/
void updatePayed(String id, Boolean isPayed);
/**
* 清空本年度全部基金会员缴费记录。
*
* @param year 缴费年度,必须为当前年度
*/
void clearCurrentYearPayList(Integer year);
/**
* 根据导入工号批量标记缴费状态。
*
* @param year 缴费年度
* @param loginNames 导入工号集合
* @return 未找到对应年度缴费记录的工号集合
*/
List<String> markPaidByLoginNames(Integer year, List<String> loginNames);
}
@@ -9,4 +9,32 @@ public interface FundMemberService extends BaseService<FundMemberApply> {
* 验证是否已经申请了
*/
void validateApply();
/**
* 判断当前登录用户是否为基金会员。
*
* @return 当前用户是基金会员时返回 true
*/
boolean isCurrentUserFundMember();
/**
* 校验当前登录用户不是基金会员,避免重复发起入会申请。
*/
void validateCurrentUserNotFundMember();
/**
* 校验基金会员申请业务字段。
*
* @param fundMember 基金会员申请信息
*/
void validateApplyForm(FundMemberApply fundMember);
/**
* 修改大病基金会员类别。
*
* @param loginName 工号
* @param aidFundMemberType 大病基金会员类别
*/
void updateAidFundMemberType(String loginName, String aidFundMemberType);
}
@@ -0,0 +1,285 @@
package com.budwk.app.zhgh.dayofficework.fund.member.service.impl;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.sys.models.Sys_user;
import com.budwk.app.web.commons.auth.utils.AuthUtil;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.dayofficework.fund.member.models.FundMemberChangeRecord;
import com.budwk.app.zhgh.dayofficework.fund.member.models.FundMemberPay;
import com.budwk.app.zhgh.dayofficework.fund.member.service.FundMemberPayService;
import com.budwk.app.zhgh.dayofficework.fund.member.vo.FundMemberPayPageForm;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.apply.model.MutualApply;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.config.model.MutualMemberConfig;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.config.service.MedicalMutualAidConfigService;
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.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* 基金会员缴费管理服务实现。
*/
@IocBean(args = {"refer:dao"})
public class FundMemberPayServiceImpl extends BaseServiceImpl<FundMemberPay> implements FundMemberPayService {
@Inject
private MedicalMutualAidConfigService medicalMutualAidConfigService;
public FundMemberPayServiceImpl(Dao dao) {
super(dao);
}
@Override
public int createPayList(Integer year) {
if (year == null) {
throw new RuntimeException("缴费年度不能为空");
}
if (count(Cnd.where(FundMemberPay::getYear, "=", year)) > 0) {
throw new RuntimeException(year + "年缴费名单已生成,请勿重复生成");
}
MutualMemberConfig memberConfig = medicalMutualAidConfigService.getMemberConfigForPay(year);
if (memberConfig == null || memberConfig.getMonthAmount() == null
|| memberConfig.getMonthAmount().compareTo(BigDecimal.ZERO) <= 0) {
throw new RuntimeException("未维护有效的会员缴费配置,请先在医疗互助配置中设置每月缴费金额");
}
if (memberConfig.getHalfPriceYear() == null || memberConfig.getHalfPriceYear() <= 0) {
throw new RuntimeException("会员缴费配置的缴费减半年限必须大于 0");
}
List<Sys_user> members = dao().query(Sys_user.class,
Cnd.where(Sys_user::getAidFundMember, "=", true));
if (members.isEmpty()) {
return 0;
}
List<String> loginNames = members.stream().map(Sys_user::getLoginname).collect(Collectors.toList());
List<FundMemberChangeRecord> joinRecords = dao().query(FundMemberChangeRecord.class,
Cnd.where(FundMemberChangeRecord::getLoginName, "in", loginNames)
.and(FundMemberChangeRecord::getIsJoin, "=", true)
.asc(FundMemberChangeRecord::getChangeTime));
Map<String, Date> joinTimeMap = new HashMap<>();
for (FundMemberChangeRecord joinRecord : joinRecords) {
// 同一会员可能存在多次加入记录,按时间升序覆盖后保留最近一次实际入会时间。
joinTimeMap.put(joinRecord.getLoginName(), joinRecord.getChangeTime());
}
List<MutualApply> approvedApplies = dao().query(MutualApply.class,
Cnd.where(MutualApply::getLoginName, "in", loginNames)
.and(MutualApply::getStatus, "=", 1)
.desc(MutualApply::getSubmitTime));
Map<String, Date> latestApplyTimeMap = new HashMap<>();
for (MutualApply approvedApply : approvedApplies) {
Date submitTime = approvedApply.getSubmitTime();
if (submitTime == null || toLocalDate(submitTime).getYear() > year) {
continue;
}
latestApplyTimeMap.merge(approvedApply.getLoginName(), submitTime,
(oldTime, newTime) -> newTime.after(oldTime) ? newTime : oldTime);
}
List<FundMemberPay> payList = members.stream().map(member -> {
BigDecimal money = calculatePayMoney(year, joinTimeMap.get(member.getLoginname()),
latestApplyTimeMap.get(member.getLoginname()), memberConfig);
if (money.compareTo(BigDecimal.ZERO) <= 0) {
return null;
}
FundMemberPay pay = new FundMemberPay();
pay.setUserId(member.getId());
pay.setYear(year);
pay.setMoney(money);
pay.setIsPayed(false);
pay.setAidFundMemberType(member.getAidFundMemberType());
return pay;
}).filter(java.util.Objects::nonNull).collect(Collectors.toList());
if (payList.isEmpty()) {
return 0;
}
dao().fastInsert(payList);
return payList.size();
}
/**
* 按缴费年度、实际入会年月和会员缴费配置计算缴费金额。
* 缺少历史入会记录的存量会员按全年收费;入会当年按入会当月(含)至年末的月份数收费。
* 入会后已满配置年限,且中间存在已审核医疗互助申请时,从最近申请年份重新计算减半年限。
*
* @param payYear 缴费年度
* @param joinTime 最近一次实际入会时间
* @param latestApplyTime 最近一次已审核医疗互助申请提交时间
* @param memberConfig 会员缴费配置
* @return 应缴金额;缴费年度早于入会年度时返回 0
*/
private BigDecimal calculatePayMoney(Integer payYear, Date joinTime, Date latestApplyTime,
MutualMemberConfig memberConfig) {
BigDecimal money = calculateBasePayMoney(payYear, joinTime, memberConfig.getMonthAmount());
if (money.compareTo(BigDecimal.ZERO) > 0
&& shouldApplyHalfPrice(payYear, joinTime, latestApplyTime, memberConfig.getHalfPriceYear())) {
return money.divide(BigDecimal.valueOf(2));
}
return money;
}
/**
* 按入会时间计算未优惠前的年度应缴金额。
*/
private BigDecimal calculateBasePayMoney(Integer payYear, Date joinTime, BigDecimal monthlyPayAmount) {
if (joinTime == null) {
return monthlyPayAmount.multiply(BigDecimal.valueOf(12));
}
LocalDate joinDate = toLocalDate(joinTime);
if (payYear < joinDate.getYear()) {
return BigDecimal.ZERO;
}
if (payYear > joinDate.getYear()) {
return monthlyPayAmount.multiply(BigDecimal.valueOf(12));
}
int payMonths = 13 - joinDate.getMonthValue();
return monthlyPayAmount.multiply(BigDecimal.valueOf(payMonths));
}
/**
* 判断是否达到减半年限:从入会年份起算,最近一次已审核医疗互助申请会重置起算年份。
*/
private boolean shouldApplyHalfPrice(Integer payYear, Date joinTime, Date latestApplyTime, Integer halfPriceYear) {
if (joinTime == null || halfPriceYear == null || halfPriceYear <= 0) {
return false;
}
int startYear = toLocalDate(joinTime).getYear();
if (latestApplyTime != null) {
startYear = Math.max(startYear, toLocalDate(latestApplyTime).getYear());
}
return payYear - startYear >= halfPriceYear;
}
/**
* 将历史日期统一转换为本地日期,避免年度判断受时区影响。
*/
private LocalDate toLocalDate(Date date) {
return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
}
@Override
public Sql buildPageSql(FundMemberPayPageForm pageForm) {
Sql sql = Sqls.create("""
SELECT
pay.id,
pay.year,
pay.money,
pay.isPayed,
pay.aidFundMemberType,
u.id AS userId,
u.loginname AS loginName,
u.username AS userName,
u.sex,
u.unitName,
u.unionName,
joinRecord.joinTime
FROM
fund_member_pay pay
LEFT JOIN vw_user u ON u.id = pay.userId
LEFT JOIN (
SELECT loginName, MAX(changeTime) AS joinTime
FROM fund_member_change_record
WHERE isJoin = 1
GROUP BY loginName
) joinRecord ON joinRecord.loginName = u.loginname
$condition
""");
Cnd cnd = Cnd.NEW();
cnd.andEX("pay.year", "=", pageForm.getYear());
cnd.andEX("pay.isPayed", "=", pageForm.getIsPayed());
cnd.andEX("pay.aidFundMemberType", "=", pageForm.getAidFundMemberType());
cnd.andEX("u.unionId", "=", pageForm.getUnionId());
cnd.andEX("u.unitId", "=", pageForm.getUnitId());
if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
SqlExpressionGroup searchGroup = new SqlExpressionGroup();
searchGroup.orLike("u.loginname", pageForm.getSearchKeyword());
searchGroup.orLike("u.username", pageForm.getSearchKeyword());
cnd.and(searchGroup);
}
if (!AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name())) {
cnd.and("u.unionId", "=", SecurityUtil.getUnionId());
}
cnd.desc("u.unitCode");
cnd.asc("u.loginname");
sql.setCondition(cnd);
return sql;
}
@Override
public void updatePayed(String id, Boolean isPayed) {
FundMemberPay pay = fetch(id);
if (pay == null) {
throw new RuntimeException("缴费记录不存在");
}
update(Chain.make("isPayed", isPayed), Cnd.where(FundMemberPay::getId, "=", id));
}
@Override
public void clearCurrentYearPayList(Integer year) {
if (year == null) {
throw new RuntimeException("缴费年度不能为空");
}
if (year != DateUtil.thisYear()) {
throw new RuntimeException("仅允许清空本年度缴费名单");
}
clear(Cnd.where(FundMemberPay::getYear, "=", year));
}
@Override
public List<String> markPaidByLoginNames(Integer year, List<String> loginNames) {
if (year == null) {
throw new RuntimeException("缴费年度不能为空");
}
if (loginNames == null || loginNames.isEmpty()) {
return Collections.emptyList();
}
Set<String> distinctLoginNames = loginNames.stream()
.filter(StrUtil::isNotBlank)
.map(String::trim)
.collect(Collectors.toSet());
if (distinctLoginNames.isEmpty()) {
return Collections.emptyList();
}
List<Sys_user> users = dao().query(Sys_user.class,
Cnd.where(Sys_user::getLoginname, "in", new ArrayList<>(distinctLoginNames)));
Map<String, Sys_user> userMap = users.stream()
.collect(Collectors.toMap(Sys_user::getLoginname, Function.identity()));
List<String> userIds = users.stream().map(Sys_user::getId).collect(Collectors.toList());
List<FundMemberPay> payList = userIds.isEmpty() ? Collections.emptyList() : query(
Cnd.where(FundMemberPay::getYear, "=", year)
.and(FundMemberPay::getUserId, "in", userIds));
Set<String> paidUserIds = payList.stream().map(FundMemberPay::getUserId).collect(Collectors.toSet());
if (!paidUserIds.isEmpty()) {
update(Chain.make("isPayed", true),
Cnd.where(FundMemberPay::getYear, "=", year)
.and(FundMemberPay::getUserId, "in", new ArrayList<>(paidUserIds)));
}
return distinctLoginNames.stream()
.filter(loginName -> !userMap.containsKey(loginName)
|| !paidUserIds.contains(userMap.get(loginName).getId()))
.sorted()
.collect(Collectors.toList());
}
}
@@ -4,9 +4,11 @@ import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.flow.entity.ProcessInstance;
import com.budwk.app.flow.enums.ProcessTaskStateEnum;
import com.budwk.app.sys.models.Sys_user;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.dayofficework.fund.member.models.FundMemberApply;
import com.budwk.app.zhgh.dayofficework.fund.member.service.FundMemberService;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.IocBean;
@@ -21,6 +23,7 @@ public class FundMemberServiceImpl extends BaseServiceImpl<FundMemberApply> impl
@Override
public void validateApply() {
validateCurrentUserNotFundMember();
FundMemberApply apply = dao().fetch(FundMemberApply.class,
Cnd.where(FundMemberApply::getLoginName, "=", SecurityUtil.getUserLoginname())
.and("isJoin", "=", 1)
@@ -36,4 +39,42 @@ public class FundMemberServiceImpl extends BaseServiceImpl<FundMemberApply> impl
throw new RuntimeException("您有申请流程正在进行中,请等待流程结束后再次操作");
}
}
@Override
public boolean isCurrentUserFundMember() {
Sys_user user = dao().fetch(Sys_user.class,
Cnd.where(Sys_user::getLoginname, "=", SecurityUtil.getUserLoginname()));
return user != null && Boolean.TRUE.equals(user.getAidFundMember());
}
@Override
public void validateCurrentUserNotFundMember() {
if (isCurrentUserFundMember()) {
throw new RuntimeException("您已是基金会员,无需重复申请");
}
}
@Override
public void validateApplyForm(FundMemberApply fundMember) {
if (fundMember == null || StrUtil.isBlank(fundMember.getAidFundMemberType())) {
throw new RuntimeException("会员类别不能为空");
}
if ("退休".equals(fundMember.getAidFundMemberType()) && fundMember.getRetireTime() == null) {
throw new RuntimeException("会员类别为退休时,退休时间不能为空");
}
}
@Override
public void updateAidFundMemberType(String loginName, String aidFundMemberType) {
if (StrUtil.isBlank(loginName)) {
throw new RuntimeException("工号不能为空");
}
if (StrUtil.isBlank(aidFundMemberType)) {
throw new RuntimeException("会员类别不能为空");
}
dao().update(Sys_user.class,
Chain.make("aidFundMemberType", aidFundMemberType),
Cnd.where(Sys_user::getLoginname, "=", loginName).and(Sys_user::getAidFundMember, "=", 1));
}
}
@@ -43,6 +43,10 @@ public class FundMemberCommonPageForm extends PageForm {
@ApiModelProperty("教职工类别")
private List<String> personTypes;
// 大病基金会员类别
@ApiModelProperty("大病基金会员类别")
private List<String> aidFundMemberTypes;
public void buildSearch(Cnd cnd, String prefix){
if (cnd == null) {
cnd = Cnd.NEW();
@@ -60,5 +64,6 @@ public class FundMemberCommonPageForm extends PageForm {
cnd.andEX(prefix + "userState", "in", this.getUserStates());
cnd.andEX(prefix + "preparedBy", "in", this.getPreparedBys());
cnd.andEX(prefix + "personType", "in", this.getPersonTypes());
cnd.andEX(prefix + "aidFundMemberType", "in", this.getAidFundMemberTypes());
}
}
@@ -0,0 +1,23 @@
package com.budwk.app.zhgh.dayofficework.fund.member.vo;
import com.budwk.app.base.param.PageForm;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 基金会员缴费记录查询参数。
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class FundMemberPayPageForm extends PageForm {
private Integer year;
private String unionId;
private String unitId;
private String aidFundMemberType;
private Boolean isPayed;
}
@@ -0,0 +1,86 @@
package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.apply.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.result.Result;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.apply.model.MutualApply;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.apply.service.MedicalMutualAidApplyService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
/**
* 互助金申请页面控制器,负责申请表单页面入口和表单保存、提交接口。
*/
@IocBean
@At("/platform/medicalMutualAid/apply")
@Api("互助金申请")
@Ok("json:full")
public class MedicalMutualAidApplyFormController {
@Inject
private MedicalMutualAidApplyService medicalMutualAidApplyService;
/**
* 打开互助金申请页面。
*/
@At("/form")
@Ok("beetl:/platform/zhgh/staffbenefit/medicalMutualAid/apply/form/index.html")
@SaCheckPermission("medicalMutualAid.apply.form")
public void index() {
}
/**
* 查询申请页初始化数据。
*
* @return 当前用户占位信息和疾病下拉数据
*/
@At("/form/initApplyData")
@ApiOperation("查询互助金申请初始化数据")
@SaCheckPermission("medicalMutualAid.apply.form")
public Result initApplyData() {
return Result.success(medicalMutualAidApplyService.initApplyData());
}
/**
* 查询申请详情,用于我的申请查看和编辑复用申请表单页面。
*
* @param id 申请ID
* @return 申请主表详情
*/
@At("/form/detail")
@ApiOperation("查询互助金申请详情")
@SaCheckPermission("medicalMutualAid.apply.form")
public Result detail(@Param("id") Long id) {
return Result.success(medicalMutualAidApplyService.applyDetail(id));
}
/**
* 保存草稿。
*
* @param apply 申请表单
* @return 保存后的申请数据
*/
@At("/form/saveDraft")
@ApiOperation("保存互助金申请草稿")
@SaCheckPermission("medicalMutualAid.apply.form")
public Result saveDraft(MutualApply apply) {
return Result.success(medicalMutualAidApplyService.saveApply(apply, false));
}
/**
* 提交申请。
*
* @param apply 申请表单
* @return 提交后的申请数据
*/
@At("/form/submitApply")
@ApiOperation("提交互助金申请")
@SaCheckPermission("medicalMutualAid.apply.form")
public Result submitApply(MutualApply apply) {
return Result.success(medicalMutualAidApplyService.saveApply(apply, true));
}
}
@@ -0,0 +1,127 @@
package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.apply.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.apply.service.MedicalMutualAidApplyService;
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;
/**
* 互助会审核控制器,负责审核列表、审核详情页面入口以及审核相关接口。
*/
@IocBean
@At("/platform/medicalMutualAid/apply")
@Api("互助会审核")
@Ok("json:full")
public class MedicalMutualAidAuditController {
@Inject
private MedicalMutualAidApplyService medicalMutualAidApplyService;
/**
* 打开互助会审核列表页面。
*/
@At("/audit")
@Ok("beetl:/platform/zhgh/staffbenefit/medicalMutualAid/apply/audit/index.html")
@SaCheckPermission("medicalMutualAid.apply.audit")
public void index() {
}
/**
* 查询互助会审核列表。
*
* @param pageForm 分页参数
* @param keyword 申请人工号或申请人姓名
* @param diseaseName 大病名称
* @param status 审核状态
* @return 审核分页数据
*/
@At("/audit/page")
@ApiOperation("查询互助会审核列表")
@SaCheckPermission("medicalMutualAid.apply.audit")
public Result page(PageForm pageForm, String keyword, String diseaseName, Integer status) {
return Result.success(medicalMutualAidApplyService.auditPage(pageForm, keyword, diseaseName, status));
}
/**
* 打开互助会审核详情页面。
*/
@At("/audit/review")
@Ok("beetl:/platform/zhgh/staffbenefit/medicalMutualAid/apply/audit/review.html")
@SaCheckPermission("medicalMutualAid.apply.audit")
public void review() {
}
/**
* 查询审核详情。
*
* @param id 申请ID
* @return 审核详情和动态补助明细
*/
@At("/audit/review/detail")
@ApiOperation("查询互助会审核详情")
@SaCheckPermission("medicalMutualAid.apply.audit")
public Result detail(Long id) {
return Result.success(medicalMutualAidApplyService.auditDetail(id));
}
/**
* 审核通过。
*
* @param id 申请ID
* @param bonusRatio 补助系数
* @param actualSubsidyAmount 实际补助金额
* @param remark 审核备注
* @return 审核后的详情数据
*/
@At("/audit/review/approve")
@ApiOperation("互助会审核通过")
@SaCheckPermission("medicalMutualAid.apply.audit")
public Result approve(Long id, String bonusRatio, String actualSubsidyAmount, String remark) {
return Result.success(medicalMutualAidApplyService.approve(id, bonusRatio, actualSubsidyAmount, remark));
}
/**
* 弃审已审核申请,恢复为未审核状态并清理审核补助结果。
*
* @param id 申请ID
* @return 弃审后的申请数据
*/
@At("/audit/cancelAudit")
@ApiOperation("互助会弃审")
@SaCheckPermission("medicalMutualAid.apply.audit")
public Result cancelAudit(Long id) {
return Result.success(medicalMutualAidApplyService.cancelAudit(id));
}
/**
* 作废未审核申请,状态置为作废并记录审核人和审核时间。
*
* @param id 申请ID
* @return 作废后的申请数据
*/
@At("/audit/discard")
@ApiOperation("互助会作废")
@SaCheckPermission("medicalMutualAid.apply.audit")
public Result discard(Long id) {
return Result.success(medicalMutualAidApplyService.discard(id));
}
/**
* 取消作废申请,状态恢复为未审核并清空审核人和审核时间。
*
* @param id 申请ID
* @return 取消作废后的申请数据
*/
@At("/audit/cancelDiscard")
@ApiOperation("互助会取消作废")
@SaCheckPermission("medicalMutualAid.apply.audit")
public Result cancelDiscard(Long id) {
return Result.success(medicalMutualAidApplyService.cancelDiscard(id));
}
}
@@ -0,0 +1,143 @@
package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.apply.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.utils.CommonDownloadUtil;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.apply.model.MutualApply;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.apply.service.MedicalMutualAidApplyService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.nutz.lang.util.NutMap;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
import javax.servlet.http.HttpServletResponse;
/**
* 我的申请页面控制器,负责个人申请列表页面入口和分页查询接口。
*/
@IocBean
@At("/platform/medicalMutualAid/apply")
@Api("我的互助金申请")
@Ok("json:full")
public class MedicalMutualAidMineApplyController {
@Inject
private MedicalMutualAidApplyService medicalMutualAidApplyService;
/**
* 打开我的申请页面。
*/
@At("/mine")
@Ok("beetl:/platform/zhgh/staffbenefit/medicalMutualAid/apply/mine/index.html")
@SaCheckPermission("medicalMutualAid.apply.mine")
public void index() {
}
/**
* 打开我的申请查看/编辑页面,页面入口独立于新增申请页,避免新增逻辑与个人记录维护逻辑互相影响。
*/
@At("/mine/form")
@Ok("beetl:/platform/zhgh/staffbenefit/medicalMutualAid/apply/mine/form.html")
@SaCheckPermission("medicalMutualAid.apply.mine")
public void form() {
}
/**
* 查询我的申请维护页初始化数据,当前主要用于加载疾病类型下拉。
*
* @return 页面初始化数据
*/
@At("/mine/form/initApplyData")
@ApiOperation("查询我的互助金申请维护页初始化数据")
@SaCheckPermission("medicalMutualAid.apply.mine")
public Result initApplyData() {
return Result.success(medicalMutualAidApplyService.initApplyData());
}
/**
* 查询我的申请详情,供个人查看和编辑页面回显使用。
*
* @param id 申请ID
* @return 申请详情
*/
@At("/mine/form/detail")
@ApiOperation("查询我的互助金申请详情")
@SaCheckPermission("medicalMutualAid.apply.mine")
public Result detail(@Param("id") Long id) {
return Result.success(medicalMutualAidApplyService.applyDetail(id));
}
/**
* 保存我的申请草稿,复用申请服务保存逻辑但使用我的申请权限入口。
*
* @param apply 申请表单
* @return 保存后的申请数据
*/
@At("/mine/form/saveDraft")
@ApiOperation("保存我的互助金申请草稿")
@SaCheckPermission("medicalMutualAid.apply.mine")
public Result saveDraft(MutualApply apply) {
return Result.success(medicalMutualAidApplyService.saveApply(apply, false));
}
/**
* 提交我的申请修改,提交后仍进入未审核状态等待审核。
*
* @param apply 申请表单
* @return 提交后的申请数据
*/
@At("/mine/form/submitApply")
@ApiOperation("提交我的互助金申请")
@SaCheckPermission("medicalMutualAid.apply.mine")
public Result submitApply(MutualApply apply) {
return Result.success(medicalMutualAidApplyService.saveApply(apply, true));
}
/**
* 查询我的申请列表。
*
* @param pageForm 分页参数
* @param diseaseName 大病名称
* @return 我的申请分页数据
*/
@At("/mine/page")
@ApiOperation("查询我的互助金申请")
@SaCheckPermission("medicalMutualAid.apply.mine")
public Result page(PageForm pageForm, String diseaseName) {
return Result.success(medicalMutualAidApplyService.minePage(pageForm, diseaseName));
}
/**
* 查询我的申请列表,兼容平台通用 initTableMixins 的默认分页接口命名。
*
* @param pageForm 分页与查询参数
* @param diseaseName 大病名称
* @return 我的申请分页数据
*/
@At("/mine/pageData")
@ApiOperation("查询我的互助金申请")
@SaCheckPermission("medicalMutualAid.apply.mine")
public Result pageData(PageForm pageForm, String diseaseName) {
return Result.success(medicalMutualAidApplyService.minePage(pageForm, diseaseName));
}
/**
* 导出我的申请单,Word 模板来源于系统模板管理,模板编码为 medical_mutual_aid_apply_form。
*
* @param id 申请ID
* @param response HTTP响应
*/
@At("/mine/exportApplyForm")
@Ok("void")
@ApiOperation("导出我的大病医疗互助金申请单")
@SaCheckPermission("medicalMutualAid.apply.mine")
public void exportApplyForm(@Param("id") Long id, HttpServletResponse response) {
NutMap exportData = medicalMutualAidApplyService.exportMineApplyForm(id);
CommonDownloadUtil.download(exportData.getString("fileName"), (byte[]) exportData.get("bytes"), response);
}
}
@@ -0,0 +1,71 @@
package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.apply.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.utils.CommonDownloadUtil;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.apply.service.MedicalMutualAidApplyService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.poi.ss.usermodel.Workbook;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import javax.servlet.http.HttpServletResponse;
/**
* 医疗互助汇总控制器,负责汇总页面入口和审核通过数据的汇总查询接口。
*/
@IocBean
@At("/platform/medicalMutualAid/apply")
@Api("医疗互助汇总")
@Ok("json:full")
public class MedicalMutualAidSummaryController {
@Inject
private MedicalMutualAidApplyService medicalMutualAidApplyService;
/**
* 打开医疗互助汇总页面。
*/
@At("/summary")
@Ok("beetl:/platform/zhgh/staffbenefit/medicalMutualAid/apply/summary/index.html")
@SaCheckPermission("medicalMutualAid.apply.summary")
public void index() {
}
/**
* 查询医疗互助汇总列表。
*
* @param pageForm 分页参数
* @param deptName 所在单位
* @param userName 姓名
* @param joinYear 加入年份
* @return 汇总分页数据
*/
@At("/summary/page")
@ApiOperation("查询医疗互助汇总")
@SaCheckPermission("medicalMutualAid.apply.summary")
public Result page(PageForm pageForm, String deptName, String userName, Integer joinYear) {
return Result.success(medicalMutualAidApplyService.summaryPage(pageForm, deptName, userName, joinYear));
}
/**
* 导出医疗互助汇总,导出列与页面当前展示列保持一致。
*
* @param deptName 所在单位
* @param userName 姓名
* @param joinYear 加入年份
* @param response HTTP 响应
*/
@At("/summary/exportExcel")
@Ok("void")
@ApiOperation("导出医疗互助汇总")
@SaCheckPermission("medicalMutualAid.apply.summary")
public void exportExcel(String deptName, String userName, Integer joinYear, HttpServletResponse response) {
Workbook workbook = medicalMutualAidApplyService.exportSummary(deptName, userName, joinYear);
CommonDownloadUtil.download("医疗互助汇总.xlsx", workbook, response);
}
}
@@ -0,0 +1,269 @@
package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.apply.model;
import lombok.Data;
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.Default;
import org.nutz.dao.entity.annotation.Id;
import org.nutz.dao.entity.annotation.Table;
import org.nutz.dao.entity.annotation.TableMeta;
import org.nutz.dao.interceptor.annotation.PrevInsert;
import org.nutz.dao.interceptor.annotation.PrevUpdate;
import java.util.Date;
/**
* 互助金申请主表实体,承载申请人信息、疾病费用、审核状态和审核后的补助汇总结果。
*/
@Data
@Table("tb_mutual_apply")
@TableMeta("{'mysql-charset':'utf8mb4'}")
@Comment("互助金申请表")
public class MutualApply {
/** 申请主键,数据库自增生成。 */
@Id
@Comment("主键")
private Long id;
/** 申请编号,用于后续查询、打印和业务留痕。 */
@Column("apply_no")
@ColDefine(type = ColType.VARCHAR, width = 50)
@Comment("申请编号")
private String applyNo;
/** 申请人用户 ID。 */
@Column("user_id")
@ColDefine(type = ColType.VARCHAR, width = 50)
@Comment("申请人用户ID")
private String userId;
/** 申请人工号或登录名。 */
@Column("login_name")
@ColDefine(type = ColType.VARCHAR, width = 50)
@Comment("申请人工号/登录名")
private String loginName;
/** 申请人姓名。 */
@Column("user_name")
@ColDefine(type = ColType.VARCHAR, width = 50)
@Comment("申请人姓名")
private String userName;
/** 性别。 */
@Column("sex")
@ColDefine(type = ColType.VARCHAR, width = 20)
@Comment("性别")
private String sex;
/** 出生年月。 */
@Column("birthday")
@Comment("出生年月")
private Date birthday;
/** 籍贯。 */
@Column("native_place")
@ColDefine(type = ColType.VARCHAR, width = 100)
@Comment("籍贯")
private String nativePlace;
/** 学历。 */
@Column("education")
@ColDefine(type = ColType.VARCHAR, width = 50)
@Comment("学历")
private String education;
/** 职称。 */
@Column("professional_title")
@ColDefine(type = ColType.VARCHAR, width = 100)
@Comment("职称")
private String professionalTitle;
/** 工作部门 ID。 */
@Column("dept_id")
@ColDefine(type = ColType.VARCHAR, width = 50)
@Comment("工作部门ID")
private String deptId;
/** 工作部门名称。 */
@Column("dept_name")
@ColDefine(type = ColType.VARCHAR, width = 200)
@Comment("工作部门(系部)")
private String deptName;
/** 身份证号码。 */
@Column("id_card")
@ColDefine(type = ColType.VARCHAR, width = 30)
@Comment("身份证号码")
private String idCard;
/** 联系电话。 */
@Column("mobile")
@ColDefine(type = ColType.VARCHAR, width = 30)
@Comment("联系电话")
private String mobile;
/** 工资银行卡号。 */
@Column("bank_card_no")
@ColDefine(type = ColType.VARCHAR, width = 50)
@Comment("工资银行卡号")
private String bankCardNo;
/** 开户行。 */
@Column("bank_name")
@ColDefine(type = ColType.VARCHAR, width = 200)
@Comment("开户行")
private String bankName;
/** 入会时间。 */
@Column("join_date")
@Comment("入会时间")
private Date joinDate;
/** 加入年份。 */
@Column("join_year")
@Comment("加入年份")
private Integer joinYear;
/** 大病类型 ID,来源于疾病类型档案。 */
@Column("disease_type_id")
@Comment("大病类型ID")
private String diseaseTypeId;
/** 大病名称,落表保存历史名称。 */
@Column("disease_name")
@ColDefine(type = ColType.VARCHAR, width = 200)
@Comment("大病名称")
private String diseaseName;
/** 何时何病住何医院。 */
@Column("hospital_desc")
@ColDefine(type = ColType.TEXT)
@Comment("何时何病住何医院")
private String hospitalDesc;
/** 全年医疗费用总金额。 */
@Column("medical_total_amount")
@Default("0.00")
@Comment("全年医疗费用总金额")
private Double medicalTotalAmount;
/** 自付金额。 */
@Column("self_pay_amount")
@Default("0.00")
@Comment("自付金额")
private Double selfPayAmount;
/** 申请人签名文件 ID。 */
@Column("sign_file_id")
@ColDefine(type = ColType.VARCHAR, width = 64)
@Comment("申请人签名文件ID")
private String signFileId;
/** 申请人签名时间。 */
@Column("sign_time")
@Comment("申请人签名时间")
private Date signTime;
/** 申请状态:0 未审核,1 已审核。 */
@Column("status")
@Default("0")
@Comment("状态 0未审核 1已审核")
private Integer status;
/** 提交时间。 */
@Column("submit_time")
@Comment("提交时间")
private Date submitTime;
/** 审核人。 */
@Column("audit_by")
@ColDefine(type = ColType.VARCHAR, width = 50)
@Comment("审核人")
private String auditBy;
/** 审核时间。 */
@Column("audit_time")
@Comment("审核时间")
private Date auditTime;
/** 审核意见。 */
@Column("audit_opinion")
@ColDefine(type = ColType.VARCHAR, width = 500)
@Comment("审核意见")
private String auditOpinion;
/** 审核使用的配置 ID。 */
@Column("config_id")
@Comment("审核使用的互助金配置ID")
private Long configId;
/** 审核使用的配置年度。 */
@Column("config_year")
@Comment("审核使用的配置年度")
private Integer configYear;
/** 区间补助小计。 */
@Column("subsidy_total_amount")
@Comment("补助小计")
private Double subsidyTotalAmount;
/** 奖励或补助系数。 */
@Column("bonus_ratio")
@Comment("奖励/补助系数")
private Double bonusRatio;
/** 实际补助金额。 */
@Column("actual_subsidy_amount")
@Comment("实际补助金额")
private Double actualSubsidyAmount;
/** 连续未申请年数。 */
@Column("continuous_unapply_year")
@Comment("连续未申请年数")
private Integer continuousUnapplyYear;
/** 本次适用最高补助限额。 */
@Column("limit_amount")
@Comment("本次适用最高补助限额")
private Double limitAmount;
/** 当前申报年度同申报人同病种已审核补助金额汇总,仅用于审核页提示展示。 */
private Double yearUsedAmount;
/** 当前申报年度同申报人同病种剩余可申报金额,仅用于审核页提示展示。 */
private Double yearRemainAmount;
/** 备注。 */
@Column("remark")
@ColDefine(type = ColType.VARCHAR, width = 500)
@Comment("备注")
private String remark;
/** 创建人。 */
@Column("create_by")
@ColDefine(type = ColType.VARCHAR, width = 50)
@Comment("创建人")
private String createBy;
/** 创建时间。 */
@Column("create_time")
@PrevInsert(now = true)
@Comment("创建时间")
private Date createTime;
/** 更新人。 */
@Column("update_by")
@ColDefine(type = ColType.VARCHAR, width = 50)
@Comment("更新人")
private String updateBy;
/** 更新时间。 */
@Column("update_time")
@PrevInsert(now = true)
@PrevUpdate(now = true)
@Comment("更新时间")
private Date updateTime;
}
@@ -0,0 +1,86 @@
package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.apply.model;
import lombok.Data;
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.Default;
import org.nutz.dao.entity.annotation.Id;
import org.nutz.dao.entity.annotation.Table;
import org.nutz.dao.entity.annotation.TableMeta;
import org.nutz.dao.interceptor.annotation.PrevInsert;
import java.util.Date;
/**
* 互助金动态补贴金额明细实体,一条申请按配置区间拆分为多条补助计算记录。
*/
@Data
@Table("tb_mutual_apply_subsidy")
@TableMeta("{'mysql-charset':'utf8mb4'}")
@Comment("互助金补贴金额明细表")
public class MutualApplySubsidy {
/** 补助明细主键。 */
@Id
@Comment("主键")
private Long id;
/** 所属申请 ID。 */
@Column("apply_id")
@Comment("申请ID")
private Long applyId;
/** 使用的补助比例配置 ID。 */
@Column("ratio_id")
@Comment("使用的补助比例配置ID")
private Long ratioId;
/** 页面展示用的区间名称。 */
@Column("range_name")
@ColDefine(type = ColType.VARCHAR, width = 100)
@Comment("区间名称")
private String rangeName;
/** 区间开始金额。 */
@Column("start_amount")
@Default("0.00")
@Comment("区间开始金额")
private Double startAmount;
/** 区间结束金额,空表示不设上限。 */
@Column("end_amount")
@Comment("区间结束金额(NULL表示无限)")
private Double endAmount;
/** 该区间的补助比例。 */
@Column("ratio")
@Default("0.00")
@Comment("补助比例(%)")
private Double ratio;
/** 本区间参与补助计算的金额。 */
@Column("base_amount")
@Default("0.00")
@Comment("本区间参与计算金额")
private Double baseAmount;
/** 本区间计算出的补助金额。 */
@Column("subsidy_amount")
@Default("0.00")
@Comment("本区间补助金额")
private Double subsidyAmount;
/** 区间排序。 */
@Column("sort")
@Default("1")
@Comment("排序")
private Integer sort;
/** 创建时间。 */
@Column("create_time")
@PrevInsert(now = true)
@Comment("创建时间")
private Date createTime;
}
@@ -0,0 +1,130 @@
package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.apply.service;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.apply.model.MutualApply;
import org.apache.poi.ss.usermodel.Workbook;
import org.nutz.lang.util.NutMap;
/**
* 医疗互助申请服务,统一承载申请、我的申请、审核和汇总页面所需的公共业务方法。
*/
public interface MedicalMutualAidApplyService extends BaseService<MutualApply> {
/**
* 构建申请页面初始化数据,包含当前用户占位信息和疾病类型下拉数据。
*
* @return 页面初始化数据
*/
NutMap initApplyData();
/**
* 保存互助金申请,草稿和提交都复用该方法并由 submit 参数控制状态。
*
* @param apply 互助金申请表单
* @param submit 是否提交审核
* @return 保存后的申请数据
*/
MutualApply saveApply(MutualApply apply, boolean submit);
/**
* 查询申请表单详情,用于我的申请查看和编辑时回显原始申请数据。
*
* @param applyId 申请ID
* @return 申请主表数据
*/
MutualApply applyDetail(Long applyId);
/**
* 查询我的申请列表。
*
* @param pageForm 分页参数
* @param diseaseName 大病名称
* @return 我的申请分页列表
*/
Pagination<MutualApply> minePage(PageForm pageForm, String diseaseName);
/**
* 导出我的申请单 Word 文件,模板来源于系统模板管理,参数在服务层统一组装。
*
* @param applyId 申请ID
* @return 包含 fileName 和 bytes 的导出结果
*/
NutMap exportMineApplyForm(Long applyId);
/**
* 查询互助会审核列表。
*
* @param pageForm 分页参数
* @param keyword 申请人工号或申请人姓名
* @param diseaseName 大病名称
* @param status 审核状态
* @return 审核分页列表
*/
Pagination<MutualApply> auditPage(PageForm pageForm, String keyword, String diseaseName, Integer status);
/**
* 查询审核详情并携带动态补助明细。
*
* @param applyId 申请ID
* @return 审核详情数据
*/
NutMap auditDetail(Long applyId);
/**
* 审核通过并保存补助计算结果。
*
* @param applyId 申请ID
* @param bonusRatio 审核页面录入的补助系数
* @param actualSubsidyAmount 实际补助金额
* @param remark 审核备注
* @return 审核后的详情数据
*/
NutMap approve(Long applyId, String bonusRatio, String actualSubsidyAmount, String remark);
/**
* 弃审已审核的互助金申请,将状态恢复为未审核并清理审核结果和补助明细。
*
* @param applyId 申请ID
* @return 弃审后的申请数据
*/
MutualApply cancelAudit(Long applyId);
/**
* 作废未审核的互助金申请,记录作废操作人和操作时间。
*
* @param applyId 申请ID
* @return 作废后的申请数据
*/
MutualApply discard(Long applyId);
/**
* 取消作废互助金申请,将状态恢复为未审核并清空审核人和审核时间。
*
* @param applyId 申请ID
* @return 取消作废后的申请数据
*/
MutualApply cancelDiscard(Long applyId);
/**
* 查询医疗互助汇总列表。
*
* @param pageForm 分页参数
* @param deptName 所在单位
* @param userName 姓名
* @param joinYear 加入年份
* @return 汇总分页列表
*/
Pagination<NutMap> summaryPage(PageForm pageForm, String deptName, String userName, Integer joinYear);
/**
* 导出医疗互助汇总,导出字段与汇总页面展示列保持一致,动态补助区间按当前查询结果生成。
*
* @param deptName 所在单位
* @param userName 姓名
* @param joinYear 加入年份
* @return Excel 工作簿
*/
Workbook exportSummary(String deptName, String userName, Integer joinYear);
}
@@ -0,0 +1,158 @@
package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.config.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.result.Result;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.config.model.MutualBonus;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.config.model.MutualConfig;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.config.model.MutualMemberConfig;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.config.model.MutualRatio;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.config.service.MedicalMutualAidConfigService;
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;
/**
* 医疗互助配置 PC 端控制器,负责页面入口与配置管理接口转发。
*/
@IocBean
@At("/platform/medicalMutualAid/config")
@Api("医疗互助配置")
@Ok("json:full")
public class MedicalMutualAidConfigController {
@Inject
private MedicalMutualAidConfigService medicalMutualAidConfigService;
/**
* 打开医疗互助配置页面。
*/
@At("")
@Ok("beetl:/platform/zhgh/staffbenefit/medicalMutualAid/config/index.html")
@SaCheckPermission("medicalMutualAid.config")
public void index() {
}
/**
* 查询互助金配置列表,供配置列表页按年度和配置名称筛选。
*
* @param year 年度,为空时查询全部年度
* @param configName 配置名称,为空时不限制名称
* @return 互助金配置列表
*/
@At
@ApiOperation("查询医疗互助配置列表")
@SaCheckPermission("medicalMutualAid.config")
public Result listConfigs(Integer year, String configName) {
return Result.success(medicalMutualAidConfigService.listConfigs(year, configName));
}
/**
* 查询指定年度的完整配置数据。
*
* @param year 年度
* @return 聚合配置数据
*/
@At
@ApiOperation("查询医疗互助配置")
@SaCheckPermission("medicalMutualAid.config")
public Result configData(Integer year) {
return Result.success(medicalMutualAidConfigService.getConfigData(year));
}
/**
* 保存互助金基础配置。
*
* @param config 基础配置表单
* @return 保存后的基础配置
*/
@At
@ApiOperation("保存互助金基础配置")
@SaCheckPermission("medicalMutualAid.config")
public Result saveConfig(MutualConfig config) {
return Result.success(medicalMutualAidConfigService.saveConfig(config));
}
/**
* 删除互助金基础配置及其关联配置。
*
* @param id 基础配置 ID
* @return 删除结果
*/
@At
@ApiOperation("删除互助金基础配置")
@SaCheckPermission("medicalMutualAid.config")
public Result deleteConfig(Long id) {
medicalMutualAidConfigService.deleteConfig(id);
return Result.success();
}
/**
* 保存补助比例配置。
*
* @param ratio 补助比例表单
* @return 保存后的补助比例
*/
@At
@ApiOperation("保存补助比例配置")
@SaCheckPermission("medicalMutualAid.config")
public Result saveRatio(MutualRatio ratio) {
return Result.success(medicalMutualAidConfigService.saveRatio(ratio));
}
/**
* 删除补助比例配置。
*
* @param id 补助比例 ID
* @return 删除结果
*/
@At
@ApiOperation("删除补助比例配置")
@SaCheckPermission("medicalMutualAid.config")
public Result deleteRatio(Long id) {
medicalMutualAidConfigService.deleteRatio(id);
return Result.success();
}
/**
* 保存连续未申请奖励配置。
*
* @param bonus 连续未申请奖励表单
* @return 保存后的奖励配置
*/
@At
@ApiOperation("保存连续未申请奖励配置")
@SaCheckPermission("medicalMutualAid.config")
public Result saveBonus(MutualBonus bonus) {
return Result.success(medicalMutualAidConfigService.saveBonus(bonus));
}
/**
* 删除连续未申请奖励配置。
*
* @param id 奖励配置 ID
* @return 删除结果
*/
@At
@ApiOperation("删除连续未申请奖励配置")
@SaCheckPermission("medicalMutualAid.config")
public Result deleteBonus(Long id) {
medicalMutualAidConfigService.deleteBonus(id);
return Result.success();
}
/**
* 保存会员缴费配置。
*
* @param memberConfig 会员缴费配置表单
* @return 保存后的会员缴费配置
*/
@At
@ApiOperation("保存会员缴费配置")
@SaCheckPermission("medicalMutualAid.config")
public Result saveMemberConfig(MutualMemberConfig memberConfig) {
return Result.success(medicalMutualAidConfigService.saveMemberConfig(memberConfig));
}
}
@@ -0,0 +1,48 @@
package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.config.model;
import lombok.Data;
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.Id;
import org.nutz.dao.entity.annotation.Table;
import org.nutz.dao.entity.annotation.TableMeta;
import java.math.BigDecimal;
/**
* 连续未申请奖励配置实体,维护连续参保未申请后的补助提升比例。
*/
@Data
@Table("tb_mutual_bonus")
@TableMeta("{'mysql-charset':'utf8mb4'}")
@Comment("连续未申请奖励配置")
public class MutualBonus {
/** 主键,数据库自增生成。 */
@Id
@Comment("主键")
private Long id;
/** 所属互助金基础配置 ID。 */
@Column("config_id")
@Comment("配置ID")
private Long configId;
/** 连续未申请年数。 */
@Column("continuous_year")
@Comment("连续未申请年数")
private Integer continuousYear;
/** 奖励增加比例,单位为百分比。 */
@Column("increase_ratio")
@Comment("增加比例(%)")
private BigDecimal increaseRatio;
/** 奖励说明。 */
@Column("remark")
@ColDefine(type = ColType.VARCHAR, width = 200)
@Comment("备注")
private String remark;
}
@@ -0,0 +1,97 @@
package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.config.model;
import lombok.Data;
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.Default;
import org.nutz.dao.entity.annotation.Id;
import org.nutz.dao.entity.annotation.Table;
import org.nutz.dao.entity.annotation.TableMeta;
import org.nutz.dao.interceptor.annotation.PrevInsert;
import org.nutz.dao.interceptor.annotation.PrevUpdate;
import java.math.BigDecimal;
import java.util.Date;
/**
* 互助金补助基础配置实体,映射年度、起补金额、年度封顶金额等主配置。
*/
@Data
@Table("tb_mutual_config")
@TableMeta("{'mysql-charset':'utf8mb4'}")
@Comment("互助金配置")
public class MutualConfig {
/** 主键,数据库自增生成。 */
@Id
@Comment("主键")
private Long id;
/** 配置所属年度,业务上同一年度只能存在一份配置。 */
@Column("year")
@Comment("年度")
private Integer year;
/** 配置名称,用于页面展示和后续业务识别。 */
@Column("config_name")
@ColDefine(type = ColType.VARCHAR, width = 100)
@Comment("配置名称")
private String configName;
/** 起补金额,低于该金额的部分不参与补助计算。 */
@Column("start_amount")
@Default("10000.00")
@Comment("起补金额")
private BigDecimal startAmount;
/** 首年申请最高补助金额。 */
@Column("first_year_max_amount")
@Default("100000.00")
@Comment("第一年最高补助")
private BigDecimal firstYearMaxAmount;
/** 非首年申请最高补助金额。 */
@Column("other_year_max_amount")
@Default("50000.00")
@Comment("其他年度最高补助")
private BigDecimal otherYearMaxAmount;
/** 启停状态:1 启用,0 停用。 */
@Column("status")
@Default("1")
@Comment("状态 1启用 0停用")
private Integer status;
/** 配置备注,用于记录人工说明。 */
@Column("remark")
@ColDefine(type = ColType.VARCHAR, width = 500)
@Comment("备注")
private String remark;
/** 创建人。 */
@Column("create_by")
@ColDefine(type = ColType.VARCHAR, width = 50)
@Comment("创建人")
private String createBy;
/** 创建时间。 */
@Column("create_time")
@PrevInsert(now = true)
@Comment("创建时间")
private Date createTime;
/** 更新人。 */
@Column("update_by")
@ColDefine(type = ColType.VARCHAR, width = 50)
@Comment("更新人")
private String updateBy;
/** 更新时间。 */
@Column("update_time")
@PrevInsert(now = true)
@PrevUpdate(now = true)
@Comment("更新时间")
private Date updateTime;
}
@@ -0,0 +1,40 @@
package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.config.model;
import lombok.Data;
import org.nutz.dao.entity.annotation.Column;
import org.nutz.dao.entity.annotation.Comment;
import org.nutz.dao.entity.annotation.Id;
import org.nutz.dao.entity.annotation.Table;
import org.nutz.dao.entity.annotation.TableMeta;
import java.math.BigDecimal;
/**
* 医疗互助会员缴费配置实体,维护每月缴费金额和连续缴费优惠年限。
*/
@Data
@Table("tb_mutual_member_config")
@TableMeta("{'mysql-charset':'utf8mb4'}")
@Comment("医疗互助会员缴费配置")
public class MutualMemberConfig {
/** 主键,数据库自增生成。 */
@Id
@Comment("主键")
private Long id;
/** 配置所属年度,用于生成对应年度的基金会员缴费名单。 */
@Column("year")
@Comment("年度")
private Integer year;
/** 每月缴纳互助金额。 */
@Column("month_amount")
@Comment("每月缴纳互助金额(元)")
private BigDecimal monthAmount;
/** 连续缴费满 N 年后,次年缴费减半。 */
@Column("half_price_year")
@Comment("连续缴费满N年次年缴费减半")
private Integer halfPriceYear;
}
@@ -0,0 +1,58 @@
package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.config.model;
import lombok.Data;
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.Id;
import org.nutz.dao.entity.annotation.Table;
import org.nutz.dao.entity.annotation.TableMeta;
import java.math.BigDecimal;
/**
* 互助金补助比例配置实体,按金额区间维护阶梯补助比例。
*/
@Data
@Table("tb_mutual_ratio")
@TableMeta("{'mysql-charset':'utf8mb4'}")
@Comment("互助金补助比例配置")
public class MutualRatio {
/** 主键,数据库自增生成。 */
@Id
@Comment("主键")
private Long id;
/** 所属互助金基础配置 ID。 */
@Column("config_id")
@Comment("配置ID")
private Long configId;
/** 当前补助区间开始金额。 */
@Column("start_amount")
@Comment("开始金额")
private BigDecimal startAmount;
/** 当前补助区间结束金额,空值表示不设上限。 */
@Column("end_amount")
@Comment("结束金额(NULL表示无限)")
private BigDecimal endAmount;
/** 当前区间补助比例,单位为百分比。 */
@Column("ratio")
@Comment("补助比例(%)")
private BigDecimal ratio;
/** 排序号,控制阶梯区间展示与计算顺序。 */
@Column("sort")
@Comment("排序")
private Integer sort;
/** 区间说明。 */
@Column("remark")
@ColDefine(type = ColType.VARCHAR, width = 200)
@Comment("备注")
private String remark;
}
@@ -0,0 +1,119 @@
package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.config.service;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.config.model.MutualBonus;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.config.model.MutualConfig;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.config.model.MutualMemberConfig;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.config.model.MutualRatio;
import org.nutz.lang.util.NutMap;
import java.util.List;
/**
* 医疗互助配置服务,统一处理基础配置、比例配置、奖励配置和会员缴费配置。
*/
public interface MedicalMutualAidConfigService extends BaseService<MutualConfig> {
/**
* 查询指定年度的完整配置数据。
*
* @param year 年度,为空时使用当前年份
* @return 包含基础配置、比例列表、奖励列表和会员缴费配置的聚合数据
*/
NutMap getConfigData(Integer year);
/**
* 查询互助金配置列表,用于配置列表页按年度和配置名称筛选历史配置。
*
* @param year 年度,为空时不限制年度
* @param configName 配置名称,为空时不限制名称
* @return 互助金基础配置列表
*/
List<MutualConfig> listConfigs(Integer year, String configName);
/**
* 保存互助金基础配置。
*
* @param config 互助金基础配置表单
* @return 保存后的互助金基础配置
*/
MutualConfig saveConfig(MutualConfig config);
/**
* 删除互助金基础配置,并同步删除其下比例配置和奖励配置。
*
* @param configId 基础配置 ID
*/
void deleteConfig(Long configId);
/**
* 查询指定基础配置下的补助比例列表。
*
* @param configId 基础配置 ID
* @return 补助比例列表
*/
List<MutualRatio> listRatios(Long configId);
/**
* 保存补助比例配置。
*
* @param ratio 补助比例表单
* @return 保存后的补助比例
*/
MutualRatio saveRatio(MutualRatio ratio);
/**
* 删除补助比例配置。
*
* @param ratioId 补助比例 ID
*/
void deleteRatio(Long ratioId);
/**
* 查询指定基础配置下的连续未申请奖励列表。
*
* @param configId 基础配置 ID
* @return 连续未申请奖励列表
*/
List<MutualBonus> listBonuses(Long configId);
/**
* 保存连续未申请奖励配置。
*
* @param bonus 连续未申请奖励表单
* @return 保存后的连续未申请奖励配置
*/
MutualBonus saveBonus(MutualBonus bonus);
/**
* 删除连续未申请奖励配置。
*
* @param bonusId 连续未申请奖励 ID
*/
void deleteBonus(Long bonusId);
/**
* 查询指定年度的会员缴费配置。
*
* @param year 配置年度
* @return 对应年度的会员缴费配置;未配置时返回 null
*/
MutualMemberConfig getMemberConfig(Integer year);
/**
* 查询生成缴费名单应使用的会员缴费配置。
* 优先使用缴费年度配置,未配置时使用最新年度配置。
*
* @param payYear 缴费年度
* @return 可用于生成名单的会员缴费配置;未配置时返回 null
*/
MutualMemberConfig getMemberConfigForPay(Integer payYear);
/**
* 保存会员缴费配置。
*
* @param memberConfig 会员缴费配置表单
* @return 保存后的会员缴费配置
*/
MutualMemberConfig saveMemberConfig(MutualMemberConfig memberConfig);
}
@@ -0,0 +1,277 @@
package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.config.service.impl;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.exception.BaseException;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.config.model.MutualBonus;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.config.model.MutualConfig;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.config.model.MutualMemberConfig;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.config.model.MutualRatio;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.config.service.MedicalMutualAidConfigService;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.util.NutMap;
import java.math.BigDecimal;
import java.util.List;
/**
* 医疗互助配置服务实现,集中放置配置保存、校验和关联数据维护逻辑。
*/
@IocBean(args = {"refer:dao"})
public class MedicalMutualAidConfigServiceImpl extends BaseServiceImpl<MutualConfig>
implements MedicalMutualAidConfigService {
/**
* 初始化医疗互助配置服务的数据访问对象。
*
* @param dao 数据访问对象
*/
public MedicalMutualAidConfigServiceImpl(Dao dao) {
super(dao);
}
/**
* 聚合返回页面初始化所需的全部配置数据,减少前端重复请求。
*/
@Override
public NutMap getConfigData(Integer year) {
Integer configYear = year == null ? DateUtil.thisYear() : year;
MutualConfig config = fetch(Cnd.where("year", "=", configYear));
if (config == null) {
config = buildDefaultConfig(configYear);
}
Long configId = config.getId();
return NutMap.NEW()
.addv("config", config)
.addv("ratios", configId == null ? List.of() : listRatios(configId))
.addv("bonuses", configId == null ? List.of() : listBonuses(configId))
.addv("memberConfig", getMemberConfig(configYear));
}
/**
* 按列表页查询条件检索年度配置,年度和名称均为空时返回全部配置并按年度倒序展示。
*/
@Override
public List<MutualConfig> listConfigs(Integer year, String configName) {
Cnd cnd = Cnd.NEW();
if (year != null) {
cnd.and("year", "=", year);
}
if (StrUtil.isNotBlank(configName)) {
cnd.and("configName", "like", "%" + configName.trim() + "%");
}
return query(cnd.desc("year"));
}
/**
* 保存基础配置前补齐默认值,并处理同一年唯一配置的更新场景。
*/
@Override
public MutualConfig saveConfig(MutualConfig config) {
if (config == null) {
throw new BaseException("配置不能为空");
}
fillDefaultConfig(config);
MutualConfig oldYearConfig = fetch(Cnd.where("year", "=", config.getYear()));
if (oldYearConfig != null && (config.getId() == null || !oldYearConfig.getId().equals(config.getId()))) {
// 同一年只允许一条配置;新增时如果年度已存在,则复用原记录执行更新。
config.setId(oldYearConfig.getId());
config.setCreateBy(oldYearConfig.getCreateBy());
config.setCreateTime(oldYearConfig.getCreateTime());
}
fillOperator(config);
return insertOrUpdate(config);
}
/**
* 删除主配置时同步清理从表,确保比例与奖励配置不会遗留无效 configId。
*/
@Override
public void deleteConfig(Long configId) {
if (configId == null) {
return;
}
dao().clear(MutualRatio.class, Cnd.where("configId", "=", configId));
dao().clear(MutualBonus.class, Cnd.where("configId", "=", configId));
delete(configId);
}
/**
* 按开始金额和排序号查询补助比例,保证页面阶梯区间顺序稳定。
*/
@Override
public List<MutualRatio> listRatios(Long configId) {
if (configId == null) {
return List.of();
}
return dao().query(MutualRatio.class, Cnd.where("configId", "=", configId).asc("sort").asc("startAmount"));
}
/**
* 保存比例前校验所属基础配置存在,并补齐默认排序。
*/
@Override
public MutualRatio saveRatio(MutualRatio ratio) {
if (ratio == null || ratio.getConfigId() == null) {
throw new BaseException("请先保存基础配置");
}
ensureConfigExists(ratio.getConfigId());
if (ratio.getSort() == null) {
ratio.setSort(listRatios(ratio.getConfigId()).size() + 1);
}
return dao().insertOrUpdate(ratio);
}
/**
* 删除指定比例配置,空 ID 直接忽略。
*/
@Override
public void deleteRatio(Long ratioId) {
if (ratioId != null) {
dao().delete(MutualRatio.class, ratioId);
}
}
/**
* 按连续未申请年数查询奖励配置,保证页面展示顺序符合业务递增关系。
*/
@Override
public List<MutualBonus> listBonuses(Long configId) {
if (configId == null) {
return List.of();
}
return dao().query(MutualBonus.class, Cnd.where("configId", "=", configId).asc("continuousYear"));
}
/**
* 保存奖励前校验所属基础配置存在,避免保存到无效主配置下。
*/
@Override
public MutualBonus saveBonus(MutualBonus bonus) {
if (bonus == null || bonus.getConfigId() == null) {
throw new BaseException("请先保存基础配置");
}
ensureConfigExists(bonus.getConfigId());
return dao().insertOrUpdate(bonus);
}
/**
* 删除指定奖励配置,空 ID 直接忽略。
*/
@Override
public void deleteBonus(Long bonusId) {
if (bonusId != null) {
dao().delete(MutualBonus.class, bonusId);
}
}
/**
* 按年度读取会员缴费配置,供年度配置页面编辑时精准回显。
*/
@Override
public MutualMemberConfig getMemberConfig(Integer year) {
if (year == null) {
return null;
}
return dao().fetch(MutualMemberConfig.class, Cnd.where("year", "=", year));
}
/**
* 生成缴费名单时优先匹配缴费年度,未维护该年度配置才兜底使用最新年度配置。
*/
@Override
public MutualMemberConfig getMemberConfigForPay(Integer payYear) {
MutualMemberConfig yearConfig = getMemberConfig(payYear);
if (yearConfig != null) {
return yearConfig;
}
return dao().query(MutualMemberConfig.class, Cnd.NEW().desc("year").desc("id")).stream()
.filter(config -> config.getYear() != null)
.findFirst()
.orElse(null);
}
/**
* 按年度保存会员缴费配置,同一年只保留一条记录。
*/
@Override
public MutualMemberConfig saveMemberConfig(MutualMemberConfig memberConfig) {
if (memberConfig == null) {
throw new BaseException("会员缴费配置不能为空");
}
if (memberConfig.getYear() == null) {
throw new BaseException("会员缴费配置年度不能为空");
}
MutualMemberConfig oldConfig = getMemberConfig(memberConfig.getYear());
if (oldConfig != null && (memberConfig.getId() == null || !oldConfig.getId().equals(memberConfig.getId()))) {
memberConfig.setId(oldConfig.getId());
}
return dao().insertOrUpdate(memberConfig);
}
/**
* 构造页面默认基础配置,数据库未保存时也能展示截图中的默认业务值。
*/
private MutualConfig buildDefaultConfig(Integer year) {
MutualConfig config = new MutualConfig();
config.setYear(year);
config.setConfigName(year + "年医疗互助补助配置");
config.setStartAmount(new BigDecimal("10000.00"));
config.setFirstYearMaxAmount(new BigDecimal("100000.00"));
config.setOtherYearMaxAmount(new BigDecimal("50000.00"));
config.setStatus(1);
return config;
}
/**
* 保存前补齐基础配置默认值,避免数据库非空字段因页面未传值保存失败。
*/
private void fillDefaultConfig(MutualConfig config) {
if (config.getYear() == null) {
config.setYear(DateUtil.thisYear());
}
if (StrUtil.isBlank(config.getConfigName())) {
config.setConfigName(config.getYear() + "年医疗互助补助配置");
}
if (config.getStartAmount() == null) {
config.setStartAmount(new BigDecimal("10000.00"));
}
if (config.getFirstYearMaxAmount() == null) {
config.setFirstYearMaxAmount(new BigDecimal("100000.00"));
}
if (config.getOtherYearMaxAmount() == null) {
config.setOtherYearMaxAmount(new BigDecimal("50000.00"));
}
if (config.getStatus() == null) {
config.setStatus(1);
}
}
/**
* 校验基础配置是否存在,比例与奖励保存必须依赖有效主配置。
*/
private void ensureConfigExists(Long configId) {
if (fetch(configId) == null) {
throw new BaseException("基础配置不存在,请刷新后重试");
}
}
/**
* 记录当前登录人,兼容未登录或测试环境下获取不到登录 ID 的情况。
*/
private void fillOperator(MutualConfig config) {
String loginId = "";
try {
loginId = StpUtil.getLoginIdAsString();
} catch (Exception ignored) {
}
if (config.getId() == null && StrUtil.isBlank(config.getCreateBy())) {
config.setCreateBy(loginId);
}
config.setUpdateBy(loginId);
}
}
@@ -0,0 +1,76 @@
package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.model.MedicalDiseaseType;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.service.MedicalDiseaseTypeService;
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;
/**
* 医疗互助疾病类型 PC 端控制器。
*/
@IocBean
@At("/platform/medicalMutualAid/medical/diseaseType")
@Api("医疗互助疾病类型")
@Ok("json:full")
public class MedicalDiseaseTypeController {
@Inject
private MedicalDiseaseTypeService medicalDiseaseTypeService;
/**
* 打开疾病类型 PC 管理页面。
*/
@At("")
@Ok("beetl:/platform/zhgh/staffbenefit/medicalMutualAid/medical/diseaseType/index.html")
@SaCheckPermission("medicalMutualAid.medical.diseaseType")
public void index() {
}
/**
* 分页查询疾病类型。
*
* @param pageForm 分页及查询参数
* @return 分页结果
*/
@At
@ApiOperation("分页查询疾病类型")
// @SaCheckPermission("medicalMutualAid.medical.diseaseType")
public Result pageData(PageForm pageForm) {
return Result.success(medicalDiseaseTypeService.pageData(pageForm));
}
/**
* 保存或修改疾病类型。
*
* @param medicalDiseaseType 疾病类型表单数据
* @return 保存结果
*/
@At
@ApiOperation("保存或修改疾病类型")
@SaCheckPermission("medicalMutualAid.medical.diseaseType")
public Result doSubmit(MedicalDiseaseType medicalDiseaseType) {
medicalDiseaseTypeService.saveDiseaseType(medicalDiseaseType);
return Result.success();
}
/**
* 删除指定疾病类型。
*
* @param id 疾病类型主键
* @return 删除结果
*/
@At
@ApiOperation("删除疾病类型")
@SaCheckPermission("medicalMutualAid.medical.diseaseType")
public Result doDelete(String id) {
medicalDiseaseTypeService.deleteDiseaseType(id);
return Result.success();
}
}
@@ -0,0 +1,76 @@
package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.h5controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.model.MedicalDiseaseType;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.service.MedicalDiseaseTypeService;
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;
/**
* 医疗互助疾病类型移动端控制器。
*/
@IocBean
@At("/platform/h5/medicalMutualAid/medical/diseaseType")
@Api("移动端医疗互助疾病类型")
@Ok("json:full")
public class H5MedicalDiseaseTypeController {
@Inject
private MedicalDiseaseTypeService medicalDiseaseTypeService;
/**
* 打开疾病类型移动端管理页面。
*/
@At("")
@Ok("beetl:/platform/zhghh5/staffbenefit/medicalMutualAid/medical/diseaseType/index.html")
@SaCheckPermission("h5.medicalMutualAid.medical.diseaseType")
public void index() {
}
/**
* 移动端分页查询疾病类型。
*
* @param pageForm 分页及查询参数
* @return 分页结果
*/
@At
@ApiOperation("移动端分页查询疾病类型")
@SaCheckPermission("h5.medicalMutualAid.medical.diseaseType")
public Result pageData(PageForm pageForm) {
return Result.success(medicalDiseaseTypeService.pageData(pageForm));
}
/**
* 移动端保存或修改疾病类型。
*
* @param medicalDiseaseType 疾病类型表单数据
* @return 保存结果
*/
@At
@ApiOperation("移动端保存或修改疾病类型")
@SaCheckPermission("h5.medicalMutualAid.medical.diseaseType")
public Result doSubmit(MedicalDiseaseType medicalDiseaseType) {
medicalDiseaseTypeService.saveDiseaseType(medicalDiseaseType);
return Result.success();
}
/**
* 移动端删除指定疾病类型。
*
* @param id 疾病类型主键
* @return 删除结果
*/
@At
@ApiOperation("移动端删除疾病类型")
@SaCheckPermission("h5.medicalMutualAid.medical.diseaseType")
public Result doDelete(String id) {
medicalDiseaseTypeService.deleteDiseaseType(id);
return Result.success();
}
}
@@ -0,0 +1,52 @@
package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.model;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
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.Default;
import org.nutz.dao.entity.annotation.Name;
import org.nutz.dao.entity.annotation.Table;
import org.nutz.dao.interceptor.annotation.PrevInsert;
/**
* 医疗互助疾病类型实体,用于维护可申请的疾病基础数据。
*/
@Data
@Comment("医疗互助疾病类型表")
@Accessors(chain = true)
@Table("medical_disease_type")
@EqualsAndHashCode(callSuper = true)
public class MedicalDiseaseType extends BaseModel {
/** 疾病类型主键。 */
@Column
@Name
@Comment("ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
@PrevInsert(uu32 = true)
private String id;
/** 疾病名称。 */
@Column
@ColDefine(type = ColType.VARCHAR, width = 50)
@Comment("疾病名称")
private String diseaseName;
/** 疾病编码,用于列表排序和业务引用。 */
@Column
@Comment("疾病编码")
@ColDefine(type = ColType.VARCHAR, width = 20)
private String diseaseCode;
/** 是否属于重大疾病,默认是。 */
@Column
@Comment("是否重大疾病")
@Default("1")
@ColDefine(type = ColType.BOOLEAN)
private Boolean isMajorDiseases;
}
@@ -0,0 +1,36 @@
package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.service;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.model.MedicalDiseaseType;
/**
* 医疗互助疾病类型服务,统一承载 PC 端与移动端的公共业务逻辑。
*/
public interface MedicalDiseaseTypeService extends BaseService<MedicalDiseaseType> {
/**
* 按疾病名称分页查询疾病类型。
*
* @param pageForm 分页及关键字参数
* @return 疾病类型分页结果
*/
Pagination<MedicalDiseaseType> pageData(PageForm pageForm);
/**
* 新增或修改疾病类型。
*
* @param medicalDiseaseType 疾病类型表单数据
* @return 保存后的疾病类型
*/
MedicalDiseaseType saveDiseaseType(MedicalDiseaseType medicalDiseaseType);
/**
* 根据主键删除疾病类型。
*
* @param id 疾病类型主键
* @return 删除记录数
*/
int deleteDiseaseType(String id);
}
@@ -0,0 +1,67 @@
package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.service.impl;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.model.MedicalDiseaseType;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.service.MedicalDiseaseTypeService;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.IocBean;
/**
* 医疗互助疾病类型服务实现。
*/
@IocBean(args = {"refer:dao"})
public class MedicalDiseaseTypeServiceImpl extends BaseServiceImpl<MedicalDiseaseType>
implements MedicalDiseaseTypeService {
/**
* 初始化疾病类型服务使用的数据访问对象。
*
* @param dao 数据访问对象
*/
public MedicalDiseaseTypeServiceImpl(Dao dao) {
super(dao);
}
/**
* 构造统一查询条件,确保 PC 端与移动端使用相同的排序和过滤规则。
*/
@Override
public Pagination<MedicalDiseaseType> pageData(PageForm pageForm) {
Cnd cnd = Cnd.NEW();
if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
// 关键字只匹配疾病名称,保持与源功能查询口径一致。
cnd.and(Cnd.likeEX("diseaseName", StrUtil.trim(pageForm.getSearchKeyword())));
}
cnd.asc("diseaseCode");
return listPage(pageForm.getPageNumber(), pageForm.getPageSize(), cnd);
}
/**
* 保存前规范文本字段,并补全重大疾病默认值。
*/
@Override
public MedicalDiseaseType saveDiseaseType(MedicalDiseaseType medicalDiseaseType) {
// 去除用户误输入的首尾空格,避免产生看似重复的数据。
medicalDiseaseType.setDiseaseName(StrUtil.trim(medicalDiseaseType.getDiseaseName()));
medicalDiseaseType.setDiseaseCode(StrUtil.trim(medicalDiseaseType.getDiseaseCode()));
if (medicalDiseaseType.getIsMajorDiseases() == null) {
medicalDiseaseType.setIsMajorDiseases(Boolean.TRUE);
}
return insertOrUpdate(medicalDiseaseType);
}
/**
* 校验主键后执行删除,避免空主键触发无意义数据库操作。
*/
@Override
public int deleteDiseaseType(String id) {
if (StrUtil.isBlank(id)) {
return 0;
}
return delete(id);
}
}
@@ -59,7 +59,7 @@ public class ChildManageManageController {
public void index() {
}
@At("/h5")
@Ok("beetl:/platform/zhghh5/staffmanage/childManage/manage/index.html")
@Ok("beetl:/platform/zhghh5/staffmanage/childmanage/manage/index.html")
@SaCheckPermission("h5.childManage.manage")
public void h5Index() {
}
@@ -39,7 +39,7 @@ public class ChildManageMineController {
public void index() {
}
@At("/h5")
@Ok("beetl:/platform/zhghh5/staffmanage/childManage/mine/index.html")
@Ok("beetl:/platform/zhghh5/staffmanage/childmanage/mine/index.html")
@SaCheckPermission("h5.childManage.mine")
public void h5Index() {
}
@@ -50,7 +50,7 @@ public class ChildManageWriteController {
public void index() {
}
@At("/h5")
@Ok("beetl:/platform/zhghh5/staffmanage/childManage/write/index.html")
@Ok("beetl:/platform/zhghh5/staffmanage/childmanage/write/index.html")
@SaCheckPermission("h5.childManage.write")
public void h5Index() {
}
@@ -1,6 +1,7 @@
package com.budwk.app.zhgh.staffmanage.retired.user.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaMode;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.zhgh.staffmanage.retired.group.service.RetiredGroupService;
@@ -36,8 +37,14 @@ public class RetiredListController {
public void index() {
}
@At("/h5")
@Ok("beetl:/platform/zhghh5/staffmanage/retired/list/index.html")
@SaCheckPermission("h5.retired.list")
public void h5Index() {
}
@At
@SaCheckPermission("retired.list")
@SaCheckPermission(value = {"retired.list", "h5.retired.list"}, mode = SaMode.OR)
public Result pageData(PageForm pageForm,
@Param("unitId") String unitId,
@Param("unionId") String unionId,
@@ -48,34 +55,34 @@ public class RetiredListController {
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("retired.list")
@SaCheckPermission(value = {"retired.list", "h5.retired.list"}, mode = SaMode.OR)
public Result changeToDie(String userId, Date deathDate) {
retiredUserService.changeToDie(userId, deathDate);
return Result.success();
}
@At
@SaCheckPermission("retired.list")
@SaCheckPermission(value = {"retired.list", "h5.retired.list"}, mode = SaMode.OR)
public Result nonRetiredUserPageData(PageForm pageForm) {
return Result.success(retiredUserService.nonRetiredUserPageData(pageForm));
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("retired.list")
@SaCheckPermission(value = {"retired.list", "h5.retired.list"}, mode = SaMode.OR)
public Result doBatchJoinRetire(@Param("userIds") String[] userIds) {
retiredUserService.batchJoinRetire(userIds);
return Result.success();
}
@At
@SaCheckPermission("retired.list")
@SaCheckPermission(value = {"retired.list", "h5.retired.list"}, mode = SaMode.OR)
public Result retiredGroup() {
return Result.success(retiredGroupService.options());
}
@At
@SaCheckPermission("retired.list")
@SaCheckPermission(value = {"retired.list", "h5.retired.list"}, mode = SaMode.OR)
public Result retiredPartyBranch() {
return Result.success(retiredPartyBranchService.options());
}
@@ -88,28 +95,28 @@ public class RetiredListController {
}
@At
@SaCheckPermission("retired.list")
@SaCheckPermission(value = {"retired.list", "h5.retired.list"}, mode = SaMode.OR)
public Result userInfo(String userId) {
return Result.success(retiredUserService.userInfo(userId));
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("retired.list")
@SaCheckPermission(value = {"retired.list", "h5.retired.list"}, mode = SaMode.OR)
public Result doSubmit(@Param("user") String user, @Param("userId") String userId) {
retiredUserService.submit(user, userId);
return Result.success();
}
@At
@SaCheckPermission("retired.list")
@SaCheckPermission(value = {"retired.list", "h5.retired.list"}, mode = SaMode.OR)
public Result getInfo(String id) {
return Result.success(retiredUserService.getInfo(id));
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission("retired.list")
@SaCheckPermission(value = {"retired.list", "h5.retired.list"}, mode = SaMode.OR)
public Result doDelete(String id) {
retiredUserService.deleteRetired(id);
return Result.success();
@@ -72,19 +72,15 @@ public class RetiredUserServiceImpl extends BaseServiceImpl<Sys_user> implements
u.education,
u.academicDegree,
u.nation,
campusDict.name as campusName,
null as campusName,
u.unionGroupName,
u.political,
u.personType,
u.userState,
u.unitName as unitname,
u.unionName as unionname,
CASE WHEN userUnit.unitLevel = 3 THEN userUnit.name ELSE NULL END as threeUnitName
null as threeUnitName
FROM vw_user u
LEFT JOIN sys_unit userUnit ON userUnit.id = u.unitId
LEFT JOIN sys_dict campusParent ON campusParent.code = 'USER_CAMPUS'
LEFT JOIN sys_dict campusDict ON campusDict.parentId = campusParent.id
AND (campusDict.id = userUnit.xqid OR campusDict.code = userUnit.xqid OR campusDict.name = userUnit.xqid)
$condition
""");
Cnd cnd = Cnd.NEW();
@@ -161,15 +157,11 @@ public class RetiredUserServiceImpl extends BaseServiceImpl<Sys_user> implements
u.memberTime,
u.families,
u.personalData,
campusDict.name as campusName,
CASE WHEN userUnit.unitLevel = 3 THEN userUnit.name ELSE NULL END as threeUnitName,
null as campusName,
null as threeUnitName,
rg.groupName as retiredGroupName,
rpb.partyBranchName as retiredPartyBranchName
FROM vw_user u
LEFT JOIN sys_unit userUnit ON userUnit.id = u.unitId
LEFT JOIN sys_dict campusParent ON campusParent.code = 'USER_CAMPUS'
LEFT JOIN sys_dict campusDict ON campusDict.parentId = campusParent.id
AND (campusDict.id = userUnit.xqid OR campusDict.code = userUnit.xqid OR campusDict.name = userUnit.xqid)
LEFT JOIN retired_group_unit rgu ON rgu.unitId = u.unitId
LEFT JOIN retired_group rg ON rg.id = rgu.retiredGroupId
LEFT JOIN retired_party_branch_user rpbu ON rpbu.userId = u.id
@@ -234,7 +226,7 @@ public class RetiredUserServiceImpl extends BaseServiceImpl<Sys_user> implements
u.education,
u.academicDegree,
u.nation,
campusDict.name as campusName,
null as campusName,
u.unionGroupName,
u.personType,
u.userState,
@@ -242,10 +234,6 @@ public class RetiredUserServiceImpl extends BaseServiceImpl<Sys_user> implements
u.unionName as unionname
FROM retired_user_change ruc
LEFT JOIN vw_user u ON u.id = ruc.userId
LEFT JOIN sys_unit userUnit ON userUnit.id = u.unitId
LEFT JOIN sys_dict campusParent ON campusParent.code = 'USER_CAMPUS'
LEFT JOIN sys_dict campusDict ON campusDict.parentId = campusParent.id
AND (campusDict.id = userUnit.xqid OR campusDict.code = userUnit.xqid OR campusDict.name = userUnit.xqid)
$condition
""");
Cnd cnd = Cnd.NEW();
@@ -276,21 +264,18 @@ public class RetiredUserServiceImpl extends BaseServiceImpl<Sys_user> implements
u.education,
u.academicDegree,
u.nation,
campusDict.name as campusName,
null as campusName,
unionGroup.name as unionGroupName,
u.personType,
u.userState,
n.name AS unitname,
gh.name AS unionname,
u.political,
CASE WHEN n.unitLevel = 3 THEN n.name ELSE NULL END as threeUnitName,
null as threeUnitName,
rg.groupName AS retiredGroupName,
rpb.partyBranchName AS retiredPartyBranchName
FROM retired_user_history u
LEFT JOIN sys_unit n ON n.id = u.unitId
LEFT JOIN sys_dict campusParent ON campusParent.code = 'USER_CAMPUS'
LEFT JOIN sys_dict campusDict ON campusDict.parentId = campusParent.id
AND (campusDict.id = n.xqid OR campusDict.code = n.xqid OR campusDict.name = n.xqid)
LEFT JOIN sys_union gh ON gh.id = n.unionId
LEFT JOIN sys_union_group unionGroup ON unionGroup.id = n.unionGroupId
LEFT JOIN retired_group_unit rgu ON rgu.unitId = u.unitId
@@ -321,7 +306,6 @@ public class RetiredUserServiceImpl extends BaseServiceImpl<Sys_user> implements
}
private Sql retiredUserSql() {
// 当前库的 vw_user 不直接提供科室和校区名称,科室按三级单位显示,校区按单位 xqid 关联 USER_CAMPUS 字典显示。
return Sqls.create("""
SELECT
u.id,
@@ -337,21 +321,17 @@ public class RetiredUserServiceImpl extends BaseServiceImpl<Sys_user> implements
u.education,
u.academicDegree,
u.nation,
campusDict.name as campusName,
null as campusName,
u.unionGroupName,
u.personType,
u.userState,
u.unitName as unitname,
u.unionName as unionname,
u.political,
CASE WHEN userUnit.unitLevel = 3 THEN userUnit.name ELSE NULL END as threeUnitName,
null as threeUnitName,
rg.groupName as retiredGroupName,
rpb.partyBranchName as retiredPartyBranchName
FROM vw_user u
LEFT JOIN sys_unit userUnit ON userUnit.id = u.unitId
LEFT JOIN sys_dict campusParent ON campusParent.code = 'USER_CAMPUS'
LEFT JOIN sys_dict campusDict ON campusDict.parentId = campusParent.id
AND (campusDict.id = userUnit.xqid OR campusDict.code = userUnit.xqid OR campusDict.name = userUnit.xqid)
LEFT JOIN retired_group_unit rgu ON rgu.unitId = u.unitId
LEFT JOIN retired_group rg ON rg.id = rgu.retiredGroupId
LEFT JOIN retired_party_branch_user rpbu ON rpbu.userId = u.id
@@ -0,0 +1,15 @@
-- 医疗互助疾病类型基础表;脚本可重复执行。
CREATE TABLE IF NOT EXISTS `medical_disease_type` (
`id` varchar(32) NOT NULL COMMENT 'ID',
`diseaseName` varchar(50) NOT NULL COMMENT '疾病名称',
`diseaseCode` varchar(20) NOT NULL COMMENT '疾病编码',
`isMajorDiseases` tinyint(1) NOT NULL DEFAULT 1 COMMENT '是否重大疾病',
`createdBy` varchar(32) DEFAULT NULL COMMENT '创建人',
`createdAt` bigint DEFAULT NULL COMMENT '创建时间',
`updatedBy` varchar(32) DEFAULT NULL COMMENT '修改人',
`updatedAt` bigint DEFAULT NULL COMMENT '修改时间',
`delFlag` tinyint(1) DEFAULT 0 COMMENT '删除标记',
PRIMARY KEY (`id`),
KEY `idx_medical_disease_type_code` (`diseaseCode`),
KEY `idx_medical_disease_type_name` (`diseaseName`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='医疗互助疾病类型表';
@@ -0,0 +1,71 @@
DROP TABLE IF EXISTS `tb_mutual_apply`;
CREATE TABLE `tb_mutual_apply` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
`apply_no` varchar(50) DEFAULT NULL COMMENT '申请编号',
`user_id` varchar(50) DEFAULT NULL COMMENT '申请人用户ID',
`login_name` varchar(50) DEFAULT NULL COMMENT '申请人工号/登录名',
`user_name` varchar(50) NOT NULL COMMENT '申请人姓名',
`sex` varchar(20) DEFAULT NULL COMMENT '性别',
`birthday` date DEFAULT NULL COMMENT '出生年月',
`native_place` varchar(100) DEFAULT NULL COMMENT '籍贯',
`education` varchar(50) DEFAULT NULL COMMENT '学历',
`professional_title` varchar(100) DEFAULT NULL COMMENT '职称',
`dept_id` varchar(50) DEFAULT NULL COMMENT '工作部门ID',
`dept_name` varchar(200) DEFAULT NULL COMMENT '工作部门(系部)',
`id_card` varchar(30) DEFAULT NULL COMMENT '身份证号码',
`mobile` varchar(30) DEFAULT NULL COMMENT '联系电话',
`bank_card_no` varchar(50) DEFAULT NULL COMMENT '工资银行卡号',
`bank_name` varchar(200) DEFAULT NULL COMMENT '开户行',
`join_date` date DEFAULT NULL COMMENT '入会时间',
`join_year` int DEFAULT NULL COMMENT '加入年份',
`disease_type_id` varchar(32) DEFAULT NULL COMMENT '大病类型ID',
`disease_name` varchar(200) NOT NULL COMMENT '大病名称',
`hospital_desc` varchar(500) DEFAULT NULL COMMENT '何时何病住何医院',
`medical_total_amount` decimal(12,2) NOT NULL DEFAULT '0.00' COMMENT '全年医疗费用总金额',
`self_pay_amount` decimal(12,2) NOT NULL DEFAULT '0.00' COMMENT '自付金额',
`sign_file_id` varchar(50) DEFAULT NULL COMMENT '申请人签名文件ID',
`sign_time` datetime DEFAULT NULL COMMENT '申请人签名时间',
`status` tinyint NOT NULL DEFAULT 0 COMMENT '状态 0未审核 1已审核',
`submit_time` datetime DEFAULT NULL COMMENT '提交时间',
`audit_by` varchar(50) DEFAULT NULL COMMENT '审核人',
`audit_time` datetime DEFAULT NULL COMMENT '审核时间',
`audit_opinion` varchar(500) DEFAULT NULL COMMENT '审核意见',
`config_id` bigint DEFAULT NULL COMMENT '审核使用的互助金配置ID',
`config_year` int DEFAULT NULL COMMENT '审核使用的配置年度',
`subsidy_total_amount` decimal(12,2) DEFAULT NULL COMMENT '补助小计',
`bonus_ratio` decimal(5,2) DEFAULT NULL COMMENT '奖励/补助系数',
`actual_subsidy_amount` decimal(12,2) DEFAULT NULL COMMENT '实际补助金额',
`continuous_unapply_year` int DEFAULT NULL COMMENT '连续未申请年数',
`limit_amount` decimal(12,2) DEFAULT NULL COMMENT '本次适用最高补助限额',
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
`create_by` varchar(50) DEFAULT NULL COMMENT '创建人',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` varchar(50) DEFAULT NULL COMMENT '更新人',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_login_name` (`login_name`),
KEY `idx_dept_id` (`dept_id`),
KEY `idx_status` (`status`),
KEY `idx_disease_type_id` (`disease_type_id`),
KEY `idx_join_year` (`join_year`),
KEY `idx_submit_time` (`submit_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='互助金申请表';
DROP TABLE IF EXISTS `tb_mutual_apply_subsidy`;
CREATE TABLE `tb_mutual_apply_subsidy` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
`apply_id` bigint NOT NULL COMMENT '申请ID',
`ratio_id` bigint DEFAULT NULL COMMENT '使用的补助比例配置ID',
`range_name` varchar(100) DEFAULT NULL COMMENT '区间名称',
`start_amount` decimal(12,2) NOT NULL DEFAULT '0.00' COMMENT '区间开始金额',
`end_amount` decimal(12,2) DEFAULT NULL COMMENT '区间结束金额(NULL表示无限)',
`ratio` decimal(5,2) NOT NULL DEFAULT '0.00' COMMENT '补助比例(%)',
`base_amount` decimal(12,2) NOT NULL DEFAULT '0.00' COMMENT '本区间参与计算金额',
`subsidy_amount` decimal(12,2) NOT NULL DEFAULT '0.00' COMMENT '本区间补助金额',
`sort` int DEFAULT 1 COMMENT '排序',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`),
KEY `idx_apply_id` (`apply_id`),
KEY `idx_ratio_id` (`ratio_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='互助金补贴金额明细表';
@@ -0,0 +1,51 @@
-- 医疗互助疾病类型 PC 端与移动端菜单。
-- 使用 permission 做幂等判断,重复执行不会插入重复菜单。
-- PC 端一级菜单。
INSERT INTO sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, updatedBy, updatedAt, delFlag, platform, moduleId, picIcon, initialPinyinName, isRecommendApp, isRecommendService)
SELECT 'e5d7a110aa214cdf8505fd0000000001', '', LPAD(IFNULL(MAX(CAST(path AS UNSIGNED)), 0) + 1, 4, '0'), '医疗互助', 'Medical Mutual Aid', 'menu', '', '', 'ti-heart', 1, 0, 'medicalMutualAid', NULL, 992, 1, '', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0, 'PC', NULL, NULL, 'y', 0, 0
FROM sys_menu
WHERE platform = 'PC' AND (parentId = '' OR parentId IS NULL) AND CHAR_LENGTH(path) = 4
HAVING NOT EXISTS (SELECT 1 FROM (SELECT id FROM sys_menu WHERE permission = 'medicalMutualAid' AND platform = 'PC') t);
-- PC 端医疗管理分组。
INSERT INTO sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, updatedBy, updatedAt, delFlag, platform, moduleId, picIcon, initialPinyinName, isRecommendApp, isRecommendService)
SELECT 'e5d7a110aa214cdf8505fd0000000002', p.id, CONCAT(p.path, '0001'), '医疗管理', 'Medical Manage', 'menu', '', '', '', 1, 0, 'medicalMutualAid.medical', NULL, 1, 1, '', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0, 'PC', NULL, NULL, 'y', 0, 0
FROM sys_menu p
WHERE p.permission = 'medicalMutualAid' AND p.platform = 'PC'
AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM sys_menu WHERE permission = 'medicalMutualAid.medical' AND platform = 'PC') t);
-- PC 端疾病种类页面。
INSERT INTO sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, updatedBy, updatedAt, delFlag, platform, moduleId, picIcon, initialPinyinName, isRecommendApp, isRecommendService)
SELECT 'e5d7a110aa214cdf8505fd0000000003', p.id, CONCAT(p.path, '0001'), '疾病种类', 'Disease Type', 'menu', '/platform/medicalMutualAid/medical/diseaseType', 'data-pjax', '', 1, 0, 'medicalMutualAid.medical.diseaseType', NULL, 1, 0, '', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0, 'PC', NULL, NULL, 'j', 0, 0
FROM sys_menu p
WHERE p.permission = 'medicalMutualAid.medical' AND p.platform = 'PC'
AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM sys_menu WHERE permission = 'medicalMutualAid.medical.diseaseType' AND platform = 'PC') t);
-- 移动端一级菜单。
INSERT INTO sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, updatedBy, updatedAt, delFlag, platform, moduleId, picIcon, initialPinyinName, isRecommendApp, isRecommendService)
SELECT 'e5d7a110aa214cdf8505fd0000000004', '', LPAD(IFNULL(MAX(CAST(path AS UNSIGNED)), 0) + 1, 4, '0'), '医疗互助', 'Medical Mutual Aid', 'menu', '', '', 'heart-o', 1, 0, 'h5.medicalMutualAid', NULL, 992, 1, '', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0, 'H5', NULL, NULL, 'y', 0, 0
FROM sys_menu
WHERE platform = 'H5' AND (parentId = '' OR parentId IS NULL) AND CHAR_LENGTH(path) = 4
HAVING NOT EXISTS (SELECT 1 FROM (SELECT id FROM sys_menu WHERE permission = 'h5.medicalMutualAid' AND platform = 'H5') t);
-- 移动端疾病种类页面,权限严格使用 h5. 前缀。
INSERT INTO sys_menu (id, parentId, path, name, aliasName, type, href, target, icon, showit, disabled, permission, note, location, hasChildren, createdBy, createdAt, updatedBy, updatedAt, delFlag, platform, moduleId, picIcon, initialPinyinName, isRecommendApp, isRecommendService)
SELECT 'e5d7a110aa214cdf8505fd0000000005', p.id, CONCAT(p.path, '0001'), '疾病种类', 'Disease Type', 'menu', '/platform/h5/medicalMutualAid/medical/diseaseType', 'data-pjax', '', 1, 0, 'h5.medicalMutualAid.medical.diseaseType', NULL, 1, 0, '', UNIX_TIMESTAMP(NOW()) * 1000, '', UNIX_TIMESTAMP(NOW()) * 1000, 0, 'H5', NULL, NULL, 'j', 0, 0
FROM sys_menu p
WHERE p.permission = 'h5.medicalMutualAid' AND p.platform = 'H5'
AND NOT EXISTS (SELECT 1 FROM (SELECT id FROM sys_menu WHERE permission = 'h5.medicalMutualAid.medical.diseaseType' AND platform = 'H5') t);
-- 默认授予系统管理员,其他角色可在角色管理中按需分配。
INSERT INTO sys_role_menu (roleId, menuId)
SELECT r.id, m.id
FROM sys_role r
JOIN sys_menu m ON m.permission IN (
'medicalMutualAid',
'medicalMutualAid.medical',
'medicalMutualAid.medical.diseaseType',
'h5.medicalMutualAid',
'h5.medicalMutualAid.medical.diseaseType'
)
LEFT JOIN sys_role_menu rm ON rm.roleId = r.id AND rm.menuId = m.id
WHERE r.code = 'SYSADMIN' AND rm.roleId IS NULL;
@@ -0,0 +1,18 @@
-- 离退休人员档案手机端初始化脚本
-- 说明:PC端离退休人员档案已部署时,只需要执行本脚本新增手机端入口和权限,菜单挂载到H5端已有的人员管理模块。
SET @now := CAST(UNIX_TIMESTAMP(NOW()) * 1000 AS UNSIGNED);
INSERT INTO `sys_menu` (`id`, `parentId`, `path`, `name`, `initialPinyinName`, `aliasName`, `type`, `href`, `target`, `icon`, `picIcon`, `showit`, `disabled`, `permission`, `note`, `location`, `hasChildren`, `createdBy`, `createdAt`, `updatedBy`, `updatedAt`, `delFlag`, `platform`, `moduleId`, `isRecommendApp`, `isRecommendService`)
SELECT 'b1f00006000000000000000000000003', '', 'H9913', '离退休人员档案', 'L', NULL, 'menu', '/platform/retired/list/h5', 'data-pjax', 'manager-o', NULL, 1, 0, 'h5.retired.list', NULL, 9913, 0, '', @now, '', @now, 0, 'H5', module.`id`, 0, 0
FROM `sys_module` module
WHERE module.`platform` = 'H5'
AND module.`name` = '人员管理'
AND NOT EXISTS (SELECT 1 FROM `sys_menu` WHERE `permission` = 'h5.retired.list' OR `path` = 'H9913');
INSERT INTO `sys_role_menu` (`roleId`, `menuId`)
SELECT r.`id`, m.`id`
FROM `sys_role` r
JOIN `sys_menu` m ON m.`permission` = 'h5.retired.list'
WHERE r.`code` IN ('SYSADMIN', 'SCHOOL_UNION_ADMIN')
AND NOT EXISTS (SELECT 1 FROM `sys_role_menu` rm WHERE rm.`roleId` = r.`id` AND rm.`menuId` = m.`id`);
@@ -111,6 +111,12 @@ module.exports = {
errorList: 0
}
},
clearImportFile() {
this.$set(this.importData, "fileList", [])
if (this.$refs.upload) {
this.$refs.upload.clearFiles()
}
},
doImport() {
if (this.importData.fileList.length === 0) {
this.$message.error({
@@ -122,6 +128,10 @@ module.exports = {
const data = new FormData()
data.append("isFlag", this.importData.isFlag)
data.append("businessId", this.business_id)
// 部分业务导入需要页面上下文参数(如缴费年度),有值时一并提交。
if (this.importData.year) {
data.append("year", this.importData.year)
}
this.importData.fileList.forEach((val) => {
data.append("file", val.raw, val.raw.name)
})
@@ -132,20 +142,28 @@ module.exports = {
background: "rgba(0, 0, 0, 0.7)"
})
this.$axios.post(this.post_url, data).then((res) => {
loading.close()
if (res.code === 0) {
if (res.data) {
this.$message.warning("导入失败")
this.errorInfoData = res.data
this.$emit("flush")
} else {
this.$message.success("导入成功")
this.$emit("flush")
this.resetImportData()
}
} else {
this.$message.warning("导入失败")
if (res.code !== 0) {
this.$message.error(res.msg || "导入失败")
return
}
// 导入接口统一返回统计数据,先回显统计再按错误数量提示结果。
if (res.data) {
this.errorInfoData = res.data
}
if (res.data && Number(res.data.errorCount) > 0) {
this.$message.warning("导入完成,存在异常记录")
this.$emit("flush")
return
}
this.$message.success("导入成功")
this.$emit("flush")
// 成功后仅清除文件,保留本次导入统计供用户核对。
this.clearImportFile()
}).catch(() => {
this.$message.error("导入失败,请稍后重试")
}).finally(() => {
// 请求成功、校验失败或网络异常时都必须关闭导入遮罩。
loading.close()
})
},
exportErrors() {
@@ -20,6 +20,10 @@ module.exports = {
value: {
type: String,
default: ""
},
loadDefault: {
type: Boolean,
default: true
}
},
watch: {
@@ -44,13 +48,17 @@ module.exports = {
}
},
methods: {
async init() {
const {code, data} = await this.$axios.post("/platform/signature/get")
if (code === 0) {
this.signatureContent = data && data.signature
this.$emit("input", this.signatureContent)
}
init() {
this.signatureAddress = origin + "/platform/signature/scanPcCode?id=" + this.id
if (!this.loadDefault || this.value) {
return Promise.resolve()
}
return this.$axios.post("/platform/signature/get").then((resp) => {
if (resp.code === 0) {
this.signatureContent = resp.data && resp.data.signature
this.$emit("input", this.signatureContent)
}
})
},
signatureAgain() {
const val = sessionStorage.getItem("h5-scan-code-signature-" + this.id)
@@ -58,25 +66,26 @@ module.exports = {
this.$emit("input", val)
}
},
async mounted() {
await this.init()
webSocketPubSub.subscribe("h5-scan-code-signature", (data) => {
console.info("ws:收到签字成功消息:", data)
this.signatureContent = data.value
this.$emit("input", data.value)
})
mounted() {
this.init().then(() => {
webSocketPubSub.subscribe("h5-scan-code-signature", (data) => {
console.info("ws:收到签字成功消息:", data)
this.signatureContent = data.value
this.$emit("input", data.value)
})
// 可选:轮询或 watch ready 状态
const checkReady = () => {
if (webSocketPubSub.ws && webSocketPubSub.ws.readyState === WebSocket.OPEN) {
console.info("ws.readyState is open");
this.loading = false;
console.log(this.signatureAddress)
} else {
setTimeout(checkReady, 100);
}
};
checkReady();
// 可选:轮询或 watch ready 状态
const checkReady = () => {
if (webSocketPubSub.ws && webSocketPubSub.ws.readyState === WebSocket.OPEN) {
console.info("ws.readyState is open");
this.loading = false;
console.log(this.signatureAddress)
} else {
setTimeout(checkReady, 100);
}
};
checkReady();
})
}
}
</script>
@@ -39,10 +39,10 @@ layout("/layouts/platform.html"){
<dict-select v-model="pageForm.personTypes" placeholder="请选择教职工类别" @change="doSearch"
code="USER_PERSON_TYPE" multiple collapse-tags></dict-select>
</search-item>
<search-item label="编制类别">
<dict-select v-model="pageForm.preparedBys" placeholder="请选择编制类别" @change="doSearch"
code="USER_PREPARED_BY_TYPE" multiple collapse-tags></dict-select>
</search-item>
<!-- <search-item label="编制类别">-->
<!-- <dict-select v-model="pageForm.preparedBys" placeholder="请选择编制类别" @change="doSearch"-->
<!-- code="USER_PREPARED_BY_TYPE" multiple collapse-tags></dict-select>-->
<!-- </search-item>-->
</search>
</el-card>
<el-card shadow="never">
@@ -106,7 +106,7 @@ layout("/layouts/platform.html"){
{ label: "出生年月", prop: "birthday" },
{ label: "在职状态", prop: "userState", sortable: true },
{ label: "教职工类别", prop: "personType", sortable: true },
{ label: '编制类别', prop: 'preparedBy', sortable: true },
// { label: '编制类别', prop: 'preparedBy', sortable: true },
{ label: "所属工会", prop: "unionName" },
{ label: "所属单位", prop: "unitName" },
],
@@ -163,7 +163,7 @@ layout("/layouts/platform.html"){
const pageForm = clone(this.pageForm)
pageForm.userStates = JSON.stringify(this.pageForm.userStates)
pageForm.personTypes = JSON.stringify(this.pageForm.personTypes)
pageForm.preparedBys = JSON.stringify(this.pageForm.preparedBys)
// pageForm.preparedBys = JSON.stringify(this.pageForm.preparedBys)
this.$axios.post('/platform/fundMember/batchJoin/pageData', pageForm)
.then(res => {
this.tableData = res.data.list
@@ -26,12 +26,7 @@ layout("/layouts/platform.html"){
<el-table-column type="index" width="50px" label="序号" :index="indexMethod"></el-table-column>
<el-table-column prop="loginName" label="工号"></el-table-column>
<el-table-column prop="userName" label="姓名"></el-table-column>
<el-table-column prop="isJoin" label="申请类型">
<template slot-scope="{row}">
<el-tag v-if="row.isJoin" size="small">加入</el-tag>
<el-tag v-else size="small" type="danger">退出</el-tag>
</template>
</el-table-column>
<el-table-column prop="aidFundMemberType" label="会员类别"></el-table-column>
<el-table-column prop="submitTime" label="申请时间"></el-table-column>
<el-table-column prop="curTaskName" label="当前节点"></el-table-column>
<el-table-column prop="instanceState" label="流程状态">
@@ -1,62 +1,61 @@
const fundMemberInfo = {
template: /*language=HTML*/ `
<div>
<div class="process-title">
申请信息
<el-link type="primary" @click="openChart">点击查看流程图</el-link>
</div>
<el-descriptions :column="2" border>
<el-descriptions-item label="姓名">{{viewData.userName}}</el-descriptions-item>
<el-descriptions-item label="工号">{{viewData.loginName}}</el-descriptions-item>
<el-descriptions-item label="性别">{{viewData.sex}}</el-descriptions-item>
<el-descriptions-item label="出生年月">{{viewData.birthday}}</el-descriptions-item>
<el-descriptions-item label="身份证号码">{{viewData.idCard}}</el-descriptions-item>
<el-descriptions-item label="参加工作时间">{{viewData.joinWorkTime}}</el-descriptions-item>
<el-descriptions-item label="退休时间">{{viewData.retireTime}}</el-descriptions-item>
<el-descriptions-item label="家庭住址">{{viewData.address}}</el-descriptions-item>
<el-descriptions-item label="手机电话">{{viewData.mobile}}</el-descriptions-item>
<el-descriptions-item label="住宅号码">{{viewData.homePhone}}</el-descriptions-item>
<el-descriptions-item label="照片">
<img :src="viewData.avatar" alt="" style="width: 120px;height: 150px" v-if="viewData.avatar">
</el-descriptions-item>
</el-descriptions>
<template v-for="task in doneTasks">
<div class="mt10">
<div class="process-title">{{ task.displayName }}</div>
<el-descriptions border class="flow-task-form" :column="3" :key="task.id"
v-if="task.ext.isFirstTaskNode">
<el-descriptions-item label="申请用户">{{ task.ext.initiatorName
}}({{task.ext.initiatorAccount}})
</el-descriptions-item>
<el-descriptions-item label="申请时间">{{ task.finishTime }}</el-descriptions-item>
<el-descriptions-item label="办理结果">
<dict-tag :options="dict.type.PROCESS_TASK_SUBMIT_TYPE"
:value="task.ext.submitType"></dict-tag>
</el-descriptions-item>
</el-descriptions>
<el-descriptions border class="flow-task-form" :column="3" :key="task.id" v-else>
<el-descriptions-item label="办理用户">{{ task.taskFormData.userName
}}({{task.taskFormData.loginName}})
</el-descriptions-item>
<el-descriptions-item label="办理时间">{{ task.finishTime }}</el-descriptions-item>
<el-descriptions-item label="办理结果">
<dict-tag :options="dict.type.PROCESS_TASK_SUBMIT_TYPE"
:value="task.ext.submitType"></dict-tag>
</el-descriptions-item>
<el-descriptions-item label="办理意见" v-if="!task.ext.isFirstTaskNode">{{
task.taskFormData.opinion }}
</el-descriptions-item>
</el-descriptions>
</div>
</template>
<slot></slot>
<snaker-chart ref="snakerChartRef"></snaker-chart>
</div>
`,
template: /*language=HTML*/ [
'<div>',
' <div class="process-title">',
' 申请信息',
' <el-link type="primary" @click="openChart" v-if="row && row.instanceId">点击查看流程图</el-link>',
' </div>',
' <el-descriptions :column="2" border>',
' <el-descriptions-item label="姓名">{{ viewData.userName || \'-\' }}</el-descriptions-item>',
' <el-descriptions-item label="工号">{{ viewData.loginName || \'-\' }}</el-descriptions-item>',
' <el-descriptions-item label="出生年月">{{ viewData.birthday || \'-\' }}</el-descriptions-item>',
' <el-descriptions-item label="性别">{{ viewData.sex || \'-\' }}</el-descriptions-item>',
' <el-descriptions-item label="籍贯">{{ viewData.nativePlace || \'-\' }}</el-descriptions-item>',
' <el-descriptions-item label="家庭住址">{{ viewData.address || \'-\' }}</el-descriptions-item>',
' <el-descriptions-item label="健康状况">{{ viewData.healthStatus || \'-\' }}</el-descriptions-item>',
' <el-descriptions-item label="学历">{{ viewData.education || \'-\' }}</el-descriptions-item>',
' <el-descriptions-item label="联系电话">{{ viewData.mobile || \'-\' }}</el-descriptions-item>',
' <el-descriptions-item label="入职时间">{{ viewData.joinWorkTime || \'-\' }}</el-descriptions-item>',
' <el-descriptions-item label="身份证号码" :span="2">{{ viewData.idCard || \'-\' }}</el-descriptions-item>',
' <el-descriptions-item label="会员类别">{{ viewData.aidFundMemberType || \'-\' }}</el-descriptions-item>',
' <el-descriptions-item label="退休时间">{{ viewData.retireTime || \'-\' }}</el-descriptions-item>',
' <el-descriptions-item label="家庭主要成员" :span="2">',
' <el-table :data="viewData.families || []" border size="small">',
' <el-table-column label="序号" width="80" align="center">',
' <template slot-scope="scope">{{ scope.row.sequence || scope.$index + 1 }}</template>',
' </el-table-column>',
' <el-table-column prop="name" label="姓名" min-width="160"></el-table-column>',
' <el-table-column prop="age" label="年龄" width="120"></el-table-column>',
' <el-table-column prop="relation" label="与本人关系" min-width="180"></el-table-column>',
' </el-table>',
' </el-descriptions-item>',
' <el-descriptions-item label="备注" :span="2">{{ viewData.remark || \'-\' }}</el-descriptions-item>',
' </el-descriptions>',
'',
' <template v-for="task in doneTasks">',
' <div class="mt10">',
' <div class="process-title">{{ task.displayName }}</div>',
' <el-descriptions border class="flow-task-form" :column="3" :key="task.id" v-if="task.ext.isFirstTaskNode">',
' <el-descriptions-item label="申请用户">{{ task.ext.initiatorName }}({{ task.ext.initiatorAccount }})</el-descriptions-item>',
' <el-descriptions-item label="申请时间">{{ task.finishTime }}</el-descriptions-item>',
' <el-descriptions-item label="办理结果">',
' <dict-tag :options="dict.type.PROCESS_TASK_SUBMIT_TYPE" :value="task.ext.submitType"></dict-tag>',
' </el-descriptions-item>',
' </el-descriptions>',
' <el-descriptions border class="flow-task-form" :column="3" :key="task.id" v-else>',
' <el-descriptions-item label="办理用户">{{ task.taskFormData.userName }}({{ task.taskFormData.loginName }})</el-descriptions-item>',
' <el-descriptions-item label="办理时间">{{ task.finishTime }}</el-descriptions-item>',
' <el-descriptions-item label="办理结果">',
' <dict-tag :options="dict.type.PROCESS_TASK_SUBMIT_TYPE" :value="task.ext.submitType"></dict-tag>',
' </el-descriptions-item>',
' <el-descriptions-item label="办理意见" v-if="!task.ext.isFirstTaskNode">{{ task.taskFormData.opinion }}</el-descriptions-item>',
' </el-descriptions>',
' </div>',
' </template>',
' <slot></slot>',
' <snaker-chart ref="snakerChartRef"></snaker-chart>',
'</div>'
].join(''),
store,
dicts: ["PROCESS_TASK_SUBMIT_TYPE"],
data(){
@@ -72,21 +71,29 @@ const fundMemberInfo = {
onOpen(row) {
this.row = row
this.visible = true
this.viewData = Object.assign({}, row || {})
this.doneTasks = []
this.getInfo()
this.getDoneTasks()
},
// 获取申请信息
getInfo() {
if (!this.row || !this.row.id) {
return
}
this.$axios.post('/platform/fundMember/common/info', {id: this.row.id}).then((res) => {
if (res.code === 0) {
this.viewData = res.data
this.viewData = Object.assign({}, this.viewData, res.data || {})
}
})
},
// 获取已办任务审批记录
getDoneTasks() {
if (!this.row || !this.row.instanceId) {
return
}
this.$axios.post("/flow/common/doneTasks", {bizId: this.row.id}).then((res) => {
if (res.code === 0) {
this.doneTasks = res.data
@@ -96,6 +103,10 @@ const fundMemberInfo = {
// 查看流程图
openChart() {
if (!this.row || !this.row.instanceId) {
this.$message.warning("暂无流程信息")
return
}
this.$refs.snakerChartRef.onOpenFull(this.row.instanceProcessDefineId, this.row.instanceId)
}
}
@@ -17,12 +17,7 @@ layout("/layouts/platform.html"){
<el-table-column type="index" width="50px" label="序号" :index="indexMethod"></el-table-column>
<el-table-column prop="loginName" label="工号"></el-table-column>
<el-table-column prop="userName" label="姓名"></el-table-column>
<el-table-column prop="isJoin" label="申请类型">
<template slot-scope="{row}">
<el-tag v-if="row.isJoin" size="small">加入</el-tag>
<el-tag v-else size="small" type="danger">退出</el-tag>
</template>
</el-table-column>
<el-table-column prop="aidFundMemberType" label="会员类别"></el-table-column>
<el-table-column prop="submitTime" label="申请时间"></el-table-column>
<el-table-column prop="taskName" label="当前节点"></el-table-column>
<el-table-column prop="instanceState" label="流程状态">
@@ -34,7 +29,6 @@ layout("/layouts/platform.html"){
<el-table-column label="操作" fixed="right" width="300px">
<template slot-scope="{row}">
<el-button @click="openView(row)" size="mini" type="primary">查看</el-button>
<el-button @click="exportDocx(row.id)" size="mini" type="primary">导出</el-button>
<el-button v-if="row.taskKey === 'startTask' || !row.instanceId" @click="onEdit(row)"
size="mini" type="primary">
编辑
@@ -42,9 +36,10 @@ layout("/layouts/platform.html"){
<el-button v-if="row.canRevoke" @click="onRevoke(row)" size="mini" type="danger">撤回
</el-button>
<!-- v-if="row.taskKey === 'startTask' || !row.instanceId"-->
<el-button @click="onDelete(row.id)"
<el-button v-if="row.taskKey === 'startTask' || !row.instanceId" @click="onDelete(row.id)"
size="mini" type="danger">删除
</el-button>
<el-button @click="exportDocx(row.id)" size="mini" type="primary">导出</el-button>
</template>
</el-table-column>
</el-table>
@@ -0,0 +1,206 @@
<!--#
layout("/layouts/platform.html"){
#-->
<div id="app">
<guava ref="guava">
<el-card shadow="never">
<search @search="doSearch">
<search-item label="缴费年度">
<el-date-picker v-model="pageForm.year" type="year" value-format="yyyy" :clearable="false"
placeholder="请选择年度" style="width: 100%" @change="doSearch"></el-date-picker>
</search-item>
<search-item label="姓名/工号">
<el-input v-model="pageForm.searchKeyword" clearable placeholder="请输入姓名或工号"
@keyup.enter.native="doSearch"></el-input>
</search-item>
<search-item label="会员类别">
<dict-select v-model="pageForm.aidFundMemberType" code="USER_AID_FUND_MEMBER_TYPE"
placeholder="请选择会员类别" style="width: 100%"></dict-select>
</search-item>
<search-item label="所属分工会">
<el-select v-model="pageForm.unionId" clearable filterable placeholder="请选择分工会"
style="width: 100%" @change="unionChange">
<el-option v-for="item in unionList" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
</search-item>
<search-item label="所属单位">
<el-select v-model="pageForm.unitId" clearable filterable placeholder="请选择单位" style="width: 100%">
<el-option v-for="item in unitList" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
</search-item>
</search>
</el-card>
<el-card shadow="never" class="mt10">
<table-tool label="基金会员缴费">
<el-radio-group v-model="pageForm.isPayed" size="small" @change="doSearch">
<el-radio-button :label="null">全部</el-radio-button>
<el-radio-button :label="true">已缴费</el-radio-button>
<el-radio-button :label="false">未缴费</el-radio-button>
</el-radio-group>
<el-button size="small" type="primary" icon="el-icon-plus" @click="createPayList">生成年度名单</el-button>
<el-button size="small" type="primary" icon="el-icon-upload2" @click="openImport">导入缴费记录</el-button>
<el-button size="small" type="primary" icon="el-icon-download" @click="exportPayRecord">导出名单</el-button>
<el-button size="small" type="danger" icon="el-icon-delete" :disabled="tableData.length === 0"
@click="clearPayRecord">清空{{pageForm.year}}年缴费名单</el-button>
</table-tool>
<el-table :data="tableData" :size="tableSize" @sort-change="pageOrder" row-key="id" style="width: 100%"
v-loading="tableLoading">
<el-table-column type="index" label="序号" width="60px" :index="indexMethod" align="center"></el-table-column>
<el-table-column prop="year" label="缴费年度" width="100px" align="center"></el-table-column>
<el-table-column prop="loginName" label="工号" min-width="110px" show-overflow-tooltip></el-table-column>
<el-table-column prop="userName" label="姓名" min-width="100px" show-overflow-tooltip></el-table-column>
<el-table-column prop="sex" label="性别" width="80px" align="center"></el-table-column>
<el-table-column prop="aidFundMemberType" label="会员类别" min-width="120px" show-overflow-tooltip></el-table-column>
<el-table-column prop="unitName" label="所属单位" min-width="150px" show-overflow-tooltip></el-table-column>
<el-table-column prop="unionName" label="所属分工会" min-width="150px" show-overflow-tooltip></el-table-column>
<el-table-column prop="joinTime" label="加入时间" width="120px" align="center"></el-table-column>
<el-table-column prop="money" label="缴费金额(元)" width="130px" align="right"></el-table-column>
<el-table-column prop="isPayed" label="缴费状态" width="100px" align="center">
<template slot-scope="{row}">
<el-tag v-if="row.isPayed" type="success">已缴费</el-tag>
<el-tag v-else type="info">未缴费</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" fixed="right" width="130px" align="center">
<template slot-scope="{row}">
<el-button v-if="!row.isPayed" size="mini" type="primary" @click="updatePayed(row, true)">设为已缴</el-button>
<el-button v-else size="mini" type="danger" @click="updatePayed(row, false)">设为未缴</el-button>
</template>
</el-table-column>
</el-table>
<!--#include("/layouts/pagination.html"){}#-->
</el-card>
<template #edit>
<file-import ref="viewImport" temp_url="/platform/fundMember/payRecord/downloadImportTemp"
post_url="/platform/fundMember/payRecord/doImport" @flush="pageData"></file-import>
</template>
</guava>
</div>
<script nonce="${cspNonce!}">
new Vue({
el: "#app",
store,
mixins: [initTableMixins],
components: {
"file-import": httpVueLoader("/components/plugins/sysImport/index.vue")
},
data() {
return {
pageForm: {
year: this.$moment().format("YYYY"),
searchKeyword: "",
unionId: "",
unitId: "",
aidFundMemberType: "",
isPayed: null
},
unionList: [],
unitList: []
}
},
methods: {
createPayList() {
if (!this.pageForm.year) {
this.notifyWarning("请选择缴费年度")
return
}
this.$axios.post(loc() + "/memberConfig", {year: this.pageForm.year}).then((configRes) => {
if (configRes.code !== 0) {
this.$message.error(configRes.msg)
return
}
this.$confirm("将按会员入会月份生成" + this.pageForm.year + "年缴费名单:入会当年从当月起按每月 " + configRes.data.monthAmount + " 元计算,是否继续?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
this.tableLoading = true
this.$axios.post(loc() + "/createPayList", {year: this.pageForm.year}).then((res) => {
if (res.code === 0) {
this.$message.success(res.msg)
this.doSearch()
}
}).finally(() => {
this.tableLoading = false
})
})
})
},
openImport() {
if (!this.pageForm.year) {
this.notifyWarning("请选择缴费年度")
return
}
this.$refs.guava.edit(() => {
setTimeout(() => {
this.$refs.viewImport.resetImportData()
this.$set(this.$refs.viewImport.importData, "year", this.pageForm.year)
}, 500)
})
},
exportPayRecord() {
if (!this.pageForm.year) {
this.notifyWarning("请选择缴费年度")
return
}
this.$downLoad(loc() + "/exportExcel", this.pageForm)
},
clearPayRecord() {
const currentYear = new Date().getFullYear().toString()
if (this.pageForm.year !== currentYear) {
this.notifyWarning("仅允许清空本年度缴费名单")
return
}
this.$confirm("清空后将删除" + this.pageForm.year + "年全部缴费记录,且无法恢复,是否继续?", "危险操作", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
this.tableLoading = true
this.$axios.post(loc() + "/clearPayList", {year: this.pageForm.year}).then((res) => {
if (res.code === 0) {
this.$message.success(res.msg)
this.doSearch()
}
}).finally(() => {
this.tableLoading = false
})
})
},
updatePayed(row, isPayed) {
const stateName = isPayed ? "已缴费" : "未缴费"
this.$confirm("确认将【" + row.userName + "】设置为" + stateName + "吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
this.tableLoading = true
this.$axios.post(loc() + "/updatePayed", {id: row.id, isPayed: isPayed}).then((res) => {
if (res.code === 0) {
this.$message.success(res.msg)
this.pageData()
}
}).finally(() => {
this.tableLoading = false
})
})
},
async unionChange(unionId) {
this.$set(this.pageForm, "unitId", "")
this.$set(this, "unitList", await this.$businessTool.listUnit(unionId))
this.doSearch()
}
},
async created() {
this.$set(this, "unionList", await this.$businessTool.listUnion())
this.$set(this, "unitList", await this.$businessTool.listUnit())
this.pageData()
}
})
</script>
<!--#
}
#-->
@@ -39,14 +39,14 @@ layout("/layouts/platform.html"){
<dict-select v-model="pageForm.userStates" placeholder="请选择在职状态" @change="doSearch"
code="USER_STATE" multiple collapse-tags></dict-select>
</search-item>
<search-item label="教职工类别">
<dict-select v-model="pageForm.personTypes" placeholder="请选择教职工类别" @change="doSearch"
code="USER_PERSON_TYPE" multiple collapse-tags></dict-select>
</search-item>
<search-item label="编制类别">
<dict-select v-model="pageForm.preparedBys" placeholder="请选择编制类别" @change="doSearch"
code="USER_PREPARED_BY_TYPE" multiple collapse-tags></dict-select>
<search-item label="会员类别">
<dict-select v-model="pageForm.aidFundMemberTypes" placeholder="请选择会员类别" @change="doSearch"
code="USER_AID_FUND_MEMBER_TYPE" multiple collapse-tags></dict-select>
</search-item>
<!-- <search-item label="编制类别">-->
<!-- <dict-select v-model="pageForm.preparedBys" placeholder="请选择编制类别" @change="doSearch"-->
<!-- code="USER_PREPARED_BY_TYPE" multiple collapse-tags></dict-select>-->
<!-- </search-item>-->
</search>
</el-card>
<el-card shadow="never">
@@ -78,6 +78,14 @@ layout("/layouts/platform.html"){
<el-tag v-if="row.isJoin" size="small">加入</el-tag>
<el-tag v-else size="small" type="danger">退出</el-tag>
</template>
<template v-else-if="column.prop === 'aidFundMemberType'" scope="{row}">
<dict-select v-model="row.aidFundMemberType" placeholder="请选择会员类别"
code="USER_AID_FUND_MEMBER_TYPE" style="width: 100%"
@change="onAidFundMemberTypeChange(row)"></dict-select>
</template>
<template v-else-if="column.prop === 'joinTime'" scope="{row}">
{{ formatDate(row.joinTime) }}
</template>
</el-table-column>
<el-table-column label="操作" fixed="right" width="200px">
<template slot-scope="{row}">
@@ -113,8 +121,8 @@ layout("/layouts/platform.html"){
{ label: "性别", prop: "sex" },
{ label: "出生年月", prop: "birthday" },
{ label: "在职状态", prop: "userState", sortable: true },
{ label: "教职工类别", prop: "personType", sortable: true },
{ label: '编制类别', prop: 'preparedBy', sortable: true },
{ label: "会员类别", prop: "aidFundMemberType", sortable: true },
// { label: '编制类别', prop: 'preparedBy', sortable: true },
{ label: "所属工会", prop: "unionName" },
{ label: "所属单位", prop: "unitName" },
{ label: "加入时间", prop: "joinTime" },
@@ -128,6 +136,10 @@ layout("/layouts/platform.html"){
}
},
methods: {
// 查询列表中的加入时间仅展示年月日,避免显示接口返回的时分秒。
formatDate(value) {
return value ? this.$moment(value).format('YYYY-MM-DD') : '-'
},
openView(row) {
this.$refs.guava.edit(() => {
this.$refs.fundMemberInfoRef.onOpen(row)
@@ -151,6 +163,22 @@ layout("/layouts/platform.html"){
})
},
// 修改会员类别
onAidFundMemberTypeChange(row) {
this.$axios.post('/platform/fundMember/query/updateAidFundMemberType', {
loginName: row.loginName,
aidFundMemberType: row.aidFundMemberType
}).then((res) => {
if (res.code === 0) {
this.$message.success('修改成功')
} else {
this.pageData()
}
}).catch(() => {
this.pageData()
})
},
// 备份会员
backup() {
// 此处需要选择年份
@@ -172,12 +200,7 @@ layout("/layouts/platform.html"){
// 导出登记汇总表
exportExcel() {
if (!this.pageForm.year) {
this.$message.error('请选择年度再导出')
return
}
this.$downLoad('/platform/fundMember/query/exportExcel', {
year: this.pageForm.year,
unionId: this.pageForm.unionId
})
},
@@ -200,8 +223,8 @@ layout("/layouts/platform.html"){
pageData() {
const pageForm = clone(this.pageForm)
pageForm.userStates = JSON.stringify(this.pageForm.userStates)
pageForm.personTypes = JSON.stringify(this.pageForm.personTypes)
pageForm.preparedBys = JSON.stringify(this.pageForm.preparedBys)
pageForm.aidFundMemberTypes = JSON.stringify(this.pageForm.aidFundMemberTypes)
// pageForm.preparedBys = JSON.stringify(this.pageForm.preparedBys)
this.$axios.post('/platform/fundMember/query/pageData', pageForm)
.then(res => {
this.tableData = res.data.list
@@ -0,0 +1,129 @@
<!--#
layout("/layouts/platform.html"){
#-->
<div id="app">
<guava ref="guava">
<el-card shadow="never">
<search @search="doSearch">
<search-item label="年度">
<el-date-picker v-model="pageForm.year" type="year" value-format="yyyy" placeholder="请选择年度"
style="width: 100%"></el-date-picker>
</search-item>
<search-item label="姓名/工号">
<el-input v-model="pageForm.searchKeyword" placeholder="请输入姓名或工号" clearable></el-input>
</search-item>
</search>
</el-card>
<el-card shadow="never">
<table-tool>
<el-radio-group v-model="pageForm.approval" size="small" @change="doSearch">
<el-radio-button :label="true">已审核</el-radio-button>
<el-radio-button :label="false">未审核</el-radio-button>
</el-radio-group>
</table-tool>
<el-table :data="tableData" @sort-change="pageOrder" style="width: 100%">
<el-table-column type="index" width="50px" label="序号" :index="indexMethod"></el-table-column>
<el-table-column prop="loginName" label="工号"></el-table-column>
<el-table-column prop="userName" label="姓名"></el-table-column>
<el-table-column prop="aidFundMemberType" label="会员类别"></el-table-column>
<el-table-column prop="submitTime" label="申请时间"></el-table-column>
<el-table-column prop="curTaskName" label="当前节点"></el-table-column>
<el-table-column prop="instanceState" label="流程状态">
<template slot-scope="{row}">
<enum-tag :value="row.instanceState" name="ProcessInstanceStateEnum" label_key="message" size="small"></enum-tag>
</template>
</el-table-column>
<el-table-column label="操作" fixed="right" width="300px">
<template slot-scope="{row}">
<el-button @click="openView(row)" size="mini" type="primary">查看</el-button>
<el-button v-if="row.taskState === 10" @click="openAudit(row)" size="mini" type="primary">审核</el-button>
<el-button v-if="row.canRevoke" @click="onRevoke(row)" size="mini" type="danger">撤回</el-button>
</template>
</el-table-column>
</el-table>
<!--#include("/layouts/pagination.html"){}#-->
</el-card>
<template #edit>
<fund-member-info ref="fundMemberInfoRef">
<div v-if="showApprovalForm">
<div class="process-title">{{formData.taskName}}</div>
<el-form :model="formData" ref="formRef" label-width="0" label-suffix=":" class="flow-task-form">
<el-form-item label="审批意见" prop="tf_opinion" :rules="[{required:true,message:'必填',trigger:['change','blur']}]">
<user-opinion-textarea v-model="formData.tf_opinion"></user-opinion-textarea>
</el-form-item>
</el-form>
<el-row type="flex" justify="end">
<el-button @click="$refs.guava.index()" size="small">取消</el-button>
<el-button @click="handleTaskAction(6)" size="small" type="info">退回到发起人</el-button>
<el-button @click="handleTaskAction(2)" size="small" type="danger">拒绝申请</el-button>
<el-button @click="handleTaskAction(1)" size="small" type="primary">同意申请</el-button>
</el-row>
</div>
</fund-member-info>
</template>
</guava>
</div>
<script nonce="${cspNonce!}">
<!--#include('../info.js'){}#-->
new Vue({
el: "#app",
store,
mixins: [initTableMixins],
components: {"fund-member-info": fundMemberInfo},
data() {
return {pageForm: {approval: false}, formData: {}, showApprovalForm: false}
},
methods: {
openView(row) {
this.$refs.guava.edit(() => {
this.showApprovalForm = false
this.$refs.fundMemberInfoRef.onOpen(row)
})
},
openAudit(row) {
this.$refs.guava.edit(() => {
this.showApprovalForm = true
this.formData = {processTaskId: row.taskId, taskName: row.curTaskName}
this.$refs.fundMemberInfoRef.onOpen(row)
})
},
handleTaskAction(val) {
this.$refs.formRef.validate(valid => {
if (!valid) return
this.$confirm("您确定要提交吗?", "提示", {
confirmButtonText: "确定", cancelButtonText: "取消", type: "warning"
}).then(() => {
this.$axios.post("/flow/common/executeTask", {
data: JSON.stringify(Object.assign({}, this.formData, {submitType: val}))
}).then((res) => {
if (res.code === 0) {
this.$refs.guava.index()
this.$message.success(res.msg)
this.doSearch()
}
})
})
})
},
onRevoke(row) {
this.$confirm("您确定要撤回吗?", "提示", {
confirmButtonText: "确定", cancelButtonText: "取消", type: "info"
}).then(() => {
this.$axios.post("/flow/common/revokeTask", {taskId: row.taskId}).then((res) => {
if (res.code === 0) {
this.$message.success(res.msg)
this.doSearch()
}
})
})
}
},
created() {
this.pageData()
}
})
</script>
<!--#
}
#-->
@@ -37,12 +37,7 @@ layout("/layouts/platform.html"){
<el-table-column prop="loginName" label="工号"></el-table-column>
<el-table-column prop="userName" label="姓名"></el-table-column>
<el-table-column prop="unionName" label="所属工会"></el-table-column>
<el-table-column prop="isJoin" label="申请类型">
<template slot-scope="{row}">
<el-tag v-if="row.isJoin" size="small">加入</el-tag>
<el-tag v-else size="small" type="danger">退出</el-tag>
</template>
</el-table-column>
<el-table-column prop="aidFundMemberType" label="会员类别"></el-table-column>
<el-table-column prop="submitTime" label="申请时间"></el-table-column>
<el-table-column prop="curTaskName" label="当前节点"></el-table-column>
<el-table-column prop="instanceState" label="流程状态">
@@ -4,7 +4,8 @@ layout("/layouts/platform.html"){
<div id="app" v-cloak>
<el-card shadow="never">
<snaker-start slot="header" label="基金会员入会申请" define_key="FUND_MEMBER"></snaker-start>
<snaker-start slot="header" label="基金会员入会申请" define_key="JJHY"></snaker-start>
<el-alert v-if="isFundMember" title="您已是基金会员,无需重复申请。" type="warning" :closable="false" show-icon class="mb20"></el-alert>
<el-form :model="formData" ref="formRef" :rules="formRules" label-width="120px">
<el-row :gutter="20">
<el-col :span="12">
@@ -43,8 +44,8 @@ layout("/layouts/platform.html"){
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="身份证号码" prop="idCard">
<el-input v-model="formData.idCard" placeholder="请输入身份证号码"></el-input>
<el-form-item label="籍贯" prop="nativePlace">
<el-input v-model="formData.nativePlace" placeholder="请输入籍贯"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
@@ -56,16 +57,51 @@ layout("/layouts/platform.html"){
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="参加工作时间" prop="joinWorkTime">
<el-form-item label="健康状况" prop="healthStatus">
<el-input v-model="formData.healthStatus" maxlength="200" placeholder="请输入健康状况"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="学历" prop="education">
<dict-select v-model="formData.education" placeholder="请选择学历" code="USER_EDUCATION" style="width: 100%"></dict-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="联系电话" prop="mobile">
<el-input v-model="formData.mobile" placeholder="请输入联系电话" maxlength="11"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="入职时间" prop="joinWorkTime">
<el-date-picker
v-model="formData.joinWorkTime"
type="date"
value-format="yyyy-MM-dd"
placeholder="选择参加工作时间"
placeholder="选择入职时间"
style="width: 100%"
></el-date-picker>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="身份证号码" prop="idCard">
<el-input v-model="formData.idCard" placeholder="请输入身份证号码"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="会员类别" prop="aidFundMemberType">
<dict-select v-model="formData.aidFundMemberType" placeholder="请选择会员类别"
code="USER_AID_FUND_MEMBER_TYPE" style="width: 100%"></dict-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="退休时间" prop="retireTime">
<el-date-picker
@@ -79,19 +115,54 @@ layout("/layouts/platform.html"){
</el-col>
</el-row>
<!--
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="手机号码" prop="mobile">
<el-input v-model="formData.mobile" placeholder="请输入手机号码" maxlength="11"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="住宅号码" prop="homePhone">
<el-input v-model="formData.homePhone" placeholder="请输入住宅电话" maxlength="20"></el-input>
</el-form-item>
</el-col>
</el-row>
-->
<el-form-item label="家庭主要成员" prop="families">
<el-table :data="formData.families" border size="small">
<el-table-column label="序号" width="80" align="center">
<template slot-scope="scope">
{{ scope.row.sequence }}
</template>
</el-table-column>
<el-table-column label="姓名" min-width="160">
<template slot-scope="scope">
<el-input v-model="scope.row.name" maxlength="50" placeholder="请输入姓名"></el-input>
</template>
</el-table-column>
<el-table-column label="年龄" width="160">
<template slot-scope="scope">
<el-input-number v-model="scope.row.age" :min="0" :max="150" :precision="0" controls-position="right" style="width: 100%"></el-input-number>
</template>
</el-table-column>
<el-table-column label="与本人关系" min-width="180">
<template slot-scope="scope">
<el-input v-model="scope.row.relation" maxlength="50" placeholder="请输入与本人关系"></el-input>
</template>
</el-table-column>
<el-table-column width="150" align="center">
<template slot="header">
<el-button type="primary" size="mini" @click="addFamily">添加成员</el-button>
</template>
<template slot-scope="scope">
<el-button type="danger" size="mini" icon="el-icon-delete" @click="removeFamily(scope.$index)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="formData.remark" type="textarea" :rows="3" maxlength="500" show-word-limit placeholder="请输入备注"></el-input>
</el-form-item>
<!--
<el-form-item label="照 片">
<file-upload
style="--upload-width: 150px;--upload-height:176px"
@@ -102,6 +173,7 @@ layout("/layouts/platform.html"){
upload_result_type="url"
></file-upload>
</el-form-item>
-->
<!-- <el-form-item>-->
<!-- <el-checkbox v-model="isAgree">-->
@@ -112,8 +184,8 @@ layout("/layouts/platform.html"){
</el-form>
<el-row type="flex" justify="end" class="mt20">
<!-- <el-button type="primary" plain @click="onSave">保存</el-button>-->
<el-button type="primary" @click="onSubmit" v-if="!taskId">提交申请</el-button>
<el-button type="primary" @click="onFinishTask" v-else>提交申请</el-button>
<el-button type="primary" @click="onSubmit" v-if="!taskId && !isFundMember">提交申请</el-button>
<el-button type="primary" @click="onFinishTask" v-if="taskId && !isFundMember">提交申请</el-button>
</el-row>
</el-card>
</div>
@@ -126,7 +198,14 @@ layout("/layouts/platform.html"){
return {
bizId: GetQueryString("bizId"),
taskId: GetQueryString("taskId"),
formData: {},
isFundMember: false,
formData: {
families: [],
nativePlace: "",
education: "",
healthStatus: "",
remark: ""
},
formRules: {
userName: [{required: true, message: '请输入姓名', trigger: 'blur'}],
loginName: [{required: true, message: '请输入工资号', trigger: 'blur'}],
@@ -134,14 +213,31 @@ layout("/layouts/platform.html"){
sex: [{required: true, message: '请选择性别', trigger: 'blur'}],
idCard: [{required: true, message: '请输入身份证号码', trigger: 'blur'},
{
pattern: /^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))([0-2][1-9]|3[0-1])\d{3}[Xx\d]$/,
pattern: /^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[Xx\d]$/,
message: '请输入正确的身份证号码',
trigger: 'blur'
}
],
address: [{required: true, message: '请输入家庭住址', trigger: 'blur'}],
joinWorkTime: [{required: true, message: '请选择参加工作时间', trigger: 'blur'}],
retireTime: [{required: false, message: '请选择退休时间', trigger: 'blur'}],
nativePlace: [{required: true, message: '请输入籍贯', trigger: 'blur'}],
healthStatus: [{required: true, message: '请输入健康状况', trigger: 'blur'}],
education: [{required: true, message: '请选择学历', trigger: 'change'}],
joinWorkTime: [{required: true, message: '请选择入职时间', trigger: 'blur'}],
retireTime: [{validator: function (rule, value, callback) {
if (this.formData.aidFundMemberType === '退休' && !value) {
callback(new Error('会员类别为退休时,请选择退休时间'))
return
}
callback()
}.bind(this), trigger: ['change', 'blur']}],
aidFundMemberType: [{required: true, message: '请选择会员类别', trigger: 'change'}],
families: [{required: true, validator: function (rule, value, callback) {
if (!Array.isArray(value) || value.length === 0) {
callback(new Error('请至少添加一名家庭主要成员'))
return
}
callback()
}, trigger: 'change'}],
mobile: [{required: true, message: '请输入手机号码', trigger: 'blur'},
{
pattern: /^1[3456789]\d{9}$/,
@@ -162,6 +258,63 @@ layout("/layouts/platform.html"){
}
},
methods: {
// 确保家庭成员字段始终为可响应式数组,兼容旧申请数据。
ensureFamilies() {
if (!Array.isArray(this.formData.families)) {
this.$set(this.formData, "families", [])
}
},
// 按当前行顺序重置家庭成员序号,避免删除成员后出现断号。
resetFamilySequence() {
this.ensureFamilies()
this.formData.families.forEach((family, index) => {
this.$set(family, "sequence", index + 1)
})
},
// 添加一名家庭成员并自动设置序号。
addFamily() {
this.ensureFamilies()
this.formData.families.push({
sequence: this.formData.families.length + 1,
name: "",
age: null,
relation: ""
})
},
// 删除指定家庭成员后重排序号。
removeFamily(index) {
this.ensureFamilies()
this.formData.families.splice(index, 1)
this.resetFamilySequence()
},
// 提交前校验家庭主要成员的必填信息。
validateFamilies() {
this.resetFamilySequence()
if (this.formData.families.length === 0) {
this.$message.error("请至少添加一名家庭主要成员")
return false
}
for (const family of this.formData.families) {
if (!family.name || !String(family.name).trim()) {
this.$message.error("请填写第" + family.sequence + "位家庭成员的姓名")
return false
}
if (family.age === null || family.age === undefined || family.age === "") {
this.$message.error("请填写第" + family.sequence + "位家庭成员的年龄")
return false
}
if (!family.relation || !String(family.relation).trim()) {
this.$message.error("请填写第" + family.sequence + "位家庭成员与本人的关系")
return false
}
}
return true
},
// 保存
// onSave() {
// this.$confirm("您确定保存吗?", "提示", {
@@ -182,6 +335,7 @@ layout("/layouts/platform.html"){
onSubmit() {
this.$refs.formRef.validate(valid => {
if (!valid) return
if (!this.validateFamilies()) return
// if (!this.isAgree) {
// this.$message.error("请先阅读并同意《南昌大学教职工医疗互助“爱心”基金管理办法》")
// return
@@ -204,19 +358,22 @@ layout("/layouts/platform.html"){
},
onFinishTask() {
this.$confirm("您确定要提交吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
this.$axios.post('/platform/fundMember/write/submitAgain', {
data: JSON.stringify(this.formData),
taskId: GetQueryString("taskId")
}).then(res => {
if (res.code === 0) {
this.$message.success("提交成功")
commonUtil.pjaxPush('/platform/fundMember/mine')
}
this.$refs.formRef.validate(valid => {
if (!valid || !this.validateFamilies()) return
this.$confirm("您确定要提交吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
this.$axios.post('/platform/fundMember/write/submitAgain', {
data: JSON.stringify(this.formData),
taskId: GetQueryString("taskId")
}).then(res => {
if (res.code === 0) {
this.$message.success("提交成功")
commonUtil.pjaxPush('/platform/fundMember/mine')
}
})
})
})
},
@@ -228,10 +385,11 @@ layout("/layouts/platform.html"){
this.$axios.post("/platform/fundMember/write/info", {id: this.bizId}).then((res) => {
if (res.code === 0) {
this.formData = res.data
this.resetFamilySequence()
}
})
} else {
const {username, loginname, sex, birthday, mobile, union, userState, idCard} = this.$store.state.user
const {username, loginname, sex, birthday, mobile, union, userState, idCard, education} = this.$store.state.user
this.formData = {
userName: username,
loginName: loginname,
@@ -241,7 +399,12 @@ layout("/layouts/platform.html"){
unionId: union?.id,
unionName: union?.name,
userState: userState,
idCard: idCard
idCard: idCard,
nativePlace: "",
education: education || "",
healthStatus: "",
remark: "",
families: []
}
}
},
@@ -249,6 +412,11 @@ layout("/layouts/platform.html"){
},
created() {
this.init()
this.$axios.post('/platform/fundMember/write/memberStatus').then((res) => {
if (res.code === 0) {
this.$set(this, "isFundMember", Boolean(res.data))
}
})
if(!this.bizId){
this.$axios.post('/platform/fundMember/write/validateApply')
}
@@ -0,0 +1,221 @@
<!--#
layout("/layouts/platform.html"){
#-->
<style>
.mutual-list-page { padding: 16px 20px; background: #f3f6fa; height: calc(100vh - 88px); box-sizing: border-box; display: flex; flex-direction: column; overflow: hidden; }
.search-panel { background: #fff; padding: 16px 42px; margin-bottom: 12px; box-sizing: border-box; flex-shrink: 0; }
.search-panel .el-form-item { margin-bottom: 0; margin-right: 14px; }
.content-panel { background: #fff; padding: 16px 20px 16px; box-sizing: border-box; flex: 1; min-height: 0; display: flex; flex-direction: column; }
.panel-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; }
.section-title { border-left: 4px solid #0079c2; padding-left: 10px; color: #0079c2; font-size: 16px; font-weight: 700; line-height: 22px; }
.table-actions .el-button { min-width: 54px; padding: 7px 14px; border-radius: 2px; }
.mutual-table.el-table th { background: #fafafa; color: #303133; font-weight: 700; }
.mutual-table.el-table td, .mutual-table.el-table th { padding: 11px 0; }
.pager { display: flex; justify-content: flex-start; align-items: center; padding-top: 12px; flex-shrink: 0; }
.pager .total-text { margin-right: 18px; color: #303133; }
</style>
<div id="app" v-cloak class="mutual-list-page">
<div class="search-panel">
<el-form :inline="true" :model="searchForm">
<el-form-item label="工号/姓名">
<el-input v-model="searchForm.keyword" clearable placeholder="请输入工号或姓名" style="width: 220px"></el-input>
</el-form-item>
<el-form-item label="大病名称">
<el-select v-model="searchForm.diseaseName" clearable filterable placeholder="请选择大病名称" style="width: 220px">
<el-option v-for="item in diseaseTypes" :key="item.id" :label="item.diseaseName" :value="item.diseaseName"></el-option>
</el-select>
</el-form-item>
<el-form-item label="状态">
<el-select v-model="searchForm.status" clearable placeholder="请选择状态" style="width: 160px">
<el-option v-for="item in statusOptions" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadPage">查询</el-button>
<el-button @click="resetSearch">重置</el-button>
</el-form-item>
</el-form>
</div>
<div class="content-panel" ref="contentPanel">
<div class="panel-header" ref="panelHeader">
<div class="section-title">互助会审核</div>
</div>
<el-table class="mutual-table" :data="tableData" border v-loading="loading" :height="tableHeight" style="width: 100%">
<el-table-column type="index" label="序号" width="70" align="center"></el-table-column>
<el-table-column prop="loginName" label="申请人工号" width="130" align="center"></el-table-column>
<el-table-column prop="userName" label="申请人姓名" width="130" align="center"></el-table-column>
<el-table-column prop="joinDate" label="入会时间" width="130" align="center"></el-table-column>
<el-table-column prop="diseaseName" label="大病名称" min-width="200" align="center"></el-table-column>
<el-table-column label="全年医疗费用总金额(元)" width="190" align="center">
<template slot-scope="{row}">{{ formatMoney(row.medicalTotalAmount) }}</template>
</el-table-column>
<el-table-column label="自费金额(元)" width="150" align="center">
<template slot-scope="{row}">{{ formatMoney(row.selfPayAmount) }}</template>
</el-table-column>
<el-table-column label="状态" width="100" align="center">
<template slot-scope="{row}">
<el-tag v-if="isAudited(row)" type="success" size="mini">已审核</el-tag>
<el-tag v-else-if="isDiscarded(row)" type="danger" size="mini">已作废</el-tag>
<el-tag v-else type="warning" size="mini">未审核</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="260" align="center">
<template slot-scope="{row}">
<div class="table-actions">
<el-button size="mini" @click="goView(row)">查看</el-button>
<el-button v-if="isPending(row)" size="mini" type="primary" @click="goReview(row)">审核</el-button>
<el-button v-if="isAudited(row)" size="mini" type="warning" @click="cancelAudit(row)">弃审</el-button>
<el-button v-if="isPending(row)" size="mini" type="danger" @click="discard(row)">作废</el-button>
<el-button v-if="isDiscarded(row)" size="mini" type="warning" @click="cancelDiscard(row)">取消作废</el-button>
</div>
</template>
</el-table-column>
</el-table>
<div class="pager" ref="pager">
<span class="total-text">共 {{ totalCount }} 条</span>
<el-pagination background layout="sizes, prev, pager, next" :current-page.sync="pageForm.pageNumber" :page-size.sync="pageForm.pageSize" :total="totalCount" @current-change="loadPage" @size-change="loadPage"></el-pagination>
</div>
</div>
</div>
<script nonce="${cspNonce!}">
new Vue({
el: "#app",
data() {
return {
loading: false,
tableData: [],
diseaseTypes: [],
statusOptions: [
{ label: "未审核", value: 0 },
{ label: "已审核", value: 1 },
{ label: "作废", value: 2 }
],
totalCount: 0,
tableHeight: 360,
apiBase: "${base!}/platform/medicalMutualAid/apply/audit",
reviewPath: "${base!}/platform/medicalMutualAid/apply/audit/review",
diseaseTypeApi: "${base!}/platform/medicalMutualAid/medical/diseaseType",
searchForm: { keyword: "", diseaseName: "", status: "" },
pageForm: { pageNumber: 1, pageSize: 10 }
}
},
methods: {
// 加载互助疾病类型下拉,查询条件与申请页面共用疾病基础数据。
loadDiseaseTypes() {
this.$axios.post(this.diseaseTypeApi + "/pageData", { pageNumber: 1, pageSize: 999 }).then((resp) => {
if (resp.code === 0) {
this.diseaseTypes = resp.data.list || []
}
})
},
// 查询互助金待审核/已审核申请列表。
loadPage() {
this.loading = true
const params = Object.assign({}, this.pageForm, this.searchForm)
if (params.status === "" || params.status === null || params.status === undefined) {
delete params.status
}
this.$axios.post(this.apiBase + "/page", params).then((resp) => {
if (resp.code === 0) {
this.tableData = resp.data.list || []
this.totalCount = resp.data.totalCount || 0
}
}).finally(() => {
this.loading = false
this.$nextTick(this.updateTableHeight)
})
},
// 重置审核列表查询条件。
resetSearch() {
this.searchForm = { keyword: "", diseaseName: "", status: "" }
this.pageForm.pageNumber = 1
this.loadPage()
},
// 进入审核详情页,具体补助公式在详情页中展示。
goReview(row) {
commonUtil.pjaxPush(this.reviewPath + "?id=" + row.id)
},
// 查看复用审核详情页,通过 mode=view 控制页面只读并隐藏审核操作。
goView(row) {
commonUtil.pjaxPush(this.reviewPath + "?id=" + row.id + "&mode=view")
},
// 统一判断审核状态,兼容后端返回数字或字符串。
isAudited(row) {
return Number(row.status) === 1
},
// 未审核状态允许进入审核或作废。
isPending(row) {
return Number(row.status) === 0
},
// 作废状态只允许取消作废,不能继续审核或弃审。
isDiscarded(row) {
return Number(row.status) === 2
},
// 已审核申请支持弃审,恢复未审核状态并清理补助明细后刷新列表。
cancelAudit(row) {
this.$confirm("确定要弃审该互助金申请吗?", "提示", {
type: "warning"
}).then(() => {
this.$axios.post(this.apiBase + "/cancelAudit", { id: row.id }).then((resp) => {
if (resp.code === 0) {
this.$message.success("弃审成功")
this.loadPage()
}
})
}).catch(() => {})
},
// 作废未审核申请,后台会记录当前操作人为审核人并记录审核时间。
discard(row) {
this.$confirm("确定要作废该互助金申请吗?", "提示", {
type: "warning"
}).then(() => {
this.$axios.post(this.apiBase + "/discard", { id: row.id }).then((resp) => {
if (resp.code === 0) {
this.$message.success("作废成功")
this.loadPage()
}
})
}).catch(() => {})
},
// 取消作废后恢复未审核状态,并清空作废时写入的审核人和审核时间。
cancelDiscard(row) {
this.$confirm("确定要取消作废该互助金申请吗?", "提示", {
type: "warning"
}).then(() => {
this.$axios.post(this.apiBase + "/cancelDiscard", { id: row.id }).then((resp) => {
if (resp.code === 0) {
this.$message.success("取消作废成功")
this.loadPage()
}
})
}).catch(() => {})
},
formatMoney(value) {
const num = Number(value || 0)
return num.toLocaleString("zh-CN", { minimumFractionDigits: 2, maximumFractionDigits: 2 })
},
// 根据内容面板剩余空间计算表格高度,避免列表高度受数据条数影响。
updateTableHeight() {
const panel = this.$refs.contentPanel
if (!panel) {
return
}
const headerHeight = this.$refs.panelHeader ? this.$refs.panelHeader.offsetHeight : 0
const pagerHeight = this.$refs.pager ? this.$refs.pager.offsetHeight : 0
this.tableHeight = Math.max(panel.clientHeight - headerHeight - pagerHeight - 32, 220)
}
},
created() {
this.loadDiseaseTypes()
this.loadPage()
},
mounted() {
this.$nextTick(this.updateTableHeight)
window.addEventListener("resize", this.updateTableHeight)
},
beforeDestroy() {
window.removeEventListener("resize", this.updateTableHeight)
}
})
</script>
<!--#}#-->
@@ -0,0 +1,275 @@
<!--#
layout("/layouts/platform.html"){
#-->
<style>
.audit-review-page { background: #f5f7fa; height: calc(100vh - 88px); display: flex; flex-direction: column; overflow: hidden; box-sizing: border-box; }
.back-bar { background: #fff; padding: 14px 24px; margin-bottom: 12px; border-bottom: 1px solid #ebeef5; flex-shrink: 0; }
.back-bar .el-button { padding: 0; font-size: 15px; }
.audit-review-card { flex: 1; min-height: 0; height: auto !important; overflow: hidden; }
.audit-review-card .card-body { flex: 1; min-height: 0; overflow-y: auto; box-sizing: border-box; }
.audit-review-card .el-card__footer { flex-shrink: 0; }
.section-title { border-left: 4px solid #0079c2; padding-left: 10px; margin: 4px 0 14px; color: #0079c2; font-weight: 700; }
.required-star { color: #f56c6c; padding-right: 4px; font-weight: 700; }
.mutual-desc .el-descriptions-item__label { width: 150px; color: #303133; background: #fafafa; }
.mutual-desc .el-descriptions-item__content { min-width: 220px; }
.mutual-desc .el-form-item { margin-bottom: 0; }
.mutual-desc .el-input-number { width: 100%; }
.calc-block { margin-top: 16px; }
.calc-table .cell { line-height: 22px; }
.limit-tip { color: #f56c6c; font-size: 13px; line-height: 22px; margin: 12px 0 0; }
.audit-actions { text-align: right; }
</style>
<div id="app" v-cloak class="audit-review-page">
<div class="back-bar">
<el-button type="text" icon="el-icon-back" @click="goBack">返回</el-button>
</div>
<custom-card class="audit-review-card" v-loading="loading">
<div class="section-title">申请信息</div>
<el-form :model="apply" label-width="0">
<el-descriptions class="mutual-desc" :column="3" border>
<el-descriptions-item>
<template slot="label"><span class="required-star">*</span>工号</template>
<el-input v-model="apply.loginName" readonly></el-input>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label"><span class="required-star">*</span>姓名</template>
<el-input v-model="apply.userName" readonly></el-input>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">入会日期</template>
<el-input :value="formatDate(apply.joinDate)" readonly></el-input>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">入会时间(年)</template>
<el-input :value="calcJoinYears(apply.joinYear)" readonly></el-input>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">性别</template>
<el-input v-model="apply.sex" readonly></el-input>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">出生年月</template>
<el-input :value="formatDate(apply.birthday)" readonly></el-input>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">籍贯</template>
<el-input v-model="apply.nativePlace" readonly></el-input>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">工作部门(系部)</template>
<el-input v-model="apply.deptName" readonly></el-input>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">学历</template>
<el-input v-model="apply.education" readonly></el-input>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">职称</template>
<el-input v-model="apply.professionalTitle" readonly></el-input>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label"><span class="required-star">*</span>身份证号码</template>
<el-input v-model="apply.idCard" readonly></el-input>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label"><span class="required-star">*</span>联系电话</template>
<el-input v-model="apply.mobile" readonly></el-input>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label"><span class="required-star">*</span>工资银行卡号</template>
<el-input v-model="apply.bankCardNo" readonly></el-input>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label"><span class="required-star">*</span>开户行</template>
<el-input v-model="apply.bankName" readonly></el-input>
</el-descriptions-item>
<el-descriptions-item></el-descriptions-item>
<el-descriptions-item :span="3">
<template slot="label"><span class="required-star">*</span>大病名称</template>
<el-input v-model="apply.diseaseName" readonly></el-input>
</el-descriptions-item>
<el-descriptions-item :span="3">
<template slot="label"><span class="required-star">*</span>何时何病住何医院</template>
<el-input type="textarea" :rows="5" v-model="apply.hospitalDesc" readonly></el-input>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label"><span class="required-star">*</span>全年医疗费用总金额(元)</template>
<el-input :value="formatMoney(apply.medicalTotalAmount)" readonly></el-input>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label"><span class="required-star">*</span>自费金额(元)</template>
<el-input :value="formatMoney(apply.selfPayAmount)" readonly></el-input>
</el-descriptions-item>
<el-descriptions-item></el-descriptions-item>
</el-descriptions>
</el-form>
<div class="calc-block">
<div class="section-title">补助计算方法</div>
<el-table class="calc-table" :data="calcRows" border style="width: 100%">
<el-table-column type="index" label="序号" width="80" align="center"></el-table-column>
<el-table-column label="补助计算方法" align="center">
<el-table-column v-for="item in subsidies" :key="item.id || item.ratioId || item.sort" :label="item.rangeName" align="center" min-width="150">
<template slot-scope="{row}">{{ formatMoney(row.amounts[item.sort]) }}</template>
</el-table-column>
</el-table-column>
<el-table-column label="补助小计" align="center" min-width="140">
<template slot-scope="{row}">{{ formatMoney(row.total) }}</template>
</el-table-column>
<el-table-column label="补助比例" align="center" min-width="120">
<template slot-scope="{row}">{{ formatRatio(row.bonusRatio) }}</template>
</el-table-column>
<el-table-column label="实际补助金额(元)" align="center" min-width="160">
<template slot-scope="{row}">{{ formatMoney(row.actualSubsidyAmount) }}</template>
</el-table-column>
</el-table>
<el-descriptions class="mutual-desc" :column="3" border style="margin-top: 12px">
<el-descriptions-item :span="3">
<template slot="label">备注</template>
<el-input type="textarea" :rows="3" maxlength="500" show-word-limit v-model="auditForm.remark" :readonly="isViewMode" placeholder="请输入备注(选填)"></el-input>
</el-descriptions-item>
</el-descriptions>
</div>
<div class="limit-tip" v-if="showYearLimitTip">
当前申报人本年度已申报:{{ formatMoney(yearLimit.yearUsedAmount) }},剩余可申报金额:{{ formatMoney(yearLimit.yearRemainAmount) }}
</div>
<template slot="footer" v-if="!isViewMode">
<div class="audit-actions">
<el-button @click="goBack">返回</el-button>
<el-button type="primary" @click="approve">审核通过</el-button>
</div>
</template>
</custom-card>
</div>
<script nonce="${cspNonce!}">
new Vue({
el: "#app",
data() {
return {
loading: false,
id: "",
pageMode: "audit",
apiBase: "${base!}/platform/medicalMutualAid/apply/audit/review",
auditPath: "${base!}/platform/medicalMutualAid/apply/audit",
apply: {},
subsidies: [],
yearLimit: { yearUsedAmount: 0, yearRemainAmount: 0, yearLimitAmount: 0 },
auditForm: { bonusRatio: "", actualSubsidyAmount: "", remark: "" }
}
},
computed: {
// 查看模式由列表页传入,统一控制审核详情页隐藏操作按钮和锁定可编辑项。
isViewMode() {
return this.pageMode === "view"
},
// 年度同病种限额提示仅在审核计算结果返回后展示。
showYearLimitTip() {
return this.yearLimit.yearLimitAmount !== undefined && this.yearLimit.yearLimitAmount !== null
},
// 审核详情只展示一条申请的动态补助结果,表格列由后台配置区间决定。
calcRows() {
const amounts = {}
let total = 0
this.subsidies.forEach((item) => {
amounts[item.sort] = item.subsidyAmount
total += Number(item.subsidyAmount || 0)
})
return [{
amounts: amounts,
total: total,
bonusRatio: this.apply.bonusRatio,
actualSubsidyAmount: this.apply.actualSubsidyAmount
}]
}
},
methods: {
// 从 URL 中读取审核申请 ID,保持审核列表跳转参数简单明确。
getQuery(name) {
const query = window.location.search ? window.location.search.substring(1) : ""
const parts = query.split("&")
for (let i = 0; i < parts.length; i++) {
const pair = parts[i].split("=")
if (decodeURIComponent(pair[0] || "") === name) {
return decodeURIComponent(pair[1] || "")
}
}
return ""
},
// 加载审核详情,后台会同步返回基金会员入会信息和本年度启用配置计算结果。
loadDetail() {
this.loading = true
this.$axios.post(this.apiBase + "/detail", { id: this.id }).then((resp) => {
if (resp.code === 0) {
this.apply = resp.data.apply || {}
this.subsidies = resp.data.subsidies || []
this.yearLimit = {
yearUsedAmount: resp.data.yearUsedAmount || 0,
yearRemainAmount: resp.data.yearRemainAmount || 0,
yearLimitAmount: resp.data.yearLimitAmount || 0
}
this.auditForm.bonusRatio = this.formatRatio(this.apply.bonusRatio)
this.auditForm.actualSubsidyAmount = this.formatMoney(this.apply.actualSubsidyAmount)
this.auditForm.remark = this.apply.remark || ""
}
}).finally(() => {
this.loading = false
})
},
// 审核通过时提交后台重算后的补助比例、实际补助金额和审核备注。
approve() {
const params = {
id: this.id,
bonusRatio: this.apply.bonusRatio,
actualSubsidyAmount: this.apply.actualSubsidyAmount,
remark: this.auditForm.remark
}
this.$axios.post(this.apiBase + "/approve", params).then((resp) => {
if (resp.code === 0) {
this.$message.success("审核通过")
this.goBack()
}
})
},
goBack() {
commonUtil.pjaxPush(this.auditPath)
},
formatDate(value) {
if (!value) {
return ""
}
return this.$moment(value).format("YYYY-MM-DD")
},
formatRatio(value) {
if (value === undefined || value === null || value === "") {
return ""
}
return Number(value).toFixed(2)
},
// 入会时间(年)只表达基金会员入会年限,不参与表格展示列控制。
calcJoinYears(joinYear) {
if (!joinYear) {
return ""
}
const currentYear = new Date().getFullYear()
return Math.max(currentYear - Number(joinYear), 0)
},
formatMoney(value) {
const num = Number(value || 0)
const fixed = Math.floor(num * 100) / 100
const parts = fixed.toFixed(2).split(".")
return parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",") + "." + parts[1]
}
},
created() {
this.id = this.getQuery("id")
this.pageMode = this.getQuery("mode") || "audit"
this.loadDetail()
}
})
</script>
<!--#
}
#-->
@@ -0,0 +1,265 @@
<!--#
layout("/layouts/platform.html"){
#-->
<style>
.mutual-apply-page { padding: 12px; background: #f5f7fa; min-height: calc(100vh - 24px); }
.mutual-apply-card { background: #fff; border: 1px solid #e4e7ed; border-radius: 4px; padding: 16px; }
.section-title { border-left: 4px solid #0079c2; padding-left: 10px; margin: 8px 0 14px; color: #0079c2; font-weight: 700; }
.required-star { color: #f56c6c; padding-right: 4px; font-weight: 700; }
.mutual-desc .el-descriptions-item__label { width: 150px; color: #303133; background: #fafafa; }
.mutual-desc .el-descriptions-item__content { min-width: 220px; }
.mutual-desc .el-form-item { margin-bottom: 0; }
.mutual-desc .el-form-item__error { padding-top: 2px; }
.mutual-desc .el-input-number { width: 100%; }
.actions { text-align: right; padding-top: 18px; }
</style>
<div id="app" v-cloak class="mutual-apply-page">
<div class="mutual-apply-card">
<div class="section-title">申请信息</div>
<el-form :model="formData" ref="form" :rules="formRules" label-width="0" v-loading="loading">
<el-descriptions class="mutual-desc" :column="3" border>
<el-descriptions-item>
<template slot="label"><span class="required-star">*</span>工号</template>
<el-form-item prop="loginName">
<el-input v-model="formData.loginName" readonly placeholder="请输入工号"></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label"><span class="required-star">*</span>姓名</template>
<el-form-item prop="userName">
<el-input v-model="formData.userName" readonly placeholder="请输入姓名"></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">性别</template>
<el-form-item prop="sex">
<el-select v-model="formData.sex" disabled placeholder="请选择性别" style="width: 100%">
<el-option label="男" value="男"></el-option>
<el-option label="女" value="女"></el-option>
</el-select>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">出生年月</template>
<el-form-item prop="birthday">
<el-date-picker v-model="formData.birthday" disabled value-format="yyyy-MM-dd" type="date" placeholder="请选择出生年月" style="width: 100%"></el-date-picker>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">籍贯</template>
<el-form-item prop="nativePlace">
<el-input v-model="formData.nativePlace" readonly placeholder="请输入籍贯"></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">工作部门(系部)</template>
<el-form-item prop="deptName">
<el-input v-model="formData.deptName" readonly placeholder="请输入工作部门(系部)"></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">学历</template>
<el-form-item prop="education">
<el-input v-model="formData.education" readonly placeholder="请输入学历"></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">职称</template>
<el-form-item prop="professionalTitle">
<el-input v-model="formData.professionalTitle" readonly placeholder="请输入职称"></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label"><span class="required-star">*</span>身份证号码</template>
<el-form-item prop="idCard">
<el-input v-model="formData.idCard" placeholder="请输入身份证号码"></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label"><span class="required-star">*</span>联系电话</template>
<el-form-item prop="mobile">
<el-input v-model="formData.mobile" placeholder="请输入联系电话"></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label"><span class="required-star">*</span>工资银行卡号</template>
<el-form-item prop="bankCardNo">
<el-input v-model="formData.bankCardNo" placeholder="请输入工资银行卡号"></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label"><span class="required-star">*</span>开户行</template>
<el-form-item prop="bankName">
<el-input v-model="formData.bankName" placeholder="请输入开户行"></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item :span="3">
<template slot="label"><span class="required-star">*</span>大病名称</template>
<el-form-item prop="diseaseTypeId">
<el-select v-model="formData.diseaseTypeId" filterable placeholder="请选择大病名称" style="width: 100%" @change="handleDiseaseChange">
<el-option v-for="item in diseaseTypes" :key="item.id" :label="item.diseaseName" :value="item.id"></el-option>
</el-select>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item :span="3">
<template slot="label"><span class="required-star">*</span>何时何病住何医院</template>
<el-form-item prop="hospitalDesc">
<el-input type="textarea" :rows="7" show-word-limit v-model="formData.hospitalDesc" placeholder="请详细说明何时何病住何医院"></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label"><span class="required-star">*</span>全年医疗费用总金额(元)</template>
<el-form-item prop="medicalTotalAmount">
<el-input-number v-model="formData.medicalTotalAmount" :min="0" :precision="2" :step="100" controls-position="right"></el-input-number>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label"><span class="required-star">*</span>自付金额(元)</template>
<el-form-item prop="selfPayAmount">
<el-input-number v-model="formData.selfPayAmount" :min="0" :precision="2" :step="100" controls-position="right"></el-input-number>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item></el-descriptions-item>
<el-descriptions-item :span="3">
<template slot="label"><span class="required-star">*</span>申请人签字</template>
<el-form-item prop="signFileId">
<pc-signature v-model="formData.signFileId"></pc-signature>
</el-form-item>
</el-descriptions-item>
</el-descriptions>
</el-form>
<div class="actions">
<el-button v-if="isFundMember" type="primary" @click="submitApply">提交申请</el-button>
</div>
</div>
</div>
<script nonce="${cspNonce!}">
new Vue({
el: "#app",
store,
data() {
return {
loading: false,
diseaseTypes: [],
isFundMember: false,
apiBase: "${base!}/platform/medicalMutualAid/apply/form",
minePath: "${base!}/platform/medicalMutualAid/apply/mine",
formRules: {
loginName: [{required: true, message: "必填", trigger: ["change", "blur"]}],
userName: [{required: true, message: "必填", trigger: ["change", "blur"]}],
idCard: [{required: true, message: "必填", trigger: ["change", "blur"]}],
mobile: [{required: true, message: "必填", trigger: ["change", "blur"]}],
bankCardNo: [{required: true, message: "必填", trigger: ["change", "blur"]}],
bankName: [{required: true, message: "必填", trigger: ["change", "blur"]}],
diseaseTypeId: [{required: true, message: "必填", trigger: ["change", "blur"]}],
hospitalDesc: [{required: true, message: "必填", trigger: ["change", "blur"]}],
medicalTotalAmount: [{required: true, message: "必填", trigger: ["change", "blur"]}],
selfPayAmount: [{required: true, message: "必填", trigger: ["change", "blur"]}],
signFileId: [{required: true, message: "请签字", trigger: ["change", "blur"]}]
},
formData: {
status: 0,
medicalTotalAmount: 0,
selfPayAmount: 0
}
}
},
methods: {
// 初始化新增申请页面:疾病类型由后端提供,当前登录用户信息从 Vuex store 回填。
initPage() {
this.fillCurrentUser()
this.loading = true
this.$axios.post(this.apiBase + "/initApplyData").then((resp) => {
if (resp.code === 0) {
this.diseaseTypes = resp.data.diseaseTypes || []
this.isFundMember = resp.data.isFundMember === true
if (!this.isFundMember) {
this.$message.warning("本次补助申请仅面向基金会员开放,您目前还不是会员!")
}
}
}).finally(() => {
this.loading = false
})
},
// 使用平台全局 store 中的当前登录用户信息回填申请人基础字段。
fillCurrentUser() {
const user = this.$store.state.user || {}
const unit = user.unit || {}
const joinDate = user.memberJoinTime || user.joinDate || user.joinTime || ""
this.formData = Object.assign({}, this.formData, {
userId: user.id,
loginName: user.loginname,
userName: user.username,
sex: user.sex,
birthday: user.birthday,
nativePlace: user.nativePlace || user.nativeplace || "",
deptId: unit.id,
deptName: unit.name,
joinDate: joinDate,
joinYear: this.getYear(joinDate),
education: user.education || user.educationName || "",
professionalTitle: user.professionalTitle || user.title || user.positionTitle || "",
idCard: user.idCard || user.idcard || "",
mobile: user.mobile
})
},
// 从入会时间中提取年份,字段不展示但随 formData 一起提交给后台保存。
getYear(value) {
if (!value) {
return null
}
const text = String(value)
const year = Number(text.substring(0, 4))
return isNaN(year) ? null : year
},
// 选择疾病类型时同步疾病名称,避免后续档案改名影响历史申请展示。
handleDiseaseChange(value) {
const item = this.diseaseTypes.find((disease) => disease.id === value)
this.formData.diseaseName = item ? item.diseaseName : ""
},
// 暂存申请,不改变审核状态。
saveDraft() {
this.$refs.form.validate((valid) => {
if (valid) {
this.$axios.post(this.apiBase + "/saveDraft", this.formData).then((resp) => {
if (resp.code === 0) {
this.formData = resp.data
this.$message.success(resp.msg)
}
})
}
})
},
// 提交申请,状态仍为未审核,等待互助会审核。
submitApply() {
if (!this.isFundMember) {
this.$message.warning("本次补助申请仅面向基金会员开放,您目前还不是会员!")
return
}
this.$refs.form.validate((valid) => {
if (valid) {
this.$axios.post(this.apiBase + "/submitApply", this.formData).then((resp) => {
if (resp.code === 0) {
this.formData = resp.data
this.$message.success(resp.msg)
window.location.href = this.minePath
}
})
}
})
},
// 返回我的申请列表,查看模式和编辑完成后共用该入口。
goMine() {
window.location.href = this.minePath
}
},
created() {
this.initPage()
}
})
</script>
<!--#
}
#-->
@@ -0,0 +1,281 @@
<!--#
layout("/layouts/platform.html"){
#-->
<style>
.mine-form-page { background: #f5f7fa; height: calc(100vh - 88px); display: flex; flex-direction: column; overflow: hidden; box-sizing: border-box; }
.back-bar { background: #fff; padding: 14px 24px; margin-bottom: 12px; border-bottom: 1px solid #ebeef5; flex-shrink: 0; }
.back-bar .el-button { padding: 0; font-size: 15px; }
.mine-form-card { flex: 1; min-height: 0; height: auto !important; overflow: hidden; }
.mine-form-card .card-body { flex: 1; min-height: 0; overflow-y: auto; box-sizing: border-box; }
.mine-form-card .el-card__footer { flex-shrink: 0; }
.required-star { color: #f56c6c; padding-right: 4px; font-weight: 700; }
.mutual-desc .el-descriptions-item__label { width: 150px; color: #303133; background: #fafafa; }
.mutual-desc .el-descriptions-item__content { min-width: 220px; }
.mutual-desc .el-form-item { margin-bottom: 0; }
.mutual-desc .el-form-item__error { padding-top: 2px; }
.mutual-desc .el-input-number { width: 100%; }
.signature-readonly { pointer-events: none; }
.actions { text-align: right; padding-top: 18px; }
</style>
<div id="app" v-cloak class="mine-form-page">
<div class="back-bar">
<el-button type="text" icon="el-icon-back" @click="goMine">返回</el-button>
</div>
<custom-card class="mine-form-card">
<el-form :model="formData" ref="form" :rules="formRules" label-width="0" v-loading="loading">
<el-descriptions class="mutual-desc" :column="3" border>
<el-descriptions-item>
<template slot="label"><span class="required-star">*</span>工号</template>
<el-form-item prop="loginName">
<el-input v-model="formData.loginName" readonly placeholder="请输入工号"></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label"><span class="required-star">*</span>姓名</template>
<el-form-item prop="userName">
<el-input v-model="formData.userName" readonly placeholder="请输入姓名"></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">性别</template>
<el-form-item prop="sex">
<el-select v-model="formData.sex" disabled placeholder="请选择性别" style="width: 100%">
<el-option label="男" value="男"></el-option>
<el-option label="女" value="女"></el-option>
</el-select>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">出生年月</template>
<el-form-item prop="birthday">
<el-date-picker v-model="formData.birthday" disabled value-format="yyyy-MM-dd" type="date" placeholder="请选择出生年月" style="width: 100%"></el-date-picker>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">籍贯</template>
<el-form-item prop="nativePlace">
<el-input v-model="formData.nativePlace" readonly placeholder="请输入籍贯"></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">工作部门(系部)</template>
<el-form-item prop="deptName">
<el-input v-model="formData.deptName" readonly placeholder="请输入工作部门(系部)"></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">学历</template>
<el-form-item prop="education">
<el-input v-model="formData.education" readonly placeholder="请输入学历"></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">职称</template>
<el-form-item prop="professionalTitle">
<el-input v-model="formData.professionalTitle" readonly placeholder="请输入职称"></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label"><span class="required-star">*</span>身份证号码</template>
<el-form-item prop="idCard">
<el-input v-model="formData.idCard" :readonly="readonlyMode" placeholder="请输入身份证号码"></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label"><span class="required-star">*</span>联系电话</template>
<el-form-item prop="mobile">
<el-input v-model="formData.mobile" :readonly="readonlyMode" placeholder="请输入联系电话"></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label"><span class="required-star">*</span>工资银行卡号</template>
<el-form-item prop="bankCardNo">
<el-input v-model="formData.bankCardNo" :readonly="readonlyMode" placeholder="请输入工资银行卡号"></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label"><span class="required-star">*</span>开户行</template>
<el-form-item prop="bankName">
<el-input v-model="formData.bankName" :readonly="readonlyMode" placeholder="请输入开户行"></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item :span="3">
<template slot="label"><span class="required-star">*</span>大病名称</template>
<el-form-item prop="diseaseTypeId">
<el-select v-model="formData.diseaseTypeId" filterable :disabled="readonlyMode" placeholder="请选择大病名称" style="width: 100%" @change="handleDiseaseChange">
<el-option v-for="item in diseaseTypes" :key="item.id" :label="item.diseaseName" :value="item.id"></el-option>
</el-select>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item :span="3">
<template slot="label"><span class="required-star">*</span>何时何病住何医院</template>
<el-form-item prop="hospitalDesc">
<el-input type="textarea" :rows="7" show-word-limit v-model="formData.hospitalDesc" :readonly="readonlyMode" placeholder="请详细说明何时何病住何医院"></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label"><span class="required-star">*</span>全年医疗费用总金额(元)</template>
<el-form-item prop="medicalTotalAmount">
<el-input-number v-model="formData.medicalTotalAmount" :disabled="readonlyMode" :min="0" :precision="2" :step="100" controls-position="right"></el-input-number>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label"><span class="required-star">*</span>自付金额(元)</template>
<el-form-item prop="selfPayAmount">
<el-input-number v-model="formData.selfPayAmount" :disabled="readonlyMode" :min="0" :precision="2" :step="100" controls-position="right"></el-input-number>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item></el-descriptions-item>
<el-descriptions-item :span="3">
<template slot="label"><span class="required-star">*</span>申请人签字</template>
<el-form-item prop="signFileId">
<div :class="{'signature-readonly': readonlyMode}">
<pc-signature v-model="formData.signFileId" :load-default="false"></pc-signature>
</div>
</el-form-item>
</el-descriptions-item>
</el-descriptions>
</el-form>
<template slot="footer" v-if="!readonlyMode">
<el-button @click="saveDraft">暂存</el-button>
<el-button type="primary" @click="submitApply">提交申请</el-button>
</template>
</custom-card>
</div>
<script nonce="${cspNonce!}">
new Vue({
el: "#app",
store,
data() {
return {
loading: false,
diseaseTypes: [],
applyId: "",
pageMode: "add",
apiBase: "${base!}/platform/medicalMutualAid/apply/mine/form",
minePath: "${base!}/platform/medicalMutualAid/apply/mine",
formRules: {
loginName: [{required: true, message: "必填", trigger: ["change", "blur"]}],
userName: [{required: true, message: "必填", trigger: ["change", "blur"]}],
idCard: [{required: true, message: "必填", trigger: ["change", "blur"]}],
mobile: [{required: true, message: "必填", trigger: ["change", "blur"]}],
bankCardNo: [{required: true, message: "必填", trigger: ["change", "blur"]}],
bankName: [{required: true, message: "必填", trigger: ["change", "blur"]}],
diseaseTypeId: [{required: true, message: "必填", trigger: ["change", "blur"]}],
hospitalDesc: [{required: true, message: "必填", trigger: ["change", "blur"]}],
medicalTotalAmount: [{required: true, message: "必填", trigger: ["change", "blur"]}],
selfPayAmount: [{required: true, message: "必填", trigger: ["change", "blur"]}],
signFileId: [{required: true, message: "请签字", trigger: ["change", "blur"]}]
},
formData: {
status: 0,
medicalTotalAmount: 0,
selfPayAmount: 0
}
}
},
computed: {
// 查看模式下页面只负责展示历史申请,所有表单控件统一禁止操作。
readonlyMode() {
return this.pageMode === "view"
}
},
methods: {
// 初始化我的申请维护页面:必须携带申请ID,按模式控制查看或编辑。
initPage() {
this.applyId = this.getQueryValue("id")
this.pageMode = this.getQueryValue("mode") || "add"
if (!this.applyId) {
this.$message.error("申请ID不能为空")
this.goMine()
return
}
this.loading = true
this.$axios.post(this.apiBase + "/initApplyData").then((resp) => {
if (resp.code === 0) {
this.diseaseTypes = resp.data.diseaseTypes || []
if (this.applyId) {
return this.loadApplyDetail()
}
}
}).finally(() => {
if (!this.applyId) {
this.loading = false
}
})
},
// 读取 URL 查询参数,避免引入额外公共依赖。
getQueryValue(name) {
const query = window.location.search ? window.location.search.substring(1) : ""
const parts = query.split("&")
for (let i = 0; i < parts.length; i++) {
const item = parts[i].split("=")
if (decodeURIComponent(item[0] || "") === name) {
return decodeURIComponent(item[1] || "")
}
}
return ""
},
// 根据申请ID加载历史申请,供我的申请查看和编辑复用当前页面。
loadApplyDetail() {
return this.$axios.post(this.apiBase + "/detail", {id: this.applyId}).then((resp) => {
if (resp.code === 0) {
this.formData = resp.data || {}
}
}).finally(() => {
this.loading = false
})
},
// 选择疾病类型时同步疾病名称,避免后续档案改名影响历史申请展示。
handleDiseaseChange(value) {
const item = this.diseaseTypes.find((disease) => disease.id === value)
this.formData.diseaseName = item ? item.diseaseName : ""
},
// 暂存申请,不改变审核状态。
saveDraft() {
if (this.readonlyMode) {
return
}
this.$refs.form.validate((valid) => {
if (valid) {
this.$axios.post(this.apiBase + "/saveDraft", this.formData).then((resp) => {
if (resp.code === 0) {
this.formData = resp.data
this.$message.success(resp.msg)
}
})
}
})
},
// 提交申请,状态仍为未审核,等待互助会审核。
submitApply() {
if (this.readonlyMode) {
return
}
this.$refs.form.validate((valid) => {
if (valid) {
this.$axios.post(this.apiBase + "/submitApply", this.formData).then((resp) => {
if (resp.code === 0) {
this.formData = resp.data
this.$message.success(resp.msg)
commonUtil.pjaxPush(this.minePath)
}
})
}
})
},
// 返回我的申请列表,查看模式和编辑完成后共用该入口。
goMine() {
commonUtil.pjaxPush(this.minePath)
}
},
created() {
this.initPage()
}
})
</script>
<!--#
}
#-->
@@ -0,0 +1,214 @@
<!--#
layout("/layouts/platform.html"){
#-->
<style>
#sub-app-container-main-content { overflow: hidden; }
#sub-app-container-main-content-body { height: 100%; overflow: hidden; }
.mine-list-page { padding: 16px 20px; background: #f3f6fa; height: 100%; box-sizing: border-box; display: flex; flex-direction: column; overflow: hidden; }
.mine-search-panel { background: #fff; padding: 16px 42px; margin-bottom: 12px; box-sizing: border-box; flex-shrink: 0; }
.mine-search-panel .el-form-item { margin-bottom: 0; margin-right: 28px; }
.mine-content-panel { background: #fff; padding: 16px 20px; box-sizing: border-box; flex: 1; min-height: 0; display: flex; flex-direction: column; overflow: hidden; }
.mine-panel-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; flex-shrink: 0; }
.mine-section-title { border-left: 4px solid #0079c2; padding-left: 10px; color: #0079c2; font-size: 16px; font-weight: 700; line-height: 22px; }
.mine-table.el-table th { background: #fafafa; color: #303133; font-weight: 700; }
.mine-table.el-table td, .mine-table.el-table th { padding: 11px 0; }
.mine-actions { display: flex; align-items: center; justify-content: center; gap: 8px; }
.mine-actions .el-button { min-width: 54px; padding: 7px 12px; border-radius: 2px; margin-left: 0; }
.mine-actions .export-apply-btn { min-width: 86px; }
.mine-pager { display: flex; justify-content: flex-start; align-items: center; padding-top: 12px; flex-shrink: 0; }
.mine-pager .total-text { margin-right: 18px; color: #303133; }
</style>
<div id="app" v-cloak class="mine-list-page">
<div class="mine-search-panel">
<el-form :inline="true" :model="searchForm">
<el-form-item label="大病名称">
<el-select v-model="searchForm.diseaseName" clearable filterable placeholder="请选择大病名称" style="width: 260px">
<el-option v-for="item in diseaseTypes" :key="item.id" :label="item.diseaseName" :value="item.diseaseName"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="search">搜索</el-button>
<el-button @click="resetSearch">重置</el-button>
</el-form-item>
</el-form>
</div>
<div class="mine-content-panel" ref="contentPanel">
<div class="mine-panel-header" ref="panelHeader">
<div class="mine-section-title">我的申请</div>
<el-button type="primary" size="small" @click="onAdd">
<i class="ti-plus"></i>
互助金申请
</el-button>
</div>
<el-table ref="mineTable" class="mine-table" :data="tableData" border v-loading="loading" :height="tableHeight" style="width: 100%">
<el-table-column type="index" label="序号" width="70" align="center"></el-table-column>
<el-table-column prop="loginName" label="工号" width="130" align="center"></el-table-column>
<el-table-column prop="userName" label="姓名" width="120" align="center"></el-table-column>
<el-table-column prop="diseaseName" label="大病名称" min-width="300" align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="全年医疗费用总金额(元)" width="190" align="center">
<template slot-scope="{row}">{{ formatMoney(row.medicalTotalAmount) }}</template>
</el-table-column>
<el-table-column label="自费金额(元)" width="160" align="center">
<template slot-scope="{row}">{{ formatMoney(row.selfPayAmount) }}</template>
</el-table-column>
<el-table-column label="状态" width="100" align="center">
<template slot-scope="{row}">
<el-tag v-if="isAudited(row)" type="success" size="mini">已审核</el-tag>
<el-tag v-else-if="isDiscarded(row)" type="danger" size="mini">已作废</el-tag>
<el-tag v-else type="warning" size="mini">未审核</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="310" align="center" fixed="right">
<template slot-scope="{row}">
<div class="mine-actions">
<el-button @click="onView(row)" size="mini" type="primary">查看</el-button>
<el-button v-if="isAudited(row)" class="export-apply-btn" @click="onExportApplyForm(row)" size="mini" type="primary">导出申请单</el-button>
<el-button v-if="isPending(row)" @click="onEdit(row)" size="mini" type="primary">编辑</el-button>
<el-button v-if="isPending(row)" @click="onDeleteTip" size="mini" type="danger">删除</el-button>
</div>
</template>
</el-table-column>
</el-table>
<div class="mine-pager" ref="pager">
<span class="total-text">共 {{ totalCount }} 条</span>
<el-pagination background layout="sizes, prev, pager, next" :current-page.sync="pageForm.pageNumber" :page-size.sync="pageForm.pageSize" :page-sizes="[5, 10, 20, 30, 50]" :total="totalCount" @current-change="loadPage" @size-change="loadPage"></el-pagination>
</div>
</div>
</div>
<script nonce="${cspNonce!}">
new Vue({
el: "#app",
store,
data() {
return {
loading: false,
tableData: [],
diseaseTypes: [],
totalCount: 0,
tableHeight: 360,
layoutTimer: null,
apiBase: "${base!}/platform/medicalMutualAid/apply/mine",
diseaseTypeApi: "${base!}/platform/medicalMutualAid/medical/diseaseType",
searchForm: { diseaseName: "" },
pageForm: { pageNumber: 1, pageSize: 10 }
}
},
methods: {
// 加载互助疾病类型下拉,和互助金申请页面保持同一疾病基础数据来源。
loadDiseaseTypes() {
this.$axios.post(this.diseaseTypeApi + "/pageData", { pageNumber: 1, pageSize: 999 }).then((resp) => {
if (resp.code === 0) {
this.diseaseTypes = resp.data.list || []
}
})
},
// 查询我的申请列表,使用独立分页结构保证列表高度不受通用组件影响。
loadPage() {
this.loading = true
const params = Object.assign({}, this.pageForm, this.searchForm)
this.$axios.post(this.apiBase + "/page", params).then((resp) => {
if (resp.code === 0) {
this.tableData = resp.data.list || []
this.totalCount = resp.data.totalCount || 0
}
}).finally(() => {
this.loading = false
this.scheduleTableLayout()
})
},
search() {
this.pageForm.pageNumber = 1
this.loadPage()
},
resetSearch() {
this.searchForm = { diseaseName: "" }
this.pageForm.pageNumber = 1
this.loadPage()
},
// 新增申请按平台通用页面跳转方式进入申请页。
onAdd() {
commonUtil.pjaxPush("${base!}/platform/medicalMutualAid/apply/form")
},
// 查看申请进入我的申请独立维护页,查看模式由 mode 参数控制。
onView(row) {
commonUtil.pjaxPush("${base!}/platform/medicalMutualAid/apply/mine/form?id=" + row.id + "&mode=view")
},
// 编辑申请进入我的申请独立维护页,编辑模式由 mode 参数控制。
onEdit(row) {
commonUtil.pjaxPush("${base!}/platform/medicalMutualAid/apply/mine/form?id=" + row.id + "&mode=edit")
},
// 按当前行申请ID导出申请单,后端通过系统模板管理中的 Word 模板完成参数渲染。
onExportApplyForm(row) {
window.open(this.apiBase + "/exportApplyForm?id=" + row.id)
},
onDeleteTip() {
this.$message.info("删除逻辑后续接入")
},
// 未审核申请允许个人继续编辑和删除。
isPending(row) {
return Number(row.status) === 0
},
// 已审核申请只允许查看,不能再编辑或删除。
isAudited(row) {
return Number(row.status) === 1
},
// 已作废申请只允许查看,不能再编辑或删除。
isDiscarded(row) {
return Number(row.status) === 2
},
formatMoney(value) {
if (value === null || value === undefined || value === "") {
return ""
}
return Number(value).toLocaleString("zh-CN", { minimumFractionDigits: 2, maximumFractionDigits: 2 })
},
// 根据内容面板剩余空间计算表格高度,保证列表铺满且页面不出现纵向滚动条。
updateTableHeight() {
const panel = this.$refs.contentPanel
if (!panel) {
return
}
const headerHeight = this.$refs.panelHeader ? this.$refs.panelHeader.offsetHeight : 0
const pagerHeight = this.$refs.pager ? this.$refs.pager.offsetHeight : 0
this.tableHeight = Math.max(panel.clientHeight - headerHeight - pagerHeight - 32, 260)
this.$nextTick(() => {
if (this.$refs.mineTable) {
this.$refs.mineTable.doLayout()
}
})
},
// 数据加载和窗口变化后延迟重算,避开菜单/顶部栏刷新时机导致的首屏高度误差。
scheduleTableLayout() {
if (this.layoutTimer) {
clearTimeout(this.layoutTimer)
}
this.$nextTick(() => {
this.updateTableHeight()
const vm = this
this.layoutTimer = setTimeout(function () {
vm.updateTableHeight()
}, 80)
})
}
},
created() {
this.loadDiseaseTypes()
this.loadPage()
},
mounted() {
this.scheduleTableLayout()
window.addEventListener("resize", this.scheduleTableLayout)
},
beforeDestroy() {
if (this.layoutTimer) {
clearTimeout(this.layoutTimer)
}
window.removeEventListener("resize", this.scheduleTableLayout)
}
})
</script>
<!--#
}
#-->
@@ -0,0 +1,200 @@
<!--#
layout("/layouts/platform.html"){
#-->
<style>
.mutual-list-page { padding: 16px 20px; background: #f3f6fa; height: calc(100vh - 88px); box-sizing: border-box; display: flex; flex-direction: column; overflow: hidden; }
.search-panel { background: #fff; padding: 16px 42px; margin-bottom: 12px; box-sizing: border-box; flex-shrink: 0; }
.search-panel .el-form-item { margin-bottom: 0; margin-right: 14px; }
.content-panel { background: #fff; padding: 16px 20px 16px; box-sizing: border-box; flex: 1; min-height: 0; display: flex; flex-direction: column; }
.panel-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; }
.section-title { border-left: 4px solid #0079c2; padding-left: 10px; color: #0079c2; font-size: 16px; font-weight: 700; line-height: 22px; }
.mutual-table.el-table th { background: #fafafa; color: #303133; font-weight: 700; }
.mutual-table.el-table td, .mutual-table.el-table th { padding: 11px 0; }
.pager { display: flex; justify-content: flex-start; align-items: center; padding-top: 12px; flex-shrink: 0; }
.pager .total-text { margin-right: 18px; color: #303133; }
</style>
<div id="app" v-cloak class="mutual-list-page">
<div class="search-panel">
<el-form :inline="true" :model="searchForm">
<el-form-item label="所在单位">
<el-input v-model="searchForm.deptName" placeholder="请输入所在单位" style="width: 240px"></el-input>
</el-form-item>
<el-form-item label="姓名">
<el-input v-model="searchForm.userName" placeholder="请输入姓名" style="width: 220px"></el-input>
</el-form-item>
<el-form-item label="加入年份">
<el-date-picker v-model="searchForm.joinYear" type="year" value-format="yyyy" placeholder="请选择加入年份" style="width: 220px"></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadPage">查询</el-button>
<el-button @click="resetSearch">重置</el-button>
<el-button icon="el-icon-upload2" @click="exportTip">导出</el-button>
</el-form-item>
</el-form>
</div>
<div class="content-panel" ref="contentPanel">
<div class="panel-header" ref="panelHeader">
<div class="section-title">医疗互助汇总</div>
</div>
<el-table ref="summaryTable" class="mutual-table" :data="tableData" border v-loading="loading" :height="tableHeight" style="width: 100%">
<el-table-column type="index" label="序号" width="70" align="center"></el-table-column>
<el-table-column prop="deptName" label="所在单位" min-width="140" align="center"></el-table-column>
<el-table-column prop="userName" label="姓名" width="110" align="center"></el-table-column>
<el-table-column prop="joinYear" label="加入时间(年)" width="120" align="center"></el-table-column>
<el-table-column prop="diseaseText" label="大病名称及病情" min-width="190" align="center"></el-table-column>
<el-table-column label="符合规定发票金额(元)" width="160" align="center">
<template slot-scope="{row}">{{ formatMoney(row.medicalTotalAmount) }}</template>
</el-table-column>
<el-table-column label="补助计算方法" align="center">
<el-table-column v-for="item in subsidyRanges" :key="item" :label="item" min-width="130" align="center">
<template slot-scope="{row}">{{ formatMoney(row.subsidyMap[item]) }}</template>
</el-table-column>
<el-table-column label="补助小计" width="120" align="center">
<template slot-scope="{row}">{{ formatMoney(row.subsidyTotalAmount) }}</template>
</el-table-column>
</el-table-column>
<el-table-column label="补助比例" width="100" align="center">
<template slot-scope="{row}">{{ row.bonusRatio || "" }}</template>
</el-table-column>
<el-table-column label="实际补助金额(元)" width="150" align="center">
<template slot-scope="{row}">{{ formatMoney(row.actualSubsidyAmount) }}</template>
</el-table-column>
<el-table-column prop="remark" label="备注" min-width="120" align="center"></el-table-column>
</el-table>
<div class="pager" ref="pager">
<span class="total-text">共 {{ totalCount }} 条</span>
<el-pagination background layout="sizes, prev, pager, next" :current-page.sync="pageForm.pageNumber" :page-size.sync="pageForm.pageSize" :total="totalCount" @current-change="loadPage" @size-change="loadPage"></el-pagination>
</div>
</div>
</div>
<script nonce="${cspNonce!}">
new Vue({
el: "#app",
data() {
return {
loading: false,
tableData: [],
subsidyRanges: [],
totalCount: 0,
tableHeight: 360,
apiBase: "${base!}/platform/medicalMutualAid/apply/summary",
searchForm: { deptName: "", userName: "", joinYear: "" },
pageForm: { pageNumber: 1, pageSize: 10 }
}
},
methods: {
// 查询审核通过的互助金申请,并把补助明细转换成动态列数据。
loadPage() {
this.loading = true
const params = Object.assign({}, this.pageForm, this.searchForm)
this.$axios.post(this.apiBase + "/page", params).then((resp) => {
if (resp.code === 0) {
const rows = resp.data.list || []
this.tableData = rows.map((item) => this.buildRow(item))
this.totalCount = resp.data.totalCount || 0
this.refreshRanges()
}
}).finally(() => {
this.loading = false
this.scheduleTableLayout()
})
},
// 将后端主表与补助明细组合为列表展示行。
buildRow(item) {
const apply = item.apply || {}
const subsidies = item.subsidies || []
const subsidyMap = {}
subsidies.forEach((subsidy) => {
subsidyMap[subsidy.rangeName] = subsidy.subsidyAmount
})
return {
deptName: apply.deptName,
userName: apply.userName,
joinYear: apply.joinYear,
diseaseText: apply.diseaseName,
medicalTotalAmount: apply.medicalTotalAmount,
subsidyTotalAmount: apply.subsidyTotalAmount,
bonusRatio: apply.bonusRatio,
actualSubsidyAmount: apply.actualSubsidyAmount,
remark: apply.remark,
subsidies: subsidies,
subsidyMap: subsidyMap
}
},
// 根据当前页明细刷新动态补助区间表头。
refreshRanges() {
const names = []
this.tableData.forEach((row) => {
row.subsidies.forEach((item) => {
if (names.indexOf(item.rangeName) === -1) {
names.push(item.rangeName)
}
})
})
this.subsidyRanges = names
this.scheduleTableLayout()
},
resetSearch() {
this.searchForm = { deptName: "", userName: "", joinYear: "" }
this.pageForm.pageNumber = 1
this.loadPage()
},
exportTip() {
const params = []
if (this.searchForm.deptName) {
params.push("deptName=" + encodeURIComponent(this.searchForm.deptName))
}
if (this.searchForm.userName) {
params.push("userName=" + encodeURIComponent(this.searchForm.userName))
}
if (this.searchForm.joinYear) {
params.push("joinYear=" + encodeURIComponent(this.searchForm.joinYear))
}
window.open(this.apiBase + "/exportExcel" + (params.length ? "?" + params.join("&") : ""))
},
formatMoney(value) {
if (value === undefined || value === null || value === "") {
return ""
}
const num = Number(value || 0)
return num.toLocaleString("zh-CN", { minimumFractionDigits: 2, maximumFractionDigits: 2 })
},
// 根据内容面板剩余空间计算表格高度,汇总动态列只影响横向滚动,不影响页面纵向高度。
updateTableHeight() {
const panel = this.$refs.contentPanel
if (!panel) {
return
}
const headerHeight = this.$refs.panelHeader ? this.$refs.panelHeader.offsetHeight : 0
const pagerHeight = this.$refs.pager ? this.$refs.pager.offsetHeight : 0
this.tableHeight = Math.max(panel.clientHeight - headerHeight - pagerHeight - 32, 220)
this.$nextTick(() => {
if (this.$refs.summaryTable) {
this.$refs.summaryTable.doLayout()
}
})
},
// F5 刷新时动态二级表头渲染和外层布局完成顺序不稳定,延迟触发表格重排保证列宽不乱。
scheduleTableLayout() {
this.$nextTick(() => {
this.updateTableHeight()
const vm = this
setTimeout(function () {
vm.updateTableHeight()
}, 80)
})
}
},
created() {
this.loadPage()
},
mounted() {
this.scheduleTableLayout()
window.addEventListener("resize", this.scheduleTableLayout)
},
beforeDestroy() {
window.removeEventListener("resize", this.scheduleTableLayout)
}
})
</script>
<!--#}#-->
@@ -0,0 +1,710 @@
<!--#
layout("/layouts/platform.html"){
#-->
<style>
.mutual-config-page {
padding: 16px 20px;
background: #f5f7fa;
height: calc(100vh - 88px);
box-sizing: border-box;
display: flex;
flex-direction: column;
overflow: hidden;
}
.mutual-config-page .search-panel,
.mutual-config-page .list-panel {
background: #fff;
border-radius: 2px;
padding: 16px 20px;
box-sizing: border-box;
}
.mutual-config-page .search-panel {
margin-bottom: 12px;
flex-shrink: 0;
}
.mutual-config-page .list-panel {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
}
.mutual-config-page .list-title {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 12px;
flex-shrink: 0;
}
.mutual-config-page .list-title-text {
border-left: 4px solid #0079c2;
padding-left: 10px;
color: #0079c2;
font-size: 16px;
font-weight: 700;
}
.mutual-config-page .pager-bar {
display: flex;
align-items: center;
padding-top: 12px;
flex-shrink: 0;
}
.el-dialog.mutual-edit-dialog {
margin-top: 3vh !important;
}
.mutual-edit-dialog .el-dialog__body {
height: 700px;
overflow: hidden;
padding: 16px 20px;
}
.mutual-config-page .config-dialog-body {
height: 100%;
overflow: hidden;
box-sizing: border-box;
}
.mutual-config-page .config-dialog-body .el-tabs {
display: flex;
flex-direction: column;
height: 100%;
}
.mutual-config-page .config-dialog-body .el-tabs__header {
flex: none;
margin-bottom: 0;
}
.mutual-config-page .config-dialog-body .el-tabs__content {
flex: 1;
overflow: hidden;
padding-top: 16px;
}
.mutual-config-page .config-dialog-body .el-tab-pane {
height: 100%;
overflow-y: auto;
overflow-x: hidden;
padding-right: 10px;
box-sizing: border-box;
}
.mutual-config-page .dialog-section {
border-top: 1px dashed #dcdfe6;
padding-top: 18px;
margin-top: 18px;
max-width: 100%;
overflow-x: hidden;
box-sizing: border-box;
}
.mutual-config-page .dialog-section:first-child {
border-top: 0;
padding-top: 0;
margin-top: 0;
}
.mutual-config-page .dialog-section-title {
border-left: 4px solid #0079c2;
padding-left: 10px;
margin-bottom: 16px;
color: #0079c2;
font-size: 15px;
font-weight: 700;
}
.mutual-config-page .section-toolbar {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 10px;
}
.mutual-config-page .config-dialog-body .el-form-item__label {
white-space: normal;
line-height: 20px;
padding-top: 0;
}
.mutual-config-page .config-dialog-body .aid-limit-label.is-required .el-form-item__label:before {
content: "";
margin-right: 0;
}
.mutual-config-page .config-dialog-body .aid-limit-label-text {
display: inline-block;
line-height: 20px;
text-align: right;
vertical-align: middle;
}
.mutual-config-page .config-dialog-body .aid-limit-label-star {
color: #F56C6C;
margin-right: 4px;
}
.mutual-config-page .config-dialog-body .el-table {
max-width: 100%;
}
.mutual-config-page .config-dialog-body .el-table__body-wrapper {
overflow-x: hidden;
}
.mutual-config-page .dialog-footer {
text-align: right;
}
</style>
<div id="app" v-cloak class="mutual-config-page">
<div class="search-panel">
<el-form :inline="true" :model="searchForm" label-width="70px">
<el-form-item label="年度">
<el-date-picker v-model="searchForm.year" type="year" value-format="yyyy" format="yyyy" clearable placeholder="请选择年度" style="width: 220px"></el-date-picker>
</el-form-item>
<el-form-item label="配置名称">
<el-input v-model="searchForm.configName" clearable placeholder="请输入配置名称" style="width: 260px"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="loadConfigList">搜索</el-button>
<el-button @click="resetSearch">重置</el-button>
</el-form-item>
</el-form>
</div>
<div class="list-panel" ref="listPanel" v-loading="loading">
<div class="list-title" ref="listTitle">
<span class="list-title-text">医疗互助配置</span>
<el-button type="primary" icon="el-icon-plus" @click="openConfigDialog()">新增</el-button>
</div>
<el-table :data="pageList" border :height="tableHeight" style="width: 100%">
<el-table-column type="index" width="70" label="序号" align="center"></el-table-column>
<el-table-column prop="year" label="年度" width="110" align="center"></el-table-column>
<el-table-column prop="configName" label="配置名称" min-width="260" align="center"></el-table-column>
<el-table-column label="起补金额(元)" width="150" align="center">
<template slot-scope="{row}">{{ formatMoney(row.startAmount) }}</template>
</el-table-column>
<el-table-column label="第一年最高补助(元)" width="170" align="center">
<template slot-scope="{row}">{{ formatMoney(row.firstYearMaxAmount) }}</template>
</el-table-column>
<el-table-column label="其他年度最高补助(元)" width="190" align="center">
<template slot-scope="{row}">{{ formatMoney(row.otherYearMaxAmount) }}</template>
</el-table-column>
<el-table-column label="是否启用" width="110" align="center">
<template slot-scope="{row}">
<el-tag v-if="row.status === 1" type="success" size="mini">启用</el-tag>
<el-tag v-else type="info" size="mini">停用</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="170" align="center">
<template slot-scope="{row}">
<el-button type="primary" size="mini" @click="openConfigDialog(row)">编辑</el-button>
<el-button type="danger" size="mini" @click="deleteConfig(row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="pager-bar" ref="pagerBar">
<span style="margin-right: 14px">共 {{ configList.length }} 条</span>
<el-pagination
background
layout="sizes, prev, pager, next"
:current-page.sync="pageNumber"
:page-size.sync="pageSize"
:page-sizes="[10, 20, 50]"
:total="configList.length">
</el-pagination>
</div>
</div>
<el-dialog :title="configDialogTitle" :visible.sync="configDialogVisible" width="1320px" custom-class="mutual-edit-dialog" :close-on-click-modal="false">
<div class="config-dialog-body" v-loading="dialogLoading">
<el-tabs v-model="activeTab" type="card">
<el-tab-pane label="互助金补助配置" name="subsidy">
<div class="dialog-section">
<div class="dialog-section-title">基本信息</div>
<el-form :model="configForm" :rules="configRules" ref="configFormRef" label-width="160px">
<el-row :gutter="28">
<el-col :span="12">
<el-form-item label="年度" prop="year">
<el-date-picker v-model="configForm.year" type="year" value-format="yyyy" format="yyyy" placeholder="请选择年度" style="width: 100%"></el-date-picker>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="配置名称" prop="configName">
<el-input v-model="configForm.configName" maxlength="100" placeholder="请输入配置名称"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="28">
<el-col :span="12">
<el-form-item label="起补金额(元)" prop="startAmount">
<el-input-number v-model="configForm.startAmount" :min="0" :precision="2" :step="1000" controls-position="right" style="width: 100%"></el-input-number>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="是否启用" prop="status">
<el-radio-group v-model="configForm.status">
<el-radio :label="1">启用</el-radio>
<el-radio :label="0">停用</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="28">
<el-col :span="12">
<el-form-item class="aid-limit-label" prop="firstYearMaxAmount">
<template slot="label">
<span class="aid-limit-label-text"><span class="aid-limit-label-star">*</span>第一年最高补助<br>(元)</span>
</template>
<el-input-number v-model="configForm.firstYearMaxAmount" :min="0" :precision="2" :step="1000" controls-position="right" style="width: 100%"></el-input-number>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item class="aid-limit-label" prop="otherYearMaxAmount">
<template slot="label">
<span class="aid-limit-label-text"><span class="aid-limit-label-star">*</span>其他年度最高补助<br>(元)</span>
</template>
<el-input-number v-model="configForm.otherYearMaxAmount" :min="0" :precision="2" :step="1000" controls-position="right" style="width: 100%"></el-input-number>
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
<div class="dialog-section">
<div class="section-toolbar">
<div class="dialog-section-title" style="margin-bottom: 0">补助比例配置(超额累计计算)</div>
<el-button type="primary" size="small" icon="el-icon-plus" @click="addRatioRow">新增比例</el-button>
</div>
<el-table :data="ratioList" border style="width: 100%">
<el-table-column type="index" width="70" label="序号" align="center"></el-table-column>
<el-table-column label="开始金额(元)" align="center">
<template slot-scope="{row}">
<el-input-number v-model="row.startAmount" :min="0" :precision="2" :step="1000" controls-position="right" style="width: 100%"></el-input-number>
</template>
</el-table-column>
<el-table-column label="结束金额(元)" align="center">
<template slot-scope="{row}">
<el-input-number v-model="row.endAmount" :min="0" :precision="2" :step="1000" controls-position="right" style="width: 100%"></el-input-number>
</template>
</el-table-column>
<el-table-column label="补助比例(%)" align="center">
<template slot-scope="{row}">
<el-input-number v-model="row.ratio" :min="0" :max="100" :precision="2" :step="1" controls-position="right" style="width: 100%"></el-input-number>
</template>
</el-table-column>
<el-table-column prop="remark" label="说明" min-width="220" align="center">
<template slot-scope="{row}">
<el-input v-model="row.remark" maxlength="200" placeholder="请输入说明"></el-input>
</template>
</el-table-column>
<el-table-column label="排序" width="120" align="center">
<template slot-scope="{row}">
<el-input-number v-model="row.sort" :min="1" :step="1" controls-position="right" style="width: 100%"></el-input-number>
</template>
</el-table-column>
<el-table-column label="操作" width="90" align="center">
<template slot-scope="scope">
<el-button type="danger" size="mini" @click="removeRatio(scope.row, scope.$index)">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="dialog-section">
<div class="section-toolbar">
<div class="dialog-section-title" style="margin-bottom: 0">连续未申请奖励配置</div>
<el-button type="primary" size="small" icon="el-icon-plus" @click="addBonusRow">新增奖励</el-button>
</div>
<el-table :data="bonusList" border style="width: 100%">
<el-table-column type="index" width="70" label="序号" align="center"></el-table-column>
<el-table-column prop="continuousYear" label="连续未申请年数(年)" align="center">
<template slot-scope="{row}">
<el-input-number v-model="row.continuousYear" :min="1" :step="1" controls-position="right" style="width: 100%"></el-input-number>
</template>
</el-table-column>
<el-table-column label="增加比例(%)" align="center">
<template slot-scope="{row}">
<el-input-number v-model="row.increaseRatio" :min="0" :max="100" :precision="2" :step="1" controls-position="right" style="width: 100%"></el-input-number>
</template>
</el-table-column>
<el-table-column prop="remark" label="说明" min-width="260" align="center">
<template slot-scope="{row}">
<el-input v-model="row.remark" maxlength="200" placeholder="请输入说明"></el-input>
</template>
</el-table-column>
<el-table-column label="操作" width="90" align="center">
<template slot-scope="scope">
<el-button type="danger" size="mini" @click="removeBonus(scope.row, scope.$index)">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
</el-tab-pane>
<el-tab-pane label="会员缴费配置" name="member">
<div class="dialog-section">
<div class="dialog-section-title">会员缴费配置</div>
<el-form :model="memberForm" :rules="memberRules" ref="memberFormRef" label-width="190px" style="max-width: 760px">
<el-form-item label="年度" prop="year">
<el-date-picker :value="memberForm.year" type="year" value-format="yyyy" format="yyyy" placeholder="请选择年度" style="width: 320px" @input="$set(memberForm,'year',$event)"></el-date-picker>
</el-form-item>
<el-form-item label="每月缴纳互助金额(元)" prop="monthAmount">
<el-input-number v-model="memberForm.monthAmount" :min="0" :precision="2" :step="1" controls-position="right" style="width: 320px"></el-input-number>
</el-form-item>
<el-form-item label="缴费减半年限" prop="halfPriceYear">
<el-input-number v-model="memberForm.halfPriceYear" :min="1" :step="1" controls-position="right" style="width: 320px"></el-input-number>
<span style="margin-left: 10px; color: #909399">自加入或最近一次医疗互助申请年份起满该年限后,次年缴费减半</span>
</el-form-item>
</el-form>
</div>
</el-tab-pane>
</el-tabs>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="configDialogVisible = false">取消</el-button>
<el-button type="primary" :loading="dialogSaving" @click="confirmConfigDialog">确认</el-button>
</span>
</el-dialog>
</div>
<script nonce="${cspNonce!}">
new Vue({
el: "#app",
data() {
return {
loading: false,
dialogLoading: false,
dialogSaving: false,
searchForm: {
year: null,
configName: ""
},
configList: [],
pageNumber: 1,
pageSize: 10,
tableHeight: 360,
configDialogVisible: false,
configDialogTitle: "新增医疗互助配置",
activeTab: "subsidy",
configForm: {},
memberForm: {},
ratioList: [],
bonusList: [],
deletedRatioIds: [],
deletedBonusIds: [],
configRules: {
year: [{ required: true, message: "请选择年度", trigger: "change" }],
configName: [{ required: true, message: "请输入配置名称", trigger: "blur" }],
startAmount: [{ required: true, message: "请输入起补金额", trigger: "change" }],
firstYearMaxAmount: [{ required: true, message: "请输入第一年最高补助", trigger: "change" }],
otherYearMaxAmount: [{ required: true, message: "请输入其他年度最高补助", trigger: "change" }],
status: [{ required: true, message: "请选择状态", trigger: "change" }]
},
memberRules: {
year: [{ required: true, message: "请选择年度", trigger: "change" }],
monthAmount: [{ required: true, message: "请输入每月缴纳互助金额", trigger: "change" }],
halfPriceYear: [{ required: true, message: "请输入缴费减半年限", trigger: "change" }]
}
}
},
computed: {
pageList() {
const start = (this.pageNumber - 1) * this.pageSize
return this.configList.slice(start, start + this.pageSize)
}
},
methods: {
// 查询年度配置列表,每年一条配置,供页面主列表展示和检索。
loadConfigList() {
this.loading = true
const params = this.copy(this.searchForm)
params.year = this.toYearNumber(params.year)
this.$axios.post(loc() + "/listConfigs", params).then((resp) => {
if (resp.code === 0) {
this.configList = resp.data || []
this.pageNumber = 1
}
}).finally(() => {
this.loading = false
this.$nextTick(this.updateTableHeight)
})
},
// 重置列表查询条件,并重新加载全部年度配置。
resetSearch() {
this.searchForm = {
year: null,
configName: ""
}
this.loadConfigList()
},
// 打开新增或编辑弹窗,编辑时加载该年度完整配置,新增时使用当前年度默认值。
openConfigDialog(row) {
this.configDialogTitle = row ? "编辑医疗互助配置" : "新增医疗互助配置"
this.activeTab = "subsidy"
this.deletedRatioIds = []
this.deletedBonusIds = []
this.configDialogVisible = true
if (!row) {
this.resetDialogForm()
return
}
this.dialogLoading = true
const year = row ? row.year : new Date().getFullYear()
this.$axios.post(loc() + "/configData", { year: year }).then((resp) => {
if (resp.code === 0) {
this.configForm = resp.data.config || this.defaultConfig(year)
this.configForm.year = this.toYearString(this.configForm.year)
const memberForm = resp.data.memberConfig || this.defaultMemberConfig(year)
this.$set(memberForm, "year", this.toYearString(memberForm.year || year))
this.$set(this, "memberForm", memberForm)
this.ratioList = resp.data.ratios || []
this.bonusList = resp.data.bonuses || []
}
}).finally(() => {
this.dialogLoading = false
this.$nextTick(() => {
this.$refs.configFormRef && this.$refs.configFormRef.clearValidate()
this.$refs.memberFormRef && this.$refs.memberFormRef.clearValidate()
})
})
},
// 新增弹窗初始化,仅保留年度、配置名称、启用状态和会员缴费配置默认值。
resetDialogForm() {
const year = new Date().getFullYear()
this.dialogLoading = false
this.configForm = this.defaultConfig(year)
this.$set(this, "memberForm", this.defaultMemberConfig(year))
this.ratioList = []
this.bonusList = []
this.$nextTick(() => {
this.$refs.configFormRef && this.$refs.configFormRef.clearValidate()
this.$refs.memberFormRef && this.$refs.memberFormRef.clearValidate()
})
},
// 删除年度主配置会同步删除后端已关联的比例和奖励配置。
deleteConfig(id) {
this.$confirm("确定删除此年度医疗互助配置吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
this.$axios.post(loc() + "/deleteConfig", { id: id }).then((resp) => {
if (resp.code === 0) {
this.$message.success(resp.msg)
this.loadConfigList()
}
})
}).catch(() => {})
},
// 弹窗确认时统一保存两个页签数据,避免每个页签出现独立保存按钮。
confirmConfigDialog() {
this.$refs.configFormRef.validate((configValid) => {
if (!configValid) {
this.activeTab = "subsidy"
return
}
this.$refs.memberFormRef.validate((memberValid) => {
if (!memberValid) {
this.activeTab = "member"
return
}
if (!this.validateDetailRows()) {
this.activeTab = "subsidy"
return
}
this.saveDialogData()
})
})
},
// 先保存基础配置拿到主键,再保存会员缴费、比例和奖励明细。
saveDialogData() {
this.dialogSaving = true
this.$axios.post(loc() + "/saveConfig", this.buildConfigPayload()).then((resp) => {
if (resp.code !== 0) {
return Promise.reject(resp)
}
const savedConfig = resp.data
const tasks = [this.$axios.post(loc() + "/saveMemberConfig", this.buildMemberConfigPayload())]
this.deletedRatioIds.forEach((id) => {
tasks.push(this.$axios.post(loc() + "/deleteRatio", { id: id }))
})
this.deletedBonusIds.forEach((id) => {
tasks.push(this.$axios.post(loc() + "/deleteBonus", { id: id }))
})
this.ratioList.forEach((row) => {
const payload = this.cleanPayload(row)
payload.configId = savedConfig.id
tasks.push(this.$axios.post(loc() + "/saveRatio", payload))
})
this.bonusList.forEach((row) => {
const payload = this.cleanPayload(row)
payload.configId = savedConfig.id
tasks.push(this.$axios.post(loc() + "/saveBonus", payload))
})
return Promise.all(tasks).then((results) => {
for (let index = 0; index < results.length; index++) {
if (results[index].code !== 0) {
return Promise.reject(results[index])
}
}
return results
})
}).then(() => {
this.$message.success("保存成功")
this.configDialogVisible = false
this.loadConfigList()
}).catch((resp) => {
if (resp && resp.msg) {
this.$message.error(resp.msg)
}
}).finally(() => {
this.dialogSaving = false
})
},
// 新增比例时直接追加可编辑行,主弹窗确认前仅保存在前端列表中。
addRatioRow() {
this.ratioList.push({
startAmount: null,
endAmount: null,
ratio: null,
sort: null,
remark: ""
})
},
// 从弹窗内存列表移除比例,已有主键的记录延迟到主弹窗确认时删除。
removeRatio(row, index) {
if (row.id) {
this.deletedRatioIds.push(row.id)
}
this.ratioList.splice(index, 1)
},
// 新增奖励时直接追加可编辑行,主弹窗确认前仅保存在前端列表中。
addBonusRow() {
this.bonusList.push({
continuousYear: null,
increaseRatio: null,
remark: ""
})
},
// 从弹窗内存列表移除奖励,已有主键的记录延迟到主弹窗确认时删除。
removeBonus(row, index) {
if (row.id) {
this.deletedBonusIds.push(row.id)
}
this.bonusList.splice(index, 1)
},
// 统一保存前校验行内明细,避免新增空行或缺少关键字段的数据提交到后台。
validateDetailRows() {
for (let index = 0; index < this.ratioList.length; index++) {
const row = this.ratioList[index]
if (row.startAmount === null || row.startAmount === undefined || row.ratio === null || row.ratio === undefined || row.sort === null || row.sort === undefined) {
this.$message.error("请完善第 " + (index + 1) + " 行补助比例配置")
return false
}
}
for (let index = 0; index < this.bonusList.length; index++) {
const row = this.bonusList[index]
if (row.continuousYear === null || row.continuousYear === undefined || row.increaseRatio === null || row.increaseRatio === undefined) {
this.$message.error("请完善第 " + (index + 1) + " 行奖励配置")
return false
}
}
return true
},
// 新增弹窗默认基础配置,仅设置用户要求保留默认值的基础字段。
defaultConfig(year) {
return {
year: this.toYearString(year),
configName: year + "年医疗互助补助配置",
status: 1
}
},
// 年度选择器使用字符串回显,提交接口前再转为数字,避免影响后端年度字段类型。
toYearString(year) {
if (year === null || year === undefined || year === "") {
return null
}
return String(year)
},
// 后端年度字段为整型,查询和保存时统一在此处转换。
toYearNumber(year) {
if (year === null || year === undefined || year === "") {
return null
}
return Number(year)
},
// 基础配置保存前清理年度类型,保持接口参数和实体字段一致。
buildConfigPayload() {
const payload = this.copy(this.configForm)
payload.year = this.toYearNumber(payload.year)
return payload
},
// 会员缴费配置按年度维护,新增配置时默认使用当前年度。
defaultMemberConfig(year) {
return {
year: this.toYearString(year),
monthAmount: 0,
halfPriceYear: 1
}
},
// 后端年度字段为整型,保存会员缴费配置前统一转换。
buildMemberConfigPayload() {
const payload = this.copy(this.memberForm)
payload.year = this.toYearNumber(payload.year)
return payload
},
// 清理前端临时字段,避免弹窗编辑辅助字段传到后端实体。
cleanPayload(row) {
const payload = this.copy(row)
delete payload._editIndex
return payload
},
// 金额展示统一保留两位小数,并添加千分位分隔。
formatMoney(value) {
if (value === null || value === undefined || value === "") {
return ""
}
return Number(value).toLocaleString("zh-CN", {
minimumFractionDigits: 2,
maximumFractionDigits: 2
})
},
// 复制行数据,避免弹窗编辑时直接污染列表原数据。
copy(row) {
return JSON.parse(JSON.stringify(row))
},
// 根据列表面板剩余空间计算主表格高度,避免页面外层出现滚动条。
updateTableHeight() {
const panel = this.$refs.listPanel
if (!panel) {
return
}
const titleHeight = this.$refs.listTitle ? this.$refs.listTitle.offsetHeight : 0
const pagerHeight = this.$refs.pagerBar ? this.$refs.pagerBar.offsetHeight : 0
this.tableHeight = Math.max(panel.clientHeight - titleHeight - pagerHeight - 32, 220)
}
},
created() {
this.loadConfigList()
},
mounted() {
this.$nextTick(this.updateTableHeight)
window.addEventListener("resize", this.updateTableHeight)
},
beforeDestroy() {
window.removeEventListener("resize", this.updateTableHeight)
}
})
</script>
<!--#
}
#-->
@@ -0,0 +1,131 @@
<!--#
layout("/layouts/platform.html"){
#-->
<div id="app" v-cloak>
<guava ref="guava">
<el-card shadow="never">
<search @search="doSearch">
<search-item label="疾病种类">
<el-input placeholder="请输入疾病种类" clearable v-model="pageForm.searchKeyword"
@keyup.enter.native="doSearch"></el-input>
</search-item>
</search>
</el-card>
<el-card shadow="never">
<table-tool label="疾病种类">
<el-button @click="openAdd" icon="el-icon-plus" size="small" type="primary">
新增疾病种类
</el-button>
</table-tool>
<el-table :data="tableData" :size="tableSize" @sort-change="pageOrder"
ref="table" row-key="id" style="width: 100%" v-loading="tableLoading">
<el-table-column label="编号" prop="diseaseCode"></el-table-column>
<el-table-column label="疾病种类" prop="diseaseName"></el-table-column>
<el-table-column label="是否重大疾病" prop="isMajorDiseases">
<template v-slot="{row}">
<el-tag v-if="row.isMajorDiseases" type="danger">是</el-tag>
<el-tag v-else>否</el-tag>
</template>
</el-table-column>
<el-table-column fixed="right" label="操作" width="180px">
<template slot-scope="{row}">
<el-button @click="openEdit(row)" size="mini" type="primary">编辑</el-button>
<el-button @click="doDelete(row.id)" size="mini" type="danger">删除</el-button>
</template>
</el-table-column>
</el-table>
<!--#include("/layouts/pagination.html"){}#-->
</el-card>
</guava>
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="50%">
<el-form :model="formData" :rules="rules" label-width="120px" ref="form">
<el-form-item label="疾病名称" prop="diseaseName">
<el-input maxlength="50" show-word-limit v-model="formData.diseaseName"></el-input>
</el-form-item>
<el-form-item label="编号" prop="diseaseCode">
<el-input maxlength="20" v-model="formData.diseaseCode"></el-input>
</el-form-item>
<el-form-item label="是否重大疾病" prop="isMajorDiseases">
<el-radio-group v-model="formData.isMajorDiseases" size="small">
<el-radio :label="true" border>是</el-radio>
<el-radio :label="false" border>否</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<el-row justify="end" type="flex">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button @click="doSubmit" class="ml10" type="primary">确定</el-button>
</el-row>
</el-dialog>
</div>
<script nonce="${cspNonce!}">
new Vue({
el: "#app",
mixins: [initTableMixins],
data() {
return {
dialogVisible: false,
dialogTitle: "新增疾病种类",
formData: {
isMajorDiseases: true
},
rules: {
diseaseName: [{ required: true, message: "请输入疾病名称", trigger: ["change", "blur"] }],
diseaseCode: [{ required: true, message: "请输入疾病编号", trigger: ["change", "blur"] }]
}
}
},
methods: {
// 删除前要求用户二次确认,避免误删疾病基础数据。
doDelete(id) {
this.$confirm("您确定要删除吗?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
}).then(() => {
this.$axios.post(loc() + "/doDelete", { id: id }).then((resp) => {
if (resp.code === 0) {
this.$message.success(resp.msg)
this.pageData()
}
})
}).catch(() => {})
},
// 打开编辑弹窗时复制行数据,防止未保存操作直接修改列表。
openEdit(row) {
this.formData = clone(row)
this.dialogTitle = "编辑疾病种类"
this.dialogVisible = true
},
// 表单校验通过后提交,成功时关闭弹窗并刷新列表。
doSubmit() {
this.$refs.form.validate((valid) => {
if (valid) {
this.$axios.post(loc() + "/doSubmit", this.formData).then((resp) => {
if (resp.code === 0) {
this.$message.success(resp.msg)
this.dialogVisible = false
this.pageData()
}
})
}
})
},
// 新增时重置默认值,确保重大疾病选项默认选中“是”。
openAdd() {
this.formData = { isMajorDiseases: true }
this.dialogTitle = "新增疾病种类"
this.dialogVisible = true
}
},
created() {
this.pageData()
}
})
</script>
<!--#
}
#-->
@@ -0,0 +1,26 @@
<!--#
layout("/layouts/platform_h5.html"){
#-->
<div id="app">
<van-nav-bar title="分工会审核" left-text="返回" left-arrow fixed placeholder @click-left="historyBack"></van-nav-bar>
<van-sticky offset-top="46px">
<van-search v-model="pageForm.searchKeyword" placeholder="请输入姓名或工号" @search="doSearch"></van-search>
<van-tabs v-model="pageForm.approvalText" @change="changeApproval">
<van-tab title="已审核" name="1"></van-tab><van-tab title="未审核" name="0"></van-tab>
</van-tabs>
</van-sticky>
<table-list ref="tableListRef" api="/platform/fundMember/branchUnionApproval/pageData" :page_form.sync="pageForm" @ready="doSearch">
<template v-slot="{row}"><table-column label="工号">{{row.loginName}}</table-column><table-column label="姓名">{{row.userName}}</table-column><table-column label="会员类别">{{row.aidFundMemberType}}</table-column><table-column label="当前节点">{{row.curTaskName}}</table-column></template>
<template #actions="{row}"><div class="action-btn" @click="view(row)"><i class="fa fa-eye"></i><span>查看</span></div><div class="action-btn" v-if="row.taskState===10" @click="audit(row)"><i class="fa fa-check"></i><span>审核</span></div><div class="action-btn delete" v-if="row.canRevoke" @click="onRevoke(row)"><i class="fa fa-undo"></i><span>撤回</span></div></template>
</table-list>
<h5-fund-member-info ref="infoRef"><div v-if="formData.processTaskId" class="p10"><van-form ref="formRef"><van-field :value="formData.tf_opinion" label="审批意见" type="textarea" required placeholder="请输入审批意见" :rules="[{required:true,message:'请填写审批意见'}]" @input="$set(formData,'tf_opinion',$event)"></van-field></van-form><div class="audit-actions"><van-button block type="info" @click="handleTaskAction(6)">退回</van-button><van-button block type="danger" @click="handleTaskAction(2)">拒绝</van-button><van-button block type="primary" @click="handleTaskAction(1)">同意</van-button></div></div></h5-fund-member-info>
</div>
<style>.audit-actions{display:flex;column-gap:10px;padding-top:10px}.audit-actions .van-button{flex:1}</style>
<script nonce="${cspNonce!}">
<!--#include('../mine/info.js'){}#-->
new Vue({el:'#app',store,components:{'h5-fund-member-info':H5_FUND_MEMBER_INFO},data(){return {pageForm:{pageNumber:1,pageSize:10,totalCount:0,searchKeyword:'',approval:false,approvalText:'0'},formData:{}}},methods:{changeApproval(value){this.doSearch(value==='1')},doSearch(approval){if(approval!==undefined){this.$set(this.pageForm,'approval',approval)}this.$set(this.pageForm,'pageNumber',1);this.$set(this.pageForm,'totalCount',0);this.$nextTick(()=>this.$refs.tableListRef.doSearch())},view(row){this.$set(this,'formData',{});this.$refs.infoRef.onOpen(row)},audit(row){this.$set(this,'formData',{processTaskId:row.taskId,taskName:row.curTaskName,tf_opinion:''});this.$refs.infoRef.onOpen(row)},onRevoke(row){this.$dialog.confirm({title:'提示',message:'您确定要撤回吗?'}).then(()=>this.$axios.post('/flow/common/revokeTask',{taskId:row.taskId}).then(res=>{if(res.code===0){this.$toast.success(res.msg||'撤回成功');this.doSearch()}}))},// 与 PC 端保持一致:6 为退回、2 为拒绝、1 为同意。
handleTaskAction(submitType){this.$refs.formRef.validate().then(()=>this.$dialog.confirm({title:'提示',message:'您确定要提交吗?'}).then(()=>this.$axios.post('/flow/common/executeTask',{data:JSON.stringify(Object.assign({},this.formData,{submitType:submitType}))}).then(res=>{if(res.code===0){this.$toast.success(res.msg||'提交成功');this.$refs.infoRef.onClose();this.doSearch()}}))).catch(()=>{})}}})
</script>
<!--#
}
#-->
@@ -0,0 +1,8 @@
<!--#
layout("/layouts/platform_h5.html"){
#-->
<div id="app" v-cloak><van-nav-bar title="我的基金会员申请" left-text="返回" left-arrow fixed placeholder @click-left="historyBack"></van-nav-bar><van-sticky offset-top="46px"><van-dropdown-menu :close-on-click-outside="false" :close-on-click-overlay="false"><year-van-dropdown-item :num="5" @change="doSearch" v-model="pageForm.year"></year-van-dropdown-item></van-dropdown-menu></van-sticky><table-list ref="tableListRef" api="/platform/fundMember/mine/pageData" :page_form.sync="pageForm" @ready="doSearch"><template v-slot="{row}"><table-column label="工号">{{row.loginName}}</table-column><table-column label="姓名">{{row.userName}}</table-column><table-column label="会员类别">{{row.aidFundMemberType}}</table-column><table-column label="申请时间">{{row.submitTime}}</table-column><table-column label="当前节点">{{row.taskName}}</table-column></template><template #actions="{row}"><div class="action-btn" @click="onView(row)"><i class="fa fa-eye"></i><span>查看</span></div><div class="action-btn" v-if="row.taskKey === 'startTask' || !row.instanceId" @click="onEdit(row)"><i class="fa fa-edit"></i><span>编辑</span></div><div class="action-btn delete" v-if="row.canRevoke" @click="onRevoke(row)"><i class="fa fa-undo"></i><span>撤回</span></div></template></table-list><h5-fund-member-info ref="infoRef"></h5-fund-member-info></div>
<script nonce="${cspNonce!}"><!--#include('info.js'){}#-->new Vue({el:'#app',store,components:{'h5-fund-member-info':H5_FUND_MEMBER_INFO},data(){return {pageForm:{pageNumber:1,pageSize:10,totalCount:0,year:new Date().getFullYear()}}},methods:{doSearch(year){if(year!==undefined){this.$set(this.pageForm,'year',year)}this.$set(this.pageForm,'pageNumber',1);this.$set(this.pageForm,'totalCount',0);this.$nextTick(()=>this.$refs.tableListRef.doSearch())},onView(row){this.$refs.infoRef.onOpen(row)},onEdit(row){this.$pjaxReplace('/platform/fundMember/write/h5?bizId='+row.id+'&taskId='+(row.startTaskId||''))},onRevoke(row){this.$dialog.confirm({title:'提示',message:'您确定要撤回吗?'}).then(()=>this.$axios.post('/flow/common/revokeTask',{taskId:row.startTaskId}).then(res=>{if(res.code===0){this.$toast.success('撤回成功');this.doSearch()}}))}}})</script>
<!--#
}
#-->
@@ -0,0 +1,42 @@
const H5_FUND_MEMBER_INFO = {
template: [
'<van-action-sheet v-model="visible" title="基金会员申请详情"><div class="detail-container">',
'<van-cell-group title="申请信息">',
'<van-cell title="姓名" :value="viewData.userName || \'-\'"></van-cell>',
'<van-cell title="工号" :value="viewData.loginName || \'-\'"></van-cell>',
'<van-cell title="出生年月" :value="formatDate(viewData.birthday)"></van-cell>',
'<van-cell title="性别" :value="viewData.sex || \'-\'"></van-cell>',
'<van-cell title="籍贯" :value="viewData.nativePlace || \'-\'"></van-cell>',
'<van-cell title="家庭住址" :value="viewData.address || \'-\'"></van-cell>',
'<van-cell title="健康状况" :value="viewData.healthStatus || \'-\'"></van-cell>',
'<van-cell title="学历" :value="viewData.education || \'-\'"></van-cell>',
'<van-cell title="联系电话" :value="viewData.mobile || \'-\'"></van-cell>',
'<van-cell title="入职时间" :value="formatDate(viewData.joinWorkTime)"></van-cell>',
'<van-cell title="身份证号码" :value="viewData.idCard || \'-\'"></van-cell>',
'<van-cell title="会员类别" :value="viewData.aidFundMemberType || \'-\'"></van-cell>',
'<van-cell title="退休时间" :value="viewData.retireTime || \'-\'"></van-cell>',
'<van-cell title="家庭主要成员" class="column-cell"><table><tr><th>序号</th><th>姓名</th><th>年龄</th><th>与本人关系</th></tr><tr v-for="(item,index) in viewData.families || []" :key="index"><td style="text-align:center">{{item.sequence || index + 1}}</td><td style="text-align:center">{{item.name}}</td><td style="text-align:center">{{item.age}}</td><td style="text-align:center">{{item.relation}}</td></tr></table></van-cell>',
'<van-cell title="备注" :value="viewData.remark || \'-\'"></van-cell>',
'</van-cell-group>',
'<div v-for="task in doneTasks" :key="task.id">',
'<div class="process-title">{{ task.displayName }}</div>',
'<van-cell-group v-if="task.ext && task.ext.isFirstTaskNode">',
'<van-cell title="申请用户" :value="(task.ext.initiatorName || \'-\') + (task.ext.initiatorAccount ? \'(\' + task.ext.initiatorAccount + \')\' : \'\')"></van-cell>',
'<van-cell title="申请时间" :value="task.finishTime || \'-\'"></van-cell>',
'<van-cell title="办理结果"><dict-tag :options="dict.type.PROCESS_TASK_SUBMIT_TYPE" :value="task.ext.submitType"></dict-tag></van-cell>',
'</van-cell-group>',
'<van-cell-group v-else>',
'<van-cell title="办理用户" :value="((task.taskFormData && task.taskFormData.userName) || \'-\') + ((task.taskFormData && task.taskFormData.loginName) ? \'(\' + task.taskFormData.loginName + \')\' : \'\')"></van-cell>',
'<van-cell title="办理时间" :value="task.finishTime || \'-\'"></van-cell>',
'<van-cell title="办理结果"><dict-tag :options="dict.type.PROCESS_TASK_SUBMIT_TYPE" :value="task.ext && task.ext.submitType"></dict-tag></van-cell>',
'<van-cell title="办理意见" class="column-cell"><template #label>{{ (task.taskFormData && (task.taskFormData.opinion || task.taskFormData.tf_opinion)) || \'-\' }}</template></van-cell>',
'</van-cell-group>',
'</div>',
'</div><slot></slot></van-action-sheet>'
].join(''),
dicts: ["PROCESS_TASK_SUBMIT_TYPE"],
data(){return {visible:false,row:{},viewData:{},doneTasks:[]}},
methods:{// 查看页面统一显示年月日,空值沿用“-”占位。
formatDate(value){return value?this.$moment(value).format('YYYY-MM-DD'):'-'},onOpen(row){this.$set(this,'row',row);this.$set(this,'visible',true);this.getInfo();this.getDoneTasks()},onClose(){this.$set(this,'visible',false)},getInfo(){this.$axios.post('/platform/fundMember/write/info',{id:this.row.id}).then(res=>{if(res.code===0){this.$set(this,'viewData',res.data||{})}})},getDoneTasks(){this.$axios.post('/flow/common/doneTasks',{bizId:this.row.id}).then(res=>{if(res.code===0){this.$set(this,'doneTasks',res.data||[])}})}},
style: '.column-cell{flex-direction:column}.column-cell table{width:100%;border-collapse:collapse;table-layout:fixed;margin-top:8px}.column-cell table th,.column-cell table td{border:1px solid #ddd;padding:6px;text-align:center;vertical-align:middle}'
}
@@ -0,0 +1,25 @@
<!--#
layout("/layouts/platform_h5.html"){
#-->
<div id="app">
<van-nav-bar title="离退休审核" left-text="返回" left-arrow fixed placeholder @click-left="historyBack"></van-nav-bar>
<van-sticky offset-top="46px">
<van-search v-model="pageForm.searchKeyword" placeholder="请输入姓名或工号" @search="doSearch"></van-search>
<van-tabs v-model="pageForm.approvalText" @change="changeApproval">
<van-tab title="已审核" name="1"></van-tab><van-tab title="未审核" name="0"></van-tab>
</van-tabs>
</van-sticky>
<table-list ref="tableListRef" api="/platform/fundMember/retiredApproval/pageData" :page_form.sync="pageForm" @ready="doSearch">
<template v-slot="{row}"><table-column label="工号">{{row.loginName}}</table-column><table-column label="姓名">{{row.userName}}</table-column><table-column label="会员类别">{{row.aidFundMemberType}}</table-column><table-column label="当前节点">{{row.curTaskName}}</table-column></template>
<template #actions="{row}"><div class="action-btn" @click="view(row)"><i class="fa fa-eye"></i><span>查看</span></div><div class="action-btn" v-if="row.taskState===10" @click="audit(row)"><i class="fa fa-check"></i><span>审核</span></div><div class="action-btn delete" v-if="row.canRevoke" @click="onRevoke(row)"><i class="fa fa-undo"></i><span>撤回</span></div></template>
</table-list>
<h5-fund-member-info ref="infoRef"><div v-if="formData.processTaskId" class="p10"><van-form ref="formRef"><van-field :value="formData.tf_opinion" label="审批意见" type="textarea" required placeholder="请输入审批意见" :rules="[{required:true,message:'请填写审批意见'}]" @input="$set(formData,'tf_opinion',$event)"></van-field></van-form><div class="audit-actions"><van-button block type="info" @click="handleTaskAction(6)">退回</van-button><van-button block type="danger" @click="handleTaskAction(2)">拒绝</van-button><van-button block type="primary" @click="handleTaskAction(1)">同意</van-button></div></div></h5-fund-member-info>
</div>
<style>.audit-actions{display:flex;column-gap:10px;padding-top:10px}.audit-actions .van-button{flex:1}</style>
<script nonce="${cspNonce!}">
<!--#include('../mine/info.js'){}#-->
new Vue({el:'#app',store,components:{'h5-fund-member-info':H5_FUND_MEMBER_INFO},data(){return {pageForm:{pageNumber:1,pageSize:10,totalCount:0,searchKeyword:'',approval:false,approvalText:'0'},formData:{}}},methods:{changeApproval(value){this.doSearch(value==='1')},doSearch(approval){if(approval!==undefined){this.$set(this.pageForm,'approval',approval)}this.$set(this.pageForm,'pageNumber',1);this.$set(this.pageForm,'totalCount',0);this.$nextTick(()=>this.$refs.tableListRef.doSearch())},view(row){this.$set(this,'formData',{});this.$refs.infoRef.onOpen(row)},audit(row){this.$set(this,'formData',{processTaskId:row.taskId,taskName:row.curTaskName,tf_opinion:''});this.$refs.infoRef.onOpen(row)},onRevoke(row){this.$dialog.confirm({title:'提示',message:'您确定要撤回吗?'}).then(()=>this.$axios.post('/flow/common/revokeTask',{taskId:row.taskId}).then(res=>{if(res.code===0){this.$toast.success(res.msg||'撤回成功');this.doSearch()}}))},handleTaskAction(submitType){this.$refs.formRef.validate().then(()=>this.$dialog.confirm({title:'提示',message:'您确定要提交吗?'}).then(()=>this.$axios.post('/flow/common/executeTask',{data:JSON.stringify(Object.assign({},this.formData,{submitType:submitType}))}).then(res=>{if(res.code===0){this.$toast.success(res.msg||'提交成功');this.$refs.infoRef.onClose();this.doSearch()}}))).catch(()=>{})}}})
</script>
<!--#
}
#-->
@@ -0,0 +1,26 @@
<!--#
layout("/layouts/platform_h5.html"){
#-->
<div id="app">
<van-nav-bar title="校工会审核" left-text="返回" left-arrow fixed placeholder @click-left="historyBack"></van-nav-bar>
<van-sticky offset-top="46px">
<van-search v-model="pageForm.searchKeyword" placeholder="请输入姓名或工号" @search="doSearch"></van-search>
<van-tabs v-model="pageForm.approvalText" @change="changeApproval">
<van-tab title="已审核" name="1"></van-tab><van-tab title="未审核" name="0"></van-tab>
</van-tabs>
</van-sticky>
<table-list ref="tableListRef" api="/platform/fundMember/schoolUnionApproval/pageData" :page_form.sync="pageForm" @ready="doSearch">
<template v-slot="{row}"><table-column label="工号">{{row.loginName}}</table-column><table-column label="姓名">{{row.userName}}</table-column><table-column label="所属分工会">{{row.unionName}}</table-column><table-column label="会员类别">{{row.aidFundMemberType}}</table-column><table-column label="当前节点">{{row.curTaskName}}</table-column></template>
<template #actions="{row}"><div class="action-btn" @click="view(row)"><i class="fa fa-eye"></i><span>查看</span></div><div class="action-btn" v-if="row.taskState===10" @click="audit(row)"><i class="fa fa-check"></i><span>审核</span></div><div class="action-btn delete" v-if="row.canRevoke" @click="onRevoke(row)"><i class="fa fa-undo"></i><span>撤回</span></div></template>
</table-list>
<h5-fund-member-info ref="infoRef"><div v-if="formData.processTaskId" class="p10"><van-form ref="formRef"><van-field :value="formData.tf_opinion" label="审批意见" type="textarea" required placeholder="请输入审批意见" :rules="[{required:true,message:'请填写审批意见'}]" @input="$set(formData,'tf_opinion',$event)"></van-field></van-form><div class="audit-actions"><van-button block type="info" @click="handleTaskAction(6)">退回</van-button><van-button block type="danger" @click="handleTaskAction(2)">拒绝</van-button><van-button block type="primary" @click="handleTaskAction(1)">同意</van-button></div></div></h5-fund-member-info>
</div>
<style>.audit-actions{display:flex;column-gap:10px;padding-top:10px}.audit-actions .van-button{flex:1}</style>
<script nonce="${cspNonce!}">
<!--#include('../mine/info.js'){}#-->
new Vue({el:'#app',store,components:{'h5-fund-member-info':H5_FUND_MEMBER_INFO},data(){return {pageForm:{pageNumber:1,pageSize:10,totalCount:0,searchKeyword:'',year:new Date().getFullYear(),approval:false,approvalText:'0'},formData:{}}},methods:{changeApproval(value){this.doSearch(value==='1')},doSearch(approval){if(approval!==undefined){this.$set(this.pageForm,'approval',approval)}this.$set(this.pageForm,'pageNumber',1);this.$set(this.pageForm,'totalCount',0);this.$nextTick(()=>this.$refs.tableListRef.doSearch())},view(row){this.$set(this,'formData',{});this.$refs.infoRef.onOpen(row)},audit(row){this.$set(this,'formData',{processTaskId:row.taskId,taskName:row.curTaskName,tf_opinion:''});this.$refs.infoRef.onOpen(row)},onRevoke(row){this.$dialog.confirm({title:'提示',message:'您确定要撤回吗?'}).then(()=>this.$axios.post('/flow/common/revokeTask',{taskId:row.taskId}).then(res=>{if(res.code===0){this.$toast.success(res.msg||'撤回成功');this.doSearch()}}))},// 与 PC 端保持一致:6 为退回、2 为拒绝、1 为同意。
handleTaskAction(submitType){this.$refs.formRef.validate().then(()=>this.$dialog.confirm({title:'提示',message:'您确定要提交吗?'}).then(()=>this.$axios.post('/flow/common/executeTask',{data:JSON.stringify(Object.assign({},this.formData,{submitType:submitType}))}).then(res=>{if(res.code===0){this.$toast.success(res.msg||'提交成功');this.$refs.infoRef.onClose();this.doSearch()}}))).catch(()=>{})}}})
</script>
<!--#
}
#-->
@@ -0,0 +1,50 @@
<!--#
layout("/layouts/platform_h5.html"){
#-->
<div id="app" v-cloak>
<van-nav-bar title="基金会员入会申请" left-text="返回" left-arrow fixed placeholder @click-left="historyBack"></van-nav-bar>
<van-notice-bar v-if="isFundMember" wrapable color="#d46b08" background="#fff7e6">您已是基金会员,无需重复申请。</van-notice-bar>
<van-form ref="formRef" :show-error-message="false">
<van-cell-group title="基本信息">
<van-field v-model="formData.userName" label="姓名" readonly></van-field>
<van-field v-model="formData.loginName" label="工号" readonly></van-field>
<van-field v-model="formData.birthday" label="出生年月" readonly is-link required @click="showBirthday = true" :rules="[{required:true,message:'请选择出生年月'}]"></van-field>
<van-field v-model="formData.sex" label="性别" readonly is-link required @click="showSex = true" :rules="[{required:true,message:'请选择性别'}]"></van-field>
<van-field v-model="formData.nativePlace" label="籍贯" required placeholder="请输入籍贯" :rules="[{required:true,message:'请输入籍贯'}]"></van-field>
<van-field v-model="formData.address" label="家庭住址" required placeholder="请输入家庭住址" :rules="[{required:true,message:'请输入家庭住址'}]"></van-field>
<van-field v-model="formData.healthStatus" label="健康状况" required placeholder="请输入健康状况" :rules="[{required:true,message:'请输入健康状况'}]"></van-field>
<van-field v-model="formData.education" label="学历" readonly is-link required placeholder="请选择学历" @click="showEducation = true" :rules="[{required:true,message:'请选择学历'}]"></van-field>
<van-field v-model="formData.mobile" label="联系电话" required type="tel" maxlength="11" placeholder="请输入联系电话" :rules="[{required:true,message:'请输入手机号码'},{pattern:/^1[3456789]\d{9}$/,message:'请输入正确的手机号码'}]"></van-field>
<van-field v-model="formData.joinWorkTime" label="入职时间" readonly is-link required @click="showJoinWorkTime = true" :rules="[{required:true,message:'请选择入职时间'}]"></van-field>
<van-field v-model="formData.idCard" label="身份证号码" required maxlength="18" placeholder="请输入身份证号码" :rules="[{required:true,message:'请输入身份证号码'},{pattern:/^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[Xx\d]$/,message:'请输入正确的身份证号码'}]"></van-field>
<van-field v-model="formData.aidFundMemberType" label="会员类别" readonly is-link required placeholder="请选择会员类别" @click="showMemberType = true" :rules="[{required:true,message:'请选择会员类别'}]"></van-field>
<van-field v-model="formData.retireTime" label="退休时间" readonly is-link :required="formData.aidFundMemberType === '退休'" @click="showRetireTime = true"></van-field>
<!-- <van-field v-model="formData.homePhone" label="住宅号码"></van-field> -->
</van-cell-group>
<van-cell-group title="家庭主要成员">
<div v-for="(family, index) in formData.families" :key="index" class="family-item">
<div class="family-title">成员{{index + 1}} <van-button size="mini" type="danger" plain @click="removeFamily(index)">删除</van-button></div>
<van-field v-model="family.name" label="姓名" required placeholder="请输入姓名"></van-field>
<van-field v-model.number="family.age" label="年龄" required type="digit" placeholder="请输入年龄"></van-field>
<van-field v-model="family.relation" label="与本人关系" required placeholder="请输入与本人关系"></van-field>
</div>
<div class="p10"><van-button block plain type="primary" icon="plus" @click="addFamily">添加家庭成员</van-button></div>
</van-cell-group>
<van-cell-group title="备注"><van-field v-model="formData.remark" type="textarea" rows="3" maxlength="500" show-word-limit placeholder="请输入备注"></van-field></van-cell-group>
<div class="p10"><van-button v-if="!isFundMember" block type="primary" @click="submit">提交申请</van-button></div>
</van-form>
<van-popup v-model="showBirthday" position="bottom"><van-datetime-picker type="date" :min-date="dateMin" :max-date="dateMax" @confirm="setDate('birthday', $event)" @cancel="showBirthday=false"></van-datetime-picker></van-popup>
<van-popup v-model="showJoinWorkTime" position="bottom"><van-datetime-picker type="date" :min-date="dateMin" :max-date="dateMax" @confirm="setDate('joinWorkTime', $event)" @cancel="showJoinWorkTime=false"></van-datetime-picker></van-popup>
<van-popup v-model="showRetireTime" position="bottom"><van-datetime-picker type="date" :min-date="dateMin" :max-date="dateMax" @confirm="setDate('retireTime', $event)" @cancel="showRetireTime=false"></van-datetime-picker></van-popup>
<van-popup v-model="showSex" position="bottom"><van-picker show-toolbar :columns="['男','女']" @confirm="setPicker('sex', $event, 'showSex')" @cancel="showSex=false"></van-picker></van-popup>
<van-popup v-model="showEducation" position="bottom"><van-picker show-toolbar :columns="educationOptions" value-key="name" @confirm="setPicker('education', $event.name, 'showEducation')" @cancel="showEducation=false"></van-picker></van-popup>
<van-popup v-model="showMemberType" position="bottom"><van-picker show-toolbar :columns="memberTypeOptions" value-key="name" @confirm="setPicker('aidFundMemberType', $event.name, 'showMemberType')" @cancel="showMemberType=false"></van-picker></van-popup>
</div>
<script nonce="${cspNonce!}">
new Vue({el:'#app',store,data(){return {bizId:GetQueryString('bizId'),taskId:GetQueryString('taskId'),isFundMember:false,dateMin:new Date(1950,0,1),dateMax:new Date(),formData:{families:[]},educationOptions:[],memberTypeOptions:[],showBirthday:false,showJoinWorkTime:false,showRetireTime:false,showSex:false,showEducation:false,showMemberType:false}},methods:{// 统一处理接口返回的日期,避免表单回显到时分秒。
formatDate(value){return value?this.$moment(value).format('YYYY-MM-DD'):value},setDate(field,date){this.$set(this.formData,field,this.$moment(date).format('YYYY-MM-DD'));this.$set(this,'show' + field.charAt(0).toUpperCase() + field.slice(1),false)},setPicker(field,value,popup){this.$set(this.formData,field,value);this.$set(this,popup,false)},addFamily(){this.formData.families.push({sequence:this.formData.families.length+1,name:'',age:null,relation:''})},removeFamily(index){this.formData.families.splice(index,1);this.formData.families.forEach((item,i)=>this.$set(item,'sequence',i+1))},validateFamilies(){return this.formData.families.length>0&&this.formData.families.every(item=>item.name&&item.age!==null&&item.age!==''&&item.relation)},submit(){this.$refs.formRef.validate().then(()=>{if(this.formData.aidFundMemberType==='退休'&&!this.formData.retireTime){this.$toast.fail('会员类别为退休时,请选择退休时间');return}if(!this.validateFamilies()){this.$toast.fail('请完整填写至少一名家庭主要成员');return}this.$dialog.confirm({title:'提示',message:'您确定要提交申请吗?'}).then(()=>this.$axios.post(this.taskId?'/platform/fundMember/write/submitAgain':'/platform/fundMember/write/submit',{data:JSON.stringify(this.formData),taskId:this.taskId}).then(res=>{if(res.code===0){this.$toast.success('提交成功');this.$pjaxReplace('/platform/fundMember/mine/h5')}}))})},init(){if(this.bizId){this.$axios.post('/platform/fundMember/write/info',{id:this.bizId}).then(res=>{if(res.code===0){this.$set(this,'formData',res.data);this.$set(this.formData,'birthday',this.formatDate(this.formData.birthday));if(!Array.isArray(this.formData.families)){this.$set(this.formData,'families',[])}}})}else{const user=this.$store.state.user;this.$set(this,'formData',{userName:user.username,loginName:user.loginname,sex:user.sex,birthday:this.formatDate(user.birthday),mobile:user.mobile,idCard:user.idCard,unionId:user.union&&user.union.id,unionName:user.union&&user.union.name,userState:user.userState,education:user.education||'',nativePlace:'',healthStatus:'',remark:'',families:[]})}this.$businessTool.getDictOptions('USER_EDUCATION').then(data=>this.$set(this,'educationOptions',data));this.$businessTool.getDictOptions('USER_AID_FUND_MEMBER_TYPE').then(data=>this.$set(this,'memberTypeOptions',data));this.$axios.post('/platform/fundMember/write/memberStatus').then(res=>{if(res.code===0){this.$set(this,'isFundMember',Boolean(res.data))}})}},created(){this.init()}})
</script>
<style>.p10{padding:10px}.family-item{padding:10px;border-bottom:1px solid #eee}.family-title{display:flex;justify-content:space-between;align-items:center;font-weight:600}</style>
<!--#
}
#-->
@@ -0,0 +1,210 @@
<!--#
layout("/layouts/platform_h5.html"){
#-->
<style scoped>
.disease-type-page {
min-height: 100vh;
background: #f7f8fa;
}
.search-toolbar {
display: flex;
align-items: center;
padding: 8px 12px;
background: #fff;
}
.search-toolbar .van-search {
flex: 1;
padding: 0 8px 0 0;
}
.disease-card {
margin: 10px 12px;
padding: 14px;
background: #fff;
border-radius: 8px;
}
.disease-card__header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
}
.disease-card__name {
font-size: 16px;
font-weight: 600;
color: #323233;
}
.disease-card__code {
margin-bottom: 12px;
color: #969799;
}
.disease-card__actions {
display: flex;
justify-content: flex-end;
gap: 8px;
}
.popup-title {
padding: 18px 16px 8px;
font-size: 18px;
font-weight: 600;
text-align: center;
}
.popup-actions {
padding: 16px;
}
</style>
<div class="disease-type-page" id="app">
<van-nav-bar title="疾病种类" left-text="返回" left-arrow @click-left="historyBack" fixed placeholder></van-nav-bar>
<van-sticky offset-top="46px">
<div class="search-toolbar">
<van-search v-model="pageForm.searchKeyword" placeholder="请输入疾病种类"
@clear="doSearch" @search="doSearch"></van-search>
<van-button size="small" type="info" icon="plus" @click="openAdd">新增</van-button>
</div>
</van-sticky>
<van-pull-refresh v-model="refreshing" @refresh="doSearch">
<van-list v-model="loading" :finished="finished" finished-text="没有更多了" @load="loadData">
<div class="disease-card" v-for="row in list" :key="row.id">
<div class="disease-card__header">
<span class="disease-card__name">{{row.diseaseName}}</span>
<van-tag :type="row.isMajorDiseases ? 'danger' : 'primary'">
{{row.isMajorDiseases ? "重大疾病" : "普通疾病"}}
</van-tag>
</div>
<div class="disease-card__code">编号:{{row.diseaseCode}}</div>
<div class="disease-card__actions">
<van-button plain size="small" type="info" @click="openEdit(row)">编辑</van-button>
<van-button plain size="small" type="danger" @click="doDelete(row.id)">删除</van-button>
</div>
</div>
</van-list>
</van-pull-refresh>
<van-popup v-model="dialogVisible" position="bottom" round :style="{ height: '68%' }">
<div class="popup-title">{{dialogTitle}}</div>
<van-form ref="form" @submit="doSubmit">
<van-field v-model="formData.diseaseName" name="diseaseName" label="疾病名称"
maxlength="50" placeholder="请输入疾病名称"
:rules="[{ required: true, message: '请输入疾病名称' }]"></van-field>
<van-field v-model="formData.diseaseCode" name="diseaseCode" label="编号"
maxlength="20" placeholder="请输入疾病编号"
:rules="[{ required: true, message: '请输入疾病编号' }]"></van-field>
<van-field name="isMajorDiseases" label="重大疾病">
<template #input>
<van-switch v-model="formData.isMajorDiseases" size="22"></van-switch>
</template>
</van-field>
<div class="popup-actions">
<van-button block round type="info" native-type="submit">确定</van-button>
</div>
</van-form>
</van-popup>
</div>
<script nonce="${cspNonce!}">
new Vue({
el: "#app",
store,
data() {
return {
apiPath: "/platform/h5/medicalMutualAid/medical/diseaseType",
list: [],
loading: false,
refreshing: false,
finished: false,
dialogVisible: false,
dialogTitle: "新增疾病种类",
formData: {
isMajorDiseases: true
},
pageForm: {
pageNumber: 1,
pageSize: 10,
totalCount: 0,
searchKeyword: ""
}
}
},
methods: {
// 分页加载疾病类型,刷新时替换列表,翻页时追加列表。
loadData() {
this.$axios.post(this.apiPath + "/pageData", this.pageForm).then((resp) => {
if (resp.code === 0) {
const rows = resp.data && resp.data.list ? resp.data.list : []
if (this.pageForm.pageNumber === 1) {
this.list = rows
} else {
this.list = this.list.concat(rows)
}
this.pageForm.totalCount = resp.data ? resp.data.totalCount : 0
if (this.list.length >= this.pageForm.totalCount || rows.length === 0) {
this.finished = true
} else {
this.pageForm.pageNumber += 1
}
} else {
this.$toast.fail(resp.msg)
this.finished = true
}
}).finally(() => {
this.loading = false
this.refreshing = false
})
},
// 重置分页状态并按当前关键字重新查询。
doSearch() {
this.pageForm.pageNumber = 1
this.pageForm.totalCount = 0
this.finished = false
this.list = []
this.loading = true
this.loadData()
},
// 新增时初始化默认表单。
openAdd() {
this.formData = { isMajorDiseases: true }
this.dialogTitle = "新增疾病种类"
this.dialogVisible = true
},
// 编辑时复制列表数据,避免关闭弹窗前污染原列表。
openEdit(row) {
this.formData = Object.assign({}, row)
this.dialogTitle = "编辑疾病种类"
this.dialogVisible = true
},
// Vant 表单校验成功后保存疾病类型。
doSubmit() {
this.$axios.post(this.apiPath + "/doSubmit", this.formData).then((resp) => {
if (resp.code === 0) {
this.$toast.success(resp.msg)
this.dialogVisible = false
this.doSearch()
} else {
this.$toast.fail(resp.msg)
}
})
},
// 删除前二次确认,成功后从第一页重新加载。
doDelete(id) {
this.$dialog.confirm({
title: "提示",
message: "您确定要删除吗?"
}).then(() => {
this.$axios.post(this.apiPath + "/doDelete", { id: id }).then((resp) => {
if (resp.code === 0) {
this.$toast.success(resp.msg)
this.doSearch()
} else {
this.$toast.fail(resp.msg)
}
})
}).catch(() => {})
}
}
})
</script>
<!--#
}
#-->
@@ -0,0 +1,417 @@
<!--#
layout("/layouts/platform_h5.html"){
#-->
<div id="app" v-cloak>
<van-nav-bar title="离退休人员档案" left-text="返回" left-arrow @click-left="historyBack" fixed placeholder></van-nav-bar>
<van-sticky offset-top="46px">
<van-search v-model="pageForm.searchKeyword" placeholder="请输入姓名或工号搜索" show-action input-align="left" @search="doSearch">
<template #action>
<div @click="doSearch">搜索</div>
</template>
</van-search>
<div class="retired-toolbar">
<van-button class="toolbar-button" icon="plus" size="small" type="primary" block @click="openAdd">新增退休人员</van-button>
<van-button class="toolbar-button" icon="setting-o" size="small" type="info" block @click="openSet">设置退休人员</van-button>
</div>
</van-sticky>
<table-list api="/platform/retired/list/pageData" :page_form.sync="pageForm" ref="tableListRef" title="username" @ready="onReady">
<template v-slot="{row}">
<table-column label="工号">{{row.loginname}}</table-column>
<table-column label="性别">{{row.sex}}</table-column>
<table-column label="联系电话">{{row.mobile}}</table-column>
<table-column label="出生日期">{{formatDate(row.birthday)}}</table-column>
<table-column label="在职状态">{{row.userState}}</table-column>
<table-column label="人员类型">{{row.personType}}</table-column>
<table-column label="所属单位">{{row.unitname}}</table-column>
<table-column label="退休小组">{{row.retiredGroupName || '暂无'}}</table-column>
</template>
<template #actions="{row}">
<div class="action-btn" @click="openView(row)">
<i class="fa fa-eye"></i>
<span>查看</span>
</div>
<div class="action-btn" @click="openEdit(row)">
<i class="fa fa-edit"></i>
<span>编辑</span>
</div>
<div class="action-btn delete" @click="doDelete(row)">
<i class="fa fa-trash"></i>
<span>删除</span>
</div>
</template>
</table-list>
<van-action-sheet v-model="infoVisible" title="人员档案详情">
<div class="detail-container">
<van-cell-group title="基本信息">
<van-cell title="工号">{{emptyText(viewData.loginname)}}</van-cell>
<van-cell title="姓名">{{emptyText(viewData.username)}}</van-cell>
<van-cell title="性别">{{emptyText(viewData.sex)}}</van-cell>
<van-cell title="出生日期">{{formatDate(viewData.birthday) || '暂无'}}</van-cell>
<van-cell title="身份证号">{{emptyText(viewData.idCard)}}</van-cell>
<van-cell title="政治面貌">{{emptyText(viewData.political)}}</van-cell>
<van-cell title="籍贯">{{emptyText(viewData.nativePlace)}}</van-cell>
<van-cell title="联系电话">{{emptyText(viewData.mobile)}}</van-cell>
<van-cell title="参加工作时间">{{emptyText(viewData.arrivalAtSchoolDate)}}</van-cell>
<van-cell title="工龄">{{workAgeText(viewData.arrivalAtSchoolDate) || '暂无'}}</van-cell>
<van-cell title="退休时间">{{formatDate(viewData.retireDate) || '暂无'}}</van-cell>
<van-cell title="职务">{{emptyText(viewData.position)}}</van-cell>
<van-cell title="职称">{{emptyText(viewData.professionalTitle)}}</van-cell>
<van-cell title="在职状态">{{emptyText(viewData.userState)}}</van-cell>
<van-cell title="人员类型">{{emptyText(viewData.personType)}}</van-cell>
<van-cell title="所属工会">{{emptyText(viewData.unionName)}}</van-cell>
<van-cell title="所属单位">{{emptyText(viewData.unitName)}}</van-cell>
<van-cell title="退休小组">{{emptyText(viewData.retiredGroupName)}}</van-cell>
<van-cell title="退休党支部">{{emptyText(viewData.retiredPartyBranchName)}}</van-cell>
</van-cell-group>
</div>
</van-action-sheet>
<van-action-sheet v-model="formVisible" :title="formUserId ? '编辑退休人员' : '新增退休人员'">
<van-form ref="formRef" class="h5-form-sheet">
<van-field v-model="formData.loginname" name="loginname" label="工号" maxlength="120" placeholder="请输入工号" required :rules="[{required:true,message:'请输入工号'}]"></van-field>
<van-field v-model="formData.username" name="username" label="姓名" maxlength="100" placeholder="请输入姓名" required :rules="[{required:true,message:'请输入姓名'}]"></van-field>
<van-field v-model="formData.nativePlace" label="籍贯" maxlength="100" placeholder="请输入籍贯"></van-field>
<van-field v-model="formData.sex" name="sex" label="性别" placeholder="请选择性别" readonly is-link required :rules="[{required:true,message:'请选择性别'}]" @click="showSexPicker = true"></van-field>
<van-field v-model="formData.birthday" label="出生日期" type="date" placeholder="请选择出生日期"></van-field>
<van-field v-model="formData.idCard" name="idCard" label="身份证号" maxlength="30" placeholder="请输入身份证号" :rules="idCardRules"></van-field>
<van-field v-model="formData.political" label="政治面貌" maxlength="100" placeholder="请输入政治面貌"></van-field>
<van-field v-model="formData.arrivalAtSchoolDate" label="参加工作时间" maxlength="20" placeholder="请输入参加工作时间"></van-field>
<van-field :value="workAgeText(formData.arrivalAtSchoolDate)" label="工龄" readonly placeholder="系统自动计算"></van-field>
<van-field v-model="formData.unitName" label="单位" placeholder="请选择单位" readonly is-link @click="showUnitPicker = true"></van-field>
<van-field v-model="formData.unionName" label="工会" readonly placeholder="系统自动显示"></van-field>
<van-field v-model="formData.position" label="职务" maxlength="50" placeholder="请输入职务"></van-field>
<van-field v-model="formData.professionalTitle" label="职称" maxlength="50" placeholder="请输入职称"></van-field>
<van-field v-model="formData.retireDate" label="退休时间" type="date" placeholder="请选择退休时间"></van-field>
<van-field v-model="formData.mobile" label="电话" maxlength="32" placeholder="请输入电话"></van-field>
<van-field v-model="formData.userState" name="userState" label="在职状态" placeholder="请选择在职状态" readonly is-link required :rules="[{required:true,message:'请选择在职状态'}]" @click="showUserStatePicker = true"></van-field>
<van-field v-model="formData.personType" name="personType" label="人员类型" placeholder="请选择人员类型" readonly is-link required :rules="[{required:true,message:'请选择人员类型'}]" @click="showPersonTypePicker = true"></van-field>
</van-form>
<div class="h5-form-actions">
<van-button class="sheet-button" block @click="formVisible = false">取消</van-button>
<van-button class="sheet-button" icon="success" block type="primary" @click="submitForm">确定</van-button>
</div>
</van-action-sheet>
<van-action-sheet v-model="setVisible" title="设置退休人员">
<van-search v-model="nonRetiredUserPageForm.searchKeyword" placeholder="请输入姓名或工号搜索" show-action @search="searchNonRetiredUsers">
<template #action>
<div @click="searchNonRetiredUsers">搜索</div>
</template>
</van-search>
<van-checkbox-group v-model="selectedUserIds" class="retired-user-list">
<van-cell v-for="item in nonRetiredUserList" :key="item.id" :title="item.username + '(' + item.loginname + ')'" :label="nonRetiredUserLabel(item)" clickable @click="toggleUser(item.id)">
<template #right-icon>
<van-checkbox :name="item.id" ref="userCheckboxes" @click.stop></van-checkbox>
</template>
</van-cell>
</van-checkbox-group>
<van-empty v-if="!nonRetiredUserList.length" description="暂无人员"></van-empty>
<div class="retired-page-actions">
<van-button class="page-button" size="small" :disabled="nonRetiredUserPageForm.pageNumber <= 1" @click="changeNonRetiredPage(-1)">上一页</van-button>
<span>第{{nonRetiredUserPageForm.pageNumber}}页 / 共{{nonRetiredUserPageForm.totalCount}}人</span>
<van-button class="page-button" size="small" :disabled="nonRetiredUserPageForm.pageNumber * nonRetiredUserPageForm.pageSize >= nonRetiredUserPageForm.totalCount" @click="changeNonRetiredPage(1)">下一页</van-button>
</div>
<div class="h5-form-actions">
<van-button class="sheet-button" block @click="setVisible = false">取消</van-button>
<van-button class="sheet-button" icon="success" block type="primary" @click="doBatchJoinRetire">确定</van-button>
</div>
</van-action-sheet>
<van-popup v-model="showSexPicker" position="bottom">
<van-picker show-toolbar :columns="sexOptions" @confirm="(value)=>{formData.sex=value;showSexPicker=false}" @cancel="showSexPicker = false"></van-picker>
</van-popup>
<van-popup v-model="showUserStatePicker" position="bottom">
<van-picker show-toolbar :columns="userStateOptions" @confirm="(value)=>{formData.userState=value;showUserStatePicker=false}" @cancel="showUserStatePicker = false"></van-picker>
</van-popup>
<van-popup v-model="showPersonTypePicker" position="bottom">
<van-picker show-toolbar :columns="personTypeOptions" @confirm="(value)=>{formData.personType=value;showPersonTypePicker=false}" @cancel="showPersonTypePicker = false"></van-picker>
</van-popup>
<van-popup v-model="showUnitPicker" position="bottom">
<van-picker show-toolbar :columns="unitColumns" @confirm="onUnitConfirm" @cancel="showUnitPicker = false"></van-picker>
</van-popup>
</div>
<script nonce="${cspNonce!}">
new Vue({
el: "#app",
store,
data() {
return {
pageForm: {pageNumber: 1, pageSize: 10, totalCount: 0},
infoVisible: false,
formVisible: false,
setVisible: false,
showSexPicker: false,
showUserStatePicker: false,
showPersonTypePicker: false,
showUnitPicker: false,
viewData: {},
formData: {},
formUserId: "",
units: [],
sexOptions: ["男", "女"],
userStateOptions: ["退休", "离休", "离退休"],
personTypeOptions: ["离退休人员"],
idCardRules: [
{
validator: (value) => !value || /(^\d{15}$)|(^\d{17}[\dXx]$)/.test(value),
message: "请输入正确的身份证号码"
}
],
nonRetiredUserPageForm: {pageNumber: 1, pageSize: 10, totalCount: 0},
nonRetiredUserList: [],
selectedUserIds: []
}
},
computed: {
unitColumns() {
return this.units.map((item) => item.name)
}
},
methods: {
onReady() {
this.doSearch()
},
doSearch() {
this.$nextTick(() => {
this.pageForm.pageNumber = 1
this.pageForm.totalCount = 0
this.$refs.tableListRef.doSearch()
})
},
openView(row) {
this.$axios.post("/platform/retired/list/userInfo", {userId: row.id}).then((res) => {
if (res.code === 0) {
this.viewData = this.normalizeUserData(res.data || row)
this.infoVisible = true
}
})
},
openAdd() {
this.formUserId = ""
this.formData = this.defaultFormData()
this.formVisible = true
},
openEdit(row) {
this.$axios.post("/platform/retired/list/userInfo", {userId: row.id}).then((res) => {
if (res.code === 0) {
this.formUserId = row.id
this.formData = this.normalizeUserData(res.data || row)
this.formVisible = true
}
})
},
submitForm() {
this.$refs.formRef.validate().then(() => {
this.$axios.post("/platform/retired/list/doSubmit", {
user: JSON.stringify(this.formData),
userId: this.formUserId
}).then((res) => {
if (res.code === 0) {
this.$toast.success(res.msg)
this.formVisible = false
this.doSearch()
}
})
})
},
doDelete(row) {
this.$dialog.confirm({title: "提示", message: "您确定要删除吗?"}).then(() => {
this.$axios.post("/platform/retired/list/doDelete", {id: row.id}).then((res) => {
if (res.code === 0) {
this.$toast.success(res.msg)
this.doSearch()
}
})
}).catch(() => {})
},
openSet() {
this.setVisible = true
this.selectedUserIds = []
this.nonRetiredUserPageForm = {pageNumber: 1, pageSize: 10, totalCount: 0}
this.nonRetiredUserPageData()
},
searchNonRetiredUsers() {
this.nonRetiredUserPageForm.pageNumber = 1
this.nonRetiredUserPageData()
},
changeNonRetiredPage(step) {
this.nonRetiredUserPageForm.pageNumber = this.nonRetiredUserPageForm.pageNumber + step
this.nonRetiredUserPageData()
},
nonRetiredUserPageData() {
this.$axios.post("/platform/retired/list/nonRetiredUserPageData", this.nonRetiredUserPageForm).then((res) => {
if (res.code === 0) {
this.nonRetiredUserList = (res.data && res.data.list) || []
this.$set(this.nonRetiredUserPageForm, "totalCount", (res.data && res.data.totalCount) || 0)
}
})
},
toggleUser(id) {
const index = this.selectedUserIds.indexOf(id)
if (index >= 0) {
this.selectedUserIds.splice(index, 1)
} else {
this.selectedUserIds.push(id)
}
},
doBatchJoinRetire() {
if (!this.selectedUserIds.length) {
this.$toast("请先选择需要设置为退休的人员")
return
}
this.$axios.post("/platform/retired/list/doBatchJoinRetire", {userIds: JSON.stringify(this.selectedUserIds)}).then((res) => {
if (res.code === 0) {
this.$toast.success(res.msg)
this.setVisible = false
this.doSearch()
}
})
},
onUnitConfirm(value) {
const unit = this.units.find((item) => item.name === value)
this.$set(this.formData, "unitName", unit ? unit.name : "")
this.$set(this.formData, "unitId", unit ? unit.id : "")
this.$set(this.formData, "unionName", unit ? (unit.unionName || unit.unionname || "") : "")
this.$set(this.formData, "unionId", unit ? (unit.unionId || unit.unionid || "") : "")
this.showUnitPicker = false
},
defaultFormData() {
return {
userState: "退休",
personType: "离退休人员",
families: []
}
},
normalizeUserData(user) {
const data = Object.assign(this.defaultFormData(), user)
const fieldMap = {
idcard: "idCard",
nativeplace: "nativePlace",
professionaltitle: "professionalTitle",
arrivalatschooldate: "arrivalAtSchoolDate",
userstate: "userState",
persontype: "personType",
retiredate: "retireDate",
unitid: "unitId",
unitname: "unitName",
unionid: "unionId",
unionname: "unionName",
retiredgroupname: "retiredGroupName",
retiredpartybranchname: "retiredPartyBranchName"
}
Object.keys(fieldMap).forEach((key) => {
if ((data[fieldMap[key]] === undefined || data[fieldMap[key]] === null) && user[key] !== undefined) {
this.$set(data, fieldMap[key], user[key])
}
})
data.birthday = this.formatDate(data.birthday)
data.retireDate = this.formatDate(data.retireDate)
if (typeof data.families === "string") {
try {
data.families = JSON.parse(data.families)
} catch (e) {
data.families = []
}
}
return data
},
formatDate(value) {
return value ? this.$moment(value).format("YYYY-MM-DD") : ""
},
emptyText(value) {
return value === undefined || value === null || value === "" ? "暂无" : value
},
nonRetiredUserLabel(item) {
return [item.sex, item.mobile, item.unitname].filter(Boolean).join(" / ")
},
workAgeText(value) {
const start = this.parseWorkDate(value)
if (!start) return ""
const today = new Date()
let years = today.getFullYear() - start.getFullYear()
if (today.getMonth() < start.getMonth() || (today.getMonth() === start.getMonth() && today.getDate() < start.getDate())) {
years = years - 1
}
return Math.max(years, 0) + "年"
},
parseWorkDate(value) {
if (!value) return null
const parts = String(value).split("-")
const year = Number(parts[0])
const month = parts.length > 1 ? Number(parts[1]) : 1
const day = parts.length > 2 ? Number(parts[2]) : 1
if (!year || !month || !day) return null
const date = new Date(year, month - 1, day)
return isNaN(date.getTime()) ? null : date
},
async loadUnits() {
this.units = await this.$businessTool.listUnit()
}
},
created() {
this.loadUnits()
}
})
</script>
<style>
.retired-toolbar {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
padding: 10px 12px 12px;
background: #fff;
border-top: 1px solid #f5f5f5;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.03);
}
.retired-toolbar .toolbar-button {
height: 36px;
border-radius: 6px;
font-weight: 500;
}
.h5-form-sheet {
max-height: 70vh;
overflow-y: auto;
}
.h5-form-actions {
display: flex;
column-gap: 10px;
padding: 10px 12px 12px;
background: #fff;
border-top: 1px solid #f5f5f5;
}
.h5-form-actions .sheet-button {
height: 40px;
border-radius: 6px;
}
.retired-user-list {
max-height: 55vh;
overflow-y: auto;
}
.retired-page-actions {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 12px;
color: #666;
font-size: 13px;
background: #fff;
}
.retired-page-actions .page-button {
min-width: 72px;
border-radius: 6px;
}
</style>
<!--#
}
#-->