feat:独生子女模块初版
This commit is contained in:
+5
@@ -15,6 +15,7 @@ import com.budwk.app.sys.models.Sys_dict;
|
||||
import com.budwk.app.sys.services.SysDictService;
|
||||
import com.budwk.app.zhgh.staffmanage.childManage.models.ChildManage;
|
||||
import com.budwk.app.zhgh.staffmanage.childManage.service.ChildManageService;
|
||||
import com.budwk.app.zhgh.staffmanage.childManage.service.OnlyChildFeeService;
|
||||
import com.budwk.app.zhgh.staffmanage.childManage.vo.ChildManagePageForm;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
@@ -50,6 +51,8 @@ public class ChildManageManageController {
|
||||
@Inject
|
||||
private ChildManageService childManageService;
|
||||
@Inject
|
||||
private OnlyChildFeeService onlyChildFeeService;
|
||||
@Inject
|
||||
private SysDictService sysDictService;
|
||||
|
||||
|
||||
@@ -125,6 +128,8 @@ public class ChildManageManageController {
|
||||
@SaCheckPermission(value = {"childManage.manage", "h5.childManage.manage"}, mode = SaMode.OR)
|
||||
@SLog(tag = "子女信息-我的填报", msg = "删除id: ${args[0]}")
|
||||
public Result doDelete(String id) {
|
||||
onlyChildFeeService.lockChildInformation();
|
||||
if (onlyChildFeeService.isChildLinked(id)) return Result.error("子女资料已关联资格申请,不能删除");
|
||||
childManageService.dao().delete(ChildManage.class, id);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
+18
-1
@@ -10,6 +10,7 @@ import com.budwk.app.base.result.Result;
|
||||
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
|
||||
import com.budwk.app.zhgh.staffmanage.childManage.models.ChildManage;
|
||||
import com.budwk.app.zhgh.staffmanage.childManage.service.ChildManageService;
|
||||
import com.budwk.app.zhgh.staffmanage.childManage.service.OnlyChildFeeService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -32,6 +33,8 @@ public class ChildManageMineController {
|
||||
|
||||
@Inject
|
||||
private ChildManageService childManageService;
|
||||
@Inject
|
||||
private OnlyChildFeeService onlyChildFeeService;
|
||||
|
||||
@At("/index")
|
||||
@Ok("beetl:/platform/zhgh/staffmanage/childManage/mine/index.html")
|
||||
@@ -59,19 +62,33 @@ public class ChildManageMineController {
|
||||
cnd.andEX("YEAR(info.applyTime)", "=", year);
|
||||
// 使用 or 条件查询两个监护人字段
|
||||
cnd.and(Cnd.exps("info.guardianUserId1", "=", SecurityUtil.getUserId())
|
||||
.or("info.guardianUserId2", "=", SecurityUtil.getUserId()));
|
||||
.or("info.guardianUserId2", "=", SecurityUtil.getUserId())
|
||||
.or("info.guardianLoginName1", "=", currentLoginName())
|
||||
.or("info.guardianLoginName2", "=", currentLoginName()));
|
||||
sql.setCondition(cnd);
|
||||
Pagination pagination = childManageService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
|
||||
return Result.success(pagination);
|
||||
}
|
||||
|
||||
|
||||
private String currentLoginName() {
|
||||
com.budwk.app.sys.views.View_user user = childManageService.dao().fetch(com.budwk.app.sys.views.View_user.class,
|
||||
Cnd.where("id", "=", SecurityUtil.getUserId()));
|
||||
return user == null ? SecurityUtil.getUserId() : user.getLoginname();
|
||||
}
|
||||
|
||||
@At
|
||||
@ApiOperation("删除子女信息")
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
@SaCheckPermission(value = {"childManage.mine", "h5.childManage.mine"}, mode = SaMode.OR)
|
||||
@SLog(tag = "子女信息-我的填报", msg = "删除id: ${args[0]}")
|
||||
public Result doDelete(String id) {
|
||||
onlyChildFeeService.lockChildInformation();
|
||||
ChildManage child = childManageService.dao().fetch(ChildManage.class, id);
|
||||
if (child == null || !(SecurityUtil.getUserId().equals(child.getGuardianUserId1())
|
||||
|| SecurityUtil.getUserId().equals(child.getGuardianUserId2())
|
||||
|| currentLoginName().equals(child.getGuardianLoginName1()) || currentLoginName().equals(child.getGuardianLoginName2()))) return Result.error("只能删除本人子女资料");
|
||||
if (onlyChildFeeService.isChildLinked(id)) return Result.error("子女资料已关联资格申请,不能删除");
|
||||
childManageService.dao().delete(ChildManage.class, id);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
+112
-3
@@ -15,10 +15,13 @@ import com.budwk.app.web.commons.auth.utils.AuthUtil;
|
||||
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
|
||||
import com.budwk.app.zhgh.staffmanage.childManage.models.ChildManage;
|
||||
import com.budwk.app.zhgh.staffmanage.childManage.service.ChildManageService;
|
||||
import com.budwk.app.zhgh.staffmanage.childManage.service.OnlyChildFeeService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.aop.interceptor.ioc.TransAop;
|
||||
import org.nutz.ioc.aop.Aop;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.dao.Sqls;
|
||||
import org.nutz.dao.sql.Sql;
|
||||
@@ -30,6 +33,7 @@ import org.nutz.mvc.annotation.Ok;
|
||||
import org.nutz.mvc.annotation.Param;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Objects;
|
||||
|
||||
@IocBean
|
||||
@At("/platform/childManage/write")
|
||||
@@ -42,6 +46,8 @@ public class ChildManageWriteController {
|
||||
private Dao dao;
|
||||
@Inject
|
||||
private ChildManageService childManageService;
|
||||
@Inject
|
||||
private OnlyChildFeeService onlyChildFeeService;
|
||||
|
||||
|
||||
@At("/index")
|
||||
@@ -57,14 +63,88 @@ public class ChildManageWriteController {
|
||||
|
||||
@At
|
||||
@ApiOperation("保存子女信息")
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
@SaCheckPermission(value = {"childManage.write", "h5.childManage.write"}, mode = SaMode.OR)
|
||||
@SLog(tag = "子女信息管理-信息填写", msg = "保存子女信息")
|
||||
public Result save(@Param("data") ChildManage childManage) {
|
||||
if(StrUtil.isBlank(childManage.getId())) childManage.setApplyTime(new Date());
|
||||
onlyChildFeeService.lockChildInformation();
|
||||
View_user current = dao.fetch(View_user.class, Cnd.where("id", "=", SecurityUtil.getUserId()));
|
||||
if (current == null) return Result.error("人员资料不存在");
|
||||
ChildManage old = StrUtil.isBlank(childManage.getId()) ? null : dao.fetch(ChildManage.class, childManage.getId());
|
||||
boolean manager = AuthUtil.hasRoleOr(RoleConstant.SYSADMIN.name(), RoleConstant.SCHOOL_UNION_ADMIN.name());
|
||||
if (StrUtil.isNotBlank(childManage.getId()) && old == null) return Result.error("子女资料不存在");
|
||||
if (old != null && !manager && !current.getId().equals(old.getGuardianUserId1())
|
||||
&& !current.getId().equals(old.getGuardianUserId2())
|
||||
&& !current.getLoginname().equals(old.getGuardianLoginName1())
|
||||
&& !current.getLoginname().equals(old.getGuardianLoginName2())) return Result.error("只能维护本人子女资料");
|
||||
if (old != null && onlyChildFeeService.isChildInformationLocked(old.getId())) {
|
||||
if (criticalInformationChanged(old, childManage)) {
|
||||
return Result.error("该子女已关联待核验、已核定或已停发的独生子女费资格,关键身份和监护关系不可直接修改");
|
||||
}
|
||||
// 锁定记录仅更新日常资料,不重新带入人员身份,也不改写资格或清算快照。
|
||||
old.setChildrenGrade(childManage.getChildrenGrade());
|
||||
old.setChildrenYear(childManage.getChildrenYear());
|
||||
old.setChildrenCountry(childManage.getChildrenCountry());
|
||||
old.setChildrenCurrentSchool(childManage.getChildrenCurrentSchool());
|
||||
old.setChildrenMiddleScore(childManage.getChildrenMiddleScore());
|
||||
old.setChildrenSubjectCombination(childManage.getChildrenSubjectCombination());
|
||||
old.setChildrenHuKouAddress(childManage.getChildrenHuKouAddress());
|
||||
old.setGuardianMobile1(childManage.getGuardianMobile1());
|
||||
old.setGuardianMobile2(childManage.getGuardianMobile2());
|
||||
old.setGuardianUnitName2(childManage.getGuardianUnitName2());
|
||||
old.setNote(childManage.getNote());
|
||||
dao.update(old);
|
||||
return Result.success();
|
||||
}
|
||||
if (StrUtil.isBlank(childManage.getChildrenName()) || StrUtil.isBlank(childManage.getChildrenIdCard())
|
||||
|| childManage.getChildrenBirthday() == null || StrUtil.isBlank(childManage.getChildrenSex())) return Result.error("请补全子女姓名、身份证、生日和性别");
|
||||
if (childManage.getChildrenBirthday().after(new Date())) return Result.error("出生日期不能晚于今天");
|
||||
// 普通填报恢复原监护人表单,监护关系在资格申请或关键资料更正时填写。
|
||||
if (dao.count(ChildManage.class, Cnd.where("childrenIdCard", "=", childManage.getChildrenIdCard())
|
||||
.andEX("id", "!=", childManage.getId())) > 0) return Result.error("该子女已登记,请维护已有资料");
|
||||
// 人员身份由系统带入;双职工共用子女资料,资格分别申请。
|
||||
View_user first = old == null ? current : dao.fetch(View_user.class, Cnd.where("loginname", "=", old.getGuardianLoginName1()));
|
||||
if (first == null) return Result.error("第一监护人人员资料不存在");
|
||||
childManage.setGuardianUserId1(first.getId());
|
||||
childManage.setGuardianUserName1(first.getUsername());
|
||||
childManage.setGuardianLoginName1(first.getLoginname());
|
||||
childManage.setGuardianUnitId1(first.getUnitId());
|
||||
childManage.setGuardianUnitName1(first.getUnitName());
|
||||
childManage.setGuardianUnionId1(first.getUnionId());
|
||||
childManage.setGuardianUnionName1(first.getUnionName());
|
||||
if (old != null && !manager && !current.getId().equals(first.getId())) childManage.setGuardianLoginName2(current.getLoginname());
|
||||
childManage.setGuardianUserId2(null);
|
||||
if (StrUtil.isNotBlank(childManage.getGuardianLoginName2())) {
|
||||
View_user second = dao.fetch(View_user.class, Cnd.where("loginname", "=", childManage.getGuardianLoginName2()));
|
||||
if (second == null) return Result.error("另一位监护人工号不存在,本校以外监护人请勿填写工号");
|
||||
if (second.getId().equals(first.getId())) return Result.error("两位监护人不能为同一教职工");
|
||||
childManage.setGuardianUserId2(second.getId());
|
||||
childManage.setGuardianUserName2(second.getUsername());
|
||||
childManage.setGuardianUnitId2(second.getUnitId());
|
||||
childManage.setGuardianUnitName2(second.getUnitName());
|
||||
childManage.setGuardianUnionId2(second.getUnionId());
|
||||
childManage.setGuardianUnionName2(second.getUnionName());
|
||||
}
|
||||
childManage.setApplyTime(old == null ? new Date() : old.getApplyTime());
|
||||
dao.insertOrUpdate(childManage);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交子女关键资料更正,核验通过前不写入原始记录。
|
||||
* @param childManage 拟更正资料
|
||||
* @param reason 更正原因和证明依据
|
||||
* @return 申请提交结果
|
||||
*/
|
||||
@At @Aop(TransAop.READ_COMMITTED)
|
||||
@SaCheckPermission(value = {"childManage.write", "h5.childManage.write"}, mode = SaMode.OR)
|
||||
public Result requestChange(@Param("data") ChildManage childManage, String reason) {
|
||||
try {
|
||||
onlyChildFeeService.requestChildChange(childManage, reason);
|
||||
return Result.success("更正申请已提交,核验通过前原资料保持不变");
|
||||
} catch (IllegalArgumentException e) { return Result.error(e.getMessage()); }
|
||||
}
|
||||
|
||||
@At
|
||||
@ApiOperation("查询用户")
|
||||
@SaCheckPermission(value = {"childManage.write", "h5.childManage.write"}, mode = SaMode.OR)
|
||||
@@ -112,7 +192,6 @@ public class ChildManageWriteController {
|
||||
public Result getIsRepeatByIdCard(String idCard, String id) {
|
||||
int count = childManageService.dao().count(ChildManage.class,
|
||||
Cnd.where(ChildManage::getChildrenIdCard, "=", idCard)
|
||||
.and("YEAR(applyTime)", "=", DateUtil.thisYear())
|
||||
.andEX(ChildManage::getId, "!=", id));
|
||||
|
||||
|
||||
@@ -122,7 +201,37 @@ public class ChildManageWriteController {
|
||||
@At
|
||||
@SaCheckLogin
|
||||
public Result findOne(String id) {
|
||||
return Result.success(childManageService.dao().fetch(ChildManage.class, id));
|
||||
ChildManage child = childManageService.dao().fetch(ChildManage.class, id);
|
||||
if (child != null) {
|
||||
child.setQualificationLocked(onlyChildFeeService.isChildInformationLocked(id));
|
||||
child.setCorrectionHistory(onlyChildFeeService.childChanges().stream()
|
||||
.filter(change -> id.equals(change.getStr("childId"))).collect(java.util.stream.Collectors.toList()));
|
||||
}
|
||||
return Result.success(child);
|
||||
}
|
||||
|
||||
// 比较原始记录而非客户端锁定标记;日期按自然日比较,避免前端日期序列化造成误判。
|
||||
private boolean criticalInformationChanged(ChildManage old, ChildManage value) {
|
||||
return !sameText(old.getChildrenName(), value.getChildrenName())
|
||||
|| !sameText(old.getChildrenIdCard(), value.getChildrenIdCard())
|
||||
|| !sameBirthday(old.getChildrenBirthday(), value.getChildrenBirthday())
|
||||
|| !sameText(old.getChildrenSex(), value.getChildrenSex())
|
||||
|| !sameText(old.getGuardianUserId1(), value.getGuardianUserId1())
|
||||
|| !sameText(old.getGuardianUserId2(), value.getGuardianUserId2())
|
||||
|| !sameText(old.getGuardianUserName1(), value.getGuardianUserName1())
|
||||
|| !sameText(old.getGuardianUserName2(), value.getGuardianUserName2())
|
||||
|| !sameText(old.getGuardianLoginName1(), value.getGuardianLoginName1())
|
||||
|| !sameText(old.getGuardianLoginName2(), value.getGuardianLoginName2())
|
||||
|| !sameText(old.getGuardianRelationship1(), value.getGuardianRelationship1())
|
||||
|| !sameText(old.getGuardianRelationship2(), value.getGuardianRelationship2());
|
||||
}
|
||||
|
||||
private boolean sameText(String a, String b) {
|
||||
return Objects.equals(StrUtil.nullToEmpty(a), StrUtil.nullToEmpty(b));
|
||||
}
|
||||
|
||||
private boolean sameBirthday(Date a, Date b) {
|
||||
return a == null || b == null ? a == b : DateUtil.formatDate(a).equals(DateUtil.formatDate(b));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+303
@@ -0,0 +1,303 @@
|
||||
package com.budwk.app.zhgh.staffmanage.childManage.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.*;
|
||||
import cn.hutool.json.*;
|
||||
import com.budwk.app.base.result.Result;
|
||||
import com.budwk.app.base.utils.CommonDownloadUtil;
|
||||
import com.budwk.app.zhgh.staffmanage.childManage.service.OnlyChildFeeService;
|
||||
import org.nutz.aop.interceptor.ioc.TransAop;
|
||||
import org.nutz.ioc.aop.Aop;
|
||||
import org.nutz.ioc.loader.annotation.*;
|
||||
import org.nutz.mvc.annotation.*;
|
||||
import org.apache.poi.ss.usermodel.*;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/** 子女信息管理中的独生子女费入口,复用既有角色并在服务端校验办理权限。 */
|
||||
@IocBean
|
||||
@At("/platform/childManage/onlyChildFee")
|
||||
@Ok("json:full")
|
||||
public class OnlyChildFeeController {
|
||||
@Inject private OnlyChildFeeService onlyChildFeeService;
|
||||
|
||||
/** 展示独生子女费工作台。 */
|
||||
@SaCheckLogin
|
||||
@At("/index")
|
||||
@Ok("beetl:/platform/zhgh/staffmanage/childManage/onlyChildFee/index.html")
|
||||
public void index() { }
|
||||
|
||||
/** 展示资格登记独立页面,数据及办理权限由服务端按子菜单授权校验。 */
|
||||
@SaCheckLogin
|
||||
@At("/qualification/index")
|
||||
@Ok("beetl:/platform/zhgh/staffmanage/childManage/onlyChildFee/qualification.html")
|
||||
public void qualificationPage() { }
|
||||
|
||||
/** 展示年度清算独立页面,数据及办理权限由服务端按子菜单授权校验。 */
|
||||
@SaCheckLogin
|
||||
@At("/settlement/index")
|
||||
@Ok("beetl:/platform/zhgh/staffmanage/childManage/onlyChildFee/settlement.html")
|
||||
public void settlementPage() { }
|
||||
|
||||
/** 展示变动报表独立页面,数据及办理权限由服务端按子菜单授权校验。 */
|
||||
@SaCheckLogin
|
||||
@At("/changes/index")
|
||||
@Ok("beetl:/platform/zhgh/staffmanage/childManage/onlyChildFee/changes.html")
|
||||
public void changesPage() { }
|
||||
|
||||
/** 展示退休奖励独立页面,数据及办理权限由服务端按子菜单授权校验。 */
|
||||
@SaCheckLogin
|
||||
@At("/retirement/index")
|
||||
@Ok("beetl:/platform/zhgh/staffmanage/childManage/onlyChildFee/retirement.html")
|
||||
public void retirementPage() { }
|
||||
|
||||
/** 展示待遇台账独立页面,数据及办理权限由服务端按子菜单授权校验。 */
|
||||
@SaCheckLogin
|
||||
@At("/ledger/index")
|
||||
@Ok("beetl:/platform/zhgh/staffmanage/childManage/onlyChildFee/ledger.html")
|
||||
public void ledgerPage() { }
|
||||
|
||||
/** 获取人员资料及可见资格。@return 初始化数据 */
|
||||
@SaCheckLogin
|
||||
@At @POST
|
||||
public Result context() { return Result.success(onlyChildFeeService.context()); }
|
||||
|
||||
private Result result(Supplier<Object> work) {
|
||||
try { return Result.success(work.get()); }
|
||||
catch (IllegalArgumentException e) { return Result.error(e.getMessage()); }
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询当前用户可见的资料更正申请。
|
||||
* @return 更正记录及审核结果
|
||||
*/
|
||||
@SaCheckLogin @At @POST
|
||||
public Result childChanges() { return Result.success(onlyChildFeeService.childChanges()); }
|
||||
|
||||
/**
|
||||
* 核验资料更正并保存生效月份和审核凭据。
|
||||
* @param data 更正申请ID、核验结论和依据
|
||||
* @return 办理结果
|
||||
*/
|
||||
@SaCheckLogin @At @POST @Aop(TransAop.READ_COMMITTED)
|
||||
public Result reviewChildChange(String data) {
|
||||
return result(() -> { onlyChildFeeService.reviewChildChange(JSONUtil.parseObj(data)); return null; });
|
||||
}
|
||||
|
||||
/** 保存或提交本人资格。@param data 申请内容 @param submit 是否提交 @return 保存结果 */
|
||||
@SaCheckLogin
|
||||
@At @POST @Aop(TransAop.READ_COMMITTED)
|
||||
public Result save(String data, boolean submit) {
|
||||
return result(() -> onlyChildFeeService.save(JSONUtil.parseObj(data), submit));
|
||||
}
|
||||
|
||||
/** 核定起发月份和证明。@param data 核验内容 @return 办理结果 */
|
||||
@SaCheckLogin
|
||||
@At @POST @Aop(TransAop.READ_COMMITTED)
|
||||
public Result review(String data) {
|
||||
return result(() -> { onlyChildFeeService.review(JSONUtil.parseObj(data)); return null; });
|
||||
}
|
||||
|
||||
/** 登记资格终止或实际停发。@param data 生效月份及依据 @return 办理结果 */
|
||||
@SaCheckLogin
|
||||
@At @POST @Aop(TransAop.READ_COMMITTED)
|
||||
public Result stop(String data) {
|
||||
return result(() -> { onlyChildFeeService.stop(JSONUtil.parseObj(data)); return null; });
|
||||
}
|
||||
|
||||
/** 登记工资实发。@param data 月份、实发金额及来源 @return 保存结果 */
|
||||
@SaCheckLogin
|
||||
@At @POST @Aop(TransAop.READ_COMMITTED)
|
||||
public Result payment(String data) {
|
||||
return result(() -> { onlyChildFeeService.payment(JSONUtil.parseObj(data)); return null; });
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存人员首次实际发放月份并同步重算各年度未确认草稿。
|
||||
* @param data 领取人、当前办理年度及首次实际发放月份
|
||||
* @return 保存结果;已确认批次或无办理权限时返回失败原因
|
||||
*/
|
||||
@SaCheckLogin
|
||||
@At @POST @Aop(TransAop.READ_COMMITTED)
|
||||
public Result paymentStart(String data) {
|
||||
return result(() -> { onlyChildFeeService.paymentStart(JSONUtil.parseObj(data)); return null; });
|
||||
}
|
||||
|
||||
/** 获取清算快照。@param year 清算年度 @return 快照和执行记录 */
|
||||
@SaCheckLogin
|
||||
@At @POST @Aop(TransAop.READ_COMMITTED)
|
||||
public Result settlement(int year) {
|
||||
// 转为原生集合及空值,避免 Hutool JSONNull 被 Nutz 序列化为对象,导致前端金额计算出现 NaN。
|
||||
return result(() -> org.nutz.json.Json.fromJson(java.util.Map.class, onlyChildFeeService.settlement(year).toString()));
|
||||
}
|
||||
|
||||
/** 生成或确认清算批次。@param year 清算年度 @param confirm 是否确认 @return 办理结果 */
|
||||
@SaCheckLogin
|
||||
@At @POST @Aop(TransAop.READ_COMMITTED)
|
||||
public Result batch(int year, boolean confirm) {
|
||||
return result(() -> { onlyChildFeeService.batch(year, confirm); return null; });
|
||||
}
|
||||
|
||||
/** 登记实际扣回。@param data 归属月份和实际执行凭据 @return 保存结果 */
|
||||
@SaCheckLogin
|
||||
@At @POST @Aop(TransAop.READ_COMMITTED)
|
||||
public Result recover(String data) {
|
||||
return result(() -> { onlyChildFeeService.recover(JSONUtil.parseObj(data)); return null; });
|
||||
}
|
||||
|
||||
/** 查询原退休奖励申请。@return 申请、审批与支付状态 */
|
||||
@SaCheckLogin
|
||||
@At @POST
|
||||
public Result retirement() { return result(() -> onlyChildFeeService.retirement()); }
|
||||
|
||||
/** 登记退休奖励发放。@param data 原申请及核实凭据 @return 保存结果 */
|
||||
@SaCheckLogin
|
||||
@At @POST @Aop(TransAop.READ_COMMITTED)
|
||||
public Result reward(String data) {
|
||||
return result(() -> { onlyChildFeeService.reward(JSONUtil.parseObj(data)); return null; });
|
||||
}
|
||||
|
||||
/** 按当前筛选导出原退休奖励申请与实际发放记录。
|
||||
* @param year 退休年度,可为空
|
||||
* @param unit 单位名称
|
||||
* @param keyword 姓名或工号
|
||||
* @param approval 审核状态
|
||||
* @param paidStatus 发放状态
|
||||
* @param response 下载响应
|
||||
*/
|
||||
@SaCheckLogin
|
||||
@At @Ok("void")
|
||||
public void exportReward(String year, String unit, String keyword, String approval, String paidStatus, HttpServletResponse response) {
|
||||
JSONArray list = onlyChildFeeService.retirement();
|
||||
Workbook book = new XSSFWorkbook(); Sheet sheet = book.createSheet("退休奖励名单");
|
||||
String[] labels = {"工号", "姓名", "单位", "退休日期", "奖励金额", "审核状态", "发放状态", "发放日期", "发放凭据", "历史核查依据"};
|
||||
Row header = sheet.createRow(0);
|
||||
for (int i = 0; i < labels.length; i++) { header.createCell(i).setCellValue(labels[i]); sheet.setColumnWidth(i, 22 * 256); }
|
||||
int index = 1;
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
JSONObject r = list.getJSONObject(i), payment = r.getJSONObject("payment");
|
||||
String date = r.getDate("retireTime") == null ? "" : new java.text.SimpleDateFormat("yyyy-MM-dd").format(r.getDate("retireTime"));
|
||||
String state = r.getBool("approved") ? "已通过" : "未通过审批", paid = payment == null ? "未发放" : "已发放";
|
||||
if (year != null && !year.isEmpty() && !date.startsWith(year)) continue;
|
||||
if (unit != null && !unit.isEmpty() && !unit.equals(r.getStr("unitName"))) continue;
|
||||
if (keyword != null && !keyword.isEmpty() && !(r.getStr("userName") + r.getStr("loginName")).contains(keyword)) continue;
|
||||
if (approval != null && !approval.isEmpty() && !approval.equals(state)) continue;
|
||||
if (paidStatus != null && !paidStatus.isEmpty() && !paidStatus.equals(paid)) continue;
|
||||
String[] values = {r.getStr("loginName"),r.getStr("userName"),r.getStr("unitName"),date,"3000.00",state,paid,
|
||||
payment == null ? "" : payment.getStr("paidDate"),payment == null ? "" : payment.getStr("voucher"),payment == null ? "待核实" : payment.getStr("historyBasis")};
|
||||
Row row = sheet.createRow(index++);
|
||||
for (int j = 0; j < values.length; j++) row.createCell(j).setCellValue(values[j]);
|
||||
}
|
||||
CommonDownloadUtil.download("退休奖励名单.xlsx", book, response);
|
||||
}
|
||||
|
||||
/** 导出当前筛选的完整变动表,工作簿只含一个工作表。
|
||||
* @param year 年度
|
||||
* @param unit 单位名称
|
||||
* @param keyword 姓名或工号
|
||||
* @param type 变动类型(停发或扣回)
|
||||
* @param response 下载响应
|
||||
*/
|
||||
@SaCheckLogin
|
||||
@At @Ok("void") @Aop(TransAop.READ_COMMITTED)
|
||||
public void export(int year, String unit, String keyword, String type, HttpServletResponse response) {
|
||||
JSONObject b = onlyChildFeeService.settlement(year).getJSONObject("batch");
|
||||
if (b == null || "草稿".equals(b.getStr("status"))) throw new IllegalArgumentException("请先复核确认清算批次");
|
||||
Workbook book = new XSSFWorkbook(); Sheet sheet = book.createSheet("变动表");
|
||||
String[] labels = {"序号", "工号", "姓名", "单位", "调前标准", "调后标准", "扣发金额", "补发金额", "停发时间", "备注说明"};
|
||||
String periodStart = b.getStr("periodStart"), periodEnd = b.getStr("periodEnd");
|
||||
sheet.createRow(0).createCell(0).setCellValue((year - 1) + "年11月—" + year + "年10月独生子女父母奖励费变动通知单");
|
||||
sheet.addMergedRegion(new org.apache.poi.ss.util.CellRangeAddress(0, 0, 0, 9));
|
||||
Row recipient = sheet.createRow(1);
|
||||
recipient.createCell(0).setCellValue("人力资源处:"); recipient.createCell(4).setCellValue("单位:元");
|
||||
sheet.addMergedRegion(new org.apache.poi.ss.util.CellRangeAddress(1, 1, 0, 3));
|
||||
sheet.addMergedRegion(new org.apache.poi.ss.util.CellRangeAddress(1, 1, 4, 9));
|
||||
Row header = sheet.createRow(2);
|
||||
int[] widths = {6, 12, 12, 28, 8, 8, 10, 8, 24, 14};
|
||||
for (int i = 0; i < labels.length; i++) {
|
||||
header.createCell(i).setCellValue(i >= 4 && i <= 7 ? labels[i].substring(0, 2) + "\n" + labels[i].substring(2) : labels[i]);
|
||||
sheet.setColumnWidth(i, widths[i] * 256);
|
||||
}
|
||||
int index = 3;
|
||||
BigDecimal[] totals = {BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO};
|
||||
JSONArray rows = b.getJSONArray("rows");
|
||||
for (int i = 0; i < rows.size(); i++) {
|
||||
JSONObject r = rows.getJSONObject(i);
|
||||
String end = r.getStr("endMonth", r.getStr("stopMonth"));
|
||||
if (end.compareTo(r.getStr("stopMonth")) > 0) end = r.getStr("stopMonth");
|
||||
boolean stop = end.compareTo(periodStart) >= 0 && end.compareTo(periodEnd) <= 0;
|
||||
BigDecimal deduction = noticeDeduction(r, periodStart, periodEnd);
|
||||
if (!stop && deduction.signum() == 0) continue;
|
||||
if (unit != null && !unit.isEmpty() && !unit.equals(r.getStr("unitName"))) continue;
|
||||
if (keyword != null && !keyword.isEmpty() && !(r.getStr("userName") + r.getStr("loginName")).contains(keyword)) continue;
|
||||
if ("停发".equals(type) && !stop || "扣回".equals(type) && deduction.signum() == 0) continue;
|
||||
Row row = sheet.createRow(index++);
|
||||
boolean stopped = end.compareTo(periodEnd) <= 0;
|
||||
String note = r.getStr("stopBasis");
|
||||
if (note == null || note.isEmpty()) note = stopped ? "停发" : "扣发调整";
|
||||
String[] values = {String.valueOf(index - 3), r.getStr("loginName"), r.getStr("userName"), r.getStr("unitName"), "10", stopped ? "0" : "10",
|
||||
deduction.toPlainString(), "0", stopped ? end.replace('-', '.') + "-" + year + ".10" : "—", note};
|
||||
for (int j = 0; j < values.length; j++) {
|
||||
Cell cell = row.createCell(j);
|
||||
if (j >= 4 && j <= 7) {
|
||||
cell.setCellValue(Double.parseDouble(values[j]));
|
||||
totals[j - 4] = totals[j - 4].add(new BigDecimal(values[j]));
|
||||
} else cell.setCellValue(values[j]);
|
||||
}
|
||||
}
|
||||
Row total = sheet.createRow(index); total.createCell(0).setCellValue("合计");
|
||||
sheet.addMergedRegion(new org.apache.poi.ss.util.CellRangeAddress(index, index, 0, 3));
|
||||
for (int i = 0; i < totals.length; i++) total.createCell(i + 4).setCellValue(totals[i].doubleValue());
|
||||
|
||||
// 通知单与页面使用同一纸质表样:黑色边框、金额合计、收件单位及独立签字栏。
|
||||
Font font = book.createFont(); font.setFontName("宋体"); font.setFontHeightInPoints((short) 12);
|
||||
CellStyle bodyStyle = book.createCellStyle(); bodyStyle.setFont(font);
|
||||
bodyStyle.setAlignment(HorizontalAlignment.CENTER); bodyStyle.setVerticalAlignment(VerticalAlignment.CENTER); bodyStyle.setWrapText(true);
|
||||
bodyStyle.setBorderTop(BorderStyle.THIN); bodyStyle.setBorderBottom(BorderStyle.THIN);
|
||||
bodyStyle.setBorderLeft(BorderStyle.THIN); bodyStyle.setBorderRight(BorderStyle.THIN);
|
||||
CellStyle amountStyle = book.createCellStyle(); amountStyle.cloneStyleFrom(bodyStyle);
|
||||
amountStyle.setDataFormat(book.createDataFormat().getFormat("0.00"));
|
||||
Font boldFont = book.createFont(); boldFont.setFontName("宋体"); boldFont.setFontHeightInPoints((short) 12); boldFont.setBold(true);
|
||||
CellStyle headerStyle = book.createCellStyle(); headerStyle.cloneStyleFrom(bodyStyle); headerStyle.setFont(boldFont);
|
||||
Font titleFont = book.createFont(); titleFont.setFontName("宋体"); titleFont.setFontHeightInPoints((short) 18); titleFont.setBold(true);
|
||||
CellStyle titleStyle = book.createCellStyle(); titleStyle.cloneStyleFrom(bodyStyle); titleStyle.setFont(titleFont);
|
||||
CellStyle leftStyle = book.createCellStyle(); leftStyle.cloneStyleFrom(bodyStyle); leftStyle.setAlignment(HorizontalAlignment.LEFT);
|
||||
CellStyle rightStyle = book.createCellStyle(); rightStyle.cloneStyleFrom(bodyStyle); rightStyle.setAlignment(HorizontalAlignment.RIGHT);
|
||||
for (int i = 0; i <= index; i++) {
|
||||
Row row = sheet.getRow(i); row.setHeightInPoints(i == 0 ? 36 : i == 2 ? 42 : 32);
|
||||
for (int j = 0; j < labels.length; j++) {
|
||||
Cell cell = row.getCell(j); if (cell == null) cell = row.createCell(j);
|
||||
cell.setCellStyle(i == 0 ? titleStyle : i == 1 ? (j < 4 ? leftStyle : rightStyle)
|
||||
: i == 2 ? headerStyle : j >= 4 && j <= 7 ? amountStyle : bodyStyle);
|
||||
}
|
||||
}
|
||||
CellStyle signStyle = book.createCellStyle(); signStyle.setFont(font); signStyle.setVerticalAlignment(VerticalAlignment.CENTER);
|
||||
Row signatures = sheet.createRow(index + 1); signatures.setHeightInPoints(54);
|
||||
signatures.createCell(0).setCellValue("审核人:"); signatures.getCell(0).setCellStyle(signStyle);
|
||||
signatures.createCell(4).setCellValue("经办人:"); signatures.getCell(4).setCellStyle(signStyle);
|
||||
sheet.addMergedRegion(new org.apache.poi.ss.util.CellRangeAddress(index + 1, index + 1, 0, 3));
|
||||
sheet.addMergedRegion(new org.apache.poi.ss.util.CellRangeAddress(index + 1, index + 1, 4, 9));
|
||||
sheet.setFitToPage(true); sheet.setHorizontallyCenter(true);
|
||||
sheet.getPrintSetup().setLandscape(true); sheet.getPrintSetup().setPaperSize(PrintSetup.A4_PAPERSIZE);
|
||||
sheet.getPrintSetup().setFitWidth((short) 1); sheet.getPrintSetup().setFitHeight((short) 0);
|
||||
book.setPrintArea(0, 0, 9, 0, index + 1);
|
||||
CommonDownloadUtil.download(year + "年独生子女费变动表.xlsx", book, response);
|
||||
}
|
||||
|
||||
// 按确认快照逐月汇总多发额,已扣回金额不冲减通知单,避免结清后扣发额变成零。
|
||||
private BigDecimal noticeDeduction(JSONObject row, String periodStart, String periodEnd) {
|
||||
JSONArray months = row.getJSONArray("months");
|
||||
if (months == null || months.isEmpty()) throw new IllegalArgumentException("清算明细不完整,无法导出通知单");
|
||||
BigDecimal total = BigDecimal.ZERO;
|
||||
for (int i = 0; i < months.size(); i++) {
|
||||
JSONObject month = months.getJSONObject(i);
|
||||
String value = month.getStr("month");
|
||||
if (value.compareTo(periodStart) < 0 || value.compareTo(periodEnd) > 0) continue;
|
||||
BigDecimal paid = month.getBigDecimal("paid"), due = month.getBigDecimal("due");
|
||||
if (paid == null || due == null) throw new IllegalArgumentException("清算实发数据不完整,无法导出通知单");
|
||||
total = total.add(paid.subtract(due).max(BigDecimal.ZERO));
|
||||
}
|
||||
return total;
|
||||
}
|
||||
}
|
||||
@@ -36,12 +36,23 @@ public class ChildManage extends BaseModel implements Serializable {
|
||||
@PrevInsert(uu32 = true)
|
||||
private String id;
|
||||
|
||||
// 由关联资格实时计算,仅用于页面展示,不作为数据库字段或保存依据。
|
||||
private boolean qualificationLocked;
|
||||
|
||||
// 更正申请和审核结果仅随详情返回,不覆盖子女原始字段。
|
||||
private List<JSONObject> correctionHistory;
|
||||
|
||||
|
||||
@Column
|
||||
@ColDefine(type = ColType.VARCHAR,width = 32)
|
||||
@Comment("guardianUserId1")
|
||||
private String guardianUserId1;
|
||||
|
||||
@Column
|
||||
@ColDefine(type = ColType.VARCHAR, width = 30)
|
||||
@Comment("第一监护人与子女关系")
|
||||
private String guardianRelationship1;
|
||||
|
||||
@Column
|
||||
@ColDefine(type = ColType.VARCHAR,width = 30)
|
||||
@Comment("监护人1(教工)姓名")
|
||||
@@ -82,6 +93,11 @@ public class ChildManage extends BaseModel implements Serializable {
|
||||
@Comment("guardianUserId2")
|
||||
private String guardianUserId2;
|
||||
|
||||
@Column
|
||||
@ColDefine(type = ColType.VARCHAR, width = 30)
|
||||
@Comment("第二监护人与子女关系")
|
||||
private String guardianRelationship2;
|
||||
|
||||
@Column
|
||||
@ColDefine(type = ColType.VARCHAR,width = 30)
|
||||
@Comment("监护人2(教工)姓名")
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package com.budwk.app.zhgh.staffmanage.childManage.models;
|
||||
|
||||
import lombok.Data;
|
||||
import org.nutz.dao.entity.annotation.*;
|
||||
import java.util.Date;
|
||||
|
||||
/** 独生子女费业务凭据,按资格、月记录、清算快照和实际支付分别保存。 */
|
||||
@Data
|
||||
@Table("only_child_fee_record")
|
||||
public class OnlyChildFeeRecord {
|
||||
@Name
|
||||
@ColDefine(type = ColType.VARCHAR, width = 100)
|
||||
private String id;
|
||||
@Column
|
||||
@ColDefine(type = ColType.VARCHAR, width = 20)
|
||||
private String kind;
|
||||
@Column
|
||||
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||
private String userId;
|
||||
@Column
|
||||
@ColDefine(type = ColType.VARCHAR, width = 10)
|
||||
private String period;
|
||||
@Column
|
||||
@ColDefine(type = ColType.TEXT)
|
||||
private String content;
|
||||
@Column
|
||||
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||
private String operatorId;
|
||||
@Column
|
||||
private Date recordedAt;
|
||||
}
|
||||
+704
@@ -0,0 +1,704 @@
|
||||
package com.budwk.app.zhgh.staffmanage.childManage.service;
|
||||
|
||||
import cn.hutool.json.*;
|
||||
import com.budwk.app.sys.services.SysUserService;
|
||||
import com.budwk.app.sys.models.Sys_menu;
|
||||
import com.budwk.app.sys.views.View_user;
|
||||
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
|
||||
import com.budwk.app.zhgh.staffmanage.childManage.models.*;
|
||||
import com.budwk.app.zhgh.staffbenefit.dsznfmtx.models.Dsznfmtx;
|
||||
import com.budwk.app.flow.entity.ProcessInstance;
|
||||
import org.nutz.dao.*;
|
||||
import org.nutz.ioc.loader.annotation.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.*;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
|
||||
/** 月费核定、逐月清算和退休奖励登记;所有写操作由控制器事务统一保护。 */
|
||||
@IocBean
|
||||
public class OnlyChildFeeService {
|
||||
@Inject private Dao dao;
|
||||
@Inject private SysUserService sysUserService;
|
||||
private static final BigDecimal TEN = new BigDecimal("10.00");
|
||||
|
||||
// 页签与菜单使用同一授权来源,角色名称本身不再授予办理权限。
|
||||
private List<String> tabs() {
|
||||
Set<String> permissions = new HashSet<>();
|
||||
for (Sys_menu menu : sysUserService.getMenus(SecurityUtil.getUserId())) {
|
||||
permissions.add(menu.getPermission());
|
||||
}
|
||||
List<String> result = new ArrayList<>();
|
||||
for (String tab : Arrays.asList("qualification", "settlement", "changes", "retirement", "ledger")) {
|
||||
if (permissions.contains("childManage.onlyChildFee." + tab)) result.add(tab);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void require(boolean valid, String message) {
|
||||
if (!valid) throw new IllegalArgumentException(message);
|
||||
}
|
||||
|
||||
private void access(String... allowedTabs) {
|
||||
List<String> granted = tabs();
|
||||
require(Arrays.stream(allowedTabs).anyMatch(granted::contains), "未分配对应的独生子女费子菜单权限");
|
||||
}
|
||||
|
||||
private void lock() {
|
||||
dao.execute(Sqls.create("INSERT IGNORE INTO only_child_fee_record (id,kind,content) VALUES ('LOCK','LOCK','{}')"));
|
||||
dao.execute(Sqls.create("SELECT id FROM only_child_fee_record WHERE id='LOCK' FOR UPDATE"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 子女保存、删除与资格办理共用事务锁,避免检查后资格状态发生变化。
|
||||
* 调用方必须处于数据库事务中。
|
||||
*/
|
||||
public void lockChildInformation() {
|
||||
lock();
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断任一监护人关联的资格是否要求锁定子女关键资料。
|
||||
*
|
||||
* @param childId 子女记录ID
|
||||
* @return 存在待核验、已核定或已停发资格时返回true
|
||||
*/
|
||||
public boolean isChildInformationLocked(String childId) {
|
||||
if (childId == null) return false;
|
||||
return records("QUALIFICATION").stream().anyMatch(q -> childId.equals(q.getStr("childId"))
|
||||
&& Arrays.asList("待核验", "已核定", "已停发").contains(q.getStr("status")));
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断子女是否已经被资格申请引用,保留申请与历史依据的关联。
|
||||
*
|
||||
* @param childId 子女记录ID
|
||||
* @return 任一监护人存在关联申请时返回true
|
||||
*/
|
||||
public boolean isChildLinked(String childId) {
|
||||
return childId != null && records("QUALIFICATION").stream()
|
||||
.anyMatch(q -> childId.equals(q.getStr("childId")));
|
||||
}
|
||||
|
||||
private JSONObject childIdentity(ChildManage c) {
|
||||
return new JSONObject().set("childrenName", c.getChildrenName()).set("childrenIdCard", c.getChildrenIdCard())
|
||||
.set("childrenBirthday", c.getChildrenBirthday() == null ? null : new SimpleDateFormat("yyyy-MM-dd").format(c.getChildrenBirthday()))
|
||||
.set("childrenSex", c.getChildrenSex()).set("guardianUserId1", c.getGuardianUserId1())
|
||||
.set("guardianUserName1", c.getGuardianUserName1()).set("guardianLoginName1", c.getGuardianLoginName1())
|
||||
.set("guardianRelationship1", c.getGuardianRelationship1()).set("guardianUserId2", c.getGuardianUserId2())
|
||||
.set("guardianUserName2", c.getGuardianUserName2()).set("guardianLoginName2", c.getGuardianLoginName2())
|
||||
.set("guardianRelationship2", c.getGuardianRelationship2());
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交关键资料更正申请,保存前后快照,审批前不修改原始资料。
|
||||
*
|
||||
* @param proposed 拟更正的子女及监护人资料
|
||||
* @param reason 更正原因和证明依据说明
|
||||
*/
|
||||
public void requestChildChange(ChildManage proposed, String reason) {
|
||||
lock();
|
||||
ChildManage old = dao.fetch(ChildManage.class, proposed.getId());
|
||||
require(old != null && isChildInformationLocked(old.getId()), "该记录无需更正审批,请通过普通保存维护");
|
||||
String user = SecurityUtil.getUserId();
|
||||
View_user profile = dao.fetch(View_user.class, Cnd.where("id", "=", user));
|
||||
require(profile != null, "人员资料不存在");
|
||||
require(tabs().contains("ledger") || user.equals(old.getGuardianUserId1()) || user.equals(old.getGuardianUserId2())
|
||||
|| profile.getLoginname().equals(old.getGuardianLoginName1()) || profile.getLoginname().equals(old.getGuardianLoginName2()), "只能申请更正本人子女资料");
|
||||
require(reason != null && !reason.trim().isEmpty(), "请填写更正原因及证明依据");
|
||||
for (JSONObject change : records("CHILD_CHANGE")) {
|
||||
require(!old.getId().equals(change.getStr("childId")) || !"待核验".equals(change.getStr("status")), "该子女已有待核验的更正申请");
|
||||
}
|
||||
JSONObject after = childIdentity(proposed);
|
||||
for (String field : Arrays.asList("childrenName", "childrenIdCard", "childrenBirthday", "childrenSex", "guardianLoginName1", "guardianRelationship1")) text(after, field);
|
||||
require(!LocalDate.parse(after.getStr("childrenBirthday")).isAfter(LocalDate.now()), "出生日期不能晚于今天");
|
||||
require(Arrays.asList("男", "女").contains(after.getStr("childrenSex")), "请选择有效性别");
|
||||
for (int i = 1; i <= 2; i++) {
|
||||
String login = after.getStr("guardianLoginName" + i, "");
|
||||
if (!login.isEmpty()) {
|
||||
View_user guardian = dao.fetch(View_user.class, Cnd.where("loginname", "=", login));
|
||||
require(guardian != null, "监护人工号不存在");
|
||||
after.set("guardianUserId" + i, guardian.getId()).set("guardianUserName" + i, guardian.getUsername());
|
||||
} else after.set("guardianUserId" + i, null);
|
||||
if (!after.getStr("guardianUserName" + i, "").isEmpty()) text(after, "guardianRelationship" + i);
|
||||
}
|
||||
require(!Objects.equals(after.getStr("guardianUserId1"), after.getStr("guardianUserId2")), "两位监护人不能为同一教职工");
|
||||
JSONObject before = childIdentity(old);
|
||||
require(!before.equals(after), "关键资料没有变化");
|
||||
String id = UUID.randomUUID().toString();
|
||||
JSONObject change = new JSONObject().set("id", id).set("childId", old.getId()).set("userId", user)
|
||||
.set("applicant", profile.getUsername()).set("reason", reason).set("oldData", before).set("newData", after)
|
||||
.set("status", "待核验").set("submittedAt", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
|
||||
put(id, "CHILD_CHANGE", user, null, change);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询更正申请的办理记录,教职工仅可查看本人提交的申请。
|
||||
*
|
||||
* @return 更正前后资料、原因、核验结果和生效月份
|
||||
*/
|
||||
public List<JSONObject> childChanges() {
|
||||
boolean reviewer = tabs().contains("ledger");
|
||||
List<JSONObject> list = records("CHILD_CHANGE");
|
||||
list.removeIf(c -> !reviewer && !SecurityUtil.getUserId().equals(c.getStr("userId")));
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 核验并批准或退回更正,按生效月保留待遇历史版本,不改写已确认清算快照。
|
||||
*
|
||||
* @param input 申请ID、核验结论、审核依据和生效月份
|
||||
*/
|
||||
public void reviewChildChange(JSONObject input) {
|
||||
access("ledger"); lock();
|
||||
OnlyChildFeeRecord record = dao.fetch(OnlyChildFeeRecord.class, input.getStr("id"));
|
||||
require(record != null && "CHILD_CHANGE".equals(record.getKind()), "更正申请不存在");
|
||||
JSONObject change = get(input.getStr("id"));
|
||||
require(change != null && change.getJSONObject("oldData") != null && "待核验".equals(change.getStr("status")), "更正申请不存在或已处理");
|
||||
require(!SecurityUtil.getUserId().equals(change.getStr("userId")), "更正申请须由另一位授权经办人核验");
|
||||
text(input, "basis");
|
||||
String status = input.getStr("status");
|
||||
require(Arrays.asList("已批准", "退回").contains(status), "请选择批准或退回");
|
||||
if ("已批准".equals(status)) {
|
||||
require(Boolean.TRUE.equals(input.getBool("verified")), "请核实更正证明及双方监护人的领取资格");
|
||||
YearMonth effective = month(input.getStr("effectiveMonth"));
|
||||
require(!effective.isAfter(YearMonth.now()), "生效月份不能晚于当前月份");
|
||||
for (JSONObject b : records("BATCH")) {
|
||||
require("草稿".equals(b.getStr("status")) || effective.isAfter(YearMonth.of(b.getInt("year"), 10)), "生效月份不能覆盖已确认清算期间");
|
||||
}
|
||||
ChildManage child = dao.fetch(ChildManage.class, change.getStr("childId"));
|
||||
require(child != null && childIdentity(child).equals(change.getJSONObject("oldData")), "原始关键资料已变化,请退回后重新申请");
|
||||
JSONObject after = change.getJSONObject("newData");
|
||||
require(dao.count(ChildManage.class, Cnd.where("childrenIdCard", "=", after.getStr("childrenIdCard")).and("id", "!=", child.getId())) == 0, "更正后的身份证号已被其他子女记录使用");
|
||||
List<JSONObject> affected = new ArrayList<>();
|
||||
for (JSONObject q : records("QUALIFICATION")) {
|
||||
if (!child.getId().equals(q.getStr("childId"))) continue;
|
||||
JSONArray history = q.getJSONArray("benefitHistory");
|
||||
if (history == null) history = new JSONArray();
|
||||
if (!history.isEmpty()) require(!effective.isBefore(month(history.getJSONObject(history.size() - 1).getStr("untilMonth"))), "生效月份不能早于上次更正");
|
||||
JSONObject previous = new JSONObject(q.toString()); previous.remove("benefitHistory");
|
||||
history.add(previous.set("untilMonth", effective.toString()));
|
||||
q.set("benefitHistory", history).set("childName", after.getStr("childrenName"))
|
||||
.set("birthday", after.getStr("childrenBirthday"))
|
||||
.set("stopMonth", YearMonth.from(LocalDate.parse(after.getStr("childrenBirthday")).plusYears(18)).toString())
|
||||
.set("changeId", change.getStr("id")).set("changeEffectiveMonth", effective.toString());
|
||||
boolean first = q.getStr("userId").equals(after.getStr("guardianUserId1"));
|
||||
boolean second = q.getStr("userId").equals(after.getStr("guardianUserId2"));
|
||||
boolean eligible = (first || second) && LocalDate.parse(after.getStr("childrenBirthday")).isBefore(LocalDate.of(2016, 1, 1));
|
||||
q.set("eligible", eligible).set("relationship", first ? after.getStr("guardianRelationship1") : second ? after.getStr("guardianRelationship2") : "已解除监护关联");
|
||||
if (q.getStr("startMonth") != null) q.set("status", eligible && q.getStr("endMonth") == null && month(q.getStr("stopMonth")).isAfter(YearMonth.now()) ? "已核定" : "已停发");
|
||||
else if (!eligible) q.set("status", "不通过").set("basis", "资料更正后不符合原领取条件");
|
||||
affected.add(q);
|
||||
}
|
||||
// 所有校验先于写入;仅应用批准的关键字段,保留等待审批期间维护的日常资料。
|
||||
child.setChildrenName(after.getStr("childrenName")); child.setChildrenIdCard(after.getStr("childrenIdCard"));
|
||||
child.setChildrenBirthday(java.sql.Date.valueOf(after.getStr("childrenBirthday"))); child.setChildrenSex(after.getStr("childrenSex"));
|
||||
child.setGuardianUserId1(after.getStr("guardianUserId1")); child.setGuardianUserName1(after.getStr("guardianUserName1"));
|
||||
child.setGuardianLoginName1(after.getStr("guardianLoginName1")); child.setGuardianRelationship1(after.getStr("guardianRelationship1"));
|
||||
child.setGuardianUserId2(after.getStr("guardianUserId2")); child.setGuardianUserName2(after.getStr("guardianUserName2"));
|
||||
child.setGuardianLoginName2(after.getStr("guardianLoginName2")); child.setGuardianRelationship2(after.getStr("guardianRelationship2"));
|
||||
View_user first = dao.fetch(View_user.class, Cnd.where("id", "=", child.getGuardianUserId1()));
|
||||
View_user second = child.getGuardianUserId2() == null ? null : dao.fetch(View_user.class, Cnd.where("id", "=", child.getGuardianUserId2()));
|
||||
require(first != null && (child.getGuardianUserId2() == null || second != null), "监护人人员资料已变化,请重新申请");
|
||||
child.setGuardianUnitId1(first.getUnitId()); child.setGuardianUnitName1(first.getUnitName());
|
||||
child.setGuardianUnionId1(first.getUnionId()); child.setGuardianUnionName1(first.getUnionName());
|
||||
child.setGuardianUnitId2(second == null ? null : second.getUnitId());
|
||||
if (second != null) child.setGuardianUnitName2(second.getUnitName());
|
||||
else if (change.getJSONObject("oldData").getStr("guardianUserId2") != null) child.setGuardianUnitName2(null);
|
||||
child.setGuardianUnionId2(second == null ? null : second.getUnionId()); child.setGuardianUnionName2(second == null ? null : second.getUnionName());
|
||||
dao.update(child);
|
||||
for (JSONObject q : affected) { put("Q:" + q.getStr("userId"), "QUALIFICATION", q.getStr("userId"), null, q); audit(q); }
|
||||
change.set("effectiveMonth", effective.toString());
|
||||
}
|
||||
change.set("status", status).set("reviewBasis", input.getStr("basis")).set("reviewer", SecurityUtil.getUserId())
|
||||
.set("reviewedAt", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
|
||||
put(change.getStr("id"), "CHILD_CHANGE", change.getStr("userId"), change.getStr("effectiveMonth"), change);
|
||||
}
|
||||
|
||||
private JSONObject get(String id) {
|
||||
OnlyChildFeeRecord r = dao.fetch(OnlyChildFeeRecord.class, id);
|
||||
return r == null ? null : JSONUtil.parseObj(r.getContent());
|
||||
}
|
||||
|
||||
private void put(String id, String kind, String user, String period, JSONObject data) {
|
||||
OnlyChildFeeRecord r = new OnlyChildFeeRecord();
|
||||
r.setId(id); r.setKind(kind); r.setUserId(user); r.setPeriod(period);
|
||||
r.setContent(data.toString()); r.setOperatorId(SecurityUtil.getUserId()); r.setRecordedAt(new Date());
|
||||
dao.insertOrUpdate(r);
|
||||
}
|
||||
|
||||
private List<JSONObject> records(String kind) {
|
||||
List<JSONObject> result = new ArrayList<>();
|
||||
for (OnlyChildFeeRecord r : dao.query(OnlyChildFeeRecord.class, Cnd.where("kind", "=", kind).asc("id"))) {
|
||||
result.add(JSONUtil.parseObj(r.getContent()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void text(JSONObject d, String key) {
|
||||
require(d.getStr(key) != null && !d.getStr(key).trim().isEmpty(), "请填写" + key);
|
||||
}
|
||||
|
||||
private YearMonth month(String value) {
|
||||
require(value != null && value.matches("\\d{4}-\\d{2}"), "月份格式应为 yyyy-MM");
|
||||
return YearMonth.parse(value);
|
||||
}
|
||||
|
||||
private BigDecimal money(JSONObject d, String key) {
|
||||
BigDecimal n = d.getBigDecimal(key);
|
||||
require(n != null && n.signum() >= 0 && n.scale() <= 2, "金额必须为非负数,最多两位小数");
|
||||
return n;
|
||||
}
|
||||
|
||||
private JSONObject qualification(String user) {
|
||||
JSONObject q = get("Q:" + user);
|
||||
require(q != null, "资格记录不存在");
|
||||
return q;
|
||||
}
|
||||
|
||||
private void audit(JSONObject data) {
|
||||
put(UUID.randomUUID().toString(), "AUDIT", data.getStr("userId"), null, data);
|
||||
}
|
||||
|
||||
/** 返回本人子女、人员资料与可见资格,历史子女不按填报年度限制。
|
||||
* @return 页面初始化数据
|
||||
*/
|
||||
public JSONObject context() {
|
||||
List<String> granted = tabs();
|
||||
if (granted.isEmpty()) return new JSONObject().set("tabs", granted);
|
||||
String user = SecurityUtil.getUserId();
|
||||
View_user profile = dao.fetch(View_user.class, Cnd.where("id", "=", user));
|
||||
List<ChildManage> children = dao.query(ChildManage.class,
|
||||
Cnd.where("guardianUserId1", "=", user).or("guardianUserId2", "=", user)
|
||||
.or("guardianLoginName1", "=", profile == null ? user : profile.getLoginname())
|
||||
.or("guardianLoginName2", "=", profile == null ? user : profile.getLoginname()));
|
||||
List<JSONObject> list = granted.contains("ledger") ? records("QUALIFICATION") : Collections.emptyList();
|
||||
for (JSONObject q : list) {
|
||||
JSONObject baseline = paymentBaseline(q.getStr("userId"));
|
||||
q.set("actualStartMonth", baseline == null ? "" : baseline.getStr("actualStartMonth"));
|
||||
}
|
||||
return new JSONObject().set("tabs", granted)
|
||||
.set("children", granted.contains("qualification") ? children : Collections.emptyList())
|
||||
.set("profile", granted.contains("qualification") ? profile : null)
|
||||
.set("qualifications", list).set("mine", granted.contains("qualification") ? get("Q:" + user) : null);
|
||||
}
|
||||
|
||||
/** 保存本人资格草稿或提交核验,不允许申请人设置核定结果。
|
||||
* @param input 关联子女、证明和监护关系
|
||||
* @param submit 是否提交审核
|
||||
* @return 保存后的资格
|
||||
*/
|
||||
public JSONObject save(JSONObject input, boolean submit) {
|
||||
access("qualification");
|
||||
lock();
|
||||
String user = SecurityUtil.getUserId();
|
||||
JSONObject old = get("Q:" + user);
|
||||
View_user profile = dao.fetch(View_user.class, Cnd.where("id", "=", user));
|
||||
require(profile != null, "人员资料不存在");
|
||||
require(old == null || Arrays.asList("草稿", "退回", "不通过").contains(old.getStr("status")), "当前资格不可重复申请");
|
||||
ChildManage child = dao.fetch(ChildManage.class, input.getStr("childId"));
|
||||
require(child != null && (user.equals(child.getGuardianUserId1()) || user.equals(child.getGuardianUserId2())
|
||||
|| profile.getLoginname().equals(child.getGuardianLoginName1()) || profile.getLoginname().equals(child.getGuardianLoginName2())), "请关联本人已提交的子女资料");
|
||||
require(child.getApplyTime() != null && child.getChildrenBirthday() != null
|
||||
&& child.getChildrenName() != null && child.getChildrenIdCard() != null && child.getChildrenSex() != null, "请先补全并提交子女信息");
|
||||
if (submit) {
|
||||
text(input, "certificateNumber"); text(input, "relationship");
|
||||
require(input.getJSONArray("proof") != null && !input.getJSONArray("proof").isEmpty(), "请上传独生子女证明");
|
||||
require(Boolean.TRUE.equals(input.getBool("onlyChild")), "需要明确声明独生子女资格并提交证明");
|
||||
}
|
||||
String birthday = new SimpleDateFormat("yyyy-MM-dd").format(child.getChildrenBirthday());
|
||||
require(LocalDate.parse(birthday).isBefore(LocalDate.of(2016, 1, 1)), "子女出生日期须早于2016-01-01");
|
||||
JSONObject q = new JSONObject().set("userId", user).set("userName", profile.getUsername())
|
||||
.set("loginName", profile.getLoginname()).set("unitId", profile.getUnitId())
|
||||
.set("unitName", profile.getUnitName()).set("unionName", profile.getUnionName())
|
||||
.set("childId", child.getId()).set("childName", child.getChildrenName()).set("birthday", birthday)
|
||||
.set("stopMonth", YearMonth.from(LocalDate.parse(birthday).plusYears(18)).toString())
|
||||
.set("certificateNumber", input.getStr("certificateNumber")).set("relationship", input.getStr("relationship"))
|
||||
.set("proof", input.getJSONArray("proof")).set("onlyChild", input.getBool("onlyChild"))
|
||||
.set("status", submit ? "待核验" : "草稿");
|
||||
put("Q:" + user, "QUALIFICATION", user, null, q);
|
||||
audit(q);
|
||||
return q;
|
||||
}
|
||||
|
||||
/** 核验资格并独立核定起发月,保存依据和经办人。
|
||||
* @param d 核验结论、起发月份及依据
|
||||
*/
|
||||
public void review(JSONObject d) {
|
||||
access("ledger"); lock();
|
||||
JSONObject q = qualification(d.getStr("userId"));
|
||||
require("待核验".equals(q.getStr("status")), "仅待核验申请可以审核");
|
||||
String status = d.getStr("status");
|
||||
require(Arrays.asList("已核定", "退回", "不通过").contains(status), "无效审核结论");
|
||||
text(d, "basis");
|
||||
if ("已核定".equals(status)) {
|
||||
require(Boolean.TRUE.equals(d.getBool("verified")), "须核验证明和重复领取情况");
|
||||
YearMonth start = month(d.getStr("startMonth"));
|
||||
require(!start.isBefore(YearMonth.from(LocalDate.parse(q.getStr("birthday"))))
|
||||
&& start.isBefore(month(q.getStr("stopMonth"))), "起发月份须在出生月至年龄停发月之前");
|
||||
q.set("startMonth", start.toString()).set("standard", TEN);
|
||||
}
|
||||
q.set("status", status).set("basis", d.getStr("basis")).set("reviewer", SecurityUtil.getUserId());
|
||||
put("Q:" + q.getStr("userId"), "QUALIFICATION", q.getStr("userId"), null, q); audit(q);
|
||||
if ("已核定".equals(status)) {
|
||||
// 核定起发月默认同步为首次实际发放月,已有人工登记或旧年度登记优先保留。
|
||||
String user = q.getStr("userId");
|
||||
if (get("S:" + user) == null) {
|
||||
JSONObject baseline = paymentBaseline(user);
|
||||
put("S:" + user, "PAY_START", user, null, baseline); audit(baseline);
|
||||
}
|
||||
for (JSONObject draft : records("BATCH")) {
|
||||
if ("草稿".equals(draft.getStr("status"))) refreshDraft(draft);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 登记资格终止及实际停发办理依据,历史有效月份仍参与清算。
|
||||
* @param d 领取人、变更生效月份和核实依据
|
||||
*/
|
||||
public void stop(JSONObject d) {
|
||||
access("ledger"); lock(); JSONObject q = qualification(d.getStr("userId"));
|
||||
require(q.getStr("startMonth") != null, "尚未核定待遇"); text(d, "basis");
|
||||
YearMonth end = month(d.getStr("endMonth"));
|
||||
JSONArray history = q.getJSONArray("benefitHistory");
|
||||
if (history != null && !history.isEmpty()) require(!end.isBefore(month(history.getJSONObject(history.size() - 1).getStr("untilMonth"))), "终止月份不能早于最近更正生效月份");
|
||||
require(!end.isBefore(month(q.getStr("startMonth"))), "终止月份不得早于起发月份");
|
||||
require(q.getStr("endMonth") == null, "已登记终止,不可覆盖历史依据");
|
||||
q.set("endMonth", end.toString()).set("stopBasis", d.getStr("basis")).set("status", "已停发")
|
||||
.set("stopOperator", SecurityUtil.getUserId());
|
||||
put("Q:" + q.getStr("userId"), "QUALIFICATION", q.getStr("userId"), null, q); audit(q);
|
||||
}
|
||||
|
||||
private BigDecimal due(JSONObject q, YearMonth m) {
|
||||
JSONArray history = q.getJSONArray("benefitHistory");
|
||||
if (history != null) {
|
||||
for (int i = 0; i < history.size(); i++) {
|
||||
JSONObject version = history.getJSONObject(i);
|
||||
if (m.isBefore(month(version.getStr("untilMonth")))) return due(version, m);
|
||||
}
|
||||
}
|
||||
return !Boolean.FALSE.equals(q.getBool("eligible")) && q.getStr("startMonth") != null && !m.isBefore(month(q.getStr("startMonth")))
|
||||
&& !m.isBefore(YearMonth.from(LocalDate.parse(q.getStr("birthday"))))
|
||||
&& m.isBefore(month(q.getStr("stopMonth")))
|
||||
&& (q.getStr("endMonth") == null || m.isBefore(month(q.getStr("endMonth")))) ? TEN : BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
/** 保存单月实发修订;空金额与零金额严格区分。
|
||||
* @param d 领取人、月份及实发金额
|
||||
*/
|
||||
public void payment(JSONObject d) {
|
||||
access("ledger", "settlement"); lock(); qualification(d.getStr("userId"));
|
||||
YearMonth m = month(d.getStr("month")); money(d, "paid");
|
||||
JSONObject batch = get("B:" + settlementYear(m));
|
||||
require(batch == null || "草稿".equals(batch.getStr("status")), "已确认期间的实发记录不可改写");
|
||||
JSONObject row = new JSONObject().set("userId", d.getStr("userId")).set("month", m.toString())
|
||||
.set("paid", d.getBigDecimal("paid")).set("source", "人工修订");
|
||||
put("M:" + d.getStr("userId") + ":" + m, "MONTH", d.getStr("userId"), m.toString(), row); audit(row);
|
||||
// 实发补录与草稿重算在同一事务中完成,确认后的历史快照保持不变。
|
||||
if (batch != null && "草稿".equals(batch.getStr("status"))) {
|
||||
refreshDraft(batch);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存领取人首次实际发放月份,各年度共用,并重算未确认草稿。
|
||||
* @param d 领取人、当前办理年度和首次实际发放月份
|
||||
* 起发前及停发当月起为零,发放期间每月10元;单月人工修订优先,已确认批次禁止修改。
|
||||
*/
|
||||
public void paymentStart(JSONObject d) {
|
||||
access("settlement"); lock();
|
||||
String user = d.getStr("userId"); qualification(user);
|
||||
Integer year = d.getInt("year");
|
||||
require(year != null && year >= 1900 && year <= 9999, "年度无效");
|
||||
YearMonth start = month(d.getStr("actualStartMonth"));
|
||||
JSONObject batch = get("B:" + year);
|
||||
require(batch != null && "草稿".equals(batch.getStr("status")), "仅可修改未确认清算草稿的起发月份");
|
||||
boolean included = false;
|
||||
JSONArray rows = batch.getJSONArray("rows");
|
||||
for (int i = 0; i < rows.size(); i++) included |= rows.getJSONObject(i).getStr("userId").equals(user);
|
||||
require(included, "该领取人不在当前清算草稿中");
|
||||
JSONObject baseline = new JSONObject().set("userId", user).set("actualStartMonth", start.toString());
|
||||
// 人员级唯一记录,不再以年度拆分;历史确认快照及人工修订保持原值。
|
||||
put("S:" + user, "PAY_START", user, null, baseline); audit(baseline);
|
||||
for (JSONObject draft : records("BATCH")) {
|
||||
if (!"草稿".equals(draft.getStr("status"))) continue;
|
||||
refreshDraft(draft);
|
||||
}
|
||||
}
|
||||
|
||||
private JSONObject paymentBaseline(String user) {
|
||||
JSONObject baseline = get("S:" + user);
|
||||
if (baseline != null) return baseline;
|
||||
// 兼容旧年度登记:沿用最后一次保存的月份,重新确认后写入人员级记录。
|
||||
OnlyChildFeeRecord legacy = dao.fetch(OnlyChildFeeRecord.class,
|
||||
Cnd.where("kind", "=", "PAY_START").and("userId", "=", user).desc("recordedAt").desc("id"));
|
||||
if (legacy != null) return JSONUtil.parseObj(legacy.getContent());
|
||||
// 兼容已核定但未登记实际起发月的人员,清算和台账共同使用核定月份作为默认值。
|
||||
JSONObject q = get("Q:" + user);
|
||||
if (q == null || q.getStr("startMonth") == null) return null;
|
||||
return new JSONObject().set("userId", user).set("actualStartMonth", q.getStr("startMonth"));
|
||||
}
|
||||
|
||||
private JSONObject actualPayment(JSONObject q, String period, JSONObject baseline) {
|
||||
String user = q.getStr("userId");
|
||||
// 先读取单月修订记录,再按首次发放月份计算默认实发。
|
||||
JSONObject actual = get("M:" + user + ":" + period);
|
||||
if (actual != null && !Boolean.TRUE.equals(actual.getBool("automatic"))) return actual;
|
||||
// 到龄或资格终止当月自动停发;真实多发由上方的单月修订记录保留。
|
||||
if (automaticPaymentStopped(q, month(period))) {
|
||||
return new JSONObject().set("userId", user).set("month", period).set("automatic", true)
|
||||
.set("paid", BigDecimal.ZERO).set("source", "按停发月份自动停发");
|
||||
}
|
||||
if (baseline == null) return actual;
|
||||
// 各年度共用首次发放月份,仅在尚未停发的期间自动计发。
|
||||
YearMonth start = month(baseline.getStr("actualStartMonth"));
|
||||
return new JSONObject().set("userId", user).set("month", period).set("automatic", true)
|
||||
.set("paid", month(period).isBefore(start) ? BigDecimal.ZERO : TEN)
|
||||
.set("source", "按首次实际发放月份" + start + "自动计算");
|
||||
}
|
||||
|
||||
private boolean automaticPaymentStopped(JSONObject q, YearMonth period) {
|
||||
// 资料更正按生效月份分段,不能用更正后的停发月倒改此前的默认实发。
|
||||
JSONArray history = q.getJSONArray("benefitHistory");
|
||||
if (history != null) {
|
||||
for (int i = 0; i < history.size(); i++) {
|
||||
JSONObject version = history.getJSONObject(i);
|
||||
if (period.isBefore(month(version.getStr("untilMonth")))) return automaticPaymentStopped(version, period);
|
||||
}
|
||||
}
|
||||
return Boolean.FALSE.equals(q.getBool("eligible")) || !period.isBefore(month(q.getStr("stopMonth")))
|
||||
|| (q.getStr("endMonth") != null && !period.isBefore(month(q.getStr("endMonth"))));
|
||||
}
|
||||
|
||||
// 11、12月归属下一年度清算,每批覆盖上一年11月至当年10月。
|
||||
private int settlementYear(YearMonth period) {
|
||||
return period.getMonthValue() >= 11 ? period.getYear() + 1 : period.getYear();
|
||||
}
|
||||
|
||||
private void refreshDraft(JSONObject draft) {
|
||||
int year = draft.getInt("year");
|
||||
draft.set("rows", calculate(year)).set("periodStart", YearMonth.of(year - 1, 11).toString())
|
||||
.set("periodEnd", YearMonth.of(year, 10).toString()).set("automaticStop", true).set("qualificationStartSynced", true);
|
||||
put("B:" + year, "BATCH", null, String.valueOf(year), draft);
|
||||
}
|
||||
|
||||
private BigDecimal recovered(String user, String period) {
|
||||
BigDecimal total = BigDecimal.ZERO;
|
||||
for (OnlyChildFeeRecord r : dao.query(OnlyChildFeeRecord.class,
|
||||
Cnd.where("kind", "=", "RECOVERY").and("userId", "=", user).and("period", "=", period))) {
|
||||
total = total.add(JSONUtil.parseObj(r.getContent()).getBigDecimal("amount"));
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
private JSONArray calculate(int year) {
|
||||
require(year >= 1900 && year <= 9999, "年度无效");
|
||||
YearMonth periodStart = YearMonth.of(year - 1, 11), periodEnd = YearMonth.of(year, 10);
|
||||
JSONArray rows = new JSONArray();
|
||||
for (JSONObject q : records("QUALIFICATION")) {
|
||||
if (q.getStr("startMonth") == null || month(q.getStr("startMonth")).isAfter(periodEnd)) continue;
|
||||
JSONObject baseline = paymentBaseline(q.getStr("userId"));
|
||||
String end = q.getStr("endMonth", q.getStr("stopMonth"));
|
||||
if (end.compareTo(q.getStr("stopMonth")) > 0) end = q.getStr("stopMonth");
|
||||
JSONArray months = new JSONArray();
|
||||
BigDecimal expected = BigDecimal.ZERO, paid = BigDecimal.ZERO, back = BigDecimal.ZERO, pending = BigDecimal.ZERO;
|
||||
boolean complete = true;
|
||||
for (int i = 0; i < 12; i++) {
|
||||
YearMonth current = periodStart.plusMonths(i);
|
||||
String m = current.toString();
|
||||
BigDecimal amount = due(q, current);
|
||||
JSONObject actual = actualPayment(q, m, baseline);
|
||||
BigDecimal deducted = recovered(q.getStr("userId"), m);
|
||||
BigDecimal over = actual == null ? null : actual.getBigDecimal("paid").subtract(amount).max(BigDecimal.ZERO);
|
||||
BigDecimal remaining = over == null ? null : over.subtract(deducted).max(BigDecimal.ZERO);
|
||||
complete &= actual != null; expected = expected.add(amount); back = back.add(deducted);
|
||||
if (actual != null) { paid = paid.add(actual.getBigDecimal("paid")); pending = pending.add(remaining); }
|
||||
months.add(new JSONObject().set("month", m).set("due", amount).set("paid", actual == null ? null : actual.getBigDecimal("paid"))
|
||||
.set("source", actual == null ? null : actual.getStr("source")).set("recovered", deducted).set("pending", remaining)
|
||||
.set("note", m.equals(q.getStr("stopMonth")) ? "满18周岁当月停发" : ""));
|
||||
}
|
||||
// 已在往期停发且本期无应发、真实实发或扣回记录的人员,不重复进入年度清算。
|
||||
if (end.compareTo(periodStart.toString()) < 0 && expected.signum() == 0 && paid.signum() == 0 && back.signum() == 0) continue;
|
||||
rows.add(new JSONObject(q.toString()).set("actualStartMonth", baseline == null ? null : baseline.getStr("actualStartMonth"))
|
||||
.set("months", months).set("due", expected).set("paid", complete ? paid : null)
|
||||
.set("recovered", back).set("pending", complete ? pending : null)
|
||||
.set("status", !complete ? "资料不全" : pending.signum() > 0 ? "待扣回" : "无变动"));
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
/** 查询年度清算;确认后只读取冻结快照。
|
||||
* @param year 清算年度
|
||||
* @return 批次、逐月依据及扣回执行记录
|
||||
*/
|
||||
public JSONObject settlement(int year) {
|
||||
access("settlement", "changes"); lock();
|
||||
require(year >= 1900 && year <= 9999, "年度无效");
|
||||
JSONObject b = get("B:" + year);
|
||||
// 自动停发及起发月同步只重算未确认草稿,已确认批次的金额及期间保持冻结。
|
||||
if (b != null && "草稿".equals(b.getStr("status"))
|
||||
&& (!YearMonth.of(year - 1, 11).toString().equals(b.getStr("periodStart"))
|
||||
|| !Boolean.TRUE.equals(b.getBool("automaticStop"))
|
||||
|| !Boolean.TRUE.equals(b.getBool("qualificationStartSynced")))) refreshDraft(b);
|
||||
if (b != null && b.getStr("periodStart") == null) {
|
||||
b.set("periodStart", YearMonth.of(year, 1).toString()).set("periodEnd", YearMonth.of(year, 10).toString());
|
||||
}
|
||||
return new JSONObject().set("batch", b).set("rows", b == null ? new JSONArray() : b.getJSONArray("rows"))
|
||||
.set("recoveries", records("RECOVERY"));
|
||||
}
|
||||
|
||||
/** 生成草稿或复核确认;确认时重新核算并拒绝缺失数据。
|
||||
* @param year 清算年度
|
||||
* @param confirm 是否确认并冻结
|
||||
*/
|
||||
public void batch(int year, boolean confirm) {
|
||||
access("settlement"); lock(); JSONObject old = get("B:" + year);
|
||||
require(old == null || "草稿".equals(old.getStr("status")), "批次已经确认,不能重建");
|
||||
require(!confirm || old != null, "请先生成草稿");
|
||||
JSONArray rows = calculate(year);
|
||||
require(!rows.isEmpty(), "没有可清算的核定待遇");
|
||||
if (confirm) {
|
||||
for (int i = 0; i < rows.size(); i++) require(!"资料不全".equals(rows.getJSONObject(i).getStr("status")), "请补齐全部实发记录后确认");
|
||||
require(rows.toString().equals(old.getJSONArray("rows").toString()), "数据已变化,请重新生成草稿并复核");
|
||||
}
|
||||
boolean pending = false;
|
||||
for (int i = 0; i < rows.size(); i++) pending |= rows.getJSONObject(i).getBigDecimal("pending", BigDecimal.ZERO).signum() > 0;
|
||||
JSONObject b = new JSONObject().set("year", year).set("id", year + "年11月").set("rows", rows)
|
||||
.set("periodStart", YearMonth.of(year - 1, 11).toString()).set("periodEnd", YearMonth.of(year, 10).toString()).set("automaticStop", true).set("qualificationStartSynced", true)
|
||||
.set("status", confirm ? (pending ? "已确认" : "已完成") : "草稿")
|
||||
.set("operator", SecurityUtil.getUserId()).set("time", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
|
||||
put("B:" + year, "BATCH", null, String.valueOf(year), b);
|
||||
}
|
||||
|
||||
/**
|
||||
* 登记本期全部结清,或按列出的多发月份分别登记实际扣回。
|
||||
* @param d 领取人、年度、全部结清标记及取消勾选时填写的月度扣回明细
|
||||
* 全部结清的金额由服务端计算;未提供的执行月份留空,单月金额不得超过余额。
|
||||
*/
|
||||
public void recover(JSONObject d) {
|
||||
access("settlement"); lock();
|
||||
// 保留旧单月调用入口;新表单由服务端分摊,不能依赖客户端计算的余额。
|
||||
if (!d.containsKey("settledAll")) { recoverMonth(d); return; }
|
||||
Integer year = d.getInt("year");
|
||||
require(year != null && year >= 1900 && year <= 9999, "年度无效");
|
||||
String user = d.getStr("userId"); qualification(user);
|
||||
JSONObject batch = settlement(year).getJSONObject("batch");
|
||||
require(batch != null, "请先生成清算批次");
|
||||
JSONObject person = null;
|
||||
JSONArray rows = batch.getJSONArray("rows");
|
||||
for (int i = 0; i < rows.size(); i++) {
|
||||
if (user.equals(rows.getJSONObject(i).getStr("userId"))) person = rows.getJSONObject(i);
|
||||
}
|
||||
require(person != null && person.getBigDecimal("paid") != null, "请先补齐该人员的实发记录");
|
||||
JSONArray months = person.getJSONArray("months"), entries = new JSONArray();
|
||||
Set<String> allowed = new HashSet<>();
|
||||
boolean all = Boolean.TRUE.equals(d.getBool("settledAll"));
|
||||
for (int i = 0; i < months.size(); i++) {
|
||||
JSONObject detail = months.getJSONObject(i);
|
||||
String period = detail.getStr("month"); allowed.add(period);
|
||||
if (!all) continue;
|
||||
BigDecimal remaining = detail.getBigDecimal("paid").subtract(detail.getBigDecimal("due")).max(BigDecimal.ZERO)
|
||||
.subtract(recovered(user, period)).max(BigDecimal.ZERO);
|
||||
if (remaining.signum() > 0) entries.add(new JSONObject().set("month", period).set("amount", remaining));
|
||||
}
|
||||
if (!all) {
|
||||
entries = d.getJSONArray("recoveryMonths");
|
||||
require(entries != null && !entries.isEmpty(), "请至少填写一个月份的实际扣回月份和金额");
|
||||
}
|
||||
Set<String> entered = new HashSet<>();
|
||||
for (int i = 0; i < entries.size(); i++) {
|
||||
JSONObject detail = entries.getJSONObject(i);
|
||||
String period = detail.getStr("month");
|
||||
require(allowed.contains(period) && entered.add(period), "扣回明细月份不在本期内或重复");
|
||||
JSONObject item = new JSONObject().set("userId", user).set("year", year).set("month", period)
|
||||
.set("amount", detail.getBigDecimal("amount")).set("executionMonth", all ? null : detail.getStr("executionMonth"))
|
||||
.set("settledAll", all).set("historical", "草稿".equals(batch.getStr("status")));
|
||||
recoverMonth(item);
|
||||
}
|
||||
if ("草稿".equals(batch.getStr("status"))) refreshDraft(batch);
|
||||
}
|
||||
|
||||
/** 登记针对某一应发月份的实际扣回,限制余额并保留办理记录。
|
||||
* @param d 归属月份、实际扣回月份、金额、凭据及是否历史扣回
|
||||
*/
|
||||
private void recoverMonth(JSONObject d) {
|
||||
access("settlement"); lock();
|
||||
String user = d.getStr("userId"); JSONObject q = qualification(user);
|
||||
YearMonth period = month(d.getStr("month"));
|
||||
int year = settlementYear(period);
|
||||
require(d.getInt("year") == null || d.getInt("year") == year, "原发放月份不在当前清算期间内");
|
||||
if (!Boolean.TRUE.equals(d.getBool("settledAll"))) {
|
||||
require(!month(d.getStr("executionMonth")).isBefore(period), "扣回月份不得早于原发放月份");
|
||||
}
|
||||
BigDecimal amount = money(d, "amount"); require(amount.signum() > 0, "实际扣回须大于零");
|
||||
JSONObject b = get("B:" + year);
|
||||
boolean confirmed = b != null && !"草稿".equals(b.getStr("status"));
|
||||
require(confirmed || Boolean.TRUE.equals(d.getBool("historical")), "请先确认批次,或明确登记历史已扣回");
|
||||
JSONObject actual = actualPayment(q, period.toString(), paymentBaseline(user));
|
||||
require(actual != null, "请先登记该月实发");
|
||||
BigDecimal over = actual.getBigDecimal("paid").subtract(due(q, period)).max(BigDecimal.ZERO);
|
||||
if (confirmed) {
|
||||
JSONObject snapshot = null;
|
||||
for (int i = 0; i < b.getJSONArray("rows").size(); i++) {
|
||||
JSONObject row = b.getJSONArray("rows").getJSONObject(i);
|
||||
if (!user.equals(row.getStr("userId"))) continue;
|
||||
JSONArray details = row.getJSONArray("months");
|
||||
for (int j = 0; j < details.size(); j++) {
|
||||
JSONObject detail = details.getJSONObject(j);
|
||||
if (period.toString().equals(detail.getStr("month"))) snapshot = detail;
|
||||
}
|
||||
}
|
||||
require(snapshot != null, "该领取人不在确认批次中");
|
||||
over = snapshot.getBigDecimal("paid").subtract(snapshot.getBigDecimal("due")).max(BigDecimal.ZERO);
|
||||
}
|
||||
require(amount.compareTo(over.subtract(recovered(user, period.toString()))) <= 0, "超过该月份尚未扣回余额");
|
||||
JSONObject record = new JSONObject().set("userId", user).set("month", period.toString()).set("amount", amount)
|
||||
.set("executionMonth", d.getStr("executionMonth")).set("voucher", d.getStr("voucher"))
|
||||
.set("operator", SecurityUtil.getUserId()).set("historical", !confirmed).set("settledAll", Boolean.TRUE.equals(d.getBool("settledAll")));
|
||||
put(UUID.randomUUID().toString(), "RECOVERY", user, period.toString(), record);
|
||||
if (confirmed) {
|
||||
boolean finished = true;
|
||||
for (int i = 0; i < b.getJSONArray("rows").size(); i++) {
|
||||
JSONObject row = b.getJSONArray("rows").getJSONObject(i);
|
||||
for (int j = 0; j < row.getJSONArray("months").size(); j++) {
|
||||
JSONObject m = row.getJSONArray("months").getJSONObject(j);
|
||||
finished &= m.getBigDecimal("paid").subtract(m.getBigDecimal("due")).max(BigDecimal.ZERO)
|
||||
.compareTo(recovered(row.getStr("userId"), m.getStr("month"))) <= 0;
|
||||
}
|
||||
}
|
||||
if (finished) { b.set("status", "已完成"); put("B:" + year, "BATCH", null, String.valueOf(year), b); }
|
||||
}
|
||||
}
|
||||
|
||||
/** 读取原退休申请及审批状态,退休奖励不套用月费年龄限制。
|
||||
* @return 原申请、审批状态和独立支付凭据
|
||||
*/
|
||||
public JSONArray retirement() {
|
||||
access("retirement"); JSONArray rows = new JSONArray();
|
||||
for (Dsznfmtx a : dao.query(Dsznfmtx.class, Cnd.NEW().desc("applyTime"))) {
|
||||
boolean approved = dao.count(ProcessInstance.class, Cnd.where("businessNo", "=", a.getId()).and("state", "=", 20)) > 0;
|
||||
ProcessInstance instance = dao.fetch(ProcessInstance.class, Cnd.where("businessNo", "=", a.getId()).desc("id"));
|
||||
rows.add(JSONUtil.parseObj(a).set("approved", approved).set("payment", get("R:" + a.getUserId()))
|
||||
.set("instanceId", instance == null ? null : instance.getId())
|
||||
.set("instanceProcessDefineId", instance == null ? null : instance.getProcessDefineId()));
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
/** 核实历史奖励后登记一次性3000元发放,同一教职工只允许登记一次。
|
||||
* @param d 原申请ID、历史核查依据、发放日期及凭据
|
||||
*/
|
||||
public void reward(JSONObject d) {
|
||||
access("retirement"); lock(); Dsznfmtx a = dao.fetch(Dsznfmtx.class, d.getStr("id"));
|
||||
require(a != null, "原退休申请不存在");
|
||||
require(dao.count(ProcessInstance.class, Cnd.where("businessNo", "=", a.getId()).and("state", "=", 20)) > 0, "原申请尚未审批通过");
|
||||
require(get("R:" + a.getUserId()) == null, "该教职工已有奖励发放记录");
|
||||
text(d, "historyBasis"); text(d, "voucher"); text(d, "paidDate");
|
||||
require(Boolean.TRUE.equals(d.getBool("historyChecked")), "须核实历史记录未重复领取");
|
||||
require(!LocalDate.parse(d.getStr("paidDate")).isAfter(LocalDate.now()), "实际发放日期不可晚于今天");
|
||||
JSONObject payment = new JSONObject().set("applicationId", a.getId()).set("userId", a.getUserId())
|
||||
.set("amount", new BigDecimal("3000.00")).set("paidDate", d.getStr("paidDate"))
|
||||
.set("voucher", d.getStr("voucher")).set("historyBasis", d.getStr("historyBasis")).set("operator", SecurityUtil.getUserId());
|
||||
put("R:" + a.getUserId(), "REWARD", a.getUserId(), d.getStr("paidDate"), payment);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
-- 月费与退休奖励凭据独立于子女资料;主键确保同一教职工、月份和年度不重复。
|
||||
CREATE TABLE IF NOT EXISTS only_child_fee_record (
|
||||
id varchar(100) PRIMARY KEY,
|
||||
kind varchar(20) NOT NULL,
|
||||
userId varchar(32),
|
||||
period varchar(10),
|
||||
content longtext,
|
||||
operatorId varchar(32),
|
||||
recordedAt datetime,
|
||||
KEY idx_fee_kind_user (kind, userId),
|
||||
KEY idx_fee_kind_period (kind, period)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
-- 事务锁串行化资格变更、快照确认与扣回登记,防止并发重复领取或超额扣回。
|
||||
INSERT IGNORE INTO only_child_fee_record (id, kind, content) VALUES ('LOCK', 'LOCK', '{}');
|
||||
|
||||
-- 补齐旧资料中缺失的账号关联,使原有完整子女信息可以直接复用。
|
||||
-- 使用信息架构判断列是否存在,兼容项目自动迁移及重复执行本脚本。
|
||||
SET @fee_column_sql = IF((SELECT COUNT(*) FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='child_manage' AND column_name='guardianRelationship1')=0,
|
||||
'ALTER TABLE child_manage ADD COLUMN guardianRelationship1 varchar(30) NULL', 'SELECT 1');
|
||||
PREPARE fee_column_stmt FROM @fee_column_sql;
|
||||
EXECUTE fee_column_stmt;
|
||||
DEALLOCATE PREPARE fee_column_stmt;
|
||||
SET @fee_column_sql = IF((SELECT COUNT(*) FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='child_manage' AND column_name='guardianRelationship2')=0,
|
||||
'ALTER TABLE child_manage ADD COLUMN guardianRelationship2 varchar(30) NULL', 'SELECT 1');
|
||||
PREPARE fee_column_stmt FROM @fee_column_sql;
|
||||
EXECUTE fee_column_stmt;
|
||||
DEALLOCATE PREPARE fee_column_stmt;
|
||||
UPDATE child_manage c JOIN vw_user u ON u.loginname = c.guardianLoginName1
|
||||
SET c.guardianUserId1 = u.id WHERE c.guardianUserId1 IS NULL OR c.guardianUserId1 = '';
|
||||
UPDATE child_manage c JOIN vw_user u ON u.loginname = c.guardianLoginName2
|
||||
SET c.guardianUserId2 = u.id WHERE c.guardianUserId2 IS NULL OR c.guardianUserId2 = '';
|
||||
|
||||
-- 与“我的子女”使用同一父菜单,沿用已有子女模块角色的可见范围。
|
||||
INSERT INTO sys_menu (id,parentId,path,name,type,href,target,icon,showit,disabled,permission,location,hasChildren,platform,moduleId,createdAt,delFlag)
|
||||
SELECT 'only_child_fee_menu',p.id,CONCAT(p.path,'0099'),'独生子女费','menu',
|
||||
'/platform/childManage/onlyChildFee/index','data-pjax','fa fa-list-alt',1,0,'childManage.onlyChildFee',99,0,'PC',m.moduleId,UNIX_TIMESTAMP(),0
|
||||
FROM sys_menu m JOIN sys_menu p ON p.id=m.parentId
|
||||
WHERE m.permission='childManage.mine'
|
||||
AND NOT EXISTS (SELECT 1 FROM sys_menu x WHERE x.permission='childManage.onlyChildFee');
|
||||
INSERT INTO sys_role_menu (roleId,menuId)
|
||||
SELECT DISTINCT rm.roleId,'only_child_fee_menu'
|
||||
FROM sys_role_menu rm JOIN sys_menu m ON m.id=rm.menuId
|
||||
WHERE m.permission IN ('childManage.mine','childManage.write','childManage.manage')
|
||||
AND EXISTS (SELECT 1 FROM sys_menu x WHERE x.id='only_child_fee_menu')
|
||||
AND NOT EXISTS (SELECT 1 FROM sys_role_menu x WHERE x.roleId=rm.roleId AND x.menuId='only_child_fee_menu');
|
||||
|
||||
-- 五个独立页面作为独生子女费的子菜单,沿用角色菜单授权;首次迁移后由角色配置维护。
|
||||
SET @fee_tabs_first_install = NOT EXISTS (
|
||||
SELECT 1 FROM sys_menu WHERE permission IN (
|
||||
'childManage.onlyChildFee.qualification', 'childManage.onlyChildFee.settlement',
|
||||
'childManage.onlyChildFee.changes', 'childManage.onlyChildFee.retirement', 'childManage.onlyChildFee.ledger'
|
||||
)
|
||||
);
|
||||
UPDATE sys_menu SET hasChildren=1, platform='PC'
|
||||
WHERE permission='childManage.onlyChildFee';
|
||||
|
||||
INSERT INTO sys_menu (id,parentId,path,name,type,href,target,icon,showit,disabled,permission,location,hasChildren,platform,moduleId,createdAt,delFlag)
|
||||
SELECT t.id,p.id,CONCAT(p.path,t.pathSuffix),t.name,'menu',
|
||||
CONCAT('/platform/childManage/onlyChildFee/',t.tab,'/index'),'data-pjax','fa fa-list-alt',1,0,
|
||||
CONCAT('childManage.onlyChildFee.',t.tab),t.location,0,'PC',p.moduleId,UNIX_TIMESTAMP(),0
|
||||
FROM sys_menu p
|
||||
CROSS JOIN (
|
||||
SELECT 'only_child_fee_qualification' AS id, 'qualification' AS tab, '资格登记' AS name, '0001' AS pathSuffix, 1 AS location
|
||||
UNION ALL SELECT 'only_child_fee_settlement','settlement','年度清算','0002',2
|
||||
UNION ALL SELECT 'only_child_fee_changes','changes','变动报表','0003',3
|
||||
UNION ALL SELECT 'only_child_fee_retirement','retirement','退休奖励','0004',4
|
||||
UNION ALL SELECT 'only_child_fee_ledger','ledger','待遇台账','0005',5
|
||||
) t
|
||||
WHERE p.permission='childManage.onlyChildFee'
|
||||
AND NOT EXISTS (SELECT 1 FROM sys_menu x WHERE x.permission=CONCAT('childManage.onlyChildFee.',t.tab));
|
||||
|
||||
-- 普通教职工保留本人资格登记;原管理角色保留办理页签,重复执行不恢复已撤销的子菜单授权。
|
||||
INSERT INTO sys_role_menu (roleId,menuId)
|
||||
SELECT DISTINCT r.id,c.id
|
||||
FROM sys_role r
|
||||
JOIN sys_role_menu rm ON rm.roleId=r.id
|
||||
JOIN sys_menu p ON p.id=rm.menuId AND p.permission='childManage.onlyChildFee'
|
||||
JOIN sys_menu c ON c.parentId=p.id
|
||||
WHERE @fee_tabs_first_install=1
|
||||
AND c.permission IN ('childManage.onlyChildFee.qualification','childManage.onlyChildFee.settlement',
|
||||
'childManage.onlyChildFee.changes','childManage.onlyChildFee.retirement','childManage.onlyChildFee.ledger')
|
||||
AND (c.permission='childManage.onlyChildFee.qualification'
|
||||
OR r.code IN ('SYSADMIN','SCHOOL_UNION_ADMIN','SCHOOL_UNION_WELFARE_ADMIN'))
|
||||
AND NOT EXISTS (SELECT 1 FROM sys_role_menu existing WHERE existing.roleId=r.id AND existing.menuId=c.id);
|
||||
|
||||
-- 将已有子菜单映射至独立页面,仅更新地址,保留现有角色授权。
|
||||
UPDATE sys_menu SET href=CONCAT('/platform/childManage/onlyChildFee/',SUBSTRING_INDEX(permission,'.',-1),'/index')
|
||||
WHERE permission IN ('childManage.onlyChildFee.qualification','childManage.onlyChildFee.settlement',
|
||||
'childManage.onlyChildFee.changes','childManage.onlyChildFee.retirement','childManage.onlyChildFee.ledger');
|
||||
@@ -1,6 +1,16 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="file-preview-container" v-if="fileList && fileList.length > 0">
|
||||
<div class="file-preview-list" v-if="compact && fileList && fileList.length > 0">
|
||||
<div class="file-preview-row" v-for="item in fileList" :key="item.id">
|
||||
<i class="el-icon-document file-preview-icon" aria-hidden="true"></i>
|
||||
<span class="file-preview-name" :title="item.name">{{ item.name }}</span>
|
||||
<div class="file-preview-actions">
|
||||
<button type="button" :aria-label="'预览' + item.name" @click="$commonUtil.previewFile(item)">预览</button>
|
||||
<button type="button" :aria-label="'下载' + item.name" @click="$downLoad(item.downloadPath)">下载</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="file-preview-container" v-else-if="fileList && fileList.length > 0">
|
||||
<div class="file-item" title="点击查看" v-for="item in fileList" :key="item.id">
|
||||
<el-image
|
||||
v-if="item.suffix === 'jpg' || item.suffix === 'png' || item.suffix === 'jpeg' || item.suffix === 'gif'"
|
||||
@@ -24,6 +34,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span v-else-if="compact" class="file-preview-empty">暂无证明材料</span>
|
||||
<el-empty v-else description="暂无" :image-size="60"></el-empty>
|
||||
</div>
|
||||
</template>
|
||||
@@ -32,6 +43,11 @@
|
||||
module.exports = {
|
||||
name: "sysFilePreview",
|
||||
props: {
|
||||
// 表单只读态使用紧凑附件行,保留预览和下载操作,不占用缩略图空间。
|
||||
compact: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
files: {
|
||||
type: [String, Array],
|
||||
default: undefined,
|
||||
@@ -112,6 +128,69 @@ module.exports = {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.file-preview-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.file-preview-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
min-width: 0;
|
||||
min-height: 40px;
|
||||
padding: 0 12px;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 4px;
|
||||
background: #f8fafc;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.file-preview-icon {
|
||||
flex-shrink: 0;
|
||||
color: #0075cf;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.file-preview-name {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: #606266;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.file-preview-actions {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.file-preview-actions button {
|
||||
padding: 8px 0;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: #0075cf;
|
||||
font: inherit;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.file-preview-actions button:focus-visible {
|
||||
outline: 2px solid #0075cf;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.file-preview-empty {
|
||||
color: #909399;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.file-preview-container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
|
||||
@@ -17,10 +17,10 @@ const childManageInfo = {
|
||||
<el-descriptions-item label="中考总成绩">{{viewData.childrenMiddleScore}}</el-descriptions-item>
|
||||
<el-descriptions-item label="学科组合">{{viewData.childrenSubjectCombination}}</el-descriptions-item>
|
||||
<el-descriptions-item label="户籍所在地" :span="2">{{viewData.childrenHuKouAddress}}</el-descriptions-item>
|
||||
<el-descriptions-item label="监护人1">{{viewData.guardianUserName1}}</el-descriptions-item>
|
||||
<el-descriptions-item label="监护人1">{{viewData.guardianUserName1}}({{viewData.guardianRelationship1 || '关系待补充'}})</el-descriptions-item>
|
||||
<el-descriptions-item label="联系电话">{{viewData.guardianMobile1}}</el-descriptions-item>
|
||||
<el-descriptions-item label="工作单位">{{viewData.guardianUnitName1}}</el-descriptions-item>
|
||||
<el-descriptions-item label="监护人2">{{viewData.guardianUserName2}}</el-descriptions-item>
|
||||
<el-descriptions-item label="监护人2">{{viewData.guardianUserName2}}({{viewData.guardianRelationship2 || '关系待补充'}})</el-descriptions-item>
|
||||
<el-descriptions-item label="联系电话">{{viewData.guardianMobile2}}</el-descriptions-item>
|
||||
<el-descriptions-item label="工作单位">{{viewData.guardianUnitName2}}</el-descriptions-item>
|
||||
<el-descriptions-item label="备注">{{viewData.note}}</el-descriptions-item>
|
||||
|
||||
@@ -20,6 +20,7 @@ layout("/layouts/platform.html"){
|
||||
</el-card>
|
||||
<el-card class="mt10" shadow="never">
|
||||
<table-tool label="填报列表">
|
||||
<el-button type="primary" @click="openOnlyChildFee">独生子女费</el-button>
|
||||
</table-tool>
|
||||
<!-- 添加一个带有横向滚动的容器 -->
|
||||
<div class="table-container">
|
||||
@@ -89,11 +90,14 @@ layout("/layouts/platform.html"){
|
||||
return {
|
||||
pageDataUrl: "/platform/childManage/mine/pageData",
|
||||
pageForm: {
|
||||
year: moment().format("YYYY"),
|
||||
year: null,
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openOnlyChildFee() {
|
||||
commonUtil.pjaxPush('/platform/childManage/onlyChildFee/qualification/index')
|
||||
},
|
||||
openView(row) {
|
||||
this.$refs.guava.view(()=>{
|
||||
this.$refs.childManageInfoRef.onOpen(row)
|
||||
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
<!--# layout("/layouts/platform.html"){ #-->
|
||||
<div id="app" v-cloak class="only-child-fee" v-loading="busy">
|
||||
<el-card shadow="never">
|
||||
<el-empty v-if="ready && !tabs.includes(tab)" description="暂无当前页面的访问权限"></el-empty>
|
||||
<template v-if="tabs.includes(tab)">
|
||||
<div class="fee-filter"><label>年度 <el-date-picker v-model="year" type="year" value-format="yyyy" :clearable="false" @change="loadSettlement"></el-date-picker></label><el-select v-model="unit" clearable placeholder="全部单位"><el-option v-for="u in units" :key="u" :value="u"></el-option></el-select><el-input v-model="keyword" placeholder="姓名 / 工号" clearable></el-input><el-select v-model="changeType" clearable placeholder="全部变动"><el-option value="停发"></el-option><el-option value="扣回"></el-option></el-select><el-button type="primary" @click="loadSettlement">查询</el-button><el-button @click="reset">重置</el-button><el-button :disabled="!confirmed" @click="exportChanges">导出Excel</el-button></div>
|
||||
<div class="fee-recovery-summary fee-period-summary">
|
||||
<div class="fee-period-item"><span class="fee-period-label">批次</span><strong class="fee-period-value">{{batch ? batch.id : '尚未生成'}}</strong></div>
|
||||
<div class="fee-period-item"><span class="fee-period-label">核算期间</span><strong class="fee-period-value">{{periodLabel}}</strong></div>
|
||||
</div>
|
||||
<div class="fee-paper fee-notice-paper">
|
||||
<table class="fee-notice-table">
|
||||
<colgroup><col style="width:4%"><col style="width:10%"><col style="width:10%"><col style="width:22%"><col style="width:6%"><col style="width:6%"><col style="width:8%"><col style="width:6%"><col style="width:19%"><col style="width:9%"></colgroup>
|
||||
<thead>
|
||||
<tr><th colspan="10" class="fee-notice-title">{{Number(year)-1}}年11月—{{year}}年10月独生子女父母奖励费变动通知单</th></tr>
|
||||
<tr><td colspan="4" class="fee-notice-recipient">人力资源处:</td><td colspan="6" class="fee-notice-unit">单位:元</td></tr>
|
||||
<tr><th>序号</th><th>工号</th><th>姓名</th><th>单位</th><th>调前<br>标准</th><th>调后<br>标准</th><th>扣发<br>金额</th><th>补发<br>金额</th><th>停发时间</th><th>备注<br>说明</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(row,index) in changes" :key="row.userId"><td>{{index+1}}</td><td>{{row.loginName}}</td><td>{{row.userName}}</td><td>{{row.unitName}}</td><td>10.00</td><td>{{cash(endMonth(row)<=periodEnd?0:10)}}</td><td>{{cash(row.deduction)}}</td><td>0.00</td><td>{{noticeStopRange(row)}}</td><td>{{row.stopBasis || (endMonth(row)<=periodEnd?'停发':'扣发调整')}}</td></tr>
|
||||
<tr v-if="!changes.length"><td colspan="10">暂无变动记录</td></tr>
|
||||
<tr><td colspan="4">合计</td><td>{{cash(changeTotals.before)}}</td><td>{{cash(changeTotals.after)}}</td><td>{{cash(changeTotals.deduction)}}</td><td>0.00</td><td></td><td></td></tr>
|
||||
</tbody>
|
||||
<tfoot><tr><td colspan="4">审核人:</td><td colspan="6">经办人:</td></tr></tfoot>
|
||||
</table>
|
||||
</div><el-alert title="导出当前筛选的全部记录,仅包含“变动表”工作表;确认前不可导出办理。" type="info" :closable="false" show-icon></el-alert>
|
||||
</template>
|
||||
</el-card>
|
||||
<!--#include('dialogs.html'){}#-->
|
||||
</div>
|
||||
<script>
|
||||
<!--#include('page.js'){}#-->
|
||||
initOnlyChildFeePage('changes');
|
||||
</script>
|
||||
<!--#include('style.html'){}#-->
|
||||
<!--# } #-->
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
<el-dialog title="子女关键资料更正核验" :visible.sync="changeDialog" width="760px" :close-on-click-modal="false">
|
||||
<template v-if="selectedChange">
|
||||
<p>申请人:{{selectedChange.applicant}} 申请时间:{{selectedChange.submittedAt}}</p>
|
||||
<p>更正原因及证明依据:{{selectedChange.reason}}</p>
|
||||
<el-table :data="correctionDiff" border><el-table-column prop="label" label="更正字段"></el-table-column><el-table-column prop="before" label="修改前"></el-table-column><el-table-column prop="after" label="拟修改为"></el-table-column></el-table>
|
||||
<el-form v-if="selectedChange.status === '待核验'" label-width="140px" class="mt20">
|
||||
<el-form-item label="核验结论" required><el-select v-model="changeReview.status"><el-option value="已批准" label="批准更正"></el-option><el-option value="退回" label="退回补正"></el-option></el-select></el-form-item>
|
||||
<el-form-item label="生效月份" :required="changeReview.status === '已批准'"><el-date-picker v-model="changeReview.effectiveMonth" type="month" value-format="yyyy-MM"></el-date-picker></el-form-item>
|
||||
<el-form-item label="证明及领取资格"><el-checkbox v-model="changeReview.verified">已核实证明及双方监护人的领取资格</el-checkbox></el-form-item>
|
||||
<el-form-item label="审核依据 / 原因" required><el-input v-model="changeReview.basis" type="textarea" maxlength="2000"></el-input></el-form-item>
|
||||
<el-alert type="info" :closable="false" title="批准后按生效月份保留待遇历史版本;不得覆盖已确认清算期间。新监护人的月费资格仍需另行申请。"></el-alert>
|
||||
</el-form>
|
||||
<el-descriptions v-else border :column="2" class="mt20"><el-descriptions-item label="审核结果">{{selectedChange.status}}</el-descriptions-item><el-descriptions-item label="生效月份">{{selectedChange.effectiveMonth || '—'}}</el-descriptions-item><el-descriptions-item label="审核人">{{selectedChange.reviewer}}</el-descriptions-item><el-descriptions-item label="审核时间">{{selectedChange.reviewedAt}}</el-descriptions-item><el-descriptions-item label="审核依据">{{selectedChange.reviewBasis}}</el-descriptions-item></el-descriptions>
|
||||
</template>
|
||||
<span slot="footer"><el-button @click="changeDialog=false">关闭</el-button><el-button v-if="selectedChange && selectedChange.status === '待核验'" type="primary" :loading="busy" @click="submitCorrectionReview">保存核验结果</el-button></span>
|
||||
</el-dialog>
|
||||
<snaker-chart ref="retireChart"></snaker-chart><el-dialog :title="dialogTitle" :visible.sync="dialog" width="680px" :close-on-click-modal="false">
|
||||
<span slot="title" class="el-dialog__title">{{dialogTitle}}<el-tooltip v-if="['payment','recover'].includes(action)" :content="action === 'payment' ? '用于修订单个月份实际发放的金额。修订优先于起发月份自动计算,并更新未确认清算草稿;不会执行工资发放。' : '用于登记已实际扣回的多发费用,不会执行工资扣款。默认勾选已全部结清,按各月剩余金额结清;取消勾选后按月填写,未填写的月份保持不变。'" placement="top" popper-class="fee-help-popper"><i class="el-icon-question fee-help-icon" tabindex="0" role="button" :aria-label="dialogTitle+'说明'"></i></el-tooltip></span>
|
||||
<div v-if="['payment','recover','review','stop'].includes(action)" class="fee-recovery-summary">
|
||||
<div class="fee-recovery-person"><span class="fee-recovery-name">{{editing.userName}}</span><span class="fee-recovery-id">工号 {{editing.loginName}}</span><div class="fee-recovery-caption">{{({recover:'本期扣回登记',payment:'逐月实发修订',review:'资格核验',stop:'停发办理'})[action]}}</div></div>
|
||||
<div v-if="action === 'recover'" class="fee-recovery-balance"><div class="fee-recovery-caption">本期尚未扣回合计</div><strong>{{cash(remaining(editing))}}</strong><span class="fee-recovery-unit">元</span></div>
|
||||
<div v-else-if="action === 'payment'" class="fee-recovery-balance fee-payment-start"><div class="fee-recovery-caption">起发月份</div><strong>{{editing.actualStartMonth || '未登记'}}</strong></div>
|
||||
<div v-else class="fee-recovery-balance fee-record-state"><div class="fee-recovery-caption">{{action === 'review' ? '核验状态' : '年龄停发月'}}</div><strong>{{(action === 'review' ? editing.status : editing.stopMonth) || '—'}}</strong></div>
|
||||
</div>
|
||||
<p v-else>{{editing.userName}} {{editing.loginName}}</p>
|
||||
<div v-if="action === 'review'" class="fee-record-card">
|
||||
<div class="fee-record-fields">
|
||||
<div><span class="fee-record-label">独生子女证号</span><span>{{editing.certificateNumber || '—'}}</span></div>
|
||||
<div><span class="fee-record-label">监护关系</span><span>{{editing.relationship || '—'}}</span></div>
|
||||
<div><span class="fee-record-label">子女姓名</span><span>{{editing.childName || '—'}}</span></div>
|
||||
<div><span class="fee-record-label">出生日期</span><span>{{editing.birthday || '—'}}</span></div>
|
||||
<div class="fee-record-wide"><span class="fee-record-label">证明材料</span><file-preview :files="editing.proof" complete_result compact></file-preview></div>
|
||||
<div class="fee-record-wide"><span class="fee-record-label">原核定依据</span><span>{{editing.basis || '—'}}</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<el-form :class="{'fee-form-card':['review','stop'].includes(action)}" :label-width="['payment','recover'].includes(action) ? '140px' : '150px'" :label-position="['payment','recover','review','stop'].includes(action) ? 'left' : 'right'" :disabled="action === 'review' && editing.status !== '待核验'">
|
||||
<template v-if="action === 'review'"><el-form-item label="审核结论" required><el-select v-model="entry.status"><el-option v-for="s in ['已核定','退回','不通过']" :key="s" :value="s"></el-option></el-select></el-form-item><el-form-item label="独立核定起发月份"><el-date-picker v-model="entry.startMonth" type="month" value-format="yyyy-MM"></el-date-picker></el-form-item><el-form-item label="资格核验"><el-checkbox v-model="entry.verified">已核验证明及重复领取情况</el-checkbox></el-form-item></template>
|
||||
<el-form-item v-if="action === 'stop'" label="终止 / 实际停发月" required><el-date-picker v-model="entry.endMonth" type="month" value-format="yyyy-MM"></el-date-picker></el-form-item>
|
||||
<el-form-item v-if="['review','stop'].includes(action)" label="核定依据 / 原因" required><el-input v-model="entry.basis" type="textarea" maxlength="2000"></el-input></el-form-item>
|
||||
<template v-if="action === 'payment'"><el-form-item label="原发放月份" required><span slot="label">原发放月份<el-tooltip content="选择要修订实发金额的那个月份,不是首次起发月份。" placement="top" popper-class="fee-help-popper"><i class="el-icon-question fee-help-icon" tabindex="0" role="button" aria-label="原发放月份说明"></i></el-tooltip></span><el-date-picker v-model="entry.month" type="month" value-format="yyyy-MM"></el-date-picker></el-form-item></template>
|
||||
<template v-if="action === 'payment'"><el-form-item label="实发金额" required><span slot="label">实发金额<el-tooltip content="填写该月实际发放的总金额,不是差额。核实未发放填0,未知金额请勿登记;其他月份不受影响。" placement="top" popper-class="fee-help-popper"><i class="el-icon-question fee-help-icon" tabindex="0" role="button" aria-label="实发金额说明"></i></el-tooltip></span><el-input-number v-model="entry.paid" :min="0" :precision="2"></el-input-number></el-form-item></template>
|
||||
<template v-if="action === 'recover'">
|
||||
<el-form-item label-width="0px"><el-checkbox v-model="entry.settledAll">已全部结清</el-checkbox><el-tooltip content="确认本期剩余多发费用均已追回后勾选,保存时按各月剩余金额结清,不要求指定扣回月份。仅追回部分时取消勾选。" placement="top" popper-class="fee-help-popper"><i class="el-icon-question fee-help-icon" tabindex="0" role="button" aria-label="已全部结清说明"></i></el-tooltip></el-form-item>
|
||||
<div v-if="!entry.settledAll" style="max-height:360px;overflow-y:auto;padding-right:8px">
|
||||
<div v-for="item in entry.recoveryMonths" :key="item.month" class="fee-recovery-month">
|
||||
<div class="fee-recovery-month-title">{{item.month}} · 尚未扣回 {{cash(item.remaining)}} 元</div>
|
||||
<el-form-item label="实际扣回月份"><span slot="label">实际扣回月份<el-tooltip content="填写实际完成扣款的月份,不得早于该笔费用原发放月份;未办理的月份留空。" placement="top" popper-class="fee-help-popper"><i class="el-icon-question fee-help-icon" tabindex="0" role="button" aria-label="实际扣回月份说明"></i></el-tooltip></span><el-date-picker v-model="item.executionMonth" type="month" value-format="yyyy-MM"></el-date-picker></el-form-item>
|
||||
<el-form-item label="实际扣回金额"><span slot="label">实际扣回金额<el-tooltip content="填写这次已扣回且归属于上方月份的金额,须大于0且不得超过该月剩余余额;已登记的扣回不要重复填写。" placement="top" popper-class="fee-help-popper"><i class="el-icon-question fee-help-icon" tabindex="0" role="button" aria-label="实际扣回金额说明"></i></el-tooltip></span><el-input-number v-model="item.amount" :min="0" :max="item.remaining" :precision="2"></el-input-number></el-form-item>
|
||||
</div>
|
||||
<el-empty v-if="!entry.recoveryMonths.length" description="暂无待扣回月份"></el-empty>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="action === 'reward'"><el-form-item label="一次性奖励">3000.00元</el-form-item><el-form-item label="历史奖励核查" required><el-checkbox v-model="entry.historyChecked">已核实该教职工未重复领取</el-checkbox></el-form-item><el-form-item label="历史核查依据" required><el-input v-model="entry.historyBasis" type="textarea"></el-input></el-form-item><el-form-item label="实际发放日期" required><el-date-picker v-model="entry.paidDate" type="date" value-format="yyyy-MM-dd"></el-date-picker></el-form-item></template>
|
||||
<el-form-item v-if="action === 'reward'" label="实际执行凭据" required><el-input v-model="entry.voucher" type="textarea"></el-input></el-form-item>
|
||||
</el-form><span slot="footer"><el-button @click="dialog=false">关闭</el-button><el-button v-if="action !== 'review' || editing.status === '待核验'" type="primary" :loading="busy" @click="submitEntry">保存办理结果</el-button></span>
|
||||
</el-dialog>
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
<!--# layout("/layouts/platform.html"){ #-->
|
||||
<div id="app" v-cloak class="only-child-fee" v-loading="busy">
|
||||
<el-card shadow="never">
|
||||
<el-empty v-if="ready && !tabs.includes(tab)" description="暂无当前页面的访问权限"></el-empty>
|
||||
</el-card>
|
||||
</div>
|
||||
<script>
|
||||
<!--#include('page.js'){}#-->
|
||||
initOnlyChildFeePage('');
|
||||
</script>
|
||||
<!--# } #-->
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
<!--# layout("/layouts/platform.html"){ #-->
|
||||
<div id="app" v-cloak class="only-child-fee" v-loading="busy">
|
||||
<el-card shadow="never">
|
||||
<el-empty v-if="ready && !tabs.includes(tab)" description="暂无当前页面的访问权限"></el-empty>
|
||||
<template v-if="tabs.includes(tab)">
|
||||
<template v-if="corrections.length">
|
||||
<h4>关键资料更正申请</h4>
|
||||
<el-table :data="corrections" border class="mb10">
|
||||
<el-table-column prop="applicant" label="申请人"></el-table-column>
|
||||
<el-table-column label="子女"><template slot-scope="{row}">{{row.oldData.childrenName}}</template></el-table-column>
|
||||
<el-table-column prop="reason" label="更正原因"></el-table-column>
|
||||
<el-table-column prop="submittedAt" label="申请时间"></el-table-column>
|
||||
<el-table-column prop="status" label="状态"></el-table-column>
|
||||
<el-table-column prop="effectiveMonth" label="生效月份"></el-table-column>
|
||||
<el-table-column label="操作"><template slot-scope="{row}"><el-button type="text" @click="openCorrection(row)">{{row.status === '待核验' ? '核验更正' : '查看更正记录'}}</el-button></template></el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
<search @search="perform(()=>fetchTab())">
|
||||
<search-item label="姓名 / 工号"><el-input v-model="ledgerKeyword" placeholder="请输入姓名或工号" clearable @keyup.enter.native="perform(()=>fetchTab())"></el-input></search-item>
|
||||
<search-item label="所属单位"><el-select v-model="ledgerUnit" placeholder="全部单位" clearable filterable><el-option v-for="unit in ledgerUnits" :key="unit" :value="unit"></el-option></el-select></search-item>
|
||||
<search-item label="子女姓名"><el-input v-model="ledgerChild" placeholder="请输入子女姓名" clearable @keyup.enter.native="perform(()=>fetchTab())"></el-input></search-item>
|
||||
<search-item label="核验状态"><el-select v-model="ledgerReviewStatus" placeholder="全部核验状态" clearable><el-option v-for="status in ledgerReviewStatuses" :key="status" :value="status"></el-option></el-select></search-item>
|
||||
<template slot="actions"><el-button @click="resetLedgerFilters">重置</el-button></template>
|
||||
</search>
|
||||
<table-tool label="待遇台账" class="fee-standard-toolbar">
|
||||
<el-radio-group v-model="ledgerState" aria-label="待遇状态筛选"><el-radio-button label="all">全部</el-radio-button><el-radio-button label="pending">未起发</el-radio-button><el-radio-button label="active">发放中</el-radio-button><el-radio-button label="stopped">已停发</el-radio-button></el-radio-group>
|
||||
</table-tool>
|
||||
<el-table :data="qualificationRows" border class="mt10">
|
||||
<el-table-column prop="loginName" label="工号"></el-table-column><el-table-column prop="userName" label="姓名"></el-table-column><el-table-column prop="unitName" label="单位"></el-table-column><el-table-column prop="childName" label="子女"></el-table-column><el-table-column prop="startMonth" label="起发月"></el-table-column><el-table-column prop="stopMonth" label="年龄停发月"></el-table-column><el-table-column prop="status" label="核验状态"></el-table-column>
|
||||
<el-table-column label="待遇状态"><template slot-scope="{row}">{{({pending:'未起发',active:'发放中',stopped:'已停发'})[ledgerBenefitStatus(row)]}}</template></el-table-column>
|
||||
<el-table-column label="操作" width="200"><template slot-scope="{row}"><el-button type="text" @click="open('review',row)">{{row.status === '待核验' ? '核验' : '查看依据'}}</el-button><el-button v-if="row.startMonth" type="text" @click="open('payment',row)">登记实发</el-button></template></el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
</el-card>
|
||||
<!--#include('dialogs.html'){}#-->
|
||||
</div>
|
||||
<script>
|
||||
<!--#include('page.js'){}#-->
|
||||
initOnlyChildFeePage('ledger');
|
||||
</script>
|
||||
<!--#include('style.html'){}#-->
|
||||
<!--# } #-->
|
||||
@@ -0,0 +1,148 @@
|
||||
// 独立页面固定业务入口;旧入口仅跳转至已授权页面,不在无权限时替换页面内容。
|
||||
function initOnlyChildFeePage(page) {
|
||||
new Vue({
|
||||
el: '#app', store,
|
||||
data() { return {tab:page,busy:false,ready:false,tabs:[],profile:{},children:[],qualifications:[],form:{proof:[]},ledgerKeyword:'',ledgerUnit:'',ledgerChild:'',ledgerReviewStatus:'',ledgerState:'all',corrections:[],changeDialog:false,selectedChange:null,changeReview:{},
|
||||
year:moment().format('YYYY'),unit:'',keyword:'',changeType:'',batch:null,rows:[],recoveries:[],expandedRowKeys:[],settlementPage:1,settlementPageSize:10,
|
||||
retirements:[],retireYear:'',retireUnit:'',retireKeyword:'',approval:'',paidStatus:'',retireDetail:null,
|
||||
dialog:false,action:'',entry:{},editing:{},amountColumns:[{key:'due',label:'应发金额'},{key:'paid',label:'实发金额'},{key:'recovered',label:'已扣回'},{key:'pending',label:'本次扣回'}]}; },
|
||||
computed:{
|
||||
periodStart(){return this.batch&&this.batch.periodStart||String(Number(this.year)-1)+'-11';},
|
||||
periodEnd(){return this.batch&&this.batch.periodEnd||this.year+'-10';},
|
||||
periodLabel(){return this.periodStart.replace('-','.')+'—'+this.periodEnd.replace('-','.');},
|
||||
correctionDiff(){
|
||||
if(!this.selectedChange)return [];
|
||||
const labels={childrenName:'子女姓名',childrenIdCard:'身份证号',childrenBirthday:'出生日期',childrenSex:'性别',guardianUserName1:'第一监护人姓名',guardianLoginName1:'第一监护人工号',guardianRelationship1:'第一监护人关系',guardianUserName2:'第二监护人姓名',guardianLoginName2:'第二监护人工号',guardianRelationship2:'第二监护人关系'};
|
||||
return Object.keys(labels).filter(k=>(this.selectedChange.oldData[k]||'')!==(this.selectedChange.newData[k]||'')).map(k=>({label:labels[k],before:this.selectedChange.oldData[k]||'—',after:this.selectedChange.newData[k]||'—'}));
|
||||
},
|
||||
editable(){return !this.form.status || ['草稿','退回','不通过'].includes(this.form.status);},
|
||||
childBirthday(){if(!this.editable && this.form.birthday)return this.form.birthday;const c=this.children.find(c=>c.id===this.form.childId);return c?this.date(c.childrenBirthday):this.form.birthday;},
|
||||
ledgerUnits(){return [...new Set(this.qualifications.map(q=>q.unitName).filter(Boolean))];},
|
||||
ledgerReviewStatuses(){return [...new Set(this.qualifications.map(q=>q.status).filter(Boolean))];},
|
||||
qualificationRows(){
|
||||
const keyword=this.ledgerKeyword.trim(),child=this.ledgerChild.trim();
|
||||
return this.qualifications.filter(q=>(!keyword||((q.userName||'')+' '+(q.loginName||'')).includes(keyword))
|
||||
&&(!this.ledgerUnit||q.unitName===this.ledgerUnit)&&(!child||(q.childName||'').includes(child))
|
||||
&&(!this.ledgerReviewStatus||q.status===this.ledgerReviewStatus)
|
||||
&&(this.ledgerState==='all'||this.ledgerBenefitStatus(q)===this.ledgerState));
|
||||
},
|
||||
visibleAmountColumns(){return this.amountColumns.filter(c=>c.key!=='pending');},
|
||||
pagedSettlementRows(){const start=(this.settlementPage-1)*this.settlementPageSize;return this.filteredRows.slice(start,start+this.settlementPageSize);},
|
||||
units(){return [...new Set(this.rows.map(r=>r.unitName).filter(Boolean))];},
|
||||
filteredRows(){return this.rows.filter(r=>(!this.unit || r.unitName===this.unit)&&(!this.keyword || (r.userName+r.loginName).includes(this.keyword)));},
|
||||
confirmed(){return this.batch && this.batch.status!=='草稿';},
|
||||
canConfirm(){return this.batch && this.batch.status==='草稿' && this.rows.length && this.rows.every(r=>this.completeAmounts(r));},
|
||||
changes(){return this.filteredRows.map(r=>Object.assign({},r,{deduction:this.noticeDeduction(r)})).filter(r=>{const stop=this.endMonth(r)>=this.periodStart&&this.endMonth(r)<=this.periodEnd;return (stop||r.deduction>0)&&(!this.changeType||(this.changeType==='停发'?stop:r.deduction>0));});},
|
||||
changeTotals(){return {before:this.changes.length*10,after:this.changes.reduce((n,r)=>n+(this.endMonth(r)<=this.periodEnd?0:10),0),deduction:this.changes.some(r=>r.deduction===null)?null:this.changes.reduce((n,r)=>n+Math.round(r.deduction*100),0)/100};},
|
||||
recoveryRows(){const ids=this.filteredRows.map(r=>r.userId);return this.recoveries.filter(r=>r.month>=this.periodStart&&r.month<=this.periodEnd&&ids.includes(r.userId));},
|
||||
retirementUnits(){return [...new Set(this.retirements.map(r=>r.unitName).filter(Boolean))];},
|
||||
retirementRows(){return this.retirements.filter(r=>(!this.retireYear||this.date(r.retireTime).startsWith(this.retireYear))&&(!this.retireUnit||r.unitName===this.retireUnit)&&(!this.retireKeyword||(r.userName+r.loginName).includes(this.retireKeyword))&&(!this.approval||(this.approval==='已通过')===r.approved)&&(!this.paidStatus||(this.paidStatus==='已发放')===!!r.payment));},
|
||||
dialogTitle(){return {review:'资格核验',stop:'办理停发 / 资格终止',payment:'登记逐月实发',recover:'登记实际扣回',reward:'登记退休奖励发放'}[this.action];}
|
||||
},
|
||||
watch:{
|
||||
unit(){this.changeSettlementPage(1);},
|
||||
keyword(){this.changeSettlementPage(1);},
|
||||
// 刷新后保留当前页;数据减少时回到有效页,避免出现空白页。
|
||||
rows(){this.settlementPage=Math.min(this.settlementPage,Math.max(1,Math.ceil(this.filteredRows.length/this.settlementPageSize)));}
|
||||
},
|
||||
methods:{
|
||||
// 通知单保留清算期间的扣发总额,已执行扣回只影响余额,不冲减通知单金额。
|
||||
noticeDeduction(row){
|
||||
if(!Array.isArray(row.months)||!row.months.length)return null;
|
||||
let cents=0;
|
||||
for(const m of row.months){
|
||||
if(m.month<this.periodStart||m.month>this.periodEnd)continue;
|
||||
const paid=this.amount(m.paid),due=this.amount(m.due);
|
||||
if(paid===null||due===null)return null;
|
||||
cents+=Math.max(0,Math.round(paid*100)-Math.round(due*100));
|
||||
}
|
||||
return cents/100;
|
||||
},
|
||||
noticeStopRange(row){const end=this.endMonth(row);return end&&end<=this.periodEnd?end.replace('-','.')+'-'+this.year+'.10':'—';},
|
||||
changeSettlementPage(page){this.settlementPage=page;this.expandedRowKeys=[];},
|
||||
changeSettlementSize(size){this.settlementPageSize=size;this.changeSettlementPage(1);},
|
||||
resetLedgerFilters(){this.ledgerKeyword='';this.ledgerUnit='';this.ledgerChild='';this.ledgerReviewStatus='';this.ledgerState='all';},
|
||||
// 待遇状态按当前月份及终止信息判断,与资料审核状态分别筛选。
|
||||
ledgerBenefitStatus(row){
|
||||
const now=moment().format('YYYY-MM'),end=this.endMonth(row);
|
||||
if(row.status==='已停发'||row.eligible===false||(row.startMonth&&end&&end<=now))return 'stopped';
|
||||
return row.status==='已核定'&&row.startMonth&&row.startMonth<=now?'active':'pending';
|
||||
},
|
||||
openCorrection(row){this.selectedChange=row;this.changeReview={id:row.id,status:'已批准',basis:'',verified:false,effectiveMonth:''};this.changeDialog=true;},
|
||||
submitCorrectionReview(){this.perform(async()=>{await this.request('reviewChildChange',{data:JSON.stringify(this.changeReview)});this.changeDialog=false;await this.context();this.corrections=await this.request('childChanges');this.$message.success('更正核验结果已保存');});},
|
||||
async request(path,data={}){
|
||||
let r;
|
||||
try { r=await this.$axios.post('/platform/childManage/onlyChildFee/'+path,data); }
|
||||
// 全局拦截器已提示业务或网络错误,页面只中断后续操作,避免重复弹窗。
|
||||
catch(e){throw {handled:true,cause:e};}
|
||||
if(r.code!==0)throw new Error(r.msg||'操作未完成');
|
||||
return r.data;
|
||||
},
|
||||
async perform(work){if(this.busy)return;this.busy=true;try{await work();}catch(e){if(!e || !e.handled)this.$message.error((e && (e.msg||e.message))||'操作未完成');}finally{this.busy=false;}},
|
||||
async context(){const d=await this.request('context');this.tabs=d.tabs||[];this.ready=true;if(!this.tab){const requested=GetQueryString('tab');const target=this.tabs.includes(requested)?requested:this.tabs[0];if(target)this.go('/platform/childManage/onlyChildFee/'+target+'/index');return;}this.profile=d.profile||{};this.children=d.children||[];this.qualifications=d.qualifications||[];this.form=d.mine||{proof:[],onlyChild:null,childId:'',relationship:'',certificateNumber:''};},
|
||||
save(submit){this.perform(async()=>{await this.request('save',{data:JSON.stringify(this.form),submit});await this.context();this.$message.success(submit?'已提交核验':'草稿已保存');});},
|
||||
async fetchTab(){if(this.tab==='ledger'){await this.context();this.corrections=await this.request('childChanges');}else if(this.tab==='retirement')await this.fetchRetirement();else if(['settlement','changes'].includes(this.tab))await this.fetchSettlement();},
|
||||
async fetchSettlement(){const d=await this.request('settlement',{year:this.year});this.batch=d.batch;this.rows=d.rows.map(row=>Object.assign({},row,{actualStartMonth:row.actualStartMonth||'',savedActualStartMonth:row.actualStartMonth||''}));this.recoveries=d.recoveries;this.expandedRowKeys=[];},
|
||||
// 选择仅作为待确认值;取消或保存失败时继续显示已保存月份。
|
||||
savePaymentStart(row){
|
||||
const selected=row.actualStartMonth,previous=row.savedActualStartMonth;
|
||||
row.actualStartMonth=previous;
|
||||
if(!selected||selected===previous)return;
|
||||
this.perform(async()=>{
|
||||
try{await this.$confirm('将首次实际发放月份设为 '+selected+'。更改后,各年度未确认的实发记录会重新计算,停发当月起默认实发为0;单月修订和已确认快照保留。是否确认?','确认起发月份',{type:'warning',confirmButtonText:'确认保存',cancelButtonText:'取消'});}
|
||||
catch(e){return;}
|
||||
await this.request('paymentStart',{data:JSON.stringify({userId:row.userId,year:Number(this.year),actualStartMonth:selected})});
|
||||
await this.fetchSettlement();this.expandedRowKeys=[row.userId];
|
||||
this.$message.success('首次实际发放月份已保存,各年度共用,未确认清算草稿已更新');
|
||||
});
|
||||
},
|
||||
loadSettlement(){this.perform(()=>this.fetchSettlement());},
|
||||
makeBatch(confirm){const run=()=>this.perform(async()=>{await this.request('batch',{year:this.year,confirm});await this.fetchSettlement();this.$message.success(confirm?'批次已确认并保存快照':'草稿已生成');});if(confirm)this.$confirm('确认当前全部单位 '+this.periodLabel+' 的逐月依据?确认后快照不可修改。','复核确认').then(run).catch(()=>{});else run();},
|
||||
async fetchRetirement(){this.retirements=await this.request('retirement');this.retireDetail=null;},
|
||||
loadRetirement(){this.perform(()=>this.fetchRetirement());},
|
||||
reset(){this.unit='';this.keyword='';this.changeType='';this.loadSettlement();},
|
||||
open(action,row){this.action=action;this.editing=row;this.entry={userId:row.userId,id:row.id,status:'已核定',verified:false,startMonth:row.startMonth||'',basis:'',endMonth:row.stopMonth||'',month:'',paid:undefined,source:'',amount:undefined,executionMonth:'',voucher:'',historical:false,historyChecked:false,historyBasis:'',paidDate:'',settledAll:true,recoveryMonths:(row.months||[]).map(m=>({month:m.month,remaining:this.remaining(Object.assign({},row,{months:[m]})),executionMonth:'',amount:undefined})).filter(m=>m.remaining>0)};this.dialog=true;},
|
||||
submitEntry(){this.perform(async()=>{
|
||||
if(this.action==='recover')this.entry.year=Number(this.year);
|
||||
const payload=Object.assign({},this.entry);
|
||||
if(this.action==='recover')payload.recoveryMonths=this.entry.settledAll?[]:this.entry.recoveryMonths.filter(m=>m.executionMonth||m.amount!==undefined&&m.amount!==null);
|
||||
await this.request(this.action,{data:JSON.stringify(payload)});this.dialog=false;await this.context();
|
||||
if(['settlement','changes'].includes(this.tab)){
|
||||
await this.fetchSettlement();
|
||||
// 补录后展开办理人员,直接呈现更新后的逐月记录。
|
||||
if(this.action==='payment'&&this.rows.some(r=>r.userId===this.entry.userId))this.expandedRowKeys=[this.entry.userId];
|
||||
}
|
||||
if(this.tab==='retirement')await this.fetchRetirement();
|
||||
this.$message.success(this.action==='payment'?this.entry.month+' 实发记录已保存('+this.cash(this.entry.paid)+'元)':'办理结果已保存');
|
||||
});},
|
||||
// 起发前、发放期间及停发后均可能多发,登记入口只按尚未扣回余额判断。
|
||||
canRegisterRecovery(row){return this.remaining(row)>0;},
|
||||
recoveryStatus(row){return this.completeAmounts(row)&&this.remaining(row)===0&&(Number(row.pending)>0||Number(row.recovered)>0)?'已结清':row.status;},
|
||||
// 汇总全部筛选结果,不受分页影响;缺失实发整行不参与金额汇总。
|
||||
summary({columns}){const valid=this.filteredRows.filter(r=>this.completeAmounts(r));return columns.map(c=>c.property==='loginName'?'筛选结果合计':this.amountColumns.some(a=>a.key===c.property)?(valid.length?valid.reduce((sum,r)=>sum+this.amount(r[c.property]),0).toFixed(2):'—'):'');},
|
||||
// 展开新行时替换唯一的展开键,重复点击当前行则收起。
|
||||
expandSettlementRow(row,expandedRows){this.expandedRowKeys=expandedRows.some(r=>r.userId===row.userId)?[row.userId]:[];},
|
||||
// 空值、空字符串及非数值不能视作实发零元,也不能进入金额汇总。
|
||||
amount(value){if(typeof value!=='number'&&(typeof value!=='string'||!value.trim()))return null;const n=Number(value);return Number.isFinite(n)?n:null;},
|
||||
completeAmounts(row){return row.status!=='资料不全'&&this.amountColumns.every(c=>this.amount(row[c.key])!==null);},
|
||||
// 执行余额按实际扣回记录计算,确认快照的应扣金额保持不变。
|
||||
remaining(row){
|
||||
if(!Array.isArray(row.months)||!row.months.length||!this.completeAmounts(row))return null;
|
||||
let total=0;
|
||||
for(const m of row.months){
|
||||
const paid=this.amount(m.paid),due=this.amount(m.due);
|
||||
const backs=this.recoveries.filter(r=>r.userId===row.userId&&r.month===m.month).map(r=>this.amount(r.amount));
|
||||
if(paid===null||due===null||backs.some(n=>n===null))return null;
|
||||
total+=Math.max(0,Math.round((paid-due-backs.reduce((sum,n)=>sum+n,0))*100)/100);
|
||||
}
|
||||
return total;
|
||||
},
|
||||
exportRewards(){window.location.href='/platform/childManage/onlyChildFee/exportReward?'+new URLSearchParams({year:this.retireYear||'',unit:this.retireUnit,keyword:this.retireKeyword,approval:this.approval,paidStatus:this.paidStatus}).toString();},
|
||||
exportChanges(){window.location.href='/platform/childManage/onlyChildFee/export?'+new URLSearchParams({year:this.year,unit:this.unit,keyword:this.keyword,type:this.changeType}).toString();},
|
||||
endMonth(row){return row.endMonth && row.endMonth<row.stopMonth?row.endMonth:row.stopMonth;},
|
||||
person(id){const r=this.qualifications.concat(this.rows).find(q=>q.userId===id);return r?r.userName+' / '+r.loginName:id;},
|
||||
cash(n){const value=this.amount(n);return value===null?'待补充':value.toFixed(2);},date(v){return v?moment(v).format('YYYY-MM-DD'):'';},go(url){commonUtil.pjaxPush(url);}
|
||||
},
|
||||
created(){this.perform(async()=>{await this.context();if(this.tabs.includes(this.tab))await this.fetchTab();});}
|
||||
});
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
<!--# layout("/layouts/platform.html"){ #-->
|
||||
<div id="app" v-cloak class="only-child-fee" v-loading="busy">
|
||||
<el-card shadow="never">
|
||||
<el-empty v-if="ready && !tabs.includes(tab)" description="暂无当前页面的访问权限"></el-empty>
|
||||
<template v-if="tabs.includes(tab)">
|
||||
<h3>教职工信息</h3>
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="姓名">{{profile.username}}</el-descriptions-item>
|
||||
<el-descriptions-item label="工号">{{profile.loginname}}</el-descriptions-item>
|
||||
<el-descriptions-item label="所属工会">{{profile.unionName}}</el-descriptions-item>
|
||||
<el-descriptions-item label="所在单位">{{profile.unitName}}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<h3>子女与资格信息</h3>
|
||||
<el-form label-width="145px" :disabled="!editable" class="fee-grid">
|
||||
<el-form-item label="关联子女" required>
|
||||
<el-select v-model="form.childId" placeholder="请选择已提交子女" style="width:100%">
|
||||
<el-option v-for="c in children" :key="c.id" :value="c.id" :label="c.childrenName + '(' + date(c.childrenBirthday) + ')'"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="出生日期"><el-input :value="childBirthday" readonly></el-input></el-form-item>
|
||||
<el-form-item label="是否独生子女" required><el-select v-model="form.onlyChild"><el-option label="是" :value="true"></el-option><el-option label="否" :value="false"></el-option></el-select></el-form-item>
|
||||
<el-form-item label="独生子女证号" required><el-input v-model="form.certificateNumber" maxlength="100"></el-input></el-form-item>
|
||||
<el-form-item label="与子女关系" required><el-select v-model="form.relationship"><el-option v-for="r in ['父亲','母亲','其他法定监护人']" :key="r" :value="r"></el-option></el-select></el-form-item>
|
||||
<el-form-item label="证明材料" required>
|
||||
<file-upload v-if="editable" :value.sync="form.proof" :upload_number="5" upload_result_type="url" complete_result upload_result_category="array"></file-upload>
|
||||
<file-preview v-else :files="form.proof" complete_result compact></file-preview>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<h3>待遇核定</h3>
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="领取主体">教职工本人(双职工分别核定)</el-descriptions-item>
|
||||
<el-descriptions-item label="每月标准">10.00元</el-descriptions-item>
|
||||
<el-descriptions-item label="起发月份">{{form.startMonth || '待独立核定'}}</el-descriptions-item>
|
||||
<el-descriptions-item label="年龄停发月份">{{form.stopMonth || '核验后计算'}}</el-descriptions-item>
|
||||
<el-descriptions-item label="资格状态">{{form.status || '未申请'}}</el-descriptions-item>
|
||||
<el-descriptions-item label="核定依据 / 退回原因">{{form.basis || '—'}}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<div class="fee-actions"><el-button @click="go('/platform/childManage/mine/index')">维护子女资料</el-button><el-button v-if="editable" @click="save(false)" :disabled="busy">保存草稿</el-button><el-button v-if="editable" type="primary" @click="save(true)" :disabled="busy">提交核定</el-button></div>
|
||||
</template>
|
||||
</el-card>
|
||||
<!--#include('dialogs.html'){}#-->
|
||||
</div>
|
||||
<script>
|
||||
<!--#include('page.js'){}#-->
|
||||
initOnlyChildFeePage('qualification');
|
||||
</script>
|
||||
<!--#include('style.html'){}#-->
|
||||
<!--# } #-->
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
<!--# layout("/layouts/platform.html"){ #-->
|
||||
<div id="app" v-cloak class="only-child-fee" v-loading="busy">
|
||||
<el-card shadow="never">
|
||||
<el-empty v-if="ready && !tabs.includes(tab)" description="暂无当前页面的访问权限"></el-empty>
|
||||
<template v-if="tabs.includes(tab)">
|
||||
<!-- <el-alert title="退休奖励是否限制子女年龄待确认,不自动套用月费停发规则。一次性3000元,审批通过且核实未重复领取后登记实际发放。" type="warning" :closable="false" show-icon></el-alert>-->
|
||||
<template v-if="tabs.includes('retirement')"><search @search="loadRetirement">
|
||||
<search-item label="年度"><el-date-picker v-model="retireYear" type="year" value-format="yyyy" placeholder="全部年度" clearable></el-date-picker></search-item>
|
||||
<search-item label="所属单位"><el-select v-model="retireUnit" clearable placeholder="全部单位"><el-option v-for="u in retirementUnits" :key="u" :value="u"></el-option></el-select></search-item>
|
||||
<search-item label="姓名 / 工号"><el-input v-model="retireKeyword" placeholder="姓名 / 工号" clearable @keyup.enter.native="loadRetirement"></el-input></search-item>
|
||||
<search-item label="审核状态"><el-select v-model="approval" clearable placeholder="全部审核状态"><el-option value="已通过"></el-option><el-option value="未通过审批"></el-option></el-select></search-item>
|
||||
<search-item label="发放状态"><el-select v-model="paidStatus" clearable placeholder="全部发放状态"><el-option value="已发放"></el-option><el-option value="未发放"></el-option></el-select></search-item>
|
||||
<template slot="actions"><el-button @click="retireYear='';retireUnit='';retireKeyword='';approval='';paidStatus=''">重置</el-button></template>
|
||||
</search>
|
||||
<table-tool label="退休奖励申请" class="fee-standard-toolbar">
|
||||
<el-button type="primary" @click="exportRewards">导出奖励名单</el-button>
|
||||
<el-button @click="go('/platform/dsznfmtx/apply/index')">进入原申请模块</el-button>
|
||||
<el-button @click="go('/platform/dsznfmtx/mine/index')">查看原申请及审批流程</el-button>
|
||||
</table-tool>
|
||||
<el-table :data="retirementRows" border><el-table-column prop="loginName" label="工号"></el-table-column><el-table-column prop="userName" label="姓名"></el-table-column><el-table-column prop="unitName" label="单位"></el-table-column><el-table-column label="退休日期"><template slot-scope="{row}">{{date(row.retireTime)}}</template></el-table-column><el-table-column label="奖励金额"><template slot-scope="scope">3000.00</template></el-table-column><el-table-column label="审核状态"><template slot-scope="{row}">{{row.approved ? '已通过' : '未通过审批'}}</template></el-table-column><el-table-column label="发放状态"><template slot-scope="{row}">{{row.payment ? '已发放' : '未发放'}}</template></el-table-column><el-table-column label="操作"><template slot-scope="{row}"><el-button type="text" @click="retireDetail=row">查看申请</el-button><el-button type="text" :disabled="!row.approved || !!row.payment" @click="open('reward',row)">登记发放</el-button></template></el-table-column></el-table>
|
||||
<template v-if="retireDetail"><div class="fee-record-card"><h3>{{retireDetail.userName}} · 申请详情</h3><el-button v-if="retireDetail.instanceId" type="text" @click="$refs.retireChart.onOpenFull(retireDetail.instanceProcessDefineId,retireDetail.instanceId)">查看原审批流程</el-button><el-descriptions :column="2" border><el-descriptions-item label="子女出生日期">{{date(retireDetail.childrenBirthday)}}</el-descriptions-item><el-descriptions-item label="子女年龄条件">待确认</el-descriptions-item><el-descriptions-item label="独生子女证号">{{retireDetail.childrenGraceNumber}}</el-descriptions-item><el-descriptions-item label="退休证"><file-preview :files="retireDetail.retireFiles" complete_result compact></file-preview></el-descriptions-item><el-descriptions-item label="独生子女证明"><file-preview :files="retireDetail.honorFiles" complete_result compact></file-preview></el-descriptions-item><el-descriptions-item label="历史核查依据">{{retireDetail.payment ? retireDetail.payment.historyBasis : '待核实'}}</el-descriptions-item></el-descriptions><h3>实际发放记录</h3><el-descriptions :column="3" border><el-descriptions-item label="发放金额">{{retireDetail.payment ? retireDetail.payment.amount : '—'}}</el-descriptions-item><el-descriptions-item label="发放日期">{{retireDetail.payment ? retireDetail.payment.paidDate : '—'}}</el-descriptions-item><el-descriptions-item label="发放凭据">{{retireDetail.payment ? retireDetail.payment.voucher : '—'}}</el-descriptions-item></el-descriptions></div></template>
|
||||
</template>
|
||||
</template>
|
||||
</el-card>
|
||||
<!--#include('dialogs.html'){}#-->
|
||||
</div>
|
||||
<script>
|
||||
<!--#include('page.js'){}#-->
|
||||
initOnlyChildFeePage('retirement');
|
||||
</script>
|
||||
<!--#include('style.html'){}#-->
|
||||
<!--# } #-->
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
<!--# layout("/layouts/platform.html"){ #-->
|
||||
<div id="app" v-cloak class="only-child-fee" v-loading="busy">
|
||||
<el-card shadow="never">
|
||||
<el-empty v-if="ready && !tabs.includes(tab)" description="暂无当前页面的访问权限"></el-empty>
|
||||
<template v-if="tabs.includes(tab)">
|
||||
<div class="fee-filter"><label>年度 <el-date-picker v-model="year" type="year" value-format="yyyy" :clearable="false" @change="loadSettlement"></el-date-picker></label><el-select v-model="unit" clearable placeholder="全部单位"><el-option v-for="u in units" :key="u" :value="u"></el-option></el-select><el-input v-model="keyword" placeholder="姓名 / 工号" clearable></el-input><el-button type="primary" @click="loadSettlement">查询</el-button><el-button @click="reset">重置</el-button></div>
|
||||
<div class="fee-recovery-summary fee-period-summary">
|
||||
<div class="fee-period-item"><span class="fee-period-label">统计月份</span><strong class="fee-period-value">11月</strong></div>
|
||||
<div class="fee-period-item"><span class="fee-period-label">核算期间</span><strong class="fee-period-value">{{periodLabel}}</strong></div>
|
||||
<div class="fee-period-item"><span class="fee-period-label">批次</span><strong class="fee-period-value">{{batch ? batch.id : '尚未生成'}}</strong></div>
|
||||
<div class="fee-period-item"><span class="fee-period-label">状态</span><strong class="fee-period-value">{{batch ? batch.status : '尚未生成'}}</strong></div>
|
||||
</div>
|
||||
<!-- <el-alert title="起发月份为第一笔费用实际开始发放的月份,一次登记、各年度共用。更改须确认后生效,未确认年度按起发前0元、起发后每月10元重算;单月修订及已确认快照保留。" type="info" :closable="false" show-icon></el-alert>-->
|
||||
<div class="fee-actions"><el-button type="primary" :disabled="batch && batch.status !== '草稿'" @click="makeBatch(false)">生成清算草稿</el-button><el-button :disabled="!canConfirm" @click="makeBatch(true)">复核确认</el-button><el-button :disabled="!confirmed" @click="exportChanges">导出变动表</el-button></div>
|
||||
<el-table :data="pagedSettlementRows" row-key="userId" :expand-row-keys="expandedRowKeys" @expand-change="expandSettlementRow" border show-summary :summary-method="summary">
|
||||
<el-table-column type="expand" width="48"><template slot-scope="{row: detail}"><h3>{{detail.userName}} · 逐月核算</h3><el-table :data="detail.months" border><el-table-column prop="month" label="月份"></el-table-column><el-table-column v-for="c in visibleAmountColumns" :key="c.key" :label="c.label"><template slot-scope="{row}">{{cash(row[c.key])}}</template></el-table-column><el-table-column prop="source" label="实发来源"></el-table-column><el-table-column prop="note" label="备注"></el-table-column></el-table></template></el-table-column>
|
||||
<el-table-column prop="loginName" label="工号" min-width="120"></el-table-column><el-table-column prop="userName" label="姓名" min-width="100"></el-table-column><el-table-column prop="birthday" label="子女出生日期" width="120"></el-table-column><el-table-column prop="actualStartMonth" label="起发月份" width="180"><template slot-scope="{row}"><span v-if="confirmed">{{row.actualStartMonth || '未设置'}}</span><template v-else><el-date-picker v-model="row.actualStartMonth" @change="savePaymentStart(row)" type="month" value-format="yyyy-MM" placeholder="实际起发月份" :clearable="false" :disabled="busy" size="small" style="width:150px"></el-date-picker></template></template></el-table-column><el-table-column prop="stopMonth" label="停发起月" min-width="100"></el-table-column>
|
||||
<el-table-column v-for="c in visibleAmountColumns" :key="c.key" :prop="c.key" :label="c.label" min-width="100"><template slot-scope="{row}">{{cash(row[c.key])}}</template></el-table-column>
|
||||
<el-table-column label="尚未扣回余额" min-width="130"><template slot-scope="{row}">{{cash(remaining(row))}}</template></el-table-column><el-table-column prop="status" label="状态" min-width="110"><template slot-scope="{row}"><el-tag :type="row.status === '资料不全' ? 'danger' : ['无变动','已结清'].includes(recoveryStatus(row)) ? 'success' : 'warning'">{{recoveryStatus(row)}}</el-tag></template></el-table-column>
|
||||
<el-table-column label="操作" width="200" fixed="right"><template slot-scope="{row}"><el-button type="text" @click="open('payment',row)" :disabled="confirmed">补录实发</el-button><el-button v-if="canRegisterRecovery(row)" type="text" @click="open('recover',row)">登记扣回</el-button></template></el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="fee-pagination"><el-pagination background :current-page="settlementPage" :page-size="settlementPageSize" :page-sizes="[10,20,50,100]" :total="filteredRows.length" layout="total, sizes, prev, pager, next, jumper" @current-change="changeSettlementPage" @size-change="changeSettlementSize"></el-pagination></div>
|
||||
</template>
|
||||
</el-card>
|
||||
<!--#include('dialogs.html'){}#-->
|
||||
</div>
|
||||
<script>
|
||||
<!--#include('page.js'){}#-->
|
||||
initOnlyChildFeePage('settlement');
|
||||
</script>
|
||||
<!--#include('style.html'){}#-->
|
||||
<!--# } #-->
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
<style>
|
||||
.only-child-fee h2,.only-child-fee h3{color:#0075cf}.only-child-fee h3{border-left:5px solid #0086ed;padding-left:12px;margin:22px 0 14px}.only-child-fee .fee-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));padding:20px 12px 0;border:1px solid #e1e6ed}.only-child-fee .fee-filter{display:flex;gap:14px;flex-wrap:wrap;align-items:center;padding:20px;background:#f4f7fa;margin-bottom:15px}.only-child-fee .fee-filter .el-input{width:180px}.only-child-fee .fee-actions{display:flex;justify-content:flex-end;align-items:center;gap:12px;flex-wrap:wrap;margin:16px 0}.only-child-fee .fee-paper{border:1px solid #dce3ec;padding:28px;margin-bottom:12px}.only-child-fee .fee-paper h2{text-align:center;color:#182333}.only-child-fee .fee-sign{display:flex;justify-content:space-between;margin:50px 20px 20px}.only-child-fee small{float:right;font-size:13px;color:#9099aa}.only-child-fee .el-table th{background:#edf2f7}.only-child-fee .el-button:active{transform:scale(.98)}@media(max-width:900px){.only-child-fee .fee-grid{grid-template-columns:1fr}.only-child-fee .fee-paper{padding:12px}.only-child-fee .fee-sign{gap:20px;flex-wrap:wrap}}@media(prefers-reduced-motion:reduce){.only-child-fee .el-button:active{transform:none}}
|
||||
.only-child-fee .fee-recovery-summary{display:flex;justify-content:space-between;align-items:center;gap:20px;flex-wrap:wrap;padding:18px 20px;margin-bottom:26px;background:#f5f8fc;border:1px solid #e1e8f0;border-radius:6px}
|
||||
.only-child-fee .fee-recovery-person{min-width:0;overflow-wrap:anywhere}
|
||||
.only-child-fee .fee-recovery-name{font-size:16px;font-weight:600;color:#303133;margin-right:12px}
|
||||
.only-child-fee .fee-recovery-id{display:inline-block;font-size:13px;color:#606266}
|
||||
.only-child-fee .fee-recovery-caption{font-size:12px;line-height:20px;color:#606266;margin-top:6px}
|
||||
.only-child-fee .fee-recovery-balance{text-align:right;margin-left:auto}
|
||||
.only-child-fee .fee-recovery-balance strong{font-size:26px;line-height:36px;font-weight:600;color:#b75b00;font-variant-numeric:tabular-nums}
|
||||
.only-child-fee .fee-recovery-unit{margin-left:6px;font-size:13px;color:#606266}
|
||||
.only-child-fee .fee-field-help{margin-top:6px;font-size:12px;line-height:20px;color:#606266;overflow-wrap:anywhere}
|
||||
.only-child-fee .fee-payment-start .fee-recovery-caption,.only-child-fee .fee-payment-start strong{color:#268545}
|
||||
.only-child-fee .fee-recovery-month{padding:12px 0;border-top:1px solid #ebeef5}.only-child-fee .fee-recovery-month-title{margin-bottom:14px;color:#606266;font-weight:600}
|
||||
.only-child-fee .fee-help-icon{margin-left:6px;color:#909399;font-size:14px;cursor:help;vertical-align:middle}.only-child-fee .fee-help-icon:hover,.only-child-fee .fee-help-icon:focus{color:#0075cf}.fee-help-popper{max-width:320px;line-height:1.6}
|
||||
.only-child-fee .fee-period-summary{justify-content:flex-start;gap:20px 48px;margin-bottom:15px}.only-child-fee .fee-period-item{display:flex;flex-direction:column;gap:6px;min-width:0}.only-child-fee .fee-period-label{font-size:12px;line-height:20px;color:#606266}.only-child-fee .fee-period-value{font-size:20px;line-height:28px;font-weight:600;color:#0075cf;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}
|
||||
.only-child-fee .fee-record-card,.only-child-fee .fee-form-card{padding:18px 20px;margin:16px 0 24px;background:#f5f8fc;border:1px solid #e1e8f0;border-radius:6px}
|
||||
.only-child-fee .fee-record-fields{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:18px 24px}.only-child-fee .fee-record-fields>div{min-width:0;overflow-wrap:anywhere}.only-child-fee .fee-record-label{display:block;margin-bottom:6px;color:#606266;font-size:12px}.only-child-fee .fee-record-wide{grid-column:1/-1}
|
||||
.only-child-fee .fee-record-state strong{font-size:20px;color:#0075cf}.only-child-fee .fee-form-card .el-form-item:last-child{margin-bottom:0}.only-child-fee .fee-record-card .el-descriptions__body{background:transparent}.only-child-fee .fee-record-card>h3:first-child{margin-top:0}
|
||||
@media(max-width:600px){.only-child-fee .fee-record-fields{grid-template-columns:1fr}.only-child-fee .fee-record-card,.only-child-fee .fee-form-card{padding:16px}}
|
||||
.only-child-fee .fee-pagination{display:flex;justify-content:flex-end;margin-top:20px;overflow-x:auto;padding-bottom:4px}
|
||||
.only-child-fee .fee-standard-toolbar{margin:16px 0}.only-child-fee .fee-standard-toolbar .ele-space{display:flex;align-items:center;flex-wrap:wrap;gap:12px}.only-child-fee .fee-standard-toolbar .el-button+.el-button{margin-left:0}
|
||||
.only-child-fee .fee-notice-paper{overflow-x:auto}.only-child-fee .fee-notice-table{width:100%;min-width:900px;border-collapse:collapse;table-layout:fixed;color:#000;background:#fff;font-family:SimSun,serif;font-size:14px}
|
||||
.only-child-fee .fee-notice-table th,.only-child-fee .fee-notice-table td{border:1px solid #000;padding:8px 6px;text-align:center;vertical-align:middle;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.only-child-fee .fee-notice-table .fee-notice-title{font-size:22px;padding:12px 6px}.only-child-fee .fee-notice-table .fee-notice-recipient{text-align:left}.only-child-fee .fee-notice-table .fee-notice-unit{text-align:right;padding-right:28px}.only-child-fee .fee-notice-table tfoot td{text-align:left;height:64px;padding-left:24px;border:0}
|
||||
</style>
|
||||
@@ -5,13 +5,14 @@ layout("/layouts/platform.html"){
|
||||
<div id="app" v-cloak>
|
||||
<el-card shadow="never">
|
||||
<h3 style="color: var(--color-primary)" slot="header">信息填报</h3>
|
||||
<el-alert v-if="formData.qualificationLocked" title="该子女已关联待核验、已核定或已停发的独生子女费资格,姓名、身份证号、生日、性别及监护人身份和关系不可直接修改;联系电话、学校、年级等日常信息仍可维护。" type="info" :closable="false" show-icon class="mb10"></el-alert>
|
||||
<el-form :model="formData" ref="formRef" :rules="formRules" label-width="0" label-suffix=":"
|
||||
class="flow-task-form">
|
||||
<table-tool label="子女信息"></table-tool>
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="就读年级">
|
||||
<el-form-item prop="childRelationship">
|
||||
<dict-select v-model="formData.childRelationship" code="CHILD_MANAGE_GRADE" style="width: 100%" placeholder="请选择就读年级">
|
||||
<el-form-item prop="childrenGrade">
|
||||
<dict-select v-model="formData.childrenGrade" code="CHILD_MANAGE_GRADE" style="width: 100%" placeholder="请选择就读年级">
|
||||
</dict-select>
|
||||
</el-form-item></el-descriptions-item>
|
||||
<el-descriptions-item label="入学年份">
|
||||
@@ -26,20 +27,20 @@ layout("/layouts/platform.html"){
|
||||
</el-form-item></el-descriptions-item>
|
||||
<el-descriptions-item label="姓名">
|
||||
<el-form-item prop="childrenName">
|
||||
<el-input v-model="formData.childrenName" placeholder="请输入子女姓名"
|
||||
<el-input :disabled="formData.qualificationLocked && !correctionMode" v-model="formData.childrenName" placeholder="请输入子女姓名"
|
||||
maxlength="30"></el-input>
|
||||
</el-form-item></el-descriptions-item>
|
||||
<el-descriptions-item label="身份证号">
|
||||
<el-form-item prop="childrenIdCard">
|
||||
<el-input v-model="formData.childrenIdCard" placeholder="请输入身份证号"
|
||||
<el-input :disabled="formData.qualificationLocked && !correctionMode" v-model="formData.childrenIdCard" placeholder="请输入身份证号"
|
||||
maxlength="19">
|
||||
<el-button slot="append" icon="el-icon-search" @click="getIsRepeatByIdCard"></el-button>
|
||||
<el-button :disabled="formData.qualificationLocked && !correctionMode" slot="append" icon="el-icon-search" @click="getIsRepeatByIdCard"></el-button>
|
||||
</el-input>
|
||||
</el-form-item></el-descriptions-item>
|
||||
<el-descriptions-item label="出生日期">
|
||||
<el-form-item prop="childrenBirthday">
|
||||
<el-date-picker
|
||||
v-model="formData.childrenBirthday"
|
||||
:disabled="formData.qualificationLocked && !correctionMode" v-model="formData.childrenBirthday"
|
||||
type="date"
|
||||
placeholder="请选择子女出生年月"
|
||||
style="width: 100%;"
|
||||
@@ -50,7 +51,7 @@ layout("/layouts/platform.html"){
|
||||
<el-form-item prop="childrenSex">
|
||||
<el-select clearable placeholder="请选择性别"
|
||||
style="width: 100%;"
|
||||
v-model="formData.childrenSex">
|
||||
:disabled="formData.qualificationLocked && !correctionMode" v-model="formData.childrenSex">
|
||||
<el-option label="男" value="男"></el-option>
|
||||
<el-option label="女" value="女"></el-option>
|
||||
</el-select>
|
||||
@@ -86,11 +87,16 @@ layout("/layouts/platform.html"){
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="监护人1">
|
||||
<el-form-item label="监护人1" prop="guardianUserName1">
|
||||
<el-input v-model="formData.guardianUserName1" readonly></el-input>
|
||||
<el-input :disabled="formData.qualificationLocked && !correctionMode" v-model="formData.guardianUserName1" readonly></el-input>
|
||||
<el-select v-if="correctionMode" v-model="formData.guardianRelationship1" placeholder="与子女关系" style="width:100%">
|
||||
<el-option label="父亲" value="父亲"></el-option>
|
||||
<el-option label="母亲" value="母亲"></el-option>
|
||||
<el-option label="其他法定监护人" value="其他法定监护人"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="工号" >
|
||||
<el-form-item prop="guardianLoginName1">{{formData.guardianLoginName1}}
|
||||
<el-form-item prop="guardianLoginName1"><el-input v-if="correctionMode" v-model="formData.guardianLoginName1" placeholder="本校教职工工号"></el-input><span v-else>{{formData.guardianLoginName1}}</span>
|
||||
</el-form-item>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="手机号码">
|
||||
@@ -105,12 +111,17 @@ layout("/layouts/platform.html"){
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="监护人2">
|
||||
<el-form-item label="监护人2" prop="guardianUserName2">
|
||||
<el-input v-model="formData.guardianUserName2" placeholder="请输入监护人姓名" maxlength="30"></el-input>
|
||||
<el-input :disabled="formData.qualificationLocked && !correctionMode" v-model="formData.guardianUserName2" placeholder="请输入监护人姓名" maxlength="30"></el-input>
|
||||
<el-select v-if="correctionMode" v-model="formData.guardianRelationship2" placeholder="与子女关系" style="width:100%">
|
||||
<el-option label="父亲" value="父亲"></el-option>
|
||||
<el-option label="母亲" value="母亲"></el-option>
|
||||
<el-option label="其他法定监护人" value="其他法定监护人"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="工号">
|
||||
<el-form-item prop="guardianLoginName2">
|
||||
<el-input v-model="formData.guardianLoginName2" placeholder="没有则不填" maxlength="30"></el-input>
|
||||
<el-input :disabled="formData.qualificationLocked && !correctionMode" v-model="formData.guardianLoginName2" placeholder="没有则不填" maxlength="30"></el-input>
|
||||
</el-form-item>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="手机号码">
|
||||
@@ -130,8 +141,18 @@ layout("/layouts/platform.html"){
|
||||
</el-form-item></el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-form>
|
||||
<el-form v-if="correctionMode" label-width="150px" class="mt20">
|
||||
<el-alert title="更正申请仅提交关键身份和监护关系,审批前原资料保持不变。日常资料请退出更正模式后保存。" type="info" :closable="false"></el-alert>
|
||||
<el-form-item label="更正原因及证明依据" required><el-input v-model="correctionReason" type="textarea" maxlength="2000" placeholder="说明更正原因及可供核验的证明依据"></el-input></el-form-item>
|
||||
</el-form>
|
||||
<el-table v-if="formData.correctionHistory && formData.correctionHistory.length" :data="formData.correctionHistory" border class="mt20">
|
||||
<el-table-column prop="submittedAt" label="更正申请时间"></el-table-column><el-table-column prop="reason" label="更正原因"></el-table-column><el-table-column prop="status" label="办理状态"></el-table-column><el-table-column prop="reviewBasis" label="核验结果"></el-table-column><el-table-column prop="effectiveMonth" label="生效月份"></el-table-column>
|
||||
</el-table>
|
||||
<el-row type="flex" justify="end" class="mt20">
|
||||
<el-button type="primary" plain @click="onSave">保存</el-button>
|
||||
<el-button v-if="formData.qualificationLocked && !correctionMode" @click="beginCorrection">申请关键资料更正</el-button>
|
||||
<el-button v-if="correctionMode" @click="cancelCorrection">取消更正</el-button>
|
||||
<el-button v-if="correctionMode" type="primary" @click="submitCorrection">提交更正申请</el-button>
|
||||
<el-button v-else type="primary" plain @click="onSave">{{formData.qualificationLocked ? '保存日常资料' : '保存'}}</el-button>
|
||||
</el-row>
|
||||
</el-card>
|
||||
</div>
|
||||
@@ -144,9 +165,10 @@ layout("/layouts/platform.html"){
|
||||
data() {
|
||||
return {
|
||||
bizId: GetQueryString("bizId"),
|
||||
formData: {},
|
||||
formData: {}, correctionMode: false, correctionReason: '', originalData: null,
|
||||
|
||||
formRules: {
|
||||
childRelationship: [{ required: true, message: '请选择就读年级', trigger: 'change' }],
|
||||
childrenGrade: [{ required: true, message: '请选择就读年级', trigger: 'change' }],
|
||||
childrenYear: [{ required: true, message: '请选择入学年份', trigger: 'change' }],
|
||||
childrenName: [{ required: true, message: '请输入子女姓名', trigger: 'blur' }],
|
||||
childrenIdCard: [{ 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])|10|20|30|31)\d{3}[0-9Xx]$/, message: '请输入正确的身份证号码', trigger: 'blur'}],
|
||||
@@ -158,6 +180,25 @@ layout("/layouts/platform.html"){
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
beginCorrection() {
|
||||
this.originalData = JSON.parse(JSON.stringify(this.formData))
|
||||
this.correctionReason = ''
|
||||
this.correctionMode = true
|
||||
},
|
||||
cancelCorrection() {
|
||||
this.formData = this.originalData
|
||||
this.correctionMode = false
|
||||
},
|
||||
submitCorrection() {
|
||||
if (!this.correctionReason.trim()) { this.$message.warning('请填写更正原因及证明依据'); return }
|
||||
this.$axios.post('/platform/childManage/write/requestChange', {data: JSON.stringify(this.formData), reason: this.correctionReason}).then(async res => {
|
||||
if (res.code === 0) {
|
||||
this.correctionMode = false
|
||||
this.formData = await this.findOne(this.bizId)
|
||||
this.$message.success(res.msg)
|
||||
}
|
||||
})
|
||||
},
|
||||
async getIsRepeatByIdCard() {
|
||||
if (!this.formData.childrenIdCard) {
|
||||
this.$message.error("请填写子女身份证号码")
|
||||
@@ -169,10 +210,10 @@ layout("/layouts/platform.html"){
|
||||
})
|
||||
if (res.code === 0) {
|
||||
if (res.data > 0) {
|
||||
this.$message.error("该身份证在本年度已填报!")
|
||||
this.$message.error("该身份证已登记,请维护已有子女资料!")
|
||||
return true
|
||||
} else {
|
||||
this.$message.success("该身份证在本年度暂未填报!")
|
||||
this.$message.success("该身份证暂未登记!")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,11 +12,11 @@ layout("/layouts/platform_h5.html"){
|
||||
<div id="app">
|
||||
<van-nav-bar title="子女信息填报" left-text="返回" left-arrow @click-left="historyBack" placeholder fixed></van-nav-bar>
|
||||
<!-- 表单容器 -->
|
||||
<van-form ref="formRef" class="form-container">
|
||||
<van-notice-bar v-if="formData.qualificationLocked" wrapable :scrollable="false" text="该子女已关联待核验、已核定或已停发的独生子女费资格,关键身份和监护关系不可直接修改;联系电话、学校、年级等日常信息仍可维护。"></van-notice-bar><van-form ref="formRef" class="form-container">
|
||||
|
||||
<!-- 子女信息 -->
|
||||
<van-cell-group title="子女信息" class="form-section">
|
||||
<van-field label="姓名" :rules="[{ required: true }]" v-model="formData.childrenName" placeholder="请输入姓名" required></van-field>
|
||||
<van-field label="姓名" :rules="[{ required: true }]" :disabled="formData.qualificationLocked && !correctionMode" v-model="formData.childrenName" placeholder="请输入姓名" required></van-field>
|
||||
<van-field
|
||||
v-model="formData.childrenGrade"
|
||||
name="childrenGrade"
|
||||
@@ -48,16 +48,16 @@ layout("/layouts/platform_h5.html"){
|
||||
</van-popup>
|
||||
<van-field
|
||||
label="身份证号"
|
||||
v-model="formData.childrenIdCard"
|
||||
:disabled="formData.qualificationLocked && !correctionMode" v-model="formData.childrenIdCard"
|
||||
placeholder="请输入身份证号"
|
||||
:rules="formRules.childrenIdCard"
|
||||
required
|
||||
></van-field> <van-field
|
||||
label="出生年月"
|
||||
v-model="formData.childrenBirthday"
|
||||
:disabled="formData.qualificationLocked && !correctionMode" v-model="formData.childrenBirthday"
|
||||
placeholder="请选择出生年月"
|
||||
readonly
|
||||
@click="showBirthdayPicker = true"
|
||||
@click="(!formData.qualificationLocked || correctionMode) && (showBirthdayPicker = true)"
|
||||
clickable
|
||||
></van-field>
|
||||
<van-popup v-model="showBirthdayPicker" position="bottom">
|
||||
@@ -74,10 +74,10 @@ layout("/layouts/platform_h5.html"){
|
||||
|
||||
<van-field
|
||||
label="性别"
|
||||
v-model="formData.childrenSex"
|
||||
:disabled="formData.qualificationLocked && !correctionMode" v-model="formData.childrenSex"
|
||||
placeholder="请选择性别"
|
||||
readonly
|
||||
@click="showSexPicker = true"
|
||||
@click="(!formData.qualificationLocked || correctionMode) && (showSexPicker = true)"
|
||||
clickable
|
||||
></van-field>
|
||||
|
||||
@@ -99,8 +99,8 @@ layout("/layouts/platform_h5.html"){
|
||||
<!-- 监护人信息 -->
|
||||
<van-cell-group title="监护人信息" class="form-section">
|
||||
|
||||
<van-field label="监护人1" v-model="formData.guardianUserName1" readonly placeholder="读取信息中心数据"></van-field>
|
||||
<van-field label="工号" v-model="formData.guardianLoginName1" readonly placeholder="读取信息中心数据"></van-field>
|
||||
<van-field label="监护人1" :disabled="formData.qualificationLocked && !correctionMode" v-model="formData.guardianUserName1" readonly placeholder="读取信息中心数据"></van-field>
|
||||
<van-field label="监护关系" :disabled="formData.qualificationLocked && !correctionMode" v-model="formData.guardianRelationship1" placeholder="父亲、母亲或其他法定监护人" required maxlength="30"></van-field><van-field label="工号" :disabled="formData.qualificationLocked && !correctionMode" v-model="formData.guardianLoginName1" :readonly="!correctionMode" placeholder="读取信息中心数据"></van-field>
|
||||
<van-field
|
||||
label="手机号码"
|
||||
v-model="formData.guardianMobile1"
|
||||
@@ -110,10 +110,10 @@ layout("/layouts/platform_h5.html"){
|
||||
<van-field label="所在单位" v-model="formData.guardianUnitName1" readonly placeholder="读取信息中心数据"></van-field>
|
||||
<van-field
|
||||
label="监护人2"
|
||||
v-model="formData.guardianUserName2"
|
||||
:disabled="formData.qualificationLocked && !correctionMode" v-model="formData.guardianUserName2"
|
||||
placeholder="请输入监护人姓名"
|
||||
></van-field>
|
||||
<van-field label="工号" v-model="formData.guardianLoginName2" placeholder="没有则不填" ></van-field>
|
||||
<van-field label="监护关系" :disabled="formData.qualificationLocked && !correctionMode" v-model="formData.guardianRelationship2" placeholder="父亲、母亲或其他法定监护人" maxlength="30"></van-field><van-field label="工号" :disabled="formData.qualificationLocked && !correctionMode" v-model="formData.guardianLoginName2" placeholder="没有则不填" ></van-field>
|
||||
<van-field
|
||||
label="手机号码"
|
||||
v-model="formData.guardianMobile2"
|
||||
@@ -132,7 +132,12 @@ layout("/layouts/platform_h5.html"){
|
||||
|
||||
<!-- 提交按钮 -->
|
||||
<div class="form-actions">
|
||||
<van-button @click="onSave" round type="info">保存申请</van-button>
|
||||
<van-field v-if="correctionMode" v-model="correctionReason" type="textarea" label="更正原因" placeholder="说明更正原因及证明依据;审批前原资料保持不变" maxlength="2000" required></van-field>
|
||||
<van-button v-if="formData.qualificationLocked && !correctionMode" @click="beginCorrection" round>申请关键资料更正</van-button>
|
||||
<van-button v-if="correctionMode" @click="cancelCorrection" round>取消更正</van-button>
|
||||
<van-button v-if="correctionMode" @click="submitCorrection" round type="info">提交更正申请</van-button>
|
||||
<van-button v-else @click="onSave" round type="info">{{formData.qualificationLocked ? '保存日常资料' : '保存申请'}}</van-button>
|
||||
<van-cell v-for="change in formData.correctionHistory || []" :key="change.id" :title="'更正申请:' + change.status" :label="change.reason + ';核验结果:' + (change.reviewBasis || '待核验') + ';生效月份:' + (change.effectiveMonth || '待核定')"></van-cell>
|
||||
</div>
|
||||
</van-form>
|
||||
</div>
|
||||
@@ -143,7 +148,7 @@ layout("/layouts/platform_h5.html"){
|
||||
dicts: ["CHILD_MANAGE_GRADE"],
|
||||
data() {
|
||||
return {
|
||||
bizId: GetQueryString("bizId"),
|
||||
bizId: GetQueryString("bizId"), correctionMode: false, correctionReason: '', originalData: null,
|
||||
formData: {
|
||||
childrenName: '',
|
||||
childrenGrade: '',
|
||||
@@ -215,6 +220,25 @@ layout("/layouts/platform_h5.html"){
|
||||
},
|
||||
|
||||
methods: {
|
||||
beginCorrection() {
|
||||
this.originalData = JSON.parse(JSON.stringify(this.formData))
|
||||
this.correctionReason = ''
|
||||
this.correctionMode = true
|
||||
},
|
||||
cancelCorrection() {
|
||||
this.formData = this.originalData
|
||||
this.correctionMode = false
|
||||
},
|
||||
submitCorrection() {
|
||||
if (!this.correctionReason.trim()) { this.$toast('请填写更正原因及证明依据'); return }
|
||||
this.$axios.post('/platform/childManage/write/requestChange', {data: JSON.stringify(this.formData), reason: this.correctionReason}).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.correctionMode = false
|
||||
this.$toast(res.msg)
|
||||
this.init()
|
||||
}
|
||||
})
|
||||
},
|
||||
onSave() {
|
||||
// 先进行表单验证
|
||||
this.$refs.formRef.validate().then(() => {
|
||||
|
||||
Reference in New Issue
Block a user