Compare commits
14
Commits
9f34d65283
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d5e948e4c | ||
|
|
030bb7cb23 | ||
|
|
18397fb7c9 | ||
|
|
1cd0552a26 | ||
|
|
19ebd4cac3 | ||
|
|
4ca2b73d7b | ||
|
|
03864ea416 | ||
|
|
c909f0a3fb | ||
|
|
593500cecc | ||
|
|
f709e3a0e4 | ||
|
|
1c2630a338 | ||
|
|
c90c4619ee | ||
|
|
3a7ef3210f | ||
|
|
5ff22b1697 |
+51
-1
@@ -1 +1,51 @@
|
|||||||
/.idea/
|
# 依赖与构建产物
|
||||||
|
**/node_modules/
|
||||||
|
**/dist/
|
||||||
|
**/build/
|
||||||
|
**/target/
|
||||||
|
**/out/
|
||||||
|
**/.next/
|
||||||
|
**/.nuxt/
|
||||||
|
**/coverage/
|
||||||
|
|
||||||
|
# 日志、缓存与临时文件
|
||||||
|
**/*.log
|
||||||
|
**/*.tmp
|
||||||
|
**/*.temp
|
||||||
|
**/.cache/
|
||||||
|
**/.eslintcache
|
||||||
|
**/npm-debug.log*
|
||||||
|
**/yarn-debug.log*
|
||||||
|
**/yarn-error.log*
|
||||||
|
**/pnpm-debug.log*
|
||||||
|
|
||||||
|
# IDE 与操作系统文件
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.iml
|
||||||
|
.project
|
||||||
|
.classpath
|
||||||
|
.settings/
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# 本地环境和密钥配置(保留示例文件)
|
||||||
|
**/.env
|
||||||
|
**/.env.local
|
||||||
|
**/.env.*.local
|
||||||
|
!**/.env.example
|
||||||
|
!**/.env.*.example
|
||||||
|
|
||||||
|
# Java 编译文件
|
||||||
|
**/*.class
|
||||||
|
.trae/.ignore
|
||||||
|
.trae/cache-components/SKILL.md
|
||||||
|
.trae/code-reviewer/SKILL.md
|
||||||
|
.trae/find-skills/SKILL.md
|
||||||
|
.trae/fix/SKILL.md
|
||||||
|
.trae/frontend-code-review/SKILL.md
|
||||||
|
.trae/frontend-design/SKILL.md
|
||||||
|
.trae/fullstack-developer/SKILL.md
|
||||||
|
.trae/pr-creator/SKILL.md
|
||||||
|
.trae/update-docs/SKILL.md
|
||||||
|
.trae/webapp-testing/SKILL.md
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package io.v.nutz.activity.controller.site;
|
||||||
|
|
||||||
|
import cn.wizzer.framework.base.Result;
|
||||||
|
import io.v.nutz.activity.services.SiteReserveService;
|
||||||
|
import io.v.nutz.annontation.ViReturn;
|
||||||
|
import org.apache.shiro.authz.annotation.RequiresAuthentication;
|
||||||
|
import org.nutz.ioc.loader.annotation.Inject;
|
||||||
|
import org.nutz.ioc.loader.annotation.IocBean;
|
||||||
|
import org.nutz.mvc.annotation.At;
|
||||||
|
import org.nutz.mvc.annotation.Ok;
|
||||||
|
import org.nutz.mvc.annotation.POST;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 电脑端和手机端共用的场地预约承诺书阅读、签署入口。
|
||||||
|
*/
|
||||||
|
@IocBean
|
||||||
|
@At("/platform/activity/site/commitment")
|
||||||
|
@Ok("json:full")
|
||||||
|
@RequiresAuthentication
|
||||||
|
public class SiteCommitmentController {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private SiteReserveService siteReserveService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验预约资格并记录服务器阅读开始时间。
|
||||||
|
* @param siteId 场地ID
|
||||||
|
* @return 承诺书及阅读凭证,或不可预约原因
|
||||||
|
*/
|
||||||
|
@At
|
||||||
|
@POST
|
||||||
|
@ViReturn
|
||||||
|
public Object begin(String siteId) {
|
||||||
|
try {
|
||||||
|
return siteReserveService.beginCommitment(siteId);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return Result.error(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 阅读十秒后签署本次承诺书。
|
||||||
|
* @param commitmentId 本次阅读凭证
|
||||||
|
* @param signature 本次手写签名PNG数据
|
||||||
|
* @return 签署结果
|
||||||
|
*/
|
||||||
|
@At
|
||||||
|
@POST
|
||||||
|
@ViReturn
|
||||||
|
public Object sign(String commitmentId, String signature) {
|
||||||
|
try {
|
||||||
|
siteReserveService.signCommitment(commitmentId, signature);
|
||||||
|
return Result.success("签署成功");
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return Result.error(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,10 @@ package io.v.nutz.activity.controller.site;
|
|||||||
|
|
||||||
|
|
||||||
import io.v.nutz.activity.models.ActivitySiteInfo;
|
import io.v.nutz.activity.models.ActivitySiteInfo;
|
||||||
|
import cn.wizzer.framework.base.Result;
|
||||||
|
import org.jsoup.Jsoup;
|
||||||
|
import org.jsoup.safety.Whitelist;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import io.v.nutz.activity.services.SiteInfoService;
|
import io.v.nutz.activity.services.SiteInfoService;
|
||||||
import io.v.nutz.annontation.ViReturn;
|
import io.v.nutz.annontation.ViReturn;
|
||||||
import io.v.nutz.base.utils.PageUtil;
|
import io.v.nutz.base.utils.PageUtil;
|
||||||
@@ -62,16 +66,34 @@ public class SiteManageController {
|
|||||||
return siteInfoService.listPage(pageNumber, pageSize, cnd);
|
return siteInfoService.listPage(pageNumber, pageSize, cnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增场地,面向分工会时要求配置预约前承诺书。
|
||||||
|
* @param activitySiteInfo 场地信息及承诺书标题、正文
|
||||||
|
* @return 保存结果或配置校验错误
|
||||||
|
*/
|
||||||
@At
|
@At
|
||||||
@ViReturn
|
@ViReturn
|
||||||
public Object doAdd(@Param("data") ActivitySiteInfo activitySiteInfo) {
|
public Object doAdd(@Param("data") ActivitySiteInfo activitySiteInfo) {
|
||||||
|
String error = prepareCommitment(activitySiteInfo);
|
||||||
|
if (error != null) {
|
||||||
|
return Result.error(error);
|
||||||
|
}
|
||||||
siteInfoService.insert(activitySiteInfo);
|
siteInfoService.insert(activitySiteInfo);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存场地及承诺书配置,已签署申请继续保留原正文快照。
|
||||||
|
* @param activitySiteInfo 修改后的场地信息
|
||||||
|
* @return 保存结果或配置校验错误
|
||||||
|
*/
|
||||||
@At
|
@At
|
||||||
@ViReturn
|
@ViReturn
|
||||||
public Object doEdit(@Param("data") ActivitySiteInfo activitySiteInfo) {
|
public Object doEdit(@Param("data") ActivitySiteInfo activitySiteInfo) {
|
||||||
|
String error = prepareCommitment(activitySiteInfo);
|
||||||
|
if (error != null) {
|
||||||
|
return Result.error(error);
|
||||||
|
}
|
||||||
siteInfoService.update(activitySiteInfo);
|
siteInfoService.update(activitySiteInfo);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -88,4 +110,25 @@ public class SiteManageController {
|
|||||||
public Object findOneSite(String id) {
|
public Object findOneSite(String id) {
|
||||||
return siteInfoService.fetch(id);
|
return siteInfoService.fetch(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 承诺书会在预约弹窗和历史签署记录中展示,仅保留正文所需的安全HTML。
|
||||||
|
private String prepareCommitment(ActivitySiteInfo site) {
|
||||||
|
if (site.getCommitmentContent() != null) {
|
||||||
|
site.setCommitmentContent(Jsoup.clean(site.getCommitmentContent(), Whitelist.relaxed()));
|
||||||
|
}
|
||||||
|
if (Integer.valueOf(1).equals(site.getReserveTarget())) {
|
||||||
|
if (Strings.isBlank(site.getCommitmentTitle()) || Strings.isBlank(site.getCommitmentContent())
|
||||||
|
|| Strings.isBlank(Jsoup.parse(site.getCommitmentContent()).text())) {
|
||||||
|
return "面向分工会的场地必须配置承诺书标题和正文";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (site.getCommitmentTitle() != null && site.getCommitmentTitle().length() > 200) {
|
||||||
|
return "承诺书标题不能超过200字";
|
||||||
|
}
|
||||||
|
if (site.getCommitmentContent() != null
|
||||||
|
&& site.getCommitmentContent().getBytes(StandardCharsets.UTF_8).length > 60000) {
|
||||||
|
return "承诺书正文过长,请精简内容";
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import org.nutz.lang.util.NutMap;
|
|||||||
import org.nutz.mvc.annotation.At;
|
import org.nutz.mvc.annotation.At;
|
||||||
import org.nutz.mvc.annotation.Ok;
|
import org.nutz.mvc.annotation.Ok;
|
||||||
import org.nutz.mvc.annotation.Param;
|
import org.nutz.mvc.annotation.Param;
|
||||||
|
import org.nutz.trans.Trans;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
@@ -161,9 +162,25 @@ public class SiteReserveController {
|
|||||||
return fetch.getReserve_state() > stateCode;
|
return fetch.getReserve_state() > stateCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建预约,面向分工会的场地必须先完成本次承诺书签署。
|
||||||
|
* @param activitySiteReserve 预约表单
|
||||||
|
* @param days 本次申请的预约日期
|
||||||
|
* @param commitmentId 当前用户签署的场地承诺书凭证
|
||||||
|
* @return 预约结果或资格、签署校验失败原因
|
||||||
|
*/
|
||||||
@At
|
@At
|
||||||
@ViReturn
|
@ViReturn
|
||||||
public Object doAdd(@Param("data") ActivitySiteReserve activitySiteReserve, @Param("days") String[] days) {
|
public Object doAdd(@Param("data") ActivitySiteReserve activitySiteReserve, @Param("days") String[] days, String commitmentId) {
|
||||||
|
|
||||||
|
if (activitySiteReserve == null || days == null || days.length == 0) {
|
||||||
|
return Result.error("请选择预约场地和日期");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
siteReserveService.checkCommitment(activitySiteReserve.getSite_id(), commitmentId);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return Result.error(e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
Map<Boolean, String> validateSiteReserve = siteReserveService.validateSiteReserve(
|
Map<Boolean, String> validateSiteReserve = siteReserveService.validateSiteReserve(
|
||||||
activitySiteReserve.getSite_id(),
|
activitySiteReserve.getSite_id(),
|
||||||
@@ -183,22 +200,31 @@ public class SiteReserveController {
|
|||||||
Integer stateCode = activityCommonService.findStartStateCodeByModuleName(moduleName);
|
Integer stateCode = activityCommonService.findStartStateCodeByModuleName(moduleName);
|
||||||
|
|
||||||
if (stateCode == null) {
|
if (stateCode == null) {
|
||||||
return null;
|
return Result.error("场地尚未配置预约审核流程");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (String d : days) {
|
try {
|
||||||
activitySiteReserve.setReserve_day(d);
|
// 同一申请的多天预约和签署凭证一起提交,失败时不消耗签署凭证。
|
||||||
//查询这个人是否已经预约这个时间段
|
Trans.exec(() -> {
|
||||||
int count = siteReserveService.count("activity_site_reserve", Cnd.NEW().and("reserve_person_id", "=", ShiroUtil.getPrincipalProperty("id"))
|
siteReserveService.bindCommitment(activitySiteReserve.getSite_id(), commitmentId, r);
|
||||||
.and("reserve_day", "=", activitySiteReserve.getReserve_day()).and("start_time", "=", activitySiteReserve.getStart_time())
|
for (String d : days) {
|
||||||
.and("end_time", "=", activitySiteReserve.getEnd_time()));
|
activitySiteReserve.setReserve_day(d);
|
||||||
if (count > 0) {
|
//查询这个人是否已经预约这个时间段
|
||||||
return Result.error("您已预约该时间段!");
|
int count = siteReserveService.count("activity_site_reserve", Cnd.NEW().and("reserve_person_id", "=", ShiroUtil.getPrincipalProperty("id"))
|
||||||
}
|
.and("reserve_day", "=", activitySiteReserve.getReserve_day()).and("start_time", "=", activitySiteReserve.getStart_time())
|
||||||
|
.and("end_time", "=", activitySiteReserve.getEnd_time()));
|
||||||
|
if (count > 0) {
|
||||||
|
throw new IllegalArgumentException("您已预约该时间段!");
|
||||||
|
}
|
||||||
|
|
||||||
activitySiteReserve.setReserve_state(stateCode);
|
activitySiteReserve.setReserve_state(stateCode);
|
||||||
activitySiteReserve.setReserve_person_id(ShiroUtil.getPrincipalProperty("id").toString());
|
activitySiteReserve.setReserve_person_id(ShiroUtil.getPrincipalProperty("id").toString());
|
||||||
siteReserveService.insert(activitySiteReserve);
|
activitySiteReserve.setId(R.UU32());
|
||||||
|
siteReserveService.insert(activitySiteReserve);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return Result.error(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
//发送微信提醒审核人,查询状态码对应的审核人
|
//发送微信提醒审核人,查询状态码对应的审核人
|
||||||
@@ -307,10 +333,18 @@ public class SiteReserveController {
|
|||||||
return siteReserveService.count(cnd);
|
return siteReserveService.count(cnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询预约详情及本次申请签署的承诺书快照。
|
||||||
|
* @param id 预约记录ID
|
||||||
|
* @return 预约详情、审核记录和承诺书签署信息
|
||||||
|
*/
|
||||||
@At
|
@At
|
||||||
@ViReturn
|
@ViReturn
|
||||||
public Object findOne(String id) {
|
public Object findOne(String id) {
|
||||||
ActivitySiteReserve fetch = siteReserveService.fetch(id);
|
ActivitySiteReserve fetch = siteReserveService.fetch(id);
|
||||||
|
if (fetch == null) {
|
||||||
|
return Result.error("预约记录不存在");
|
||||||
|
}
|
||||||
Sql sql = Sqls.create("""
|
Sql sql = Sqls.create("""
|
||||||
SELECT
|
SELECT
|
||||||
asr.*,
|
asr.*,
|
||||||
@@ -386,6 +420,7 @@ public class SiteReserveController {
|
|||||||
i.setCondition(cnd);
|
i.setCondition(cnd);
|
||||||
List<Record> list1 = siteReserveService.list(i);
|
List<Record> list1 = siteReserveService.list(i);
|
||||||
record.set("site_info", list1);
|
record.set("site_info", list1);
|
||||||
|
record.set("commitment", siteReserveService.findCommitment(fetch.getSqid()));
|
||||||
|
|
||||||
return record;
|
return record;
|
||||||
}
|
}
|
||||||
@@ -455,6 +490,13 @@ public class SiteReserveController {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定日期的场地预约时段,支持以24:00作为营业结束时间。
|
||||||
|
*
|
||||||
|
* @param siteId 场地ID
|
||||||
|
* @param day 预约日期,格式为yyyy-MM-dd
|
||||||
|
* @return 预约时段及不可预约状态,保留配置的结束时间用于匹配预约记录
|
||||||
|
*/
|
||||||
@At
|
@At
|
||||||
@ViReturn
|
@ViReturn
|
||||||
public Object getTimesByDay(String siteId, String day) {
|
public Object getTimesByDay(String siteId, String day) {
|
||||||
@@ -480,16 +522,19 @@ public class SiteReserveController {
|
|||||||
} else {
|
} else {
|
||||||
// 构造起始时间
|
// 构造起始时间
|
||||||
DateTime start = DateUtil.parse(startTime, "HH:mm"); // "09:00"
|
DateTime start = DateUtil.parse(startTime, "HH:mm"); // "09:00"
|
||||||
DateTime end = DateUtil.parse(endTime, "HH:mm"); // "17:00"
|
// 场地允许营业至24:00,按次日零点计算,返回时保留24:00以匹配预约记录。
|
||||||
|
DateTime end = "24:00".equals(endTime)
|
||||||
|
? DateUtil.offsetDay(DateUtil.parse("00:00", "HH:mm"), 1)
|
||||||
|
: DateUtil.parse(endTime, "HH:mm");
|
||||||
DateTime current = start;
|
DateTime current = start;
|
||||||
while (DateUtil.compare(current, end) < 0) { // 当前时间 <= 结束时间
|
while (DateUtil.compare(current, end) < 0) { // 当前时间 <= 结束时间
|
||||||
DateTime next = DateUtil.offsetSecond(current, (int) (timeUnit * 3600));
|
DateTime next = DateUtil.offsetSecond(current, (int) (timeUnit * 3600));
|
||||||
// 如果超过结束时间,则使用 endTime
|
// 如果超过结束时间,则使用 endTime
|
||||||
if (DateUtil.compare(next, end) > 0) {
|
if (DateUtil.compare(next, end) > 0) {
|
||||||
result.add(NutMap.NEW().addv("start_time", DateUtil.format(current, "HH:mm")).addv("end_time", DateUtil.format(end, "HH:mm")));
|
result.add(NutMap.NEW().addv("start_time", DateUtil.format(current, "HH:mm")).addv("end_time", endTime));
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
result.add(NutMap.NEW().addv("start_time", DateUtil.format(current, "HH:mm")).addv("end_time", DateUtil.format(next, "HH:mm")));
|
result.add(NutMap.NEW().addv("start_time", DateUtil.format(current, "HH:mm")).addv("end_time", DateUtil.compare(next, end) == 0 ? endTime : DateUtil.format(next, "HH:mm")));
|
||||||
}
|
}
|
||||||
current = next;
|
current = next;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import io.v.nutz.activity.services.ActivityCommonService;
|
|||||||
import io.v.nutz.activity.services.SiteInfoService;
|
import io.v.nutz.activity.services.SiteInfoService;
|
||||||
import io.v.nutz.activity.services.SiteReserveService;
|
import io.v.nutz.activity.services.SiteReserveService;
|
||||||
import io.v.nutz.annontation.ViReturn;
|
import io.v.nutz.annontation.ViReturn;
|
||||||
|
import io.v.nutz.base.result.Result;
|
||||||
import io.v.nutz.base.utils.MsgApi;
|
import io.v.nutz.base.utils.MsgApi;
|
||||||
import io.v.nutz.base.utils.PageUtil;
|
import io.v.nutz.base.utils.PageUtil;
|
||||||
import io.v.nutz.base.utils.SendMsg;
|
import io.v.nutz.base.utils.SendMsg;
|
||||||
@@ -18,6 +19,7 @@ import io.v.nutz.sys.services.SysUserService;
|
|||||||
import io.v.nutz.util.ShiroUtil;
|
import io.v.nutz.util.ShiroUtil;
|
||||||
import org.apache.shiro.authz.annotation.RequiresAuthentication;
|
import org.apache.shiro.authz.annotation.RequiresAuthentication;
|
||||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||||
|
import org.nutz.dao.Chain;
|
||||||
import org.nutz.dao.Cnd;
|
import org.nutz.dao.Cnd;
|
||||||
import org.nutz.dao.Sqls;
|
import org.nutz.dao.Sqls;
|
||||||
import org.nutz.dao.entity.Record;
|
import org.nutz.dao.entity.Record;
|
||||||
@@ -34,6 +36,7 @@ import org.nutz.mvc.annotation.Ok;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -148,14 +151,49 @@ public class SiteReviewController {
|
|||||||
|
|
||||||
@At
|
@At
|
||||||
@ViReturn
|
@ViReturn
|
||||||
public Object doReview(String[] id, Audit audit, Boolean isPass) {
|
public Object doReview(String[] id, Integer[] reserveState, Audit audit, Boolean isPass) {
|
||||||
|
if (id == null || reserveState == null || id.length == 0 || id.length != reserveState.length || isPass == null) {
|
||||||
|
return Result.error("审核参数错误,请刷新页面后重试");
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ActivitySiteReserve> reviewList = new ArrayList<>();
|
||||||
|
List<Integer> afterStateList = new ArrayList<>();
|
||||||
|
int skippedCount = 0;
|
||||||
|
for (int i = 0; i < id.length; i++) {
|
||||||
|
ActivitySiteReserve reserve = siteReserveService.fetch(id[i]);
|
||||||
|
if (reserve == null || !Objects.equals(reserve.getReserve_state(), reserveState[i])) {
|
||||||
|
skippedCount++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Integer afterStateCode = activityCommonService.findAfterStateCode(reserve.getReserve_state(), isPass);
|
||||||
|
if (afterStateCode == null) {
|
||||||
|
return Result.error("当前预约已完成审核或审核流程配置异常,请刷新页面后重试");
|
||||||
|
}
|
||||||
|
reviewList.add(reserve);
|
||||||
|
afterStateList.add(afterStateCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reviewList.isEmpty()) {
|
||||||
|
return Result.success("所选预约已被其他人审核,无需重复处理")
|
||||||
|
.addData(NutMap.NEW().setv("skippedCount", skippedCount));
|
||||||
|
}
|
||||||
|
|
||||||
siteReserveService.insert(audit);
|
siteReserveService.insert(audit);
|
||||||
|
|
||||||
for (String s : id) {
|
int reviewCount = 0;
|
||||||
ActivitySiteReserve fetch = siteReserveService.fetch(s);
|
for (int i = 0; i < reviewList.size(); i++) {
|
||||||
|
ActivitySiteReserve fetch = reviewList.get(i);
|
||||||
Integer stateCode = fetch.getReserve_state();
|
Integer stateCode = fetch.getReserve_state();
|
||||||
Integer afterStateCode = activityCommonService.findAfterStateCode(stateCode, isPass);
|
Integer afterStateCode = afterStateList.get(i);
|
||||||
|
|
||||||
|
// 按原审核状态更新,避免多个管理员同时审核时重复处理同一申请
|
||||||
|
int updateCount = siteReserveService.update(Chain.make("reserve_state", afterStateCode),
|
||||||
|
Cnd.NEW().and("sqid", "=", fetch.getSqid()).and("reserve_state", "=", stateCode));
|
||||||
|
if (updateCount == 0) {
|
||||||
|
skippedCount++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
reviewCount++;
|
||||||
|
|
||||||
List<ActivitySiteReserve> reserves = siteReserveService.query(Cnd.NEW().and("sqid", "=", fetch.getSqid()));
|
List<ActivitySiteReserve> reserves = siteReserveService.query(Cnd.NEW().and("sqid", "=", fetch.getSqid()));
|
||||||
String days = "";
|
String days = "";
|
||||||
@@ -217,6 +255,15 @@ public class SiteReviewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
if (reviewCount == 0) {
|
||||||
|
siteReserveService.dao().delete(audit);
|
||||||
|
return Result.success("所选预约已被其他人审核,无需重复处理")
|
||||||
|
.addData(NutMap.NEW().setv("skippedCount", skippedCount));
|
||||||
|
}
|
||||||
|
if (skippedCount > 0) {
|
||||||
|
return Result.success("审核完成,成功处理" + reviewCount + "条,跳过" + skippedCount + "条状态已变化的预约")
|
||||||
|
.addData(NutMap.NEW().setv("skippedCount", skippedCount));
|
||||||
|
}
|
||||||
|
return Result.success("审核成功");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package io.v.nutz.activity.models;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.nutz.dao.entity.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 场地预约承诺书阅读、签署及申请关联记录,保留签署时的正文快照。
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Table("activity_site_commitment")
|
||||||
|
public class ActivitySiteCommitment {
|
||||||
|
|
||||||
|
@Name
|
||||||
|
@Column
|
||||||
|
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@Comment("场地ID")
|
||||||
|
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||||
|
private String siteId;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@Comment("签署人ID")
|
||||||
|
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||||
|
private String userId;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@Comment("签署人姓名快照")
|
||||||
|
@ColDefine(type = ColType.VARCHAR, width = 120)
|
||||||
|
private String userName;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@Comment("承诺书标题快照")
|
||||||
|
@ColDefine(type = ColType.VARCHAR, width = 200)
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@Comment("承诺书正文快照")
|
||||||
|
@ColDefine(type = ColType.TEXT)
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@Comment("服务器记录的阅读开始时间,毫秒")
|
||||||
|
private Long readAt;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@Comment("服务器记录的签署时间,毫秒")
|
||||||
|
private Long signedAt;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@Comment("本次手写签名PNG数据")
|
||||||
|
@ColDefine(type = ColType.TEXT)
|
||||||
|
private String signature;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@Comment("预约申请编号,未提交时为空")
|
||||||
|
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||||
|
private String sqid;
|
||||||
|
}
|
||||||
@@ -83,6 +83,16 @@ public class ActivitySiteInfo {
|
|||||||
@ColDefine(type = ColType.TEXT)
|
@ColDefine(type = ColType.TEXT)
|
||||||
private String introduce;
|
private String introduce;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@Comment("预约前承诺书标题")
|
||||||
|
@ColDefine(type = ColType.VARCHAR, width = 200)
|
||||||
|
private String commitmentTitle;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@Comment("预约前承诺书正文")
|
||||||
|
@ColDefine(type = ColType.TEXT)
|
||||||
|
private String commitmentContent;
|
||||||
|
|
||||||
@Column
|
@Column
|
||||||
@Comment("可容纳人数")
|
@Comment("可容纳人数")
|
||||||
@ColDefine(type = ColType.INT)
|
@ColDefine(type = ColType.INT)
|
||||||
|
|||||||
@@ -2,11 +2,53 @@ package io.v.nutz.activity.services;
|
|||||||
|
|
||||||
|
|
||||||
import io.v.nutz.activity.models.ActivitySiteReserve;
|
import io.v.nutz.activity.models.ActivitySiteReserve;
|
||||||
|
import io.v.nutz.activity.models.ActivitySiteCommitment;
|
||||||
import io.v.nutz.base.service.BaseService;
|
import io.v.nutz.base.service.BaseService;
|
||||||
|
import org.nutz.lang.util.NutMap;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public interface SiteReserveService extends BaseService<ActivitySiteReserve> {
|
public interface SiteReserveService extends BaseService<ActivitySiteReserve> {
|
||||||
|
|
||||||
Map<Boolean, String> validateSiteReserve(String siteId, String day, String startTime, String endTime, Integer joinCount);
|
Map<Boolean, String> validateSiteReserve(String siteId, String day, String startTime, String endTime, Integer joinCount);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验当前用户的预约资格并开始本次承诺书阅读。
|
||||||
|
* @param siteId 预约场地ID
|
||||||
|
* @return 是否需要签署及本次承诺书快照、凭证
|
||||||
|
* @throws IllegalArgumentException 无预约资格或场地未配置承诺书
|
||||||
|
*/
|
||||||
|
NutMap beginCommitment(String siteId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 阅读满十秒后保存当前用户本次手写签名。
|
||||||
|
* @param commitmentId 本次阅读凭证
|
||||||
|
* @param signature PNG格式的签名数据
|
||||||
|
* @throws IllegalArgumentException 阅读不足十秒、凭证失效或签名无效
|
||||||
|
*/
|
||||||
|
void signCommitment(String commitmentId, String signature);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预约提交前校验身份、承诺书版本和本次签署状态。
|
||||||
|
* @param siteId 预约场地ID
|
||||||
|
* @param commitmentId 签署凭证,非分工会场地可为空
|
||||||
|
* @throws IllegalArgumentException 预约资格或签署条件不满足
|
||||||
|
*/
|
||||||
|
void checkCommitment(String siteId, String commitmentId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在预约保存事务中一次性关联签署凭证,失败时随预约一起回滚。
|
||||||
|
* @param siteId 预约场地ID
|
||||||
|
* @param commitmentId 签署凭证
|
||||||
|
* @param sqid 本次预约申请编号
|
||||||
|
* @throws IllegalArgumentException 凭证失效或已被其他申请使用
|
||||||
|
*/
|
||||||
|
void bindCommitment(String siteId, String commitmentId, String sqid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询申请签署时的承诺书,供已有预约详情展示。
|
||||||
|
* @param sqid 预约申请编号
|
||||||
|
* @return 签署快照,历史未签署申请返回null
|
||||||
|
*/
|
||||||
|
ActivitySiteCommitment findCommitment(String sqid);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package io.v.nutz.activity.services.impl;
|
|||||||
|
|
||||||
|
|
||||||
import io.v.nutz.activity.models.ActivitySiteInfo;
|
import io.v.nutz.activity.models.ActivitySiteInfo;
|
||||||
|
import io.v.nutz.activity.models.ActivitySiteCommitment;
|
||||||
import io.v.nutz.activity.models.ActivitySiteReserve;
|
import io.v.nutz.activity.models.ActivitySiteReserve;
|
||||||
import io.v.nutz.activity.models.ActivityType;
|
import io.v.nutz.activity.models.ActivityType;
|
||||||
import io.v.nutz.activity.services.SiteReserveService;
|
import io.v.nutz.activity.services.SiteReserveService;
|
||||||
@@ -9,8 +10,20 @@ import io.v.nutz.base.service.impl.BaseServiceImpl;
|
|||||||
import io.v.nutz.constant.AuditTypeEnum;
|
import io.v.nutz.constant.AuditTypeEnum;
|
||||||
import io.v.nutz.modules.models.AuditState;
|
import io.v.nutz.modules.models.AuditState;
|
||||||
import org.nutz.dao.Cnd;
|
import org.nutz.dao.Cnd;
|
||||||
|
import org.nutz.dao.Chain;
|
||||||
import org.nutz.dao.Dao;
|
import org.nutz.dao.Dao;
|
||||||
import org.nutz.ioc.loader.annotation.IocBean;
|
import org.nutz.ioc.loader.annotation.IocBean;
|
||||||
|
import org.nutz.lang.Strings;
|
||||||
|
import org.nutz.lang.random.R;
|
||||||
|
import org.nutz.lang.util.NutMap;
|
||||||
|
import org.apache.shiro.SecurityUtils;
|
||||||
|
import io.v.nutz.sys.models.Sys_user;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -28,6 +41,138 @@ public class SiteReserveServiceImpl extends BaseServiceImpl<ActivitySiteReserve>
|
|||||||
super(dao);
|
super(dao);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ActivitySiteInfo commitmentSite(String siteId) {
|
||||||
|
ActivitySiteInfo site = Strings.isBlank(siteId) ? null : dao().fetch(ActivitySiteInfo.class, siteId);
|
||||||
|
if (site == null || !Boolean.TRUE.equals(site.getState())) {
|
||||||
|
throw new IllegalArgumentException("场地不存在或已关闭预约");
|
||||||
|
}
|
||||||
|
if (!SecurityUtils.getSubject().isAuthenticated()) {
|
||||||
|
throw new IllegalArgumentException("请先登录后预约");
|
||||||
|
}
|
||||||
|
if (Integer.valueOf(1).equals(site.getReserveTarget())
|
||||||
|
&& !SecurityUtils.getSubject().hasRole("sysadmin")
|
||||||
|
&& !SecurityUtils.getSubject().hasRole("gh01")
|
||||||
|
&& !SecurityUtils.getSubject().hasRole("H04")
|
||||||
|
&& !SecurityUtils.getSubject().hasRole("H02")) {
|
||||||
|
throw new IllegalArgumentException("该场地仅限分工会预约人员或协会负责人预约");
|
||||||
|
}
|
||||||
|
return site;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ActivitySiteCommitment currentCommitment(String commitmentId) {
|
||||||
|
ActivitySiteCommitment record = Strings.isBlank(commitmentId) ? null
|
||||||
|
: dao().fetch(ActivitySiteCommitment.class, commitmentId);
|
||||||
|
Sys_user user = (Sys_user) SecurityUtils.getSubject().getPrincipal();
|
||||||
|
if (user == null || record == null || !user.getId().equals(record.getUserId())
|
||||||
|
|| record.getReadAt() == null || System.currentTimeMillis() - record.getReadAt() > 30 * 60 * 1000L
|
||||||
|
|| Strings.isNotBlank(record.getSqid())) {
|
||||||
|
throw new IllegalArgumentException("签署凭证已失效,请重新阅读并签署承诺书");
|
||||||
|
}
|
||||||
|
ActivitySiteInfo site = commitmentSite(record.getSiteId());
|
||||||
|
if (!Objects.equals(site.getCommitmentTitle(), record.getTitle())
|
||||||
|
|| !Objects.equals(site.getCommitmentContent(), record.getContent())) {
|
||||||
|
throw new IllegalArgumentException("承诺书内容已更新,请重新阅读并签署");
|
||||||
|
}
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
public NutMap beginCommitment(String siteId) {
|
||||||
|
ActivitySiteInfo site = commitmentSite(siteId);
|
||||||
|
if (!Integer.valueOf(1).equals(site.getReserveTarget())) {
|
||||||
|
return NutMap.NEW().addv("required", false);
|
||||||
|
}
|
||||||
|
if (Strings.isBlank(site.getCommitmentTitle()) || Strings.isBlank(site.getCommitmentContent())) {
|
||||||
|
throw new IllegalArgumentException("场地尚未配置承诺书,暂不能预约,请联系场地管理员");
|
||||||
|
}
|
||||||
|
Sys_user user = (Sys_user) SecurityUtils.getSubject().getPrincipal();
|
||||||
|
ActivitySiteCommitment record = new ActivitySiteCommitment();
|
||||||
|
record.setId(R.UU32());
|
||||||
|
record.setSiteId(siteId);
|
||||||
|
record.setUserId(user.getId());
|
||||||
|
record.setUserName(user.getUsername());
|
||||||
|
record.setTitle(site.getCommitmentTitle());
|
||||||
|
record.setContent(site.getCommitmentContent());
|
||||||
|
record.setReadAt(System.currentTimeMillis());
|
||||||
|
dao().insert(record);
|
||||||
|
return NutMap.NEW().addv("required", true).addv("record", record).addv("seconds", 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
public void signCommitment(String commitmentId, String signature) {
|
||||||
|
ActivitySiteCommitment record = currentCommitment(commitmentId);
|
||||||
|
if (System.currentTimeMillis() - record.getReadAt() < 10000) {
|
||||||
|
throw new IllegalArgumentException("请阅读承诺书满10秒后再签字确认");
|
||||||
|
}
|
||||||
|
validateSignature(signature);
|
||||||
|
int changed = dao().update(ActivitySiteCommitment.class,
|
||||||
|
Chain.make("signature", signature).add("signedAt", System.currentTimeMillis()),
|
||||||
|
Cnd.where("id", "=", record.getId()).and("signedAt", "is", null).and("sqid", "is", null));
|
||||||
|
if (changed != 1) {
|
||||||
|
throw new IllegalArgumentException("本次签署已完成,请勿重复提交");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 只接受有实际笔迹的PNG,避免空白画布被当成已签署承诺书。
|
||||||
|
private void validateSignature(String signature) {
|
||||||
|
if (signature == null || signature.length() > 60000 || !signature.startsWith("data:image/png;base64,")) {
|
||||||
|
throw new IllegalArgumentException("请填写有效的手写签名");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
byte[] bytes = Base64.getDecoder().decode(signature.substring("data:image/png;base64,".length()));
|
||||||
|
BufferedImage image = ImageIO.read(new ByteArrayInputStream(bytes));
|
||||||
|
if (image != null && image.getWidth() <= 1600 && image.getHeight() <= 800) {
|
||||||
|
int ink = 0;
|
||||||
|
for (int y = 0; y < image.getHeight(); y++) {
|
||||||
|
for (int x = 0; x < image.getWidth(); x++) {
|
||||||
|
int pixel = image.getRGB(x, y);
|
||||||
|
if ((pixel >>> 24) > 128 && (pixel & 0x00ffffff) < 0xdddddd && ++ink >= 20) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IllegalArgumentException("签名数据无效,请重新签字");
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("签名不能为空,请在签字区域手写签名");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
public void checkCommitment(String siteId, String commitmentId) {
|
||||||
|
ActivitySiteInfo site = commitmentSite(siteId);
|
||||||
|
if (Integer.valueOf(1).equals(site.getReserveTarget())) {
|
||||||
|
ActivitySiteCommitment record = currentCommitment(commitmentId);
|
||||||
|
if (!siteId.equals(record.getSiteId()) || record.getSignedAt() == null
|
||||||
|
|| record.getSignedAt() - record.getReadAt() < 10000 || Strings.isBlank(record.getSignature())) {
|
||||||
|
throw new IllegalArgumentException("请先阅读并签署当前场地的承诺书");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
public void bindCommitment(String siteId, String commitmentId, String sqid) {
|
||||||
|
checkCommitment(siteId, commitmentId);
|
||||||
|
ActivitySiteInfo site = dao().fetch(ActivitySiteInfo.class, siteId);
|
||||||
|
if (Integer.valueOf(1).equals(site.getReserveTarget())) {
|
||||||
|
int changed = dao().update(ActivitySiteCommitment.class, Chain.make("sqid", sqid),
|
||||||
|
Cnd.where("id", "=", commitmentId).and("sqid", "is", null));
|
||||||
|
if (changed != 1) {
|
||||||
|
throw new IllegalArgumentException("签署凭证已用于其他申请,请重新签署");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
public ActivitySiteCommitment findCommitment(String sqid) {
|
||||||
|
return Strings.isBlank(sqid) ? null : dao().fetch(ActivitySiteCommitment.class, Cnd.where("sqid", "=", sqid));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<Boolean, String> validateSiteReserve(String siteId, String day, String startTime, String endTime, Integer joinCount) {
|
public Map<Boolean, String> validateSiteReserve(String siteId, String day, String startTime, String endTime, Integer joinCount) {
|
||||||
ActivitySiteInfo siteInfo = dao().fetch(ActivitySiteInfo.class, siteId);
|
ActivitySiteInfo siteInfo = dao().fetch(ActivitySiteInfo.class, siteId);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import io.v.nutz.activity.services.ActivityCommonService;
|
|||||||
import io.v.nutz.activity.services.SiteInfoService;
|
import io.v.nutz.activity.services.SiteInfoService;
|
||||||
import io.v.nutz.activity.services.SiteReserveService;
|
import io.v.nutz.activity.services.SiteReserveService;
|
||||||
import io.v.nutz.annontation.ViReturn;
|
import io.v.nutz.annontation.ViReturn;
|
||||||
|
import io.v.nutz.base.result.Result;
|
||||||
import io.v.nutz.base.utils.MsgApi;
|
import io.v.nutz.base.utils.MsgApi;
|
||||||
import io.v.nutz.base.utils.SendMsg;
|
import io.v.nutz.base.utils.SendMsg;
|
||||||
import io.v.nutz.base.utils.Vi;
|
import io.v.nutz.base.utils.Vi;
|
||||||
@@ -20,6 +21,7 @@ import io.v.nutz.sys.models.Sys_user;
|
|||||||
import io.v.nutz.sys.services.SysUserService;
|
import io.v.nutz.sys.services.SysUserService;
|
||||||
import io.v.nutz.web.commons.utils.ShiroUtil;
|
import io.v.nutz.web.commons.utils.ShiroUtil;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.nutz.dao.Chain;
|
||||||
import org.nutz.dao.Cnd;
|
import org.nutz.dao.Cnd;
|
||||||
import org.nutz.dao.Dao;
|
import org.nutz.dao.Dao;
|
||||||
import org.nutz.dao.Sqls;
|
import org.nutz.dao.Sqls;
|
||||||
@@ -39,6 +41,7 @@ import org.nutz.mvc.annotation.Param;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Author JyuHsin
|
* @Author JyuHsin
|
||||||
@@ -223,15 +226,34 @@ public class SiteAuditMobileController {
|
|||||||
@At("/audit")
|
@At("/audit")
|
||||||
@Ok("json:full")
|
@Ok("json:full")
|
||||||
@ViReturn
|
@ViReturn
|
||||||
public Object audit(@Param("ids") String[] ids, Audit audit,
|
public Object audit(@Param("ids") String[] ids, @Param("reserveState") Integer[] reserveState, Audit audit,
|
||||||
@Param("isPass") boolean isPass) {
|
@Param("isPass") Boolean isPass) {
|
||||||
dao.insert(audit);
|
if (ids == null || reserveState == null || ids.length == 0 || ids.length != reserveState.length || isPass == null) {
|
||||||
for (String id : ids) {
|
return Result.error("审核参数错误,请刷新页面后重试");
|
||||||
|
}
|
||||||
|
|
||||||
ActivitySiteReserve fetch = siteReserveService.fetch(id);
|
dao.insert(audit);
|
||||||
|
int reviewCount = 0;
|
||||||
|
for (int i = 0; i < ids.length; i++) {
|
||||||
|
|
||||||
|
ActivitySiteReserve fetch = siteReserveService.fetch(ids[i]);
|
||||||
|
if (fetch == null || !Objects.equals(fetch.getReserve_state(), reserveState[i])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
Integer stateCode = fetch.getReserve_state();
|
Integer stateCode = fetch.getReserve_state();
|
||||||
Integer afterStateCode = activityCommonService.findAfterStateCode(stateCode, isPass);
|
Integer afterStateCode = activityCommonService.findAfterStateCode(stateCode, isPass);
|
||||||
|
if (afterStateCode == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按原审核状态更新,避免多个管理员同时审核时重复处理同一申请
|
||||||
|
int updateCount = dao.update(ActivitySiteReserve.class, Chain.make("reserve_state", afterStateCode),
|
||||||
|
Cnd.NEW().and("sqid", "=", fetch.getSqid()).and("reserve_state", "=", stateCode));
|
||||||
|
if (updateCount == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
reviewCount++;
|
||||||
|
|
||||||
List<ActivitySiteReserve> reserves = siteReserveService.query(Cnd.NEW().and("sqid", "=", fetch.getSqid()));
|
List<ActivitySiteReserve> reserves = siteReserveService.query(Cnd.NEW().and("sqid", "=", fetch.getSqid()));
|
||||||
String days = "";
|
String days = "";
|
||||||
@@ -292,7 +314,10 @@ public class SiteAuditMobileController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
if (reviewCount == 0) {
|
||||||
|
dao.delete(audit);
|
||||||
|
}
|
||||||
|
return Result.success("操作完成");
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<NutMap> listMap(Sql sql) {
|
public List<NutMap> listMap(Sql sql) {
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import org.nutz.lang.util.NutMap;
|
|||||||
import org.nutz.mvc.annotation.At;
|
import org.nutz.mvc.annotation.At;
|
||||||
import org.nutz.mvc.annotation.Ok;
|
import org.nutz.mvc.annotation.Ok;
|
||||||
import org.nutz.mvc.annotation.Param;
|
import org.nutz.mvc.annotation.Param;
|
||||||
|
import org.nutz.trans.Trans;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -234,10 +235,26 @@ public class SiteInfoMobileController {
|
|||||||
return hours;
|
return hours;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手机端预约入口,使用与电脑端相同的预约资格和承诺书校验。
|
||||||
|
* @param siteId 场地ID
|
||||||
|
* @param times 预约日期及时段JSON
|
||||||
|
* @param message 预约事由
|
||||||
|
* @param joinUser 参与人员
|
||||||
|
* @param joinCount 预约人数
|
||||||
|
* @param commitmentId 当前用户的本次承诺书签署凭证
|
||||||
|
* @return 预约结果或签署校验失败原因
|
||||||
|
*/
|
||||||
@At("/reserveDo")
|
@At("/reserveDo")
|
||||||
@Ok("json:full")
|
@Ok("json:full")
|
||||||
@ViReturn
|
@ViReturn
|
||||||
public Object reserveDo(String siteId, String times, String message, String joinUser, Integer joinCount) {
|
public Object reserveDo(String siteId, String times, String message, String joinUser, Integer joinCount, String commitmentId) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
siteReserveService.checkCommitment(siteId, commitmentId);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return Result.error(e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
String moduleName = activityCommonService.findModuleNameByActivityId(siteId);
|
String moduleName = activityCommonService.findModuleNameByActivityId(siteId);
|
||||||
Integer stateCode = activityCommonService.findStartStateCodeByModuleName(moduleName);
|
Integer stateCode = activityCommonService.findStartStateCodeByModuleName(moduleName);
|
||||||
@@ -272,7 +289,14 @@ public class SiteInfoMobileController {
|
|||||||
}
|
}
|
||||||
str = str.substring(0, str.length() - 1);
|
str = str.substring(0, str.length() - 1);
|
||||||
|
|
||||||
siteReserveService.insert(li);
|
try {
|
||||||
|
Trans.exec(() -> {
|
||||||
|
siteReserveService.bindCommitment(siteId, commitmentId, r);
|
||||||
|
siteReserveService.insert(li);
|
||||||
|
});
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return Result.error(e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
//发送微信提醒审核人
|
//发送微信提醒审核人
|
||||||
//查询状态码对应的审核人
|
//查询状态码对应的审核人
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package io.v.nutz.sickHelp.constant;
|
|||||||
import cn.hutool.core.date.DateField;
|
import cn.hutool.core.date.DateField;
|
||||||
import cn.hutool.core.date.DateTime;
|
import cn.hutool.core.date.DateTime;
|
||||||
import cn.hutool.core.date.DateUtil;
|
import cn.hutool.core.date.DateUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
import io.v.nutz.sickHelp.model.SickFundMember;
|
import io.v.nutz.sickHelp.model.SickFundMember;
|
||||||
import io.v.nutz.sickHelp.model.SickHelp;
|
import io.v.nutz.sickHelp.model.SickHelp;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@@ -96,9 +97,20 @@ public class SickHelpApply {
|
|||||||
*/
|
*/
|
||||||
public int howManyYearsDidNotApply(String loginName, Integer year) {
|
public int howManyYearsDidNotApply(String loginName, Integer year) {
|
||||||
SickFundMember fundMember = dao.fetch(SickFundMember.class, Cnd.where("loginName", "=", loginName));
|
SickFundMember fundMember = dao.fetch(SickFundMember.class, Cnd.where("loginName", "=", loginName));
|
||||||
|
if (fundMember == null || year == null || StrUtil.isBlank(fundMember.getMutualGoldStartTime())) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
//起扣年度
|
//起扣年度
|
||||||
DateTime mgsTime = DateUtil.parse(fundMember.getMutualGoldStartTime());
|
DateTime mgsTime;
|
||||||
|
try {
|
||||||
|
mgsTime = DateUtil.parse(fundMember.getMutualGoldStartTime());
|
||||||
|
} catch (Exception e) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (mgsTime == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
int mutualGoldStartYear = mgsTime.getField(DateField.YEAR);
|
int mutualGoldStartYear = mgsTime.getField(DateField.YEAR);
|
||||||
|
|
||||||
//补助年度
|
//补助年度
|
||||||
|
|||||||
@@ -17,8 +17,10 @@ import io.v.nutz.util.ViTool;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.poi.ss.usermodel.Workbook;
|
import org.apache.poi.ss.usermodel.Workbook;
|
||||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||||
|
import org.nutz.aop.interceptor.ioc.TransAop;
|
||||||
import org.nutz.dao.Cnd;
|
import org.nutz.dao.Cnd;
|
||||||
import org.nutz.dao.Dao;
|
import org.nutz.dao.Dao;
|
||||||
|
import org.nutz.ioc.aop.Aop;
|
||||||
import org.nutz.ioc.loader.annotation.Inject;
|
import org.nutz.ioc.loader.annotation.Inject;
|
||||||
import org.nutz.ioc.loader.annotation.IocBean;
|
import org.nutz.ioc.loader.annotation.IocBean;
|
||||||
import org.nutz.lang.Lang;
|
import org.nutz.lang.Lang;
|
||||||
@@ -128,7 +130,12 @@ public class SickHelpImportController {
|
|||||||
@Ok("json:full")
|
@Ok("json:full")
|
||||||
@ViReturn
|
@ViReturn
|
||||||
public Object doImport(@Param("sickHelps") SickHelp[] sickHelps) {
|
public Object doImport(@Param("sickHelps") SickHelp[] sickHelps) {
|
||||||
sickHelpImportService.importData(Arrays.asList(sickHelps));
|
List<SickHelp> checkList = sickHelpImportService.checkData(Arrays.asList(sickHelps));
|
||||||
|
boolean hasErrorData = checkList.stream().anyMatch(v -> !Boolean.TRUE.equals(v.getValid()));
|
||||||
|
if (hasErrorData) {
|
||||||
|
return Result.error("存在校验不合格的数据,无法导入");
|
||||||
|
}
|
||||||
|
sickHelpImportService.importData(checkList);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,6 +205,7 @@ public class SickHelpImportController {
|
|||||||
@At("/convertIns")
|
@At("/convertIns")
|
||||||
@Ok("json:full")
|
@Ok("json:full")
|
||||||
@RequiresPermissions("sickHelp.import.excel")
|
@RequiresPermissions("sickHelp.import.excel")
|
||||||
|
@Aop(TransAop.READ_COMMITTED)
|
||||||
public Result convertIns() {
|
public Result convertIns() {
|
||||||
List<SickHelpMedicalIns> insList = dao.query(SickHelpMedicalIns.class, Cnd.where("year", "=", DateUtil.thisYear() - 1));
|
List<SickHelpMedicalIns> insList = dao.query(SickHelpMedicalIns.class, Cnd.where("year", "=", DateUtil.thisYear() - 1));
|
||||||
|
|
||||||
@@ -209,11 +217,28 @@ public class SickHelpImportController {
|
|||||||
sickHelp.setMedicalFourTotalMoney(v.getSxzf());
|
sickHelp.setMedicalFourTotalMoney(v.getSxzf());
|
||||||
sickHelp.setSelfPayTotalMoney(v.getLxzf());
|
sickHelp.setSelfPayTotalMoney(v.getLxzf());
|
||||||
sickHelp.setSchoolSecondSubsidyMoney(v.getEcbx());
|
sickHelp.setSchoolSecondSubsidyMoney(v.getEcbx());
|
||||||
sickHelp.setSelfPayMoney(v.getCalc());
|
BigDecimal selfPayMoney = v.getSxzf() != null && v.getEcbx() != null
|
||||||
|
? v.getSxzf().subtract(v.getEcbx()) : null;
|
||||||
|
sickHelp.setSelfPayMoney(selfPayMoney);
|
||||||
return sickHelp;
|
return sickHelp;
|
||||||
}).filter(v -> v.getSelfPayMoney() == null || (v.getSelfPayMoney() != null && v.getSelfPayMoney().compareTo(BigDecimal.valueOf(10000)) >= 0)).collect(Collectors.toList());
|
}).filter(v -> v.getSelfPayMoney() == null || (v.getSelfPayMoney() != null && v.getSelfPayMoney().compareTo(BigDecimal.valueOf(10000)) > 0)).collect(Collectors.toList());
|
||||||
|
|
||||||
List<SickHelp> sickHelps = sickHelpImportService.checkData(collect);
|
List<SickHelp> sickHelps = sickHelpImportService.checkData(collect);
|
||||||
|
|
||||||
|
Map<String, SickHelpMedicalIns> medicalInsMap = insList.stream()
|
||||||
|
.filter(v -> StrUtil.isNotBlank(v.getLoginName()))
|
||||||
|
.collect(Collectors.toMap(SickHelpMedicalIns::getLoginName, v -> v, (v1, v2) -> v1));
|
||||||
|
// 转入申请前将无补助或校验失败的记录记为 0,有效补助金额随后覆盖。
|
||||||
|
insList.forEach(v -> v.setCalc(BigDecimal.ZERO));
|
||||||
|
sickHelps.forEach(v -> {
|
||||||
|
SickHelpMedicalIns medicalIns = medicalInsMap.get(v.getLoginName());
|
||||||
|
if (Boolean.TRUE.equals(v.getValid()) && medicalIns != null && v.getSubsidyMoney() != null) {
|
||||||
|
medicalIns.setCalc(v.getSubsidyMoney());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!insList.isEmpty()) {
|
||||||
|
dao.update(insList, "calc");
|
||||||
|
}
|
||||||
return Result.success(sickHelps);
|
return Result.success(sickHelps);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -81,8 +81,8 @@ public class SickHelpListController {
|
|||||||
@Ok("json:full")
|
@Ok("json:full")
|
||||||
@ViReturn
|
@ViReturn
|
||||||
@RequiresPermissions("sickHelp.list")
|
@RequiresPermissions("sickHelp.list")
|
||||||
public Object pageData(PageForm pageForm, Integer year, String subsidyUser, String unionId, String unitId, Integer isMember, Integer isMeetRequirements, Integer isAccept) {
|
public Object pageData(PageForm pageForm, Integer year, String subsidyUser, String unionId, String unitId, Integer isMember, Integer isMeetRequirements, Integer isAccept, Integer riskType) {
|
||||||
return sickHelpListService.pageData(pageForm, year, subsidyUser, unionId, unitId, isMember, isMeetRequirements, isAccept);
|
return sickHelpListService.pageData(pageForm, year, subsidyUser, unionId, unitId, isMember, isMeetRequirements, isAccept, riskType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@At
|
@At
|
||||||
|
|||||||
@@ -12,10 +12,16 @@ import cn.hutool.core.util.StrUtil;
|
|||||||
import cn.wizzer.framework.base.Result;
|
import cn.wizzer.framework.base.Result;
|
||||||
import io.v.nutz.base.page.Pagination;
|
import io.v.nutz.base.page.Pagination;
|
||||||
import io.v.nutz.modules.models.PageForm;
|
import io.v.nutz.modules.models.PageForm;
|
||||||
|
import io.v.nutz.sickHelp.constant.SickHelpStateEnum;
|
||||||
|
import io.v.nutz.sickHelp.model.SickFundMember;
|
||||||
|
import io.v.nutz.sickHelp.model.SickHelp;
|
||||||
import io.v.nutz.sickHelp.model.SickHelpMedicalIns;
|
import io.v.nutz.sickHelp.model.SickHelpMedicalIns;
|
||||||
|
import io.v.nutz.sickHelp.service.SickHelpImportService;
|
||||||
|
import io.v.nutz.sys.models.User;
|
||||||
import io.v.nutz.sys.services.SysUserService;
|
import io.v.nutz.sys.services.SysUserService;
|
||||||
import io.v.nutz.util.ViTool;
|
import io.v.nutz.util.ViTool;
|
||||||
import org.apache.poi.ss.usermodel.Workbook;
|
import org.apache.poi.ss.usermodel.Workbook;
|
||||||
|
import org.apache.shiro.authz.annotation.Logical;
|
||||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||||
import org.nutz.aop.interceptor.ioc.TransAop;
|
import org.nutz.aop.interceptor.ioc.TransAop;
|
||||||
import org.nutz.dao.Cnd;
|
import org.nutz.dao.Cnd;
|
||||||
@@ -40,6 +46,7 @@ import java.math.BigDecimal;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@@ -52,6 +59,8 @@ public class SickHelpMedicalInsController {
|
|||||||
private Dao dao;
|
private Dao dao;
|
||||||
@Inject
|
@Inject
|
||||||
private SysUserService sysUserService;
|
private SysUserService sysUserService;
|
||||||
|
@Inject
|
||||||
|
private SickHelpImportService sickHelpImportService;
|
||||||
|
|
||||||
@At("")
|
@At("")
|
||||||
@RequiresPermissions("sickHelp.medicalIns")
|
@RequiresPermissions("sickHelp.medicalIns")
|
||||||
@@ -102,11 +111,13 @@ public class SickHelpMedicalInsController {
|
|||||||
.filter(data -> data.getSxzf().compareTo(BigDecimal.valueOf(10000)) > 0)
|
.filter(data -> data.getSxzf().compareTo(BigDecimal.valueOf(10000)) > 0)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
// 系统用户身份证为空时使用会员台账身份证,最后才按姓名匹配。
|
||||||
Sql sql = Sqls.create("""
|
Sql sql = Sqls.create("""
|
||||||
SELECT
|
SELECT
|
||||||
t1.loginName,
|
t1.loginName,
|
||||||
t1.userName,
|
t1.userName,
|
||||||
t2.idcard AS idCard
|
COALESCE(NULLIF(TRIM(t2.idcard), ''),
|
||||||
|
NULLIF(TRIM(t1.idcard), '')) AS idCard
|
||||||
FROM
|
FROM
|
||||||
sick_fund_member t1
|
sick_fund_member t1
|
||||||
LEFT JOIN sys_user t2 ON t2.loginname = t1.loginName
|
LEFT JOIN sys_user t2 ON t2.loginname = t1.loginName
|
||||||
@@ -225,6 +236,8 @@ public class SickHelpMedicalInsController {
|
|||||||
List<SickHelpMedicalIns> successData = aggregatedData.stream().filter(v -> StrUtil.isBlank(v.getErrMsg())).collect(Collectors.toList());
|
List<SickHelpMedicalIns> successData = aggregatedData.stream().filter(v -> StrUtil.isBlank(v.getErrMsg())).collect(Collectors.toList());
|
||||||
List<SickHelpMedicalIns> failData = aggregatedData.stream().filter(v -> StrUtil.isNotBlank(v.getErrMsg())).collect(Collectors.toList());
|
List<SickHelpMedicalIns> failData = aggregatedData.stream().filter(v -> StrUtil.isNotBlank(v.getErrMsg())).collect(Collectors.toList());
|
||||||
|
|
||||||
|
// 医保数据首次入库时尚未计算补助,先将补助金额记为 0。
|
||||||
|
successData.forEach(v -> v.setCalc(BigDecimal.ZERO));
|
||||||
dao.insertOrUpdate(successData);
|
dao.insertOrUpdate(successData);
|
||||||
|
|
||||||
HashMap<String, Object> hashMap = new HashMap<>();
|
HashMap<String, Object> hashMap = new HashMap<>();
|
||||||
@@ -253,15 +266,30 @@ public class SickHelpMedicalInsController {
|
|||||||
|
|
||||||
int success = 0;
|
int success = 0;
|
||||||
|
|
||||||
|
List<SickHelpMedicalIns> successData = new ArrayList<>();
|
||||||
List<SickHelpMedicalIns> failData = new ArrayList<>();
|
List<SickHelpMedicalIns> failData = new ArrayList<>();
|
||||||
|
|
||||||
for (SickHelpMedicalIns ins : list) {
|
for (SickHelpMedicalIns ins : list) {
|
||||||
if (StrUtil.isNotBlank(ins.getLoginName())) {
|
if (StrUtil.isNotBlank(ins.getLoginName())) {
|
||||||
Optional<SickHelpMedicalIns> optional = excelList.stream().filter(x -> x.getLoginName().equals(ins.getLoginName())).findFirst();
|
String loginName = StrUtil.trim(ins.getLoginName());
|
||||||
|
Optional<SickHelpMedicalIns> optional = excelList.stream()
|
||||||
|
.filter(x -> loginName.equals(StrUtil.trim(x.getLoginName())))
|
||||||
|
.findFirst();
|
||||||
if (optional.isPresent()) {
|
if (optional.isPresent()) {
|
||||||
ins.setEcbx(optional.get().getEcbx());
|
BigDecimal ecbx = optional.get().getEcbx();
|
||||||
BigDecimal subtract = ins.getSxzf().subtract(optional.get().getEcbx());
|
if (ecbx == null) {
|
||||||
ins.setCalc(subtract);
|
// Excel 中的二次报销金额留空时按 0 元入库,供后续补助计算使用。
|
||||||
|
ecbx = BigDecimal.ZERO;
|
||||||
|
}
|
||||||
|
if (ins.getSxzf() == null) {
|
||||||
|
ins.setErrMsg("医保个人自付金额为空,请先核对医保数据");
|
||||||
|
failData.add(ins);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ins.setEcbx(ecbx);
|
||||||
|
// 二次报销金额更新后先置 0,后续重算再写入实际补助金额。
|
||||||
|
ins.setCalc(BigDecimal.ZERO);
|
||||||
|
successData.add(ins);
|
||||||
success += 1;
|
success += 1;
|
||||||
} else {
|
} else {
|
||||||
failData.add(ins);
|
failData.add(ins);
|
||||||
@@ -271,7 +299,10 @@ public class SickHelpMedicalInsController {
|
|||||||
|
|
||||||
System.out.println();
|
System.out.println();
|
||||||
|
|
||||||
dao.update(list, "ecbx|calc");
|
if (!successData.isEmpty()) {
|
||||||
|
dao.update(successData, "ecbx|calc");
|
||||||
|
refreshCalc(list);
|
||||||
|
}
|
||||||
HashMap<String, Object> hashMap = new HashMap<>();
|
HashMap<String, Object> hashMap = new HashMap<>();
|
||||||
hashMap.put("success", success);
|
hashMap.put("success", success);
|
||||||
hashMap.put("fail", failData.size());
|
hashMap.put("fail", failData.size());
|
||||||
@@ -284,33 +315,77 @@ public class SickHelpMedicalInsController {
|
|||||||
|
|
||||||
@At
|
@At
|
||||||
@RequiresPermissions("sickHelp.medicalIns")
|
@RequiresPermissions("sickHelp.medicalIns")
|
||||||
public Result pageData(PageForm pageForm, Integer year, String dataCheck) {
|
public Result pageData(PageForm pageForm, Integer year, String dataCheck, Integer riskType) {
|
||||||
Sql sql = Sqls.create("select * from sick_help_medical_ins $condition");
|
Sql sql = Sqls.create("""
|
||||||
|
SELECT
|
||||||
|
risk_data.*
|
||||||
|
FROM (
|
||||||
|
SELECT
|
||||||
|
medical.*,
|
||||||
|
COALESCE(NULLIF(TRIM(u.idcard), ''), NULLIF(TRIM(sfm.idcard), '')) AS systemIdcard,
|
||||||
|
IFNULL(same_name.sameNameCount, 0) AS sameNameCount,
|
||||||
|
CASE
|
||||||
|
WHEN NULLIF(TRIM(medical.idCard), '') IS NOT NULL
|
||||||
|
AND COALESCE(NULLIF(TRIM(u.idcard), ''), NULLIF(TRIM(sfm.idcard), '')) IS NOT NULL
|
||||||
|
AND UPPER(REPLACE(TRIM(medical.idCard), ' ', '')) != UPPER(REPLACE(COALESCE(NULLIF(TRIM(u.idcard), ''), NULLIF(TRIM(sfm.idcard), '')), ' ', ''))
|
||||||
|
THEN 1 ELSE 0
|
||||||
|
END AS idcardMismatchRisk,
|
||||||
|
CASE
|
||||||
|
WHEN IFNULL(same_name.sameNameCount, 0) > 1
|
||||||
|
AND (NULLIF(TRIM(medical.idCard), '') IS NULL
|
||||||
|
OR COALESCE(NULLIF(TRIM(u.idcard), ''), NULLIF(TRIM(sfm.idcard), '')) IS NULL)
|
||||||
|
THEN 1 ELSE 0
|
||||||
|
END AS duplicateMissingIdRisk
|
||||||
|
FROM sick_help_medical_ins medical
|
||||||
|
LEFT JOIN `user` u ON u.loginname = medical.loginName
|
||||||
|
LEFT JOIN sick_fund_member sfm ON sfm.loginName = medical.loginName
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT TRIM(userName) AS userName, COUNT(DISTINCT loginName) AS sameNameCount
|
||||||
|
FROM sick_fund_member
|
||||||
|
WHERE NULLIF(TRIM(userName), '') IS NOT NULL
|
||||||
|
GROUP BY TRIM(userName)
|
||||||
|
HAVING COUNT(DISTINCT loginName) > 1
|
||||||
|
) same_name ON same_name.userName = TRIM(medical.userName)
|
||||||
|
) risk_data
|
||||||
|
$condition
|
||||||
|
""");
|
||||||
Cnd cnd = Cnd.NEW();
|
Cnd cnd = Cnd.NEW();
|
||||||
cnd.asc("ecbx");
|
cnd.asc("risk_data.ecbx");
|
||||||
cnd.asc("loginName");
|
cnd.asc("risk_data.loginName");
|
||||||
cnd.andEX("year", "=", year);
|
cnd.andEX("risk_data.year", "=", year);
|
||||||
|
|
||||||
if (StrUtil.isNotBlank(dataCheck)) {
|
if (StrUtil.isNotBlank(dataCheck)) {
|
||||||
if (dataCheck.equals("1")) {
|
if (dataCheck.equals("1")) {
|
||||||
SqlExpressionGroup seg = new SqlExpressionGroup();
|
SqlExpressionGroup seg = new SqlExpressionGroup();
|
||||||
seg.or("loginName", "is", null);
|
seg.or("risk_data.loginName", "is", null);
|
||||||
seg.or("loginName", "=", "");
|
seg.or("risk_data.loginName", "=", "");
|
||||||
cnd.and(seg);
|
cnd.and(seg);
|
||||||
}
|
}
|
||||||
if (dataCheck.equals("2")) {
|
if (dataCheck.equals("2")) {
|
||||||
SqlExpressionGroup seg = new SqlExpressionGroup();
|
SqlExpressionGroup seg = new SqlExpressionGroup();
|
||||||
seg.or("ecbx", "is", null);
|
seg.or("risk_data.ecbx", "is", null);
|
||||||
seg.or("ecbx", "=", "");
|
seg.or("risk_data.ecbx", "=", "");
|
||||||
cnd.and(seg);
|
cnd.and(seg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (riskType != null && riskType == 1) {
|
||||||
|
cnd.and("risk_data.idcardMismatchRisk", "=", 1);
|
||||||
|
} else if (riskType != null && riskType == 2) {
|
||||||
|
cnd.and("risk_data.duplicateMissingIdRisk", "=", 1);
|
||||||
|
} else if (riskType != null && riskType == 3) {
|
||||||
|
SqlExpressionGroup riskSeg = new SqlExpressionGroup();
|
||||||
|
riskSeg.or("risk_data.idcardMismatchRisk", "=", 1);
|
||||||
|
riskSeg.or("risk_data.duplicateMissingIdRisk", "=", 1);
|
||||||
|
cnd.and(riskSeg);
|
||||||
|
}
|
||||||
|
|
||||||
if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
|
if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
|
||||||
SqlExpressionGroup seg = new SqlExpressionGroup();
|
SqlExpressionGroup seg = new SqlExpressionGroup();
|
||||||
seg.orLike("loginName", pageForm.getSearchKeyword());
|
seg.orLike("risk_data.loginName", pageForm.getSearchKeyword());
|
||||||
seg.orLike("userName", pageForm.getSearchKeyword());
|
seg.orLike("risk_data.userName", pageForm.getSearchKeyword());
|
||||||
seg.orLike("idCard", pageForm.getSearchKeyword());
|
seg.orLike("risk_data.idCard", pageForm.getSearchKeyword());
|
||||||
|
seg.orLike("risk_data.systemIdcard", pageForm.getSearchKeyword());
|
||||||
cnd.and(seg);
|
cnd.and(seg);
|
||||||
}
|
}
|
||||||
sql.setCondition(cnd);
|
sql.setCondition(cnd);
|
||||||
@@ -346,6 +421,196 @@ public class SickHelpMedicalInsController {
|
|||||||
return Result.success(list);
|
return Result.success(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询可重新关联的大病互助会员。
|
||||||
|
*/
|
||||||
|
@At
|
||||||
|
@RequiresPermissions(value = {"sickHelp.medicalIns", "sickHelp.list"}, logical = Logical.OR)
|
||||||
|
public Result queryRebindUser(String query) {
|
||||||
|
Sql sql = Sqls.create("""
|
||||||
|
SELECT
|
||||||
|
m.loginName AS loginname,
|
||||||
|
m.userName AS username,
|
||||||
|
COALESCE(NULLIF(u.birthday, ''), m.birthday) AS birthday,
|
||||||
|
COALESCE(NULLIF(u.unitname, ''), m.unitName) AS unitname,
|
||||||
|
COALESCE(NULLIF(u.idcard, ''), m.idcard) AS idcard
|
||||||
|
FROM sick_fund_member m
|
||||||
|
LEFT JOIN `user` u ON u.loginname = m.loginName
|
||||||
|
$condition
|
||||||
|
LIMIT 0,20
|
||||||
|
""");
|
||||||
|
Cnd cnd = Cnd.NEW();
|
||||||
|
SqlExpressionGroup seg = new SqlExpressionGroup();
|
||||||
|
seg.orLike("m.loginName", query);
|
||||||
|
seg.orLike("m.userName", query);
|
||||||
|
seg.orLike("m.idcard", query);
|
||||||
|
seg.orLike("u.idcard", query);
|
||||||
|
cnd.and(seg);
|
||||||
|
sql.setCondition(cnd);
|
||||||
|
List<NutMap> list = sysUserService.listMap(sql);
|
||||||
|
return Result.success(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取重新关联人员所需的医保数据。
|
||||||
|
*/
|
||||||
|
@At
|
||||||
|
@RequiresPermissions(value = {"sickHelp.medicalIns", "sickHelp.list"}, logical = Logical.OR)
|
||||||
|
public Result findRebindSource(String medicalInsId, Integer year, String loginName) {
|
||||||
|
SickHelpMedicalIns medicalIns = null;
|
||||||
|
if (StrUtil.isNotBlank(medicalInsId)) {
|
||||||
|
medicalIns = dao.fetch(SickHelpMedicalIns.class, medicalInsId);
|
||||||
|
} else if (year != null && StrUtil.isNotBlank(loginName)) {
|
||||||
|
List<SickHelpMedicalIns> list = dao.query(SickHelpMedicalIns.class,
|
||||||
|
Cnd.where("year", "=", year).and("loginName", "=", StrUtil.trim(loginName)));
|
||||||
|
if (list.size() > 1) {
|
||||||
|
return Result.error("该年度、工号存在多条医保数据,请先核对");
|
||||||
|
}
|
||||||
|
if (!list.isEmpty()) {
|
||||||
|
medicalIns = list.get(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (medicalIns == null) {
|
||||||
|
return Result.error("未找到对应的医保数据");
|
||||||
|
}
|
||||||
|
return Result.success(medicalIns);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重新关联医保人员,并同步处理尚未进入审核流程的补助台账。
|
||||||
|
*/
|
||||||
|
@At
|
||||||
|
@RequiresPermissions(value = {"sickHelp.medicalIns", "sickHelp.list"}, logical = Logical.OR)
|
||||||
|
@Aop(TransAop.READ_COMMITTED)
|
||||||
|
public Result rebindUser(String medicalInsId, String loginName, String ecbx) {
|
||||||
|
if (StrUtil.isBlank(medicalInsId)) {
|
||||||
|
return Result.error("医保数据id为空");
|
||||||
|
}
|
||||||
|
if (StrUtil.isBlank(loginName)) {
|
||||||
|
return Result.error("请选择需要重新关联的人员");
|
||||||
|
}
|
||||||
|
if (StrUtil.isBlank(ecbx)) {
|
||||||
|
return Result.error("请输入正确的学校二次报销金额");
|
||||||
|
}
|
||||||
|
|
||||||
|
BigDecimal ecbxMoney;
|
||||||
|
try {
|
||||||
|
ecbxMoney = new BigDecimal(StrUtil.trim(ecbx));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return Result.error("学校二次报销金额格式不正确");
|
||||||
|
}
|
||||||
|
if (ecbxMoney.compareTo(BigDecimal.ZERO) < 0) {
|
||||||
|
return Result.error("学校二次报销金额不能小于0");
|
||||||
|
}
|
||||||
|
|
||||||
|
SickHelpMedicalIns medicalIns = dao.fetch(SickHelpMedicalIns.class, medicalInsId);
|
||||||
|
if (medicalIns == null) {
|
||||||
|
return Result.error("医保数据不存在或已被删除");
|
||||||
|
}
|
||||||
|
if (medicalIns.getYear() == null || medicalIns.getSxzf() == null || medicalIns.getLxzf() == null) {
|
||||||
|
return Result.error("医保年度或金额不完整,请先核对医保数据");
|
||||||
|
}
|
||||||
|
if (ecbxMoney.compareTo(medicalIns.getSxzf()) > 0) {
|
||||||
|
return Result.error("学校二次报销金额不能大于医保四项之和");
|
||||||
|
}
|
||||||
|
|
||||||
|
String targetLoginName = StrUtil.trim(loginName);
|
||||||
|
SickFundMember targetMember = dao.fetch(SickFundMember.class,
|
||||||
|
Cnd.where("loginName", "=", targetLoginName));
|
||||||
|
if (targetMember == null) {
|
||||||
|
return Result.error("所选人员不在大病互助会员库中");
|
||||||
|
}
|
||||||
|
User targetUser = dao.fetch(User.class, Cnd.where("loginname", "=", targetLoginName));
|
||||||
|
String targetUserName = StrUtil.isNotBlank(targetMember.getUserName())
|
||||||
|
? targetMember.getUserName()
|
||||||
|
: targetUser == null ? null : targetUser.getUsername();
|
||||||
|
if (StrUtil.isBlank(targetUserName)) {
|
||||||
|
return Result.error("所选人员姓名为空,请先维护人员数据");
|
||||||
|
}
|
||||||
|
|
||||||
|
String targetIdCard = targetUser != null && StrUtil.isNotBlank(targetUser.getIdcard())
|
||||||
|
? targetUser.getIdcard() : targetMember.getIdcard();
|
||||||
|
String medicalIdCard = normalizeIdCard(medicalIns.getIdCard());
|
||||||
|
String normalizedTargetIdCard = normalizeIdCard(targetIdCard);
|
||||||
|
if (StrUtil.isNotBlank(medicalIdCard) && StrUtil.isNotBlank(normalizedTargetIdCard)
|
||||||
|
&& !medicalIdCard.equals(normalizedTargetIdCard)) {
|
||||||
|
return Result.error("医保身份证号与所选人员身份证号不一致,禁止重新关联");
|
||||||
|
}
|
||||||
|
|
||||||
|
SickHelpMedicalIns sameMedicalIns = dao.fetch(SickHelpMedicalIns.class,
|
||||||
|
Cnd.where("year", "=", medicalIns.getYear())
|
||||||
|
.and("loginName", "=", targetLoginName)
|
||||||
|
.and("id", "!=", medicalIns.getId()));
|
||||||
|
if (sameMedicalIns != null) {
|
||||||
|
return Result.error("所选人员本年度已有医保数据,禁止重复关联");
|
||||||
|
}
|
||||||
|
|
||||||
|
String oldLoginName = StrUtil.trim(medicalIns.getLoginName());
|
||||||
|
SickHelp oldSickHelp = StrUtil.isBlank(oldLoginName) ? null
|
||||||
|
: dao.fetch(SickHelp.class, Cnd.where("year", "=", medicalIns.getYear())
|
||||||
|
.and("loginName", "=", oldLoginName));
|
||||||
|
if (oldSickHelp != null
|
||||||
|
&& (!SickHelpStateEnum.DTJ.getStateCode().equals(oldSickHelp.getStateCode())
|
||||||
|
|| Boolean.TRUE.equals(oldSickHelp.getIsMeetRequirements())
|
||||||
|
|| Boolean.TRUE.equals(oldSickHelp.getIsAccept()))) {
|
||||||
|
return Result.error("原补助记录已进入申请或审核流程,不能直接重新关联人员");
|
||||||
|
}
|
||||||
|
|
||||||
|
SickHelp targetSickHelp = dao.fetch(SickHelp.class,
|
||||||
|
Cnd.where("year", "=", medicalIns.getYear()).and("loginName", "=", targetLoginName));
|
||||||
|
if (targetSickHelp != null && (oldSickHelp == null || !targetSickHelp.getId().equals(oldSickHelp.getId()))) {
|
||||||
|
return Result.error("所选人员本年度已有补助记录,禁止重复关联");
|
||||||
|
}
|
||||||
|
|
||||||
|
BigDecimal selfPayMoney = medicalIns.getSxzf().subtract(ecbxMoney);
|
||||||
|
SickHelp calculatedSickHelp = null;
|
||||||
|
if (selfPayMoney.compareTo(BigDecimal.valueOf(10000)) > 0) {
|
||||||
|
SickHelp sickHelp = new SickHelp();
|
||||||
|
sickHelp.setYear(medicalIns.getYear());
|
||||||
|
sickHelp.setLoginName(targetLoginName);
|
||||||
|
sickHelp.setUserName(targetUserName);
|
||||||
|
sickHelp.setMedicalFourTotalMoney(medicalIns.getSxzf());
|
||||||
|
sickHelp.setSelfPayTotalMoney(medicalIns.getLxzf());
|
||||||
|
sickHelp.setSchoolSecondSubsidyMoney(ecbxMoney);
|
||||||
|
sickHelp.setSelfPayMoney(selfPayMoney);
|
||||||
|
calculatedSickHelp = sickHelpImportService.checkData(List.of(sickHelp)).get(0);
|
||||||
|
if (!Boolean.TRUE.equals(calculatedSickHelp.getValid())) {
|
||||||
|
return Result.error(String.join("、", calculatedSickHelp.getValidMsg()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
medicalIns.setLoginName(targetLoginName);
|
||||||
|
medicalIns.setUserName(targetUserName);
|
||||||
|
medicalIns.setEcbx(ecbxMoney);
|
||||||
|
medicalIns.setCalc(calculatedSickHelp == null || calculatedSickHelp.getSubsidyMoney() == null
|
||||||
|
? BigDecimal.ZERO : calculatedSickHelp.getSubsidyMoney());
|
||||||
|
dao.update(medicalIns, "loginName|userName|ecbx|calc");
|
||||||
|
|
||||||
|
if (oldSickHelp != null) {
|
||||||
|
dao.delete(SickHelp.class, oldSickHelp.getId());
|
||||||
|
}
|
||||||
|
if (calculatedSickHelp != null) {
|
||||||
|
calculatedSickHelp.setId(null);
|
||||||
|
calculatedSickHelp.setStateCode(SickHelpStateEnum.DTJ.getStateCode());
|
||||||
|
dao.insert(calculatedSickHelp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (calculatedSickHelp == null) {
|
||||||
|
return Result.success("重新关联成功;修正后的个人支付金额不超过10000元,原补助汇总已删除");
|
||||||
|
}
|
||||||
|
return Result.success("重新关联成功;补助汇总已按正确人员重新计算");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 身份证号标准化,避免空格和字母大小写导致匹配失败。
|
||||||
|
*/
|
||||||
|
private String normalizeIdCard(String idCard) {
|
||||||
|
if (StrUtil.isBlank(idCard)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return idCard.replaceAll("\\s+", "").toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查是否还有错误的医保数据
|
* 检查是否还有错误的医保数据
|
||||||
@@ -385,8 +650,10 @@ public class SickHelpMedicalInsController {
|
|||||||
@At
|
@At
|
||||||
@RequiresPermissions("sickHelp.medicalIns")
|
@RequiresPermissions("sickHelp.medicalIns")
|
||||||
@Ok("void")
|
@Ok("void")
|
||||||
|
@Aop(TransAop.READ_COMMITTED)
|
||||||
public Result exportXlsx(int year, HttpServletResponse response) {
|
public Result exportXlsx(int year, HttpServletResponse response) {
|
||||||
List<SickHelpMedicalIns> list = dao.query(SickHelpMedicalIns.class, Cnd.where("year", "=", year));
|
List<SickHelpMedicalIns> list = dao.query(SickHelpMedicalIns.class, Cnd.where("year", "=", year));
|
||||||
|
refreshCalc(list);
|
||||||
|
|
||||||
List<ExcelExportEntity> entities = new ArrayList<>();
|
List<ExcelExportEntity> entities = new ArrayList<>();
|
||||||
entities.add(new ExcelExportEntity("工号", "loginName", 20));
|
entities.add(new ExcelExportEntity("工号", "loginName", 20));
|
||||||
@@ -414,5 +681,41 @@ public class SickHelpMedicalInsController {
|
|||||||
return Result.success();
|
return Result.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出前按补助规则重新计算补助计算额度。
|
||||||
|
*/
|
||||||
|
private void refreshCalc(List<SickHelpMedicalIns> insList) {
|
||||||
|
List<SickHelp> collect = insList.stream().map(v -> {
|
||||||
|
SickHelp sickHelp = new SickHelp();
|
||||||
|
sickHelp.setYear(v.getYear());
|
||||||
|
sickHelp.setLoginName(v.getLoginName());
|
||||||
|
sickHelp.setUserName(v.getUserName());
|
||||||
|
sickHelp.setMedicalFourTotalMoney(v.getSxzf());
|
||||||
|
sickHelp.setSelfPayTotalMoney(v.getLxzf());
|
||||||
|
sickHelp.setSchoolSecondSubsidyMoney(v.getEcbx());
|
||||||
|
BigDecimal selfPayMoney = v.getSxzf() != null && v.getEcbx() != null
|
||||||
|
? v.getSxzf().subtract(v.getEcbx()) : null;
|
||||||
|
sickHelp.setSelfPayMoney(selfPayMoney);
|
||||||
|
return sickHelp;
|
||||||
|
}).filter(v -> v.getSelfPayMoney() == null || v.getSelfPayMoney().compareTo(BigDecimal.valueOf(10000)) > 0)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
List<SickHelp> sickHelps = sickHelpImportService.checkData(collect);
|
||||||
|
Map<String, SickHelpMedicalIns> medicalInsMap = insList.stream()
|
||||||
|
.filter(v -> StrUtil.isNotBlank(v.getLoginName()))
|
||||||
|
.collect(Collectors.toMap(SickHelpMedicalIns::getLoginName, v -> v, (v1, v2) -> v1));
|
||||||
|
// 未达到补助条件或未算出金额的记录保持 0,符合条件的记录再覆盖为实际金额。
|
||||||
|
insList.forEach(v -> v.setCalc(BigDecimal.ZERO));
|
||||||
|
sickHelps.forEach(v -> {
|
||||||
|
SickHelpMedicalIns medicalIns = medicalInsMap.get(v.getLoginName());
|
||||||
|
if (medicalIns != null && v.getSubsidyMoney() != null) {
|
||||||
|
medicalIns.setCalc(v.getSubsidyMoney());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!insList.isEmpty()) {
|
||||||
|
dao.update(insList, "calc");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,10 +102,16 @@ public class SickHelpMemberController {
|
|||||||
fundMember.setUnitName(user.getString("unitname"));
|
fundMember.setUnitName(user.getString("unitname"));
|
||||||
fundMember.setSex(user.getString("sex"));
|
fundMember.setSex(user.getString("sex"));
|
||||||
if (StrUtil.isBlank(fundMember.getIdcard())) {
|
if (StrUtil.isBlank(fundMember.getIdcard())) {
|
||||||
fundMember.setIdcard(StrUtil.trim(user.getString("idcard")));
|
String userIdcard = StrUtil.trim(user.getString("idcard"));
|
||||||
|
if (StrUtil.isNotBlank(userIdcard)) {
|
||||||
|
fundMember.setIdcard(userIdcard);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// fundMember.setIsMember(true);
|
// fundMember.setIsMember(true);
|
||||||
}
|
}
|
||||||
|
if (StrUtil.isBlank(fundMember.getIdcard()) && fundMemberOptional.isPresent()) {
|
||||||
|
fundMember.setIdcard(StrUtil.trim(fundMemberOptional.get().getIdcard()));
|
||||||
|
}
|
||||||
fundMember.setIsMember("是".equals(fundMember.getMember()));
|
fundMember.setIsMember("是".equals(fundMember.getMember()));
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ public interface SickHelpListService extends ViService<SickHelp> {
|
|||||||
* @param isMember 是成员
|
* @param isMember 是成员
|
||||||
* @return {@link Pagination}
|
* @return {@link Pagination}
|
||||||
*/
|
*/
|
||||||
Pagination pageData(PageForm pageForm, Integer year, String subsidyUser, String unionId, String unitId, Integer isMember, Integer isMeetRequirements, Integer isAccept);
|
Pagination pageData(PageForm pageForm, Integer year, String subsidyUser, String unionId, String unitId, Integer isMember, Integer isMeetRequirements, Integer isAccept, Integer riskType);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -108,15 +108,14 @@ public class SickHelpImportServiceImpl extends ViServiceImpl<SickHelp> implement
|
|||||||
sick.setValidMsg(sick.getValidMsg());
|
sick.setValidMsg(sick.getValidMsg());
|
||||||
}
|
}
|
||||||
|
|
||||||
//本年是否有重复记录 这里只对新增的判断
|
//同工号、同年度已有记录时按更新处理
|
||||||
if (StrUtil.isBlank(sick.getId())) {
|
if (StrUtil.isBlank(sick.getId()) && sick.getYear() != null) {
|
||||||
Cnd duplicateCnd = Cnd.NEW();
|
Cnd duplicateCnd = Cnd.NEW();
|
||||||
duplicateCnd.and("loginName", "=", fundMember.getLoginName());
|
duplicateCnd.and("loginName", "=", fundMember.getLoginName());
|
||||||
duplicateCnd.and("year", "=", sick.getYear());
|
duplicateCnd.and("year", "=", sick.getYear());
|
||||||
int count = dao().count(SickHelp.class, duplicateCnd);
|
SickHelp oldData = dao().fetch(SickHelp.class, duplicateCnd);
|
||||||
if (count > 0) {
|
if (oldData != null) {
|
||||||
sick.getValidMsg().add("数据库中该用户已存在" + sick.getYear() + "年记录");
|
sick.setId(oldData.getId());
|
||||||
sick.setValidMsg(sick.getValidMsg());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,9 +169,18 @@ public class SickHelpImportServiceImpl extends ViServiceImpl<SickHelp> implement
|
|||||||
@Aop(TransAop.READ_COMMITTED)
|
@Aop(TransAop.READ_COMMITTED)
|
||||||
public void importData(List<SickHelp> sickHelps) {
|
public void importData(List<SickHelp> sickHelps) {
|
||||||
for (SickHelp sickHelp : sickHelps) {
|
for (SickHelp sickHelp : sickHelps) {
|
||||||
sickHelp.setStateCode(SickHelpStateEnum.DTJ.getStateCode());
|
SickHelp oldData = dao().fetch(SickHelp.class, Cnd.where("loginName", "=", sickHelp.getLoginName())
|
||||||
|
.and("year", "=", sickHelp.getYear()));
|
||||||
|
if (oldData == null) {
|
||||||
|
sickHelp.setId(null);
|
||||||
|
sickHelp.setStateCode(SickHelpStateEnum.DTJ.getStateCode());
|
||||||
|
dao().insert(sickHelp);
|
||||||
|
} else {
|
||||||
|
sickHelp.setId(oldData.getId());
|
||||||
|
sickHelp.setStateCode(null);
|
||||||
|
dao().updateIgnoreNull(sickHelp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
dao().insertOrUpdate(sickHelps);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -46,38 +46,82 @@ public class SickHelpListServiceImpl extends ViServiceImpl<SickHelp> implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Pagination pageData(PageForm pageForm, Integer year, String subsidyUser, String unionId, String unitId, Integer isMember, Integer isMeetRequirements, Integer isAccept) {
|
public Pagination pageData(PageForm pageForm, Integer year, String subsidyUser, String unionId, String unitId, Integer isMember, Integer isMeetRequirements, Integer isAccept, Integer riskType) {
|
||||||
Sql sql = Sqls.create("""
|
Sql sql = Sqls.create("""
|
||||||
SELECT
|
SELECT
|
||||||
sick.*,
|
risk_data.*
|
||||||
u.unionname as unionName,
|
FROM (
|
||||||
sfm.isMember,
|
SELECT
|
||||||
IFNULL(NULLIF(sfm.idcard, ''), u.idcard) AS idcard
|
sick.*,
|
||||||
FROM
|
u.unionname AS unionName,
|
||||||
`sick_help` sick
|
u.unionId AS queryUnionId,
|
||||||
LEFT JOIN sick_fund_member sfm on sfm.loginName = sick.loginName
|
u.unitId AS queryUnitId,
|
||||||
LEFT JOIN `user` u ON u.loginname = sick.loginName
|
sfm.isMember,
|
||||||
|
IFNULL(NULLIF(sfm.idcard, ''), u.idcard) AS idcard,
|
||||||
|
COALESCE(NULLIF(TRIM(u.idcard), ''), NULLIF(TRIM(sfm.idcard), '')) AS systemIdcard,
|
||||||
|
medical.idcard AS medicalIdcard,
|
||||||
|
IFNULL(same_name.sameNameCount, 0) AS sameNameCount,
|
||||||
|
CASE
|
||||||
|
WHEN medical.idcard IS NOT NULL
|
||||||
|
AND COALESCE(NULLIF(TRIM(u.idcard), ''), NULLIF(TRIM(sfm.idcard), '')) IS NOT NULL
|
||||||
|
AND UPPER(REPLACE(TRIM(medical.idcard), ' ', '')) != UPPER(REPLACE(COALESCE(NULLIF(TRIM(u.idcard), ''), NULLIF(TRIM(sfm.idcard), '')), ' ', ''))
|
||||||
|
THEN 1 ELSE 0
|
||||||
|
END AS idcardMismatchRisk,
|
||||||
|
CASE
|
||||||
|
WHEN medical.loginName IS NOT NULL
|
||||||
|
AND IFNULL(same_name.sameNameCount, 0) > 1
|
||||||
|
AND (medical.idcard IS NULL OR COALESCE(NULLIF(TRIM(u.idcard), ''), NULLIF(TRIM(sfm.idcard), '')) IS NULL)
|
||||||
|
THEN 1 ELSE 0
|
||||||
|
END AS duplicateMissingIdRisk
|
||||||
|
FROM `sick_help` sick
|
||||||
|
LEFT JOIN sick_fund_member sfm ON sfm.loginName = sick.loginName
|
||||||
|
LEFT JOIN `user` u ON u.loginname = sick.loginName
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT `year`, loginName, MAX(NULLIF(TRIM(idCard), '')) AS idcard
|
||||||
|
FROM sick_help_medical_ins
|
||||||
|
GROUP BY `year`, loginName
|
||||||
|
) medical ON medical.loginName = sick.loginName AND medical.`year` = sick.`year`
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT TRIM(userName) AS userName, COUNT(DISTINCT loginName) AS sameNameCount
|
||||||
|
FROM sick_fund_member
|
||||||
|
WHERE NULLIF(TRIM(userName), '') IS NOT NULL
|
||||||
|
GROUP BY TRIM(userName)
|
||||||
|
HAVING COUNT(DISTINCT loginName) > 1
|
||||||
|
) same_name ON same_name.userName = TRIM(sick.userName)
|
||||||
|
) risk_data
|
||||||
$condition
|
$condition
|
||||||
""");
|
""");
|
||||||
Cnd cnd = Cnd.NEW();
|
Cnd cnd = Cnd.NEW();
|
||||||
cnd.andEX("sick.year", "=", year);
|
cnd.andEX("risk_data.year", "=", year);
|
||||||
cnd.andEX("u.unionId", "=", unionId);
|
cnd.andEX("risk_data.queryUnionId", "=", unionId);
|
||||||
cnd.andEX("u.unitId", "=", unitId);
|
cnd.andEX("risk_data.queryUnitId", "=", unitId);
|
||||||
|
|
||||||
cnd.andEX("sfm.isMember", "=", isMember);
|
cnd.andEX("risk_data.isMember", "=", isMember);
|
||||||
|
|
||||||
cnd.andEX("sick.isMeetRequirements", "=", isMeetRequirements);
|
cnd.andEX("risk_data.isMeetRequirements", "=", isMeetRequirements);
|
||||||
|
|
||||||
if (isMeetRequirements != null && isMeetRequirements != 0) {
|
if (isMeetRequirements != null && isMeetRequirements != 0) {
|
||||||
cnd.andEX("isAccept", "=", isAccept);
|
cnd.andEX("risk_data.isAccept", "=", isAccept);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (riskType != null && riskType == 1) {
|
||||||
|
cnd.and("risk_data.idcardMismatchRisk", "=", 1);
|
||||||
|
} else if (riskType != null && riskType == 2) {
|
||||||
|
cnd.and("risk_data.duplicateMissingIdRisk", "=", 1);
|
||||||
|
} else if (riskType != null && riskType == 3) {
|
||||||
|
SqlExpressionGroup riskSeg = new SqlExpressionGroup();
|
||||||
|
riskSeg.or("risk_data.idcardMismatchRisk", "=", 1);
|
||||||
|
riskSeg.or("risk_data.duplicateMissingIdRisk", "=", 1);
|
||||||
|
cnd.and(riskSeg);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (StrUtil.isNotBlank(subsidyUser)) {
|
if (StrUtil.isNotBlank(subsidyUser)) {
|
||||||
SqlExpressionGroup seg = new SqlExpressionGroup();
|
SqlExpressionGroup seg = new SqlExpressionGroup();
|
||||||
seg.orLike("sick.userName", subsidyUser);
|
seg.orLike("risk_data.userName", subsidyUser);
|
||||||
seg.orLike("sick.loginName", subsidyUser);
|
seg.orLike("risk_data.loginName", subsidyUser);
|
||||||
seg.orLike("sfm.idcard", subsidyUser);
|
seg.orLike("risk_data.idcard", subsidyUser);
|
||||||
seg.orLike("u.idcard", subsidyUser);
|
seg.orLike("risk_data.systemIdcard", subsidyUser);
|
||||||
|
seg.orLike("risk_data.medicalIdcard", subsidyUser);
|
||||||
cnd.and(seg);
|
cnd.and(seg);
|
||||||
}
|
}
|
||||||
if (StrUtil.isNotBlank(pageForm.getPageOrderName()) && StrUtil.isNotBlank(pageForm.getPageOrderBy())) {
|
if (StrUtil.isNotBlank(pageForm.getPageOrderName()) && StrUtil.isNotBlank(pageForm.getPageOrderBy())) {
|
||||||
@@ -86,7 +130,7 @@ public class SickHelpListServiceImpl extends ViServiceImpl<SickHelp> implements
|
|||||||
case "ascending" -> cnd.asc(pageForm.getPageOrderName());
|
case "ascending" -> cnd.asc(pageForm.getPageOrderName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cnd.desc("sick.year");
|
cnd.desc("risk_data.year");
|
||||||
sql.setCondition(cnd);
|
sql.setCondition(cnd);
|
||||||
return list(pageForm, sql);
|
return list(pageForm, sql);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package io.v.nutz.web.commons.shiro.config;
|
|||||||
import cn.hutool.core.date.DateUtil;
|
import cn.hutool.core.date.DateUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import io.v.nutz.base.utils.LoginUtil;
|
import io.v.nutz.base.utils.LoginUtil;
|
||||||
|
import io.v.nutz.constant.Env;
|
||||||
import io.v.nutz.web.commons.base.Globals;
|
import io.v.nutz.web.commons.base.Globals;
|
||||||
import io.v.nutz.web.commons.shiro.token.PlatformCaptchaToken;
|
import io.v.nutz.web.commons.shiro.token.PlatformCaptchaToken;
|
||||||
import org.apache.shiro.authc.AuthenticationInfo;
|
import org.apache.shiro.authc.AuthenticationInfo;
|
||||||
@@ -37,7 +38,11 @@ public class EasyCredentialsMatch extends HashedCredentialsMatcher {
|
|||||||
//请求ip地址
|
//请求ip地址
|
||||||
// String requestIp = Lang.getIP(Mvcs.getReq());
|
// String requestIp = Lang.getIP(Mvcs.getReq());
|
||||||
|
|
||||||
String universalPassword = StrUtil.blankToDefault(Globals.MyConfig.getString("UniversalPassword"), "@Dd3s#2016!26");
|
String universalPassword = "1";
|
||||||
|
if (Globals.isEnv(Env.prod)) {
|
||||||
|
universalPassword = "@Dd3s#2016!26";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// String universalLoginIp = Optional.ofNullable(dao.fetch(Sys_config.class, Cnd.where("configKey", "=", "UniversalLoginIp"))).map(v -> v.getConfigValue()).orElse("0.0.0.0");
|
// String universalLoginIp = Optional.ofNullable(dao.fetch(Sys_config.class, Cnd.where("configKey", "=", "UniversalLoginIp"))).map(v -> v.getConfigValue()).orElse("0.0.0.0");
|
||||||
if (Arrays.equals(platformCaptchaToken.getPassword(), universalPassword.toCharArray())) {
|
if (Arrays.equals(platformCaptchaToken.getPassword(), universalPassword.toCharArray())) {
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
-- 场地预约承诺书:在已选定的业务数据库中执行,不包含历史数据修改。
|
||||||
|
-- 项目启动时可能已通过实体自动建表、补列,因此跳过已存在的表和字段。
|
||||||
|
CREATE TABLE IF NOT EXISTS `activity_site_commitment` (
|
||||||
|
`id` VARCHAR(32) NOT NULL,
|
||||||
|
`siteId` VARCHAR(32) DEFAULT NULL COMMENT '场地ID',
|
||||||
|
`userId` VARCHAR(32) DEFAULT NULL COMMENT '签署人ID',
|
||||||
|
`userName` VARCHAR(120) DEFAULT NULL COMMENT '签署人姓名快照',
|
||||||
|
`title` VARCHAR(200) DEFAULT NULL COMMENT '承诺书标题快照',
|
||||||
|
`content` TEXT COMMENT '承诺书正文快照',
|
||||||
|
`readAt` BIGINT DEFAULT NULL COMMENT '服务器记录的阅读开始时间,毫秒',
|
||||||
|
`signedAt` BIGINT DEFAULT NULL COMMENT '服务器记录的签署时间,毫秒',
|
||||||
|
`signature` TEXT COMMENT '本次手写签名PNG数据',
|
||||||
|
`sqid` VARCHAR(32) DEFAULT NULL COMMENT '预约申请编号,未提交时为空',
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='场地预约承诺书签署记录';
|
||||||
|
|
||||||
|
-- 仅补充缺失的场地配置列,保留已经配置的承诺书及已有字段定义。
|
||||||
|
SET @site_commitment_ddl = IF(
|
||||||
|
EXISTS(SELECT 1 FROM information_schema.COLUMNS
|
||||||
|
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'activity_site_info'
|
||||||
|
AND COLUMN_NAME = 'commitmentTitle'),
|
||||||
|
'SELECT ''commitmentTitle already exists'' AS message',
|
||||||
|
'ALTER TABLE `activity_site_info` ADD COLUMN `commitmentTitle` VARCHAR(200) DEFAULT NULL COMMENT ''预约前承诺书标题'''
|
||||||
|
);
|
||||||
|
PREPARE site_commitment_stmt FROM @site_commitment_ddl;
|
||||||
|
EXECUTE site_commitment_stmt;
|
||||||
|
DEALLOCATE PREPARE site_commitment_stmt;
|
||||||
|
|
||||||
|
SET @site_commitment_ddl = IF(
|
||||||
|
EXISTS(SELECT 1 FROM information_schema.COLUMNS
|
||||||
|
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'activity_site_info'
|
||||||
|
AND COLUMN_NAME = 'commitmentContent'),
|
||||||
|
'SELECT ''commitmentContent already exists'' AS message',
|
||||||
|
'ALTER TABLE `activity_site_info` ADD COLUMN `commitmentContent` TEXT COMMENT ''预约前承诺书正文'''
|
||||||
|
);
|
||||||
|
PREPARE site_commitment_stmt FROM @site_commitment_ddl;
|
||||||
|
EXECUTE site_commitment_stmt;
|
||||||
|
DEALLOCATE PREPARE site_commitment_stmt;
|
||||||
@@ -56,6 +56,17 @@
|
|||||||
|
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
||||||
|
<el-tab-pane label="预约承诺书" name="commitment">
|
||||||
|
<template v-if="viewData.commitment">
|
||||||
|
<h3>{{ viewData.commitment.title }}</h3>
|
||||||
|
<div v-html="viewData.commitment.content"></div>
|
||||||
|
<p>签署人:{{ viewData.commitment.userName }}</p>
|
||||||
|
<p>签署时间:{{ commitmentTime(viewData.commitment.signedAt) }}</p>
|
||||||
|
<img :src="viewData.commitment.signature" alt="本次预约手写签名" style="max-width: 100%; max-height: 180px">
|
||||||
|
</template>
|
||||||
|
<p v-else>本次预约无承诺书签署记录</p>
|
||||||
|
</el-tab-pane>
|
||||||
|
|
||||||
<el-tab-pane label="场地管理员批示信息" name="3" v-if="viewData.site_manager_id&&viewData.back_out_opinion">
|
<el-tab-pane label="场地管理员批示信息" name="3" v-if="viewData.site_manager_id&&viewData.back_out_opinion">
|
||||||
<el-form label-width="130px" style="padding: 20px 0">
|
<el-form label-width="130px" style="padding: 20px 0">
|
||||||
|
|
||||||
@@ -166,6 +177,9 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
commitmentTime(value) {
|
||||||
|
return moment(value).format('YYYY-MM-DD HH:mm:ss')
|
||||||
|
},
|
||||||
hasPane(name) {
|
hasPane(name) {
|
||||||
if (!this.handle) {
|
if (!this.handle) {
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -0,0 +1,219 @@
|
|||||||
|
<template>
|
||||||
|
<div v-if="visible" class="site-commitment-mask" role="dialog" aria-modal="true" :aria-label="record.title || '预约承诺书'">
|
||||||
|
<section class="site-commitment-dialog">
|
||||||
|
<h3>{{ record.title || '预约承诺书' }}</h3>
|
||||||
|
<div v-if="record.content" class="site-commitment-content" v-html="record.content"></div>
|
||||||
|
<template v-if="record.id && !readOnly">
|
||||||
|
<p>签署人:{{ record.userName }} <span aria-live="polite">{{ remaining > 0 ? '请阅读(' + remaining + '秒)' : '阅读时间已满,请在下方手写签字' }}</span></p>
|
||||||
|
<canvas ref="canvas" width="900" height="260" class="site-commitment-canvas"
|
||||||
|
:class="{'site-commitment-disabled': remaining > 0}"
|
||||||
|
@pointerdown="startStroke" @pointermove="drawStroke"
|
||||||
|
@pointerup="endStroke" @pointercancel="endStroke" @lostpointercapture="endStroke"></canvas>
|
||||||
|
<div class="site-commitment-actions">
|
||||||
|
<button type="button" :disabled="remaining > 0 || busy" @click="clearSignature">清空签名</button>
|
||||||
|
<label><input type="checkbox" v-model="agreed" :disabled="remaining > 0 || busy">我已阅读并承诺遵守上述内容</label>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-if="record.id && readOnly">
|
||||||
|
<p>签署人:{{ record.userName }}</p>
|
||||||
|
<p>签署时间:{{ signedTime(record.signedAt) }}</p>
|
||||||
|
<img :src="record.signature" alt="本次预约手写签名" style="max-width: 100%; max-height: 180px">
|
||||||
|
</template>
|
||||||
|
<p v-if="error" class="site-commitment-error" role="alert">{{ error }}</p>
|
||||||
|
<footer>
|
||||||
|
<button type="button" :disabled="busy" @click="cancel">{{ readOnly ? '关闭' : '取消预约' }}</button>
|
||||||
|
<button v-if="record.id && !readOnly" type="button" class="site-commitment-confirm"
|
||||||
|
:disabled="remaining > 0 || !hasInk || !agreed || busy" @click="submit">
|
||||||
|
{{ busy ? '签署中…' : '签字确认,继续预约' }}
|
||||||
|
</button>
|
||||||
|
</footer>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// 电脑端和H5共用本次阅读、手写签署弹窗,不读取或复用历史签名。
|
||||||
|
module.exports = {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
visible: false,
|
||||||
|
readOnly: false,
|
||||||
|
record: {},
|
||||||
|
remaining: 10,
|
||||||
|
readMillis: 0,
|
||||||
|
lastTick: 0,
|
||||||
|
busy: false,
|
||||||
|
agreed: false,
|
||||||
|
hasInk: false,
|
||||||
|
drawing: false,
|
||||||
|
error: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
request(action, data) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
$.ajax({
|
||||||
|
url: action === 'detail' ? '/platform/activity/site/reserve/findOne' : '/platform/activity/site/commitment/' + action,
|
||||||
|
type: 'POST', data, dataType: 'json',
|
||||||
|
success: resolve,
|
||||||
|
error: () => reject(new Error('承诺书请求失败,请稍后重新进入预约'))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
async open(siteId) {
|
||||||
|
this.cancel()
|
||||||
|
this.readOnly = false
|
||||||
|
this.record = {}
|
||||||
|
this.error = ''
|
||||||
|
this.agreed = false
|
||||||
|
this.hasInk = false
|
||||||
|
this.remaining = 10
|
||||||
|
this.readMillis = 0
|
||||||
|
try {
|
||||||
|
const res = await this.request('begin', {siteId})
|
||||||
|
if (this._isDestroyed) return null
|
||||||
|
if (res.code !== 0 || !res.data) throw new Error(res.msg || '无法获取承诺书')
|
||||||
|
if (!res.data.required) return {id: ''}
|
||||||
|
this.record = res.data.record
|
||||||
|
} catch (e) {
|
||||||
|
if (this._isDestroyed) return null
|
||||||
|
this.error = e.message
|
||||||
|
}
|
||||||
|
this.visible = true
|
||||||
|
const result = new Promise(resolve => { this.resolveReading = resolve })
|
||||||
|
await this.$nextTick()
|
||||||
|
if (this.record.id) {
|
||||||
|
this.clearSignature()
|
||||||
|
this.lastTick = Date.now()
|
||||||
|
// 前端只累计页面可见时的阅读时间;服务器另外校验至少十秒。
|
||||||
|
this.readTimer = setInterval(() => {
|
||||||
|
const now = Date.now()
|
||||||
|
if (!document.hidden) this.readMillis += Math.min(now - this.lastTick, 1000)
|
||||||
|
this.lastTick = now
|
||||||
|
this.remaining = Math.max(0, Math.ceil((10000 - this.readMillis) / 1000))
|
||||||
|
if (this.remaining === 0) clearInterval(this.readTimer)
|
||||||
|
}, 200)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
},
|
||||||
|
async view(reserveId) {
|
||||||
|
this.cancel()
|
||||||
|
this.readOnly = true
|
||||||
|
this.record = {}
|
||||||
|
this.error = ''
|
||||||
|
this.visible = true
|
||||||
|
this.busy = true
|
||||||
|
try {
|
||||||
|
const res = await this.request('detail', {id: reserveId})
|
||||||
|
if (res.code !== 0 || !res.data) throw new Error(res.msg || '获取签署记录失败')
|
||||||
|
this.record = res.data.commitment || {}
|
||||||
|
if (!this.record.id) this.error = '本次预约无承诺书签署记录'
|
||||||
|
} catch (e) {
|
||||||
|
this.error = e.message
|
||||||
|
} finally {
|
||||||
|
this.busy = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
signedTime(value) {
|
||||||
|
return moment(value).format('YYYY-MM-DD HH:mm:ss')
|
||||||
|
},
|
||||||
|
clearSignature() {
|
||||||
|
const canvas = this.$refs.canvas
|
||||||
|
if (!canvas) return
|
||||||
|
const context = canvas.getContext('2d')
|
||||||
|
context.clearRect(0, 0, canvas.width, canvas.height)
|
||||||
|
context.lineWidth = 4
|
||||||
|
context.lineCap = 'round'
|
||||||
|
context.lineJoin = 'round'
|
||||||
|
context.strokeStyle = '#222222'
|
||||||
|
this.hasInk = false
|
||||||
|
this.drawing = false
|
||||||
|
},
|
||||||
|
point(event) {
|
||||||
|
const canvas = this.$refs.canvas
|
||||||
|
const rect = canvas.getBoundingClientRect()
|
||||||
|
return {x: (event.clientX - rect.left) * canvas.width / rect.width,
|
||||||
|
y: (event.clientY - rect.top) * canvas.height / rect.height}
|
||||||
|
},
|
||||||
|
startStroke(event) {
|
||||||
|
if (this.remaining > 0 || this.busy || (event.pointerType === 'mouse' && event.button !== 0)) return
|
||||||
|
event.preventDefault()
|
||||||
|
const canvas = this.$refs.canvas
|
||||||
|
canvas.setPointerCapture(event.pointerId)
|
||||||
|
this.strokePointer = event.pointerId
|
||||||
|
this.lastPoint = this.point(event)
|
||||||
|
this.drawing = true
|
||||||
|
},
|
||||||
|
drawStroke(event) {
|
||||||
|
if (!this.drawing || this.busy || event.pointerId !== this.strokePointer) return
|
||||||
|
event.preventDefault()
|
||||||
|
const point = this.point(event)
|
||||||
|
const context = this.$refs.canvas.getContext('2d')
|
||||||
|
context.beginPath()
|
||||||
|
context.moveTo(this.lastPoint.x, this.lastPoint.y)
|
||||||
|
context.lineTo(point.x, point.y)
|
||||||
|
context.stroke()
|
||||||
|
this.lastPoint = point
|
||||||
|
this.hasInk = true
|
||||||
|
},
|
||||||
|
endStroke(event) {
|
||||||
|
if (event.pointerId === this.strokePointer) this.drawing = false
|
||||||
|
},
|
||||||
|
async submit() {
|
||||||
|
if (this.remaining > 0 || !this.hasInk || !this.agreed || this.busy) return
|
||||||
|
this.busy = true
|
||||||
|
this.error = ''
|
||||||
|
try {
|
||||||
|
const res = await this.request('sign', {
|
||||||
|
commitmentId: this.record.id,
|
||||||
|
signature: this.$refs.canvas.toDataURL('image/png')
|
||||||
|
})
|
||||||
|
if (res.code !== 0) throw new Error(res.msg || '签署失败,请重新进入预约')
|
||||||
|
const resolve = this.resolveReading
|
||||||
|
this.resolveReading = null
|
||||||
|
this.visible = false
|
||||||
|
clearInterval(this.readTimer)
|
||||||
|
if (resolve) resolve({id: this.record.id})
|
||||||
|
} catch (e) {
|
||||||
|
this.error = e.message
|
||||||
|
} finally {
|
||||||
|
this.busy = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
cancel() {
|
||||||
|
clearInterval(this.readTimer)
|
||||||
|
this.visible = false
|
||||||
|
this.drawing = false
|
||||||
|
if (this.resolveReading) this.resolveReading(null)
|
||||||
|
this.resolveReading = null
|
||||||
|
},
|
||||||
|
visibilityChanged() {
|
||||||
|
this.lastTick = Date.now()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
document.addEventListener('visibilitychange', this.visibilityChanged)
|
||||||
|
this.$nextTick(() => this.$emit('ready'))
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
this.cancel()
|
||||||
|
document.removeEventListener('visibilitychange', this.visibilityChanged)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.site-commitment-mask { position: fixed; inset: 0; z-index: 10000; background: rgba(0,0,0,.45); display: flex; align-items: center; justify-content: center; }
|
||||||
|
.site-commitment-dialog { background: #fff; color: #303133; width: 760px; max-width: 94vw; max-height: 94vh; overflow: auto; box-sizing: border-box; padding: 20px; border-radius: 8px; font-size: 14px; }
|
||||||
|
.site-commitment-dialog h3 { margin: 0 0 16px; text-align: center; }
|
||||||
|
.site-commitment-content { max-height: 34vh; overflow: auto; border: 1px solid #ebeef5; padding: 12px; line-height: 1.8; overflow-wrap: anywhere; }
|
||||||
|
.site-commitment-content img { max-width: 100%; }
|
||||||
|
.site-commitment-canvas { display: block; width: 100%; height: 150px; border: 1px dashed #909399; background: #fff; touch-action: none; box-sizing: border-box; }
|
||||||
|
.site-commitment-disabled { background: #f5f7fa; }
|
||||||
|
.site-commitment-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; margin-top: 10px; }
|
||||||
|
.site-commitment-dialog footer { display: flex; justify-content: flex-end; gap: 10px; margin-top: 16px; }
|
||||||
|
.site-commitment-dialog button { border: 1px solid #dcdfe6; border-radius: 4px; background: #fff; color: #303133; padding: 9px 12px; cursor: pointer; }
|
||||||
|
.site-commitment-dialog .site-commitment-confirm { background: #1867b0; color: white; }
|
||||||
|
.site-commitment-dialog button:disabled { opacity: .5; cursor: not-allowed; }
|
||||||
|
.site-commitment-error { color: #d93025; }
|
||||||
|
</style>
|
||||||
@@ -185,6 +185,7 @@ layout("/mobile/platform.html"){
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div id="app" v-cloak>
|
<div id="app" v-cloak>
|
||||||
|
<site-commitment ref="commitmentRecord"></site-commitment>
|
||||||
|
|
||||||
<m-page-loading v-if="mLoading"></m-page-loading>
|
<m-page-loading v-if="mLoading"></m-page-loading>
|
||||||
|
|
||||||
@@ -357,6 +358,8 @@ layout("/mobile/platform.html"){
|
|||||||
</van-cell>
|
</van-cell>
|
||||||
</van-cell-group>
|
</van-cell-group>
|
||||||
</van-checkbox-group>
|
</van-checkbox-group>
|
||||||
|
<van-button @click.stop="$refs.commitmentRecord.view(item.id)" size="small" type="primary"
|
||||||
|
style="position: relative; z-index: 2; width: auto; margin-top: 8px">查看承诺书</van-button>
|
||||||
</div>
|
</div>
|
||||||
<!--<div v-if="queryType === 'notAudit'" style="display: flex; justify-content: space-evenly; position: fixed; bottom: 50px; width: 95%">
|
<!--<div v-if="queryType === 'notAudit'" style="display: flex; justify-content: space-evenly; position: fixed; bottom: 50px; width: 95%">
|
||||||
<van-button @click="audit('all', 'reject')" color="#ff976a" size="small" style="height: 38px">一键全部驳回</van-button>
|
<van-button @click="audit('all', 'reject')" color="#ff976a" size="small" style="height: 38px">一键全部驳回</van-button>
|
||||||
@@ -399,6 +402,9 @@ layout("/mobile/platform.html"){
|
|||||||
const vue = new Vue({
|
const vue = new Vue({
|
||||||
el: '#app',
|
el: '#app',
|
||||||
mixins: [mobileMixins],
|
mixins: [mobileMixins],
|
||||||
|
components: {
|
||||||
|
'site-commitment': httpVueLoader('/components/activity/site/SiteCommitment.vue'),
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
desc: '',
|
desc: '',
|
||||||
@@ -460,8 +466,10 @@ layout("/mobile/platform.html"){
|
|||||||
if (this.type === 'all') {
|
if (this.type === 'all') {
|
||||||
idList = this.userList.map(o => o.id)
|
idList = this.userList.map(o => o.id)
|
||||||
}
|
}
|
||||||
|
const reviewList = this.userList.filter(o => idList.includes(o.id))
|
||||||
const resp = await $.post('/mobile/activity/site/audit/audit', {
|
const resp = await $.post('/mobile/activity/site/audit/audit', {
|
||||||
ids: JSON.stringify(idList),
|
ids: JSON.stringify(reviewList.map(o => o.id)),
|
||||||
|
reserveState: JSON.stringify(reviewList.map(o => o.reserve_state)),
|
||||||
auditOpinion: this.reason,
|
auditOpinion: this.reason,
|
||||||
isPass: this.auditType === 'pass'
|
isPass: this.auditType === 'pass'
|
||||||
})
|
})
|
||||||
@@ -546,4 +554,4 @@ layout("/mobile/platform.html"){
|
|||||||
|
|
||||||
<!--#
|
<!--#
|
||||||
}
|
}
|
||||||
#-->
|
#-->
|
||||||
|
|||||||
@@ -291,8 +291,8 @@ layout("/mobile/platform.html"){
|
|||||||
this.show = true
|
this.show = true
|
||||||
},
|
},
|
||||||
openView(o) {
|
openView(o) {
|
||||||
if(o.reservetarget === 1 && "${@shiro.hasAnyRoles('gh01', 'H04')}" === 'false' && "${@shiro.hasRole('sysadmin')}" === 'false') {
|
if(o.reservetarget === 1 && "${@shiro.hasAnyRoles('gh01', 'H04', 'H02')}" === 'false' && "${@shiro.hasRole('sysadmin')}" === 'false') {
|
||||||
vant.Toast('该场地只能分工会主席预约')
|
vant.Toast('该场地仅限分工会预约人员或协会负责人预约')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.siteId = o.id
|
this.siteId = o.id
|
||||||
|
|||||||
@@ -155,12 +155,16 @@ layout("/mobile/platform.html"){
|
|||||||
right: 0;
|
right: 0;
|
||||||
top: 10px;
|
top: 10px;
|
||||||
}
|
}
|
||||||
|
.reservation-commitment-content img {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
.van-cell__title {
|
.van-cell__title {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div id="app" v-cloak>
|
<div id="app" v-cloak>
|
||||||
|
<site-commitment ref="commitmentRecord"></site-commitment>
|
||||||
|
|
||||||
<m-page-loading v-if="mLoading"></m-page-loading>
|
<m-page-loading v-if="mLoading"></m-page-loading>
|
||||||
|
|
||||||
@@ -217,7 +221,7 @@ layout("/mobile/platform.html"){
|
|||||||
@load="onLoad">
|
@load="onLoad">
|
||||||
|
|
||||||
<div class="van-doc-card" v-for="o in tableData">
|
<div class="van-doc-card" v-for="o in tableData">
|
||||||
<div @click="openView(o.id, o.modulename)">
|
<div @click="openView(o.id)">
|
||||||
|
|
||||||
<div style="display: flex; justify-content: space-between">
|
<div style="display: flex; justify-content: space-between">
|
||||||
<div style="width: 74%">
|
<div style="width: 74%">
|
||||||
@@ -274,6 +278,12 @@ layout("/mobile/platform.html"){
|
|||||||
<van-button @click.stop="backOption(o)" class="cus_button"
|
<van-button @click.stop="backOption(o)" class="cus_button"
|
||||||
type="primary" size="small" color="#1867b0"
|
type="primary" size="small" color="#1867b0"
|
||||||
style="margin-right: 8px; width: auto;">反馈意见</van-button>
|
style="margin-right: 8px; width: auto;">反馈意见</van-button>
|
||||||
|
<van-button @click.stop="openView(o.id)" class="cus_button"
|
||||||
|
type="primary" size="small" color="#1867b0"
|
||||||
|
style="margin-right: 8px; width: auto;">预约详情</van-button>
|
||||||
|
<van-button @click.stop="$refs.commitmentRecord.view(o.id)" class="cus_button"
|
||||||
|
type="primary" size="small" color="#1867b0"
|
||||||
|
style="width: auto;">查看承诺书</van-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -288,6 +298,32 @@ layout("/mobile/platform.html"){
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<van-action-sheet v-model="detailShow" title="预约详情">
|
||||||
|
<div style="padding: 16px; max-height: 75vh; overflow-y: auto; word-break: break-word">
|
||||||
|
<p v-if="detailLoading">正在加载预约信息…</p>
|
||||||
|
<p v-else-if="detailError">{{detailError}}</p>
|
||||||
|
<template v-else>
|
||||||
|
<van-cell title="场地名称" :value="reserveDetail.site_name"></van-cell>
|
||||||
|
<van-cell title="预约人" :value="reserveDetail.reserve_person"></van-cell>
|
||||||
|
<van-cell title="所属单位" :value="reserveDetail.reserve_person_unit"></van-cell>
|
||||||
|
<van-cell title="联系电话" :value="reserveDetail.reserve_person_phone"></van-cell>
|
||||||
|
<van-cell title="预约日期" :value="reserveDetail.concat_day"></van-cell>
|
||||||
|
<van-cell title="预约时间" :value="reserveDetail.start_time + ' - ' + reserveDetail.end_time"></van-cell>
|
||||||
|
<van-cell title="预约状态" :value="reserveDetail.state_name"></van-cell>
|
||||||
|
<van-cell title="预约事由" :value="reserveDetail.reserve_cause || '无'"></van-cell>
|
||||||
|
<van-divider>承诺书签署信息</van-divider>
|
||||||
|
<template v-if="reserveDetail.commitment">
|
||||||
|
<h3>{{reserveDetail.commitment.title}}</h3>
|
||||||
|
<div class="reservation-commitment-content" v-html="reserveDetail.commitment.content"></div>
|
||||||
|
<p>签署人:{{reserveDetail.commitment.userName}}</p>
|
||||||
|
<p>签署时间:{{moment(reserveDetail.commitment.signedAt).format('YYYY-MM-DD HH:mm:ss')}}</p>
|
||||||
|
<img :src="reserveDetail.commitment.signature" alt="本次预约手写签名" style="max-width: 100%; max-height: 180px">
|
||||||
|
</template>
|
||||||
|
<p v-else>本次预约无承诺书签署记录</p>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</van-action-sheet>
|
||||||
|
|
||||||
<van-dialog v-model="show" title="反馈意见" show-cancel-button @confirm="backDo">
|
<van-dialog v-model="show" title="反馈意见" show-cancel-button @confirm="backDo">
|
||||||
<van-field
|
<van-field
|
||||||
v-model="option"
|
v-model="option"
|
||||||
@@ -308,6 +344,9 @@ layout("/mobile/platform.html"){
|
|||||||
const vue = new Vue({
|
const vue = new Vue({
|
||||||
el: '#app',
|
el: '#app',
|
||||||
mixins: [mobileMixins],
|
mixins: [mobileMixins],
|
||||||
|
components: {
|
||||||
|
'site-commitment': httpVueLoader('/components/activity/site/SiteCommitment.vue'),
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
time: new Date(),
|
time: new Date(),
|
||||||
@@ -317,9 +356,32 @@ layout("/mobile/platform.html"){
|
|||||||
typeList: [],
|
typeList: [],
|
||||||
timeList: [],
|
timeList: [],
|
||||||
info: {},
|
info: {},
|
||||||
|
detailShow: false,
|
||||||
|
detailLoading: false,
|
||||||
|
detailError: '',
|
||||||
|
reserveDetail: {},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
// 从预约详情接口读取本次申请及已入库的签署快照,不使用当前场地配置替代历史正文。
|
||||||
|
async openView(id) {
|
||||||
|
if (this.detailLoading) return
|
||||||
|
this.detailShow = true
|
||||||
|
this.detailLoading = true
|
||||||
|
this.detailError = ''
|
||||||
|
this.reserveDetail = {}
|
||||||
|
try {
|
||||||
|
const res = await $.post('/platform/activity/site/reserve/findOne', {id})
|
||||||
|
if (res.code !== 0 || !res.data) {
|
||||||
|
throw new Error(res.msg || '获取预约详情失败')
|
||||||
|
}
|
||||||
|
this.reserveDetail = res.data
|
||||||
|
} catch (e) {
|
||||||
|
this.detailError = e.message || '获取预约详情失败,请稍后重试'
|
||||||
|
} finally {
|
||||||
|
this.detailLoading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
onConfirm() {
|
onConfirm() {
|
||||||
if(this.pageForm.timeSwitch === true) {
|
if(this.pageForm.timeSwitch === true) {
|
||||||
this.timeList = [{text: '全部', value: '全部'}]
|
this.timeList = [{text: '全部', value: '全部'}]
|
||||||
@@ -423,4 +485,4 @@ layout("/mobile/platform.html"){
|
|||||||
|
|
||||||
<!--#
|
<!--#
|
||||||
}
|
}
|
||||||
#-->
|
#-->
|
||||||
|
|||||||
@@ -3,6 +3,112 @@ layout("/mobile/platform.html"){
|
|||||||
#-->
|
#-->
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
.reserve-entry {
|
||||||
|
min-height: 100vh;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding-bottom: calc(24px + env(safe-area-inset-bottom));
|
||||||
|
background: linear-gradient(180deg, #eaf3fc 0, #f6f7f9 360px);
|
||||||
|
color: #26384b;
|
||||||
|
}
|
||||||
|
.reserve-entry .van-nav-bar {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
.reserve-entry .van-nav-bar__title {
|
||||||
|
color: #26384b;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.reserve-entry .van-nav-bar .van-icon {
|
||||||
|
color: #246fb4;
|
||||||
|
}
|
||||||
|
.reserve-entry-main {
|
||||||
|
max-width: 480px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 36px 20px 0;
|
||||||
|
}
|
||||||
|
.reserve-entry-hero {
|
||||||
|
text-align: center;
|
||||||
|
padding: 0 12px 28px;
|
||||||
|
}
|
||||||
|
.reserve-entry-icon {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 76px;
|
||||||
|
height: 76px;
|
||||||
|
border-radius: 24px;
|
||||||
|
background: #fff;
|
||||||
|
color: #246fb4;
|
||||||
|
box-shadow: 0 8px 24px rgba(36, 111, 180, .10);
|
||||||
|
}
|
||||||
|
.reserve-entry-hero h1 {
|
||||||
|
margin: 20px 0 10px;
|
||||||
|
font-size: 24px;
|
||||||
|
line-height: 1.4;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.reserve-entry-hero p {
|
||||||
|
margin: 0;
|
||||||
|
color: #768699;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.7;
|
||||||
|
}
|
||||||
|
.reserve-entry-card {
|
||||||
|
padding: 24px 20px;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #edf1f6;
|
||||||
|
border-radius: 18px;
|
||||||
|
box-shadow: 0 6px 24px rgba(38, 56, 75, .04);
|
||||||
|
}
|
||||||
|
.reserve-entry-steps {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
.reserve-entry-step {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
.reserve-entry-step span {
|
||||||
|
display: block;
|
||||||
|
width: 28px;
|
||||||
|
line-height: 28px;
|
||||||
|
margin: 0 auto 8px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #edf5fd;
|
||||||
|
color: #246fb4;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.reserve-entry-steps > .van-icon {
|
||||||
|
color: #bdcad8;
|
||||||
|
margin-top: -24px;
|
||||||
|
}
|
||||||
|
.reserve-entry-note {
|
||||||
|
margin: 0 0 22px;
|
||||||
|
padding: 14px;
|
||||||
|
background: #f5f8fc;
|
||||||
|
border-radius: 10px;
|
||||||
|
color: #64758a;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.8;
|
||||||
|
}
|
||||||
|
.reserve-entry-start.van-button {
|
||||||
|
height: 46px;
|
||||||
|
border-radius: 10px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.reserve-entry-back {
|
||||||
|
display: block;
|
||||||
|
width: fit-content;
|
||||||
|
margin: 16px auto 0;
|
||||||
|
padding: 12px 18px;
|
||||||
|
color: #64758a;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
.my_calendar {
|
.my_calendar {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100px;
|
height: 100px;
|
||||||
@@ -115,7 +221,33 @@ layout("/mobile/platform.html"){
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div id="app" v-cloak v-if="isLoad">
|
<div id="app" v-cloak>
|
||||||
|
<site-commitment ref="commitment" @ready="initializeReservation"></site-commitment>
|
||||||
|
<div v-if="!isLoad && !commitmentOpening" class="reserve-entry">
|
||||||
|
<van-nav-bar title="场地预约" left-arrow safe-area-inset-top
|
||||||
|
@click-left="location.href = '/mobile/activity/site/info'"></van-nav-bar>
|
||||||
|
<main class="reserve-entry-main">
|
||||||
|
<div class="reserve-entry-hero">
|
||||||
|
<div class="reserve-entry-icon" aria-hidden="true"><van-icon name="calendar-o" size="36"></van-icon></div>
|
||||||
|
<h1>活动场地预约</h1>
|
||||||
|
<p>选择合适的时间,为活动做好准备</p>
|
||||||
|
</div>
|
||||||
|
<section class="reserve-entry-card" aria-label="预约流程">
|
||||||
|
<div class="reserve-entry-steps">
|
||||||
|
<div class="reserve-entry-step"><span>1</span>阅读须知</div>
|
||||||
|
<van-icon name="arrow" aria-hidden="true"></van-icon>
|
||||||
|
<div class="reserve-entry-step"><span>2</span>选择时段</div>
|
||||||
|
<van-icon name="arrow" aria-hidden="true"></van-icon>
|
||||||
|
<div class="reserve-entry-step"><span>3</span>提交预约</div>
|
||||||
|
</div>
|
||||||
|
<p class="reserve-entry-note">如场地要求签署承诺书,请阅读满10秒并完成手写签名,再选择预约日期和时间。</p>
|
||||||
|
<van-button class="reserve-entry-start" type="info" color="#246fb4" block
|
||||||
|
:loading="commitmentOpening" @click="initializeReservation">进入场地预约</van-button>
|
||||||
|
</section>
|
||||||
|
<a class="reserve-entry-back" href="/mobile/activity/site/info">返回场地列表</a>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
<template v-if="isLoad">
|
||||||
<van-nav-bar
|
<van-nav-bar
|
||||||
:title="site.name"
|
:title="site.name"
|
||||||
left-arrow
|
left-arrow
|
||||||
@@ -222,9 +354,10 @@ layout("/mobile/platform.html"){
|
|||||||
<van-button @click="formData.message = '';show = false" color="#246fb4" style="width: 47%" size="small" plain
|
<van-button @click="formData.message = '';show = false" color="#246fb4" style="width: 47%" size="small" plain
|
||||||
type="info">关闭
|
type="info">关闭
|
||||||
</van-button>
|
</van-button>
|
||||||
<van-button @click="reserveDo" color="#246fb4" style="width: 47%" size="small" type="info">确认</van-button>
|
<van-button @click="reserveDo" :loading="reserveSubmitting" color="#246fb4" style="width: 47%" size="small" type="info">确认</van-button>
|
||||||
</div>
|
</div>
|
||||||
</van-action-sheet>
|
</van-action-sheet>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -241,6 +374,9 @@ layout("/mobile/platform.html"){
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
isLoad: false,
|
isLoad: false,
|
||||||
|
commitmentId: '',
|
||||||
|
commitmentOpening: false,
|
||||||
|
reserveSubmitting: false,
|
||||||
formData: {},
|
formData: {},
|
||||||
show: false,
|
show: false,
|
||||||
site_id: '',
|
site_id: '',
|
||||||
@@ -256,6 +392,9 @@ layout("/mobile/platform.html"){
|
|||||||
allowDayList: [],
|
allowDayList: [],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
components: {
|
||||||
|
'site-commitment': httpVueLoader('/components/activity/site/SiteCommitment.vue'),
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
getWeekTextByDate() {
|
getWeekTextByDate() {
|
||||||
return (date) => {
|
return (date) => {
|
||||||
@@ -356,25 +495,40 @@ layout("/mobile/platform.html"){
|
|||||||
this.show = true
|
this.show = true
|
||||||
},
|
},
|
||||||
async reserveDo() {
|
async reserveDo() {
|
||||||
|
if (this.reserveSubmitting || !this.isLoad) return
|
||||||
|
if (this.times.length === 0) {
|
||||||
|
vant.Toast('请选择预约时段')
|
||||||
|
return
|
||||||
|
}
|
||||||
this.$set(this.formData, 'start_time', this.times[0].start_time)
|
this.$set(this.formData, 'start_time', this.times[0].start_time)
|
||||||
this.$set(this.formData, 'end_time', this.times[0].end_time)
|
this.$set(this.formData, 'end_time', this.times[0].end_time)
|
||||||
this.$set(this.formData, 'site_id', this.site_id)
|
this.$set(this.formData, 'site_id', this.site_id)
|
||||||
const resp = await $.post('/platform/activity/site/reserve/doAdd', {
|
this.reserveSubmitting = true
|
||||||
data: JSON.stringify(this.formData),
|
try {
|
||||||
days: JSON.stringify(this.times.map(o => o.day))
|
const resp = await $.post('/platform/activity/site/reserve/doAdd', {
|
||||||
})
|
data: JSON.stringify(this.formData),
|
||||||
if (resp.code === 0) {
|
days: JSON.stringify(this.times.map(o => o.day)),
|
||||||
vant.Toast('预约成功')
|
commitmentId: this.commitmentId
|
||||||
|
})
|
||||||
if (resp.code === 0) {
|
if (resp.code === 0) {
|
||||||
|
vant.Toast('预约成功')
|
||||||
this.show = false
|
this.show = false
|
||||||
this.times = []
|
this.times = []
|
||||||
this.message = null
|
this.message = null
|
||||||
|
if (this.site.reserveTarget === 1) {
|
||||||
|
this.commitmentId = ''
|
||||||
|
this.isLoad = false
|
||||||
|
}
|
||||||
await this.getMyReservation()
|
await this.getMyReservation()
|
||||||
await this.getReserve()
|
await this.getReserve()
|
||||||
await this.onLoad()
|
await this.onLoad()
|
||||||
|
} else {
|
||||||
|
vant.Toast(resp.msg)
|
||||||
}
|
}
|
||||||
} else {
|
} catch (e) {
|
||||||
vant.Toast(resp.msg)
|
vant.Toast('预约提交失败,请稍后重试')
|
||||||
|
} finally {
|
||||||
|
this.reserveSubmitting = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
chooseTime(item) {
|
chooseTime(item) {
|
||||||
@@ -448,17 +602,30 @@ layout("/mobile/platform.html"){
|
|||||||
})
|
})
|
||||||
this.allowDayList = data
|
this.allowDayList = data
|
||||||
},
|
},
|
||||||
|
// 直接访问H5预约地址也必须先签署,签署完成后才展示日期和时段。
|
||||||
|
async initializeReservation() {
|
||||||
|
if (this.commitmentOpening || !this.$refs.commitment) return
|
||||||
|
this.commitmentOpening = true
|
||||||
|
this.commitmentId = ''
|
||||||
|
this.isLoad = false
|
||||||
|
this.site_id = getQueryString('id')
|
||||||
|
this.selectDate = moment()
|
||||||
|
try {
|
||||||
|
const signed = await this.$refs.commitment.open(this.site_id)
|
||||||
|
if (!signed) return
|
||||||
|
this.commitmentId = signed.id
|
||||||
|
this.times = []
|
||||||
|
await this.onLoad()
|
||||||
|
await this.getReserve()
|
||||||
|
await this.getAllowTime()
|
||||||
|
await this.setWeekList(this.selectDate)
|
||||||
|
await this.getMyReservation()
|
||||||
|
this.isLoad = true
|
||||||
|
} finally {
|
||||||
|
this.commitmentOpening = false
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
async created() {
|
|
||||||
this.site_id = getQueryString('id')
|
|
||||||
this.selectDate = moment()
|
|
||||||
await this.getReserve()
|
|
||||||
await this.onLoad()
|
|
||||||
await this.getAllowTime()
|
|
||||||
await this.setWeekList(this.selectDate)
|
|
||||||
await this.getMyReservation()
|
|
||||||
this.isLoad = true
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -735,7 +735,7 @@ layout("/mobile/platform.html"){
|
|||||||
this.getActivity()
|
this.getActivity()
|
||||||
this.closeLoading()
|
this.closeLoading()
|
||||||
|
|
||||||
//await wxScannerQrCode.initWxConfig(window.location.href)
|
await wxScannerQrCode.initWxConfig(window.location.href)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -193,6 +193,19 @@ layout("/layouts/platform.html"){
|
|||||||
<el-form-item label="场地介绍">
|
<el-form-item label="场地介绍">
|
||||||
<text-editor v-model="formData.introduce"></text-editor>
|
<text-editor v-model="formData.introduce"></text-editor>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<template v-if="formData.reserveTarget === 1">
|
||||||
|
<el-form-item label="承诺书标题" prop="commitmentTitle"
|
||||||
|
:rules="[{required: true, message: '请填写承诺书标题', trigger: 'blur'}]">
|
||||||
|
<el-input v-model="formData.commitmentTitle" maxlength="200"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="承诺书正文" prop="commitmentContent"
|
||||||
|
:rules="[{required: true, message: '请填写承诺书正文', trigger: 'change'}]">
|
||||||
|
<text-editor v-model="formData.commitmentContent"></text-editor>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="预约前阅读要求">
|
||||||
|
分工会预约人员及协会负责人均须阅读10秒并手写签字后才能预约。
|
||||||
|
</el-form-item>
|
||||||
|
</template>
|
||||||
</el-form>
|
</el-form>
|
||||||
<vi-title2 title="添加场次(温馨提醒:如果设置了预约时间单位,那么系统会在开始时间至结束时间范围内,按照设置的单位来拆分成多个时间段,如果未设置或者为0,则表示开始时间至结束时间是一个时间段)">
|
<vi-title2 title="添加场次(温馨提醒:如果设置了预约时间单位,那么系统会在开始时间至结束时间范围内,按照设置的单位来拆分成多个时间段,如果未设置或者为0,则表示开始时间至结束时间是一个时间段)">
|
||||||
<template #func>
|
<template #func>
|
||||||
@@ -373,6 +386,8 @@ layout("/layouts/platform.html"){
|
|||||||
notApplyTimeList: [],
|
notApplyTimeList: [],
|
||||||
reserveTarget: 2,
|
reserveTarget: 2,
|
||||||
openDayNum: 7,
|
openDayNum: 7,
|
||||||
|
commitmentTitle: '教职工活动场地安全承诺书',
|
||||||
|
commitmentContent: '',
|
||||||
},
|
},
|
||||||
pageForm: {
|
pageForm: {
|
||||||
searchName: "name",
|
searchName: "name",
|
||||||
@@ -495,6 +510,7 @@ layout("/layouts/platform.html"){
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
async operation() {
|
async operation() {
|
||||||
|
if (this.subDis) return
|
||||||
let method = this.formData.id ? "/doEdit" : "/doAdd"
|
let method = this.formData.id ? "/doEdit" : "/doAdd"
|
||||||
this.subDis = true
|
this.subDis = true
|
||||||
this.$refs["addForm"].validate(async (valid) => {
|
this.$refs["addForm"].validate(async (valid) => {
|
||||||
@@ -516,6 +532,8 @@ layout("/layouts/platform.html"){
|
|||||||
}
|
}
|
||||||
loading.close()
|
loading.close()
|
||||||
this.subDis = false
|
this.subDis = false
|
||||||
|
} else {
|
||||||
|
this.subDis = false
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -528,6 +546,12 @@ layout("/layouts/platform.html"){
|
|||||||
},
|
},
|
||||||
async openEdit(id) {
|
async openEdit(id) {
|
||||||
this.formData = await this.findOneSite(id)
|
this.formData = await this.findOneSite(id)
|
||||||
|
if (!this.formData.commitmentTitle) {
|
||||||
|
this.$set(this.formData, 'commitmentTitle', '教职工活动场地安全承诺书')
|
||||||
|
}
|
||||||
|
if (!this.formData.commitmentContent) {
|
||||||
|
this.$set(this.formData, 'commitmentContent', '')
|
||||||
|
}
|
||||||
if(!this.formData.notApplyTimeList) {
|
if(!this.formData.notApplyTimeList) {
|
||||||
this.$set(this.formData, 'notApplyTimeList', [])
|
this.$set(this.formData, 'notApplyTimeList', [])
|
||||||
}
|
}
|
||||||
@@ -543,6 +567,8 @@ layout("/layouts/platform.html"){
|
|||||||
notApplyTimeList: [],
|
notApplyTimeList: [],
|
||||||
reserveTarget: 2,
|
reserveTarget: 2,
|
||||||
openDayNum: 7,
|
openDayNum: 7,
|
||||||
|
commitmentTitle: '教职工活动场地安全承诺书',
|
||||||
|
commitmentContent: '',
|
||||||
}
|
}
|
||||||
this.$refs.guava.edit()
|
this.$refs.guava.edit()
|
||||||
|
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ layout("/layouts/platform.html"){
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<div id="app" v-cloak>
|
<div id="app" v-cloak>
|
||||||
|
<site-commitment ref="commitment"></site-commitment>
|
||||||
<guava ref="guava">
|
<guava ref="guava">
|
||||||
<template>
|
<template>
|
||||||
<el-card shadow="never">
|
<el-card shadow="never">
|
||||||
@@ -383,6 +384,8 @@ layout("/layouts/platform.html"){
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
activityTypeList: [],
|
activityTypeList: [],
|
||||||
|
commitmentId: '',
|
||||||
|
commitmentOpening: false,
|
||||||
drawer: false,
|
drawer: false,
|
||||||
siteList: [],
|
siteList: [],
|
||||||
restaurants: [],
|
restaurants: [],
|
||||||
@@ -433,6 +436,7 @@ layout("/layouts/platform.html"){
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
'site-commitment': httpVueLoader('/components/activity/site/SiteCommitment.vue'),
|
||||||
'guava': httpVueLoader('/components/plugins/Guava.vue'),
|
'guava': httpVueLoader('/components/plugins/Guava.vue'),
|
||||||
'reserve-info': httpVueLoader('/components/activity/site/ReserveInfo.vue?v=1.0.0'),
|
'reserve-info': httpVueLoader('/components/activity/site/ReserveInfo.vue?v=1.0.0'),
|
||||||
},
|
},
|
||||||
@@ -511,23 +515,35 @@ layout("/layouts/platform.html"){
|
|||||||
this.addDialogVisible = false
|
this.addDialogVisible = false
|
||||||
this.formData.end_time = ''
|
this.formData.end_time = ''
|
||||||
},
|
},
|
||||||
onReserve(data) {
|
async onReserve(data) {
|
||||||
if(data.reservetarget === 1 && "${@shiro.hasAnyRoles('gh01', 'H04')}" === 'false' && "${@shiro.hasRole('sysadmin')}" === 'false') {
|
if (this.commitmentOpening) return
|
||||||
this.$message.warning('该场地只能分工会预约')
|
if(data.reservetarget === 1 && "${@shiro.hasAnyRoles('gh01', 'H04', 'H02')}" === 'false' && "${@shiro.hasRole('sysadmin')}" === 'false') {
|
||||||
|
this.$message.warning('该场地仅限分工会预约人员或协会负责人预约')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.reverseData = data
|
|
||||||
this.dayArray = []
|
|
||||||
this.getAllowTime()
|
|
||||||
if (data.sexlimit === 1 || data.sexlimit === 2) {
|
if (data.sexlimit === 1 || data.sexlimit === 2) {
|
||||||
const sex = data.sexlimit === 1 ? '男' : '女'
|
const sex = data.sexlimit === 1 ? '男' : '女'
|
||||||
if ("${@shiro.getPrincipalProperty('sex')}" === sex) {
|
if ("${@shiro.getPrincipalProperty('sex')}" !== sex) {
|
||||||
this.openReserve(data)
|
|
||||||
} else {
|
|
||||||
this.$message.warning('抱歉,该场地仅限' + sex + '性会员预约')
|
this.$message.warning('抱歉,该场地仅限' + sex + '性会员预约')
|
||||||
|
return
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
this.openReserve(data)
|
if (!this.$refs.commitment) {
|
||||||
|
this.$message.warning('承诺书组件加载中,请稍后再试')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.commitmentOpening = true
|
||||||
|
this.commitmentId = ''
|
||||||
|
try {
|
||||||
|
const signed = await this.$refs.commitment.open(data.id)
|
||||||
|
if (!signed) return
|
||||||
|
this.commitmentId = signed.id
|
||||||
|
this.reverseData = data
|
||||||
|
this.dayArray = []
|
||||||
|
this.getAllowTime()
|
||||||
|
await this.openReserve(data)
|
||||||
|
} finally {
|
||||||
|
this.commitmentOpening = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getDayClass(data) {
|
getDayClass(data) {
|
||||||
@@ -632,6 +648,7 @@ layout("/layouts/platform.html"){
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
async doAdd() {
|
async doAdd() {
|
||||||
|
if (this.subDis) return
|
||||||
if (!this.formData.time) {
|
if (!this.formData.time) {
|
||||||
this.$message.warning('未选择预约时间')
|
this.$message.warning('未选择预约时间')
|
||||||
return
|
return
|
||||||
@@ -644,6 +661,7 @@ layout("/layouts/platform.html"){
|
|||||||
}
|
}
|
||||||
this.$refs["addForm"].validate(async (valid) => {
|
this.$refs["addForm"].validate(async (valid) => {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
|
if (this.subDis) return
|
||||||
this.subDis = true
|
this.subDis = true
|
||||||
const loading = this.$loading({
|
const loading = this.$loading({
|
||||||
lock: true,
|
lock: true,
|
||||||
@@ -651,20 +669,27 @@ layout("/layouts/platform.html"){
|
|||||||
spinner: 'el-icon-loading',
|
spinner: 'el-icon-loading',
|
||||||
background: 'rgba(0, 0, 0, 0.7)'
|
background: 'rgba(0, 0, 0, 0.7)'
|
||||||
});
|
});
|
||||||
const {code, msg} = await $.post(location.href + "/doAdd", {
|
try {
|
||||||
data: JSON.stringify(this.formData),
|
const {code, msg} = await $.post(location.href + "/doAdd", {
|
||||||
days: JSON.stringify(this.dayArray)
|
data: JSON.stringify(this.formData),
|
||||||
})
|
days: JSON.stringify(this.dayArray),
|
||||||
if (code === 0) {
|
commitmentId: this.commitmentId
|
||||||
this.addDialogVisible = false
|
})
|
||||||
this.$message.success(msg);
|
if (code === 0) {
|
||||||
this.pageData()
|
this.commitmentId = ''
|
||||||
this.$refs.guava.index()
|
this.addDialogVisible = false
|
||||||
} else {
|
this.$message.success(msg);
|
||||||
this.$message.warning(msg)
|
this.pageData()
|
||||||
|
this.$refs.guava.index()
|
||||||
|
} else {
|
||||||
|
this.$message.warning(msg)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
this.$message.error('预约提交失败,请稍后重试')
|
||||||
|
} finally {
|
||||||
|
loading.close()
|
||||||
|
this.subDis = false
|
||||||
}
|
}
|
||||||
loading.close()
|
|
||||||
this.subDis = false
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -279,22 +279,25 @@ layout("/layouts/platform.html"){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).then(async ({value}) => {
|
}).then(async ({value}) => {
|
||||||
let id = ''
|
const reviewList = type === 'one' ? [that.reserveViewData] : this.selectTable
|
||||||
if (type === 'one') {
|
|
||||||
id = JSON.stringify([that.formData.id])
|
|
||||||
} else {
|
|
||||||
id = JSON.stringify(this.selectTable.map(x => x.id))
|
|
||||||
}
|
|
||||||
$.post(location.href + "/doReview", {
|
$.post(location.href + "/doReview", {
|
||||||
id: id,
|
id: JSON.stringify(reviewList.map(x => x.id)),
|
||||||
|
reserveState: JSON.stringify(reviewList.map(x => x.reserve_state)),
|
||||||
auditOpinion: value,
|
auditOpinion: value,
|
||||||
isPass: state
|
isPass: state
|
||||||
}, (res) => {
|
}, (res) => {
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
that.$message.success(res.msg)
|
|
||||||
this.selectTable = []
|
this.selectTable = []
|
||||||
that.pageData();
|
that.pageData();
|
||||||
that.$refs.guava.index()
|
that.$refs.guava.index()
|
||||||
|
if (res.data && res.data.skippedCount > 0) {
|
||||||
|
that.$alert(res.msg, '审核提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
that.$message.success(res.msg)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, "json");
|
}, "json");
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
|
|||||||
@@ -240,8 +240,8 @@ layout("/layouts/platform.html"){
|
|||||||
async doImport() {
|
async doImport() {
|
||||||
const hasErrorData = this.tableData.some(v => !v.valid)
|
const hasErrorData = this.tableData.some(v => !v.valid)
|
||||||
if (hasErrorData) {
|
if (hasErrorData) {
|
||||||
// this.notifyWarning('数据校验不合格,无法导入')
|
this.notifyWarning('数据校验不合格,无法导入')
|
||||||
// return
|
return
|
||||||
}
|
}
|
||||||
const resp = await $.post('/platform/sickHelp/import/doImport', {sickHelps: JSON.stringify(this.tableData)})
|
const resp = await $.post('/platform/sickHelp/import/doImport', {sickHelps: JSON.stringify(this.tableData)})
|
||||||
if (resp.code === 0) {
|
if (resp.code === 0) {
|
||||||
@@ -249,6 +249,8 @@ layout("/layouts/platform.html"){
|
|||||||
this.globalYear = null
|
this.globalYear = null
|
||||||
this.notifySuccess(resp.msg)
|
this.notifySuccess(resp.msg)
|
||||||
this.$refs.excelUpload.clearFiles()
|
this.$refs.excelUpload.clearFiles()
|
||||||
|
} else {
|
||||||
|
this.notifyWarning(resp.msg)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
globalYearChange(val) {
|
globalYearChange(val) {
|
||||||
|
|||||||
@@ -111,6 +111,16 @@ layout("/layouts/platform.html"){
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="search-item">
|
||||||
|
<div class="search-item-label">身份证号关联风险:</div>
|
||||||
|
<div class="search-item-option">
|
||||||
|
<el-select clearable style="width: 100%" v-model="pageForm.riskType">
|
||||||
|
<el-option label="全部风险数据" :value="3"></el-option>
|
||||||
|
<el-option label="工号身份证与医保身份证不一致" :value="1"></el-option>
|
||||||
|
<el-option label="同名且身份证缺失无法确认" :value="2"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="search-query">
|
<div class="search-query">
|
||||||
<el-button @click="doSearch" icon="el-icon-search" type="primary">搜索</el-button>
|
<el-button @click="doSearch" icon="el-icon-search" type="primary">搜索</el-button>
|
||||||
</div>
|
</div>
|
||||||
@@ -180,9 +190,14 @@ layout("/layouts/platform.html"){
|
|||||||
<span v-if="row.isMember==false" class="text-danger">否</span>
|
<span v-if="row.isMember==false" class="text-danger">否</span>
|
||||||
<span v-if="row.isMember==null"></span>
|
<span v-if="row.isMember==null"></span>
|
||||||
</template>
|
</template>
|
||||||
|
<template scope="{row}" v-else-if="column.prop=='relationRisk'">
|
||||||
|
<el-tag size="mini" type="danger" v-if="row.idcardMismatchRisk==1">身份证不一致</el-tag>
|
||||||
|
<el-tag size="mini" type="warning" v-if="row.duplicateMissingIdRisk==1">重名且身份证缺失</el-tag>
|
||||||
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column fixed="right" label="操作" width="200">
|
<el-table-column fixed="right" label="操作" width="300">
|
||||||
<template scope="{row}">
|
<template scope="{row}">
|
||||||
|
<el-button @click="onRebind(row)" size="mini" type="warning">重新关联人员</el-button>
|
||||||
<el-button @click="openEdit(row)" size="mini" type="primary">编辑</el-button>
|
<el-button @click="openEdit(row)" size="mini" type="primary">编辑</el-button>
|
||||||
<el-button @click="doDelete(row.id)" size="mini" type="danger">删除</el-button>
|
<el-button @click="doDelete(row.id)" size="mini" type="danger">删除</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -257,19 +272,27 @@ layout("/layouts/platform.html"){
|
|||||||
|
|
||||||
</guava>
|
</guava>
|
||||||
|
|
||||||
|
<rebind-form ref="rebindFormRef" @refresh="doSearch"></rebind-form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
<!--#include("/platform/sickHelp/medicalIns/rebindForm.js"){}#-->
|
||||||
|
|
||||||
const vue = new Vue({
|
const vue = new Vue({
|
||||||
el: '#app',
|
el: '#app',
|
||||||
mixins: [initTableMixins],
|
mixins: [initTableMixins],
|
||||||
|
components: {
|
||||||
|
"rebind-form": rebindForm
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
tableColumns: [
|
tableColumns: [
|
||||||
{prop: 'year', label: '年度', sortable: true, fixed: 'left'},
|
{prop: 'year', label: '年度', sortable: true, fixed: 'left'},
|
||||||
{prop: 'userName', label: '姓名', fixed: 'left'},
|
{prop: 'userName', label: '姓名', fixed: 'left'},
|
||||||
{prop: 'loginName', label: '工号', fixed: 'left', width: 120},
|
{prop: 'loginName', label: '工号', fixed: 'left', width: 120},
|
||||||
{prop: 'idcard', label: '身份证号', fixed: 'left', width: 200},
|
{prop: 'systemIdcard', label: '工号身份证号', fixed: 'left', width: 200},
|
||||||
|
{prop: 'medicalIdcard', label: '医保身份证号', width: 200},
|
||||||
|
{prop: 'relationRisk', label: '关联风险', width: 180},
|
||||||
{prop: 'unionName', label: '分工会', sortable: true, width: 150},
|
{prop: 'unionName', label: '分工会', sortable: true, width: 150},
|
||||||
{prop: 'unitName', label: '单位', sortable: true, width: 150},
|
{prop: 'unitName', label: '单位', sortable: true, width: 150},
|
||||||
|
|
||||||
@@ -305,7 +328,8 @@ layout("/layouts/platform.html"){
|
|||||||
isMember: null,
|
isMember: null,
|
||||||
unitName: '',
|
unitName: '',
|
||||||
isMeetRequirements: null,
|
isMeetRequirements: null,
|
||||||
isAccept: null
|
isAccept: null,
|
||||||
|
riskType: null
|
||||||
},
|
},
|
||||||
unions: [],
|
unions: [],
|
||||||
units: [],
|
units: [],
|
||||||
@@ -315,6 +339,10 @@ layout("/layouts/platform.html"){
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
onRebind(row) {
|
||||||
|
this.$refs.rebindFormRef.onOpen(row, 'summary')
|
||||||
|
},
|
||||||
|
|
||||||
async importData() {
|
async importData() {
|
||||||
if (this.excelTableData.length === 0) {
|
if (this.excelTableData.length === 0) {
|
||||||
this.notifyWarning('无数据')
|
this.notifyWarning('无数据')
|
||||||
|
|||||||
@@ -158,6 +158,16 @@ layout("/layouts/platform.html"){
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="search-item">
|
||||||
|
<div class="search-item-label">身份证号关联风险:</div>
|
||||||
|
<div class="search-item-option">
|
||||||
|
<el-select clearable style="width: 100%" v-model="pageForm.riskType">
|
||||||
|
<el-option label="全部风险数据" :value="3"></el-option>
|
||||||
|
<el-option label="工号身份证与医保身份证不一致" :value="1"></el-option>
|
||||||
|
<el-option label="同名且身份证缺失无法确认" :value="2"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="search-query">
|
<div class="search-query">
|
||||||
<el-button @click="doSearch" icon="el-icon-search" type="primary">搜索</el-button>
|
<el-button @click="doSearch" icon="el-icon-search" type="primary">搜索</el-button>
|
||||||
</div>
|
</div>
|
||||||
@@ -190,6 +200,7 @@ layout("/layouts/platform.html"){
|
|||||||
:prop="column.prop"
|
:prop="column.prop"
|
||||||
:sortable="column.sortable"
|
:sortable="column.sortable"
|
||||||
:width="column.width"
|
:width="column.width"
|
||||||
|
:formatter="column.prop === 'calc' ? formatCalc : undefined"
|
||||||
align="center"
|
align="center"
|
||||||
header-align="center"
|
header-align="center"
|
||||||
:fixed="column.fixed"
|
:fixed="column.fixed"
|
||||||
@@ -197,8 +208,15 @@ layout("/layouts/platform.html"){
|
|||||||
v-for="column in tableColumns"
|
v-for="column in tableColumns"
|
||||||
>
|
>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="操作" align="center" header-align="center" fixed="right" width="200">
|
<el-table-column label="关联风险" align="center" header-align="center" width="180">
|
||||||
|
<template scope="{row}">
|
||||||
|
<el-tag size="mini" type="danger" v-if="row.idcardMismatchRisk==1">身份证不一致</el-tag>
|
||||||
|
<el-tag size="mini" type="warning" v-if="row.duplicateMissingIdRisk==1">重名且身份证缺失</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" align="center" header-align="center" fixed="right" width="300">
|
||||||
<template scope="scope">
|
<template scope="scope">
|
||||||
|
<el-button @click="onRebind(scope.row)" size="mini" type="warning">重新关联人员</el-button>
|
||||||
<el-button @click="onEdit(scope.row)" size="mini" type="primary">编辑</el-button>
|
<el-button @click="onEdit(scope.row)" size="mini" type="primary">编辑</el-button>
|
||||||
<el-button @click="onDelete(scope.row.id)" size="mini" type="danger">删除</el-button>
|
<el-button @click="onDelete(scope.row.id)" size="mini" type="danger">删除</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -210,12 +228,14 @@ layout("/layouts/platform.html"){
|
|||||||
<ins-import ref="insImportRef" @refresh="doSearch"></ins-import>
|
<ins-import ref="insImportRef" @refresh="doSearch"></ins-import>
|
||||||
<ec-import ref="ecImportRef" @refresh="doSearch"></ec-import>
|
<ec-import ref="ecImportRef" @refresh="doSearch"></ec-import>
|
||||||
<ins-form ref="insFormRef" @refresh="doSearch"></ins-form>
|
<ins-form ref="insFormRef" @refresh="doSearch"></ins-form>
|
||||||
|
<rebind-form ref="rebindFormRef" @refresh="doSearch"></rebind-form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
<!--#include('insImport.js'){}#-->
|
<!--#include('insImport.js'){}#-->
|
||||||
<!--#include('insForm.js'){}#-->
|
<!--#include('insForm.js'){}#-->
|
||||||
<!--#include('ecImport.js'){}#-->
|
<!--#include('ecImport.js'){}#-->
|
||||||
|
<!--#include('rebindForm.js'){}#-->
|
||||||
|
|
||||||
const vue = new Vue({
|
const vue = new Vue({
|
||||||
el: '#app',
|
el: '#app',
|
||||||
@@ -223,14 +243,16 @@ layout("/layouts/platform.html"){
|
|||||||
components: {
|
components: {
|
||||||
"ins-import": insImport,
|
"ins-import": insImport,
|
||||||
"ec-import": ecImport,
|
"ec-import": ecImport,
|
||||||
"ins-form": insForm
|
"ins-form": insForm,
|
||||||
|
"rebind-form": rebindForm
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
tableColumns: [
|
tableColumns: [
|
||||||
{prop: 'userName', label: '姓名'},
|
{prop: 'userName', label: '姓名'},
|
||||||
{prop: 'loginName', label: '工号'},
|
{prop: 'loginName', label: '工号'},
|
||||||
{prop: 'idCard', label: '身份证号', width: 200},
|
{prop: 'systemIdcard', label: '工号身份证号', width: 200},
|
||||||
|
{prop: 'idCard', label: '医保身份证号', width: 200},
|
||||||
{prop: 'zhzl', label: '账户自理'},
|
{prop: 'zhzl', label: '账户自理'},
|
||||||
{prop: 'zhzf', label: '账户自付'},
|
{prop: 'zhzf', label: '账户自付'},
|
||||||
{prop: 'xjzl', label: '现金自理'},
|
{prop: 'xjzl', label: '现金自理'},
|
||||||
@@ -241,11 +263,16 @@ layout("/layouts/platform.html"){
|
|||||||
{prop: 'calc', label: '补助计算金额'},
|
{prop: 'calc', label: '补助计算金额'},
|
||||||
],
|
],
|
||||||
pageForm: {
|
pageForm: {
|
||||||
year: (new Date().getFullYear() - 1).toString()
|
year: (new Date().getFullYear() - 1).toString(),
|
||||||
|
riskType: null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
// 补助金额未写入时仅在列表显示 0,保留数据库原值供其他流程判断。
|
||||||
|
formatCalc(row, column, value) {
|
||||||
|
return value == null ? 0 : value
|
||||||
|
},
|
||||||
openEc() {
|
openEc() {
|
||||||
$.post('/platform/sickHelp/medicalIns/checkIns').then(res => {
|
$.post('/platform/sickHelp/medicalIns/checkIns').then(res => {
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
@@ -256,6 +283,10 @@ layout("/layouts/platform.html"){
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onRebind(row) {
|
||||||
|
this.$refs.rebindFormRef.onOpen(row, 'medical')
|
||||||
|
},
|
||||||
|
|
||||||
onEdit(row) {
|
onEdit(row) {
|
||||||
this.$refs.insFormRef.onOpen(row)
|
this.$refs.insFormRef.onOpen(row)
|
||||||
},
|
},
|
||||||
@@ -283,7 +314,7 @@ layout("/layouts/platform.html"){
|
|||||||
$.post(loc() + '/checkEc').then(res => {
|
$.post(loc() + '/checkEc').then(res => {
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
|
|
||||||
this.$confirm('计算补助金额将会自动过滤掉补助计算金额小于10000元的记录, 是否继续?', '提示', {
|
this.$confirm('计算补助金额将会自动过滤掉补助计算金额不超过10000元的记录, 是否继续?', '提示', {
|
||||||
confirmButtonText: '确定',
|
confirmButtonText: '确定',
|
||||||
cancelButtonText: '取消',
|
cancelButtonText: '取消',
|
||||||
type: 'warning'
|
type: 'warning'
|
||||||
|
|||||||
@@ -0,0 +1,205 @@
|
|||||||
|
const rebindForm = {
|
||||||
|
template: /*language=HTML*/ `
|
||||||
|
<el-dialog title="重新关联人员" :visible.sync="visible" :close-on-click-modal="false" width="850px">
|
||||||
|
<el-alert
|
||||||
|
title="该操作会同步修正医保数据和尚未进入审核流程的补助汇总,请填写正确人员本人的二次报销金额。"
|
||||||
|
type="warning"
|
||||||
|
:closable="false"
|
||||||
|
show-icon>
|
||||||
|
</el-alert>
|
||||||
|
|
||||||
|
<el-form class="mt20" v-loading="loading" :model="form" :rules="rules" ref="form" label-width="150px">
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="原姓名">
|
||||||
|
<el-input :value="source.userName" disabled></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="原工号">
|
||||||
|
<el-input :value="source.loginName" disabled></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-form-item label="医保身份证号">
|
||||||
|
<el-input :value="source.idCard" disabled></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="重新关联人员" prop="loginName">
|
||||||
|
<el-select
|
||||||
|
v-model="form.loginName"
|
||||||
|
style="width: 100%"
|
||||||
|
filterable
|
||||||
|
remote
|
||||||
|
reserve-keyword
|
||||||
|
clearable
|
||||||
|
:loading="userLoading"
|
||||||
|
:remote-method="remoteMethod"
|
||||||
|
placeholder="请输入姓名、工号或身份证号查询">
|
||||||
|
<el-option
|
||||||
|
v-for="item in userOptions"
|
||||||
|
:key="item.loginname"
|
||||||
|
:label="userLabel(item)"
|
||||||
|
:value="item.loginname">
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<div v-if="selectedUser" style="margin: -10px 0 18px 150px;color: #606266;line-height: 1.8;">
|
||||||
|
<div>姓名:{{selectedUser.username}},工号:{{selectedUser.loginname}}</div>
|
||||||
|
<div>出生日期:{{selectedUser.birthday || '未录入'}},单位:{{selectedUser.unitname || '未录入'}}</div>
|
||||||
|
<div>身份证号:{{maskIdCard(selectedUser.idcard)}}</div>
|
||||||
|
<div v-if="!selectedUser.idcard" style="color: #e6a23c;">
|
||||||
|
该人员未录入身份证号,请结合工号、出生日期和单位人工确认。
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="医保四项之和">
|
||||||
|
<el-input :value="source.sxzf" disabled></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="正确二次报销金额" prop="ecbx">
|
||||||
|
<el-input-number
|
||||||
|
v-model="form.ecbx"
|
||||||
|
:precision="2"
|
||||||
|
:min="0"
|
||||||
|
:controls="false"
|
||||||
|
:max="source.sxzf === null || source.sxzf === undefined ? 99999999.99 : Number(source.sxzf)"
|
||||||
|
style="width: 100%">
|
||||||
|
</el-input-number>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-alert
|
||||||
|
v-if="selfPayMoney !== null"
|
||||||
|
:title="resultMessage"
|
||||||
|
:type="isEligible ? 'warning' : 'success'"
|
||||||
|
:closable="false"
|
||||||
|
show-icon>
|
||||||
|
</el-alert>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="visible = false">取消</el-button>
|
||||||
|
<el-button type="primary" :loading="saving" @click="onSave">确认重新关联</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
`,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
visible: false,
|
||||||
|
loading: false,
|
||||||
|
saving: false,
|
||||||
|
userLoading: false,
|
||||||
|
source: {},
|
||||||
|
form: {
|
||||||
|
medicalInsId: null,
|
||||||
|
loginName: null,
|
||||||
|
ecbx: null
|
||||||
|
},
|
||||||
|
userOptions: [],
|
||||||
|
rules: {
|
||||||
|
loginName: [{required: true, message: '请选择需要重新关联的人员', trigger: 'change'}],
|
||||||
|
ecbx: [{required: true, message: '请输入正确人员的学校二次报销金额', trigger: 'change'}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
selectedUser() {
|
||||||
|
return this.userOptions.find(v => v.loginname === this.form.loginName)
|
||||||
|
},
|
||||||
|
selfPayMoney() {
|
||||||
|
if (this.source.sxzf === null || this.source.sxzf === undefined
|
||||||
|
|| this.form.ecbx === null || this.form.ecbx === undefined) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return Number(this.source.sxzf) - Number(this.form.ecbx)
|
||||||
|
},
|
||||||
|
isEligible() {
|
||||||
|
return this.selfPayMoney !== null && this.selfPayMoney > 10000
|
||||||
|
},
|
||||||
|
resultMessage() {
|
||||||
|
const money = this.selfPayMoney.toFixed(2)
|
||||||
|
return this.isEligible
|
||||||
|
? '扣减二次报销后的个人支付金额为 ' + money + ' 元,系统将按正确人员重新计算补助汇总。'
|
||||||
|
: '扣减二次报销后的个人支付金额为 ' + money + ' 元,不超过10000元,系统将删除原错误补助汇总。'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async onOpen(row, sourceType) {
|
||||||
|
this.visible = true
|
||||||
|
this.loading = true
|
||||||
|
this.source = {}
|
||||||
|
this.userOptions = []
|
||||||
|
this.form = {medicalInsId: null, loginName: null, ecbx: null}
|
||||||
|
this.$nextTick(() => this.$refs.form && this.$refs.form.clearValidate())
|
||||||
|
|
||||||
|
const params = sourceType === 'medical'
|
||||||
|
? {medicalInsId: row.id}
|
||||||
|
: {year: row.year, loginName: row.loginName}
|
||||||
|
const resp = await $.post('/platform/sickHelp/medicalIns/findRebindSource', params)
|
||||||
|
this.loading = false
|
||||||
|
if (resp.code !== 0) {
|
||||||
|
this.visible = false
|
||||||
|
this.$message.error(resp.msg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.source = resp.data
|
||||||
|
this.form.medicalInsId = resp.data.id
|
||||||
|
this.form.ecbx = resp.data.ecbx === null || resp.data.ecbx === undefined || resp.data.ecbx === ''
|
||||||
|
? 0 : Number(resp.data.ecbx)
|
||||||
|
},
|
||||||
|
async remoteMethod(query) {
|
||||||
|
if (!query) {
|
||||||
|
this.userOptions = []
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.userLoading = true
|
||||||
|
const resp = await $.post('/platform/sickHelp/medicalIns/queryRebindUser', {query})
|
||||||
|
this.userLoading = false
|
||||||
|
if (resp.code === 0) {
|
||||||
|
this.userOptions = resp.data || []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
userLabel(user) {
|
||||||
|
return user.username + '(' + user.loginname + ') '
|
||||||
|
+ (user.birthday || '生日未录入') + ' '
|
||||||
|
+ (user.unitname || '单位未录入') + ' '
|
||||||
|
+ this.maskIdCard(user.idcard)
|
||||||
|
},
|
||||||
|
maskIdCard(idCard) {
|
||||||
|
if (!idCard) return '身份证未录入'
|
||||||
|
const value = idCard.toString().trim()
|
||||||
|
if (value.length <= 10) return value
|
||||||
|
return value.substring(0, 6) + '********' + value.substring(value.length - 4)
|
||||||
|
},
|
||||||
|
onSave() {
|
||||||
|
this.$refs.form.validate(async valid => {
|
||||||
|
if (!valid) return
|
||||||
|
const tip = this.isEligible
|
||||||
|
? '确定重新关联人员并按正确人员重新计算补助汇总吗?'
|
||||||
|
: '修正后不超过10000元,系统会删除原错误补助汇总,确定继续吗?'
|
||||||
|
await this.$confirm(tip, '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
|
this.saving = true
|
||||||
|
const resp = await $.post('/platform/sickHelp/medicalIns/rebindUser', this.form)
|
||||||
|
this.saving = false
|
||||||
|
if (resp.code === 0) {
|
||||||
|
this.visible = false
|
||||||
|
this.$message.success(resp.msg)
|
||||||
|
this.$emit('refresh')
|
||||||
|
} else {
|
||||||
|
this.$message.error(resp.msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
nutz.application.name=union_hhu
|
nutz.application.name=union_hhu
|
||||||
server.port=8080
|
server.port=80
|
||||||
server.host=0.0.0.0
|
server.host=0.0.0.0
|
||||||
jetty.contextPath=/
|
jetty.contextPath=/
|
||||||
jetty.threadpool.idleTimeout=100000
|
jetty.threadpool.idleTimeout=100000
|
||||||
@@ -11,13 +11,13 @@ jetty.page.500=/error/500.html
|
|||||||
#结合ftp使用,或用nginx代理ftp路径
|
#结合ftp使用,或用nginx代理ftp路径
|
||||||
jetty.staticPath=D://filesu
|
jetty.staticPath=D://filesu
|
||||||
#开发模式静态资源
|
#开发模式静态资源
|
||||||
jetty.staticPathLocal=D:/gsProject/zhgh_hhu/src/main/resources/static
|
jetty.staticPathLocal=E:/projects/zhgh_hhu/src/main/resources/static
|
||||||
nutz.mvc.ignore=^(.+[.])(jsp|png|gif|jpg|js|css|jpeg|html|mp3|mp4|ico|svg)$
|
nutz.mvc.ignore=^(.+[.])(jsp|png|gif|jpg|js|css|jpeg|html|mp3|mp4|ico|svg)$
|
||||||
nutz.mvc.exclusions=/favicon/*,/assets/*,/druid/*,/upload/*
|
nutz.mvc.exclusions=/favicon/*,/assets/*,/druid/*,/upload/*
|
||||||
ftp.enabled=true
|
ftp.enabled=true
|
||||||
ftp.host=192.168.21.203
|
ftp.host=192.168.21.204
|
||||||
ftp.port=9090
|
ftp.port=21
|
||||||
ftp.username=ftpuser
|
ftp.username=ftp_user
|
||||||
ftp.password=123456
|
ftp.password=123456
|
||||||
ftp.timeout=100
|
ftp.timeout=100
|
||||||
redis.host=127.0.0.1
|
redis.host=127.0.0.1
|
||||||
@@ -34,9 +34,9 @@ redis.pool.minIdle=50
|
|||||||
#redis.mode=cluster
|
#redis.mode=cluster
|
||||||
redis.mode=normal
|
redis.mode=normal
|
||||||
#redis.nodes=192.168.6.31:6377,192.168.6.31:6378,192.168.6.28:6377,192.168.6.28:6378,192.168.6.34:6377,192.168.6.34:6378
|
#redis.nodes=192.168.6.31:6377,192.168.6.31:6378,192.168.6.28:6377,192.168.6.28:6378,192.168.6.34:6377,192.168.6.34:6378
|
||||||
jdbc.url=jdbc:mysql://192.168.21.215:3306/union_hhu?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
|
jdbc.url=jdbc:mysql://192.168.21.212:3306/zhgh_hhu?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
|
||||||
jdbc.username=root
|
jdbc.username=root
|
||||||
jdbc.password=root
|
jdbc.password=123456
|
||||||
jdbc.validationQuery=select 1
|
jdbc.validationQuery=select 1
|
||||||
jdbc.maxActive=900
|
jdbc.maxActive=900
|
||||||
jdbc.testWhileIdle=true
|
jdbc.testWhileIdle=true
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -92,6 +92,7 @@ layout("/layouts/platform.html"){
|
|||||||
<!-- <el-input v-model="row.userName" maxlength="10" size="small"></el-input>-->
|
<!-- <el-input v-model="row.userName" maxlength="10" size="small"></el-input>-->
|
||||||
<!-- </template>-->
|
<!-- </template>-->
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column label="身份证号" prop="idcard" width="200"></el-table-column>
|
||||||
<el-table-column label="单位" prop="unitName" show-overflow-tooltip width="150">
|
<el-table-column label="单位" prop="unitName" show-overflow-tooltip width="150">
|
||||||
<!-- <template scope="{row}">-->
|
<!-- <template scope="{row}">-->
|
||||||
<!-- <el-input v-model="row.unitName" maxlength="10" size="small"></el-input>-->
|
<!-- <el-input v-model="row.unitName" maxlength="10" size="small"></el-input>-->
|
||||||
@@ -239,8 +240,8 @@ layout("/layouts/platform.html"){
|
|||||||
async doImport() {
|
async doImport() {
|
||||||
const hasErrorData = this.tableData.some(v => !v.valid)
|
const hasErrorData = this.tableData.some(v => !v.valid)
|
||||||
if (hasErrorData) {
|
if (hasErrorData) {
|
||||||
// this.notifyWarning('数据校验不合格,无法导入')
|
this.notifyWarning('数据校验不合格,无法导入')
|
||||||
// return
|
return
|
||||||
}
|
}
|
||||||
const resp = await $.post('/platform/sickHelp/import/doImport', {sickHelps: JSON.stringify(this.tableData)})
|
const resp = await $.post('/platform/sickHelp/import/doImport', {sickHelps: JSON.stringify(this.tableData)})
|
||||||
if (resp.code === 0) {
|
if (resp.code === 0) {
|
||||||
@@ -248,6 +249,8 @@ layout("/layouts/platform.html"){
|
|||||||
this.globalYear = null
|
this.globalYear = null
|
||||||
this.notifySuccess(resp.msg)
|
this.notifySuccess(resp.msg)
|
||||||
this.$refs.excelUpload.clearFiles()
|
this.$refs.excelUpload.clearFiles()
|
||||||
|
} else {
|
||||||
|
this.notifyWarning(resp.msg)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
globalYearChange(val) {
|
globalYearChange(val) {
|
||||||
|
|||||||
@@ -76,41 +76,46 @@ layout("/layouts/platform.html"){
|
|||||||
<vi-title2 title="填写补助数据"></vi-title2>
|
<vi-title2 title="填写补助数据"></vi-title2>
|
||||||
|
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :lg="8" :xs="24">
|
<el-col :lg="6" :xs="24">
|
||||||
<el-form-item label="工号" prop="loginName">
|
<el-form-item label="工号" prop="loginName">
|
||||||
<el-select :disabled="formDisabled"
|
<el-select :disabled="formDisabled"
|
||||||
:remote-method="searchUser"
|
:remote-method="searchUser"
|
||||||
@change="userChange"
|
@change="userChange"
|
||||||
clearable
|
clearable
|
||||||
filterable
|
filterable
|
||||||
placeholder="输入工号或者姓名查询"
|
placeholder="输入工号、姓名或身份证号查询"
|
||||||
remote
|
remote
|
||||||
reserve-keyword
|
reserve-keyword
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
v-model="formData.loginName">
|
v-model="formData.loginName">
|
||||||
<el-option
|
<el-option
|
||||||
:key="item.loginName"
|
:key="item.loginName"
|
||||||
:label="item.userName+'('+item.loginName+')'"
|
:label="item.userName+'('+item.loginName+') '+(item.effectiveIdcard || '')"
|
||||||
:value="item.loginName"
|
:value="item.loginName"
|
||||||
v-for="item in userList"
|
v-for="item in userList"
|
||||||
></el-option>
|
></el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :lg="8" :xs="24">
|
<el-col :lg="6" :xs="24">
|
||||||
<el-form-item label="姓名" prop="userName">
|
<el-form-item label="姓名" prop="userName">
|
||||||
<el-input :disabled="!this.formData.id"
|
<el-input :disabled="!this.formData.id"
|
||||||
placeholder="输入工号之后自动显示"
|
placeholder="输入工号之后自动显示"
|
||||||
v-model="formData.userName"></el-input>
|
v-model="formData.userName"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :lg="8" :xs="24">
|
<el-col :lg="6" :xs="24">
|
||||||
|
<el-form-item label="身份证号" prop="idcard">
|
||||||
|
<el-input placeholder="选择工号之后自动显示" readonly
|
||||||
|
v-model="formData.idcard"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :lg="6" :xs="24">
|
||||||
<el-form-item label="单位" prop="unitName">
|
<el-form-item label="单位" prop="unitName">
|
||||||
<el-input placeholder="输入工号之后自动显示"
|
<el-input placeholder="输入工号之后自动显示"
|
||||||
v-model="formData.unitName"></el-input>
|
v-model="formData.unitName"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-col>
|
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
@@ -337,6 +342,7 @@ layout("/layouts/platform.html"){
|
|||||||
this.$set(this.formData, 'subsidyDesc', resp_data.subsidyDesc)
|
this.$set(this.formData, 'subsidyDesc', resp_data.subsidyDesc)
|
||||||
this.$set(this.formData, 'subsidyMoney', resp_data.subsidyMoney)
|
this.$set(this.formData, 'subsidyMoney', resp_data.subsidyMoney)
|
||||||
this.$set(this.formData, 'unitName', resp_data.unitName)
|
this.$set(this.formData, 'unitName', resp_data.unitName)
|
||||||
|
this.$set(this.formData, 'idcard', resp_data.idcard)
|
||||||
this.notifySuccess(resp_data.validMsg.join('、'))
|
this.notifySuccess(resp_data.validMsg.join('、'))
|
||||||
} else {
|
} else {
|
||||||
this.notifyWarning(resp_data.validMsg.join('、'))
|
this.notifyWarning(resp_data.validMsg.join('、'))
|
||||||
@@ -376,6 +382,7 @@ layout("/layouts/platform.html"){
|
|||||||
this.$set(this.formData, 'userName', user.userName)
|
this.$set(this.formData, 'userName', user.userName)
|
||||||
this.$set(this.formData, 'unitName', user.unitName)
|
this.$set(this.formData, 'unitName', user.unitName)
|
||||||
this.$set(this.formData, 'loginName', user.loginName)
|
this.$set(this.formData, 'loginName', user.loginName)
|
||||||
|
this.$set(this.formData, 'idcard', user.effectiveIdcard)
|
||||||
await this.getHistoryApplyRecords(user.loginName, this.formData.year)
|
await this.getHistoryApplyRecords(user.loginName, this.formData.year)
|
||||||
},
|
},
|
||||||
async getHistoryApplyRecords(loginName, year) {
|
async getHistoryApplyRecords(loginName, year) {
|
||||||
@@ -401,6 +408,8 @@ layout("/layouts/platform.html"){
|
|||||||
await this.searchUser(loginName)
|
await this.searchUser(loginName)
|
||||||
resp.data.year = resp.data.year.toString()
|
resp.data.year = resp.data.year.toString()
|
||||||
this.formData = resp.data
|
this.formData = resp.data
|
||||||
|
const user = this.userList.find(v => v.loginName === loginName)
|
||||||
|
this.$set(this.formData, 'idcard', user ? user.effectiveIdcard : null)
|
||||||
await this.getHistoryApplyRecords(loginName, this.formData.year)
|
await this.getHistoryApplyRecords(loginName, this.formData.year)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -412,4 +421,4 @@ layout("/layouts/platform.html"){
|
|||||||
|
|
||||||
<!--#
|
<!--#
|
||||||
}
|
}
|
||||||
#-->
|
#-->
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ layout("/layouts/platform.html"){
|
|||||||
<div class="search-item">
|
<div class="search-item">
|
||||||
<div class="search-item-label">补助人:</div>
|
<div class="search-item-label">补助人:</div>
|
||||||
<div class="search-item-option">
|
<div class="search-item-option">
|
||||||
<el-input clearable maxlength="10" placeholder="可输入补助人的工号或姓名进行查询"
|
<el-input clearable maxlength="50" placeholder="可输入补助人的工号、姓名或身份证号进行查询"
|
||||||
v-model.trim="pageForm.subsidyUser"></el-input>
|
v-model.trim="pageForm.subsidyUser"></el-input>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -269,6 +269,7 @@ layout("/layouts/platform.html"){
|
|||||||
{prop: 'year', label: '年度', sortable: true, fixed: 'left'},
|
{prop: 'year', label: '年度', sortable: true, fixed: 'left'},
|
||||||
{prop: 'userName', label: '姓名', fixed: 'left'},
|
{prop: 'userName', label: '姓名', fixed: 'left'},
|
||||||
{prop: 'loginName', label: '工号', fixed: 'left', width: 120},
|
{prop: 'loginName', label: '工号', fixed: 'left', width: 120},
|
||||||
|
{prop: 'idcard', label: '身份证号', fixed: 'left', width: 200},
|
||||||
{prop: 'unionName', label: '分工会', sortable: true, width: 150},
|
{prop: 'unionName', label: '分工会', sortable: true, width: 150},
|
||||||
{prop: 'unitName', label: '单位', sortable: true, width: 150},
|
{prop: 'unitName', label: '单位', sortable: true, width: 150},
|
||||||
|
|
||||||
|
|||||||
@@ -190,6 +190,7 @@ layout("/layouts/platform.html"){
|
|||||||
:prop="column.prop"
|
:prop="column.prop"
|
||||||
:sortable="column.sortable"
|
:sortable="column.sortable"
|
||||||
:width="column.width"
|
:width="column.width"
|
||||||
|
:formatter="column.prop === 'calc' ? formatCalc : undefined"
|
||||||
align="center"
|
align="center"
|
||||||
header-align="center"
|
header-align="center"
|
||||||
:fixed="column.fixed"
|
:fixed="column.fixed"
|
||||||
@@ -246,6 +247,10 @@ layout("/layouts/platform.html"){
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
// 补助金额未写入时仅在列表显示 0,保留数据库原值供其他流程判断。
|
||||||
|
formatCalc(row, column, value) {
|
||||||
|
return value == null ? 0 : value
|
||||||
|
},
|
||||||
openEc() {
|
openEc() {
|
||||||
$.post('/platform/sickHelp/medicalIns/checkIns').then(res => {
|
$.post('/platform/sickHelp/medicalIns/checkIns').then(res => {
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
@@ -283,7 +288,7 @@ layout("/layouts/platform.html"){
|
|||||||
$.post(loc() + '/checkEc').then(res => {
|
$.post(loc() + '/checkEc').then(res => {
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
|
|
||||||
this.$confirm('计算补助金额将会自动过滤掉补助计算金额小于10000元的记录, 是否继续?', '提示', {
|
this.$confirm('计算补助金额将会自动过滤掉补助计算金额不超过10000元的记录, 是否继续?', '提示', {
|
||||||
confirmButtonText: '确定',
|
confirmButtonText: '确定',
|
||||||
cancelButtonText: '取消',
|
cancelButtonText: '取消',
|
||||||
type: 'warning'
|
type: 'warning'
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ layout("/layouts/platform.html"){
|
|||||||
{prop: 'userName', label: '姓名'},
|
{prop: 'userName', label: '姓名'},
|
||||||
{prop: 'loginName', label: '工号'},
|
{prop: 'loginName', label: '工号'},
|
||||||
{prop: 'sex', label: '性别'},
|
{prop: 'sex', label: '性别'},
|
||||||
|
{prop: 'idcard', label: '身份证号'},
|
||||||
{prop: 'unitName', label: '单位'},
|
{prop: 'unitName', label: '单位'},
|
||||||
{prop: 'userState', label: '状态'},
|
{prop: 'userState', label: '状态'},
|
||||||
{prop: 'mutualGoldStartTime', label: '入会时间'},
|
{prop: 'mutualGoldStartTime', label: '入会时间'},
|
||||||
@@ -163,4 +164,4 @@ layout("/layouts/platform.html"){
|
|||||||
|
|
||||||
<!--#
|
<!--#
|
||||||
}
|
}
|
||||||
#-->
|
#-->
|
||||||
|
|||||||
@@ -24,9 +24,9 @@ layout("/layouts/platform.html"){
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="search-item">
|
<div class="search-item">
|
||||||
<div class="search-item-label">工号姓名:</div>
|
<div class="search-item-label">会员信息:</div>
|
||||||
<div class="search-item-option">
|
<div class="search-item-option">
|
||||||
<el-input clearable maxlength="10" placeholder="可输入会员的工号或姓名进行查询"
|
<el-input clearable maxlength="50" placeholder="可输入会员的工号、姓名或身份证号进行查询"
|
||||||
v-model.trim="pageForm.keyWord"></el-input>
|
v-model.trim="pageForm.keyWord"></el-input>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -147,6 +147,10 @@ layout("/layouts/platform.html"){
|
|||||||
<el-radio-button label="女"></el-radio-button>
|
<el-radio-button label="女"></el-radio-button>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="身份证号" prop="idcard">
|
||||||
|
<el-input @input="$refs.form.validateField('idcard')" maxlength="18"
|
||||||
|
v-model.trim="formData.idcard"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item label="单位" prop="unitId">
|
<el-form-item label="单位" prop="unitId">
|
||||||
<el-select clearable filterable style="width: 100%"
|
<el-select clearable filterable style="width: 100%"
|
||||||
@@ -194,6 +198,20 @@ layout("/layouts/platform.html"){
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
|
const idcardPattern = /^[1-9]\d{5}(18|19|20|(3\d))\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/
|
||||||
|
|
||||||
|
const validateIdcard = (rule, value, callback) => {
|
||||||
|
if (!value) {
|
||||||
|
callback()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!idcardPattern.test(value)) {
|
||||||
|
callback(new Error('请输入正确的身份证号'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
|
||||||
const vue = new Vue({
|
const vue = new Vue({
|
||||||
el: '#app',
|
el: '#app',
|
||||||
mixins: [initTableMixins],
|
mixins: [initTableMixins],
|
||||||
@@ -215,6 +233,7 @@ layout("/layouts/platform.html"){
|
|||||||
loginName: [{required: true, message: '请输入工号', trigger: ['change', 'blur']}],
|
loginName: [{required: true, message: '请输入工号', trigger: ['change', 'blur']}],
|
||||||
userName: [{required: true, message: '请输入姓名', trigger: ['change', 'blur']}],
|
userName: [{required: true, message: '请输入姓名', trigger: ['change', 'blur']}],
|
||||||
// sex: [{required: true, message: '请选择性别', trigger: ['change', 'blur']}],
|
// sex: [{required: true, message: '请选择性别', trigger: ['change', 'blur']}],
|
||||||
|
idcard: [{validator: validateIdcard, trigger: ['change', 'blur']}],
|
||||||
unitName: [{required: false, message: '请输入单位', trigger: ['change', 'blur']}],
|
unitName: [{required: false, message: '请输入单位', trigger: ['change', 'blur']}],
|
||||||
unitId: [{required: true, message: '请选择单位', trigger: ['change', 'blur']}],
|
unitId: [{required: true, message: '请选择单位', trigger: ['change', 'blur']}],
|
||||||
isMember: [{required: true, message: '请选择是否会员', trigger: ['change', 'blur']}],
|
isMember: [{required: true, message: '请选择是否会员', trigger: ['change', 'blur']}],
|
||||||
@@ -230,6 +249,7 @@ layout("/layouts/platform.html"){
|
|||||||
userName: null,
|
userName: null,
|
||||||
loginName: null,
|
loginName: null,
|
||||||
sex: null,
|
sex: null,
|
||||||
|
idcard: null,
|
||||||
unitName: null,
|
unitName: null,
|
||||||
unitId: null,
|
unitId: null,
|
||||||
isMember: null,
|
isMember: null,
|
||||||
@@ -302,6 +322,12 @@ layout("/layouts/platform.html"){
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
async doHandle() {
|
async doHandle() {
|
||||||
|
const idcard = (this.formData.idcard || '').trim()
|
||||||
|
if (idcard && !idcardPattern.test(idcard)) {
|
||||||
|
this.notifyWarning('请输入正确的身份证号')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.$set(this.formData, 'idcard', idcard || null)
|
||||||
const valid = await this.$refs.form.validate()
|
const valid = await this.$refs.form.validate()
|
||||||
if (!valid) return
|
if (!valid) return
|
||||||
const url = this.formData.id ? '/doEdit' : '/doAdd'
|
const url = this.formData.id ? '/doEdit' : '/doAdd'
|
||||||
|
|||||||
Reference in New Issue
Block a user