feat:场地预约-面向分工会场地,1、支持协会负责人进行预约,2、预约前签字
This commit is contained in:
@@ -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 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.annontation.ViReturn;
|
||||
import io.v.nutz.base.utils.PageUtil;
|
||||
@@ -62,16 +66,34 @@ public class SiteManageController {
|
||||
return siteInfoService.listPage(pageNumber, pageSize, cnd);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增场地,面向分工会时要求配置预约前承诺书。
|
||||
* @param activitySiteInfo 场地信息及承诺书标题、正文
|
||||
* @return 保存结果或配置校验错误
|
||||
*/
|
||||
@At
|
||||
@ViReturn
|
||||
public Object doAdd(@Param("data") ActivitySiteInfo activitySiteInfo) {
|
||||
String error = prepareCommitment(activitySiteInfo);
|
||||
if (error != null) {
|
||||
return Result.error(error);
|
||||
}
|
||||
siteInfoService.insert(activitySiteInfo);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存场地及承诺书配置,已签署申请继续保留原正文快照。
|
||||
* @param activitySiteInfo 修改后的场地信息
|
||||
* @return 保存结果或配置校验错误
|
||||
*/
|
||||
@At
|
||||
@ViReturn
|
||||
public Object doEdit(@Param("data") ActivitySiteInfo activitySiteInfo) {
|
||||
String error = prepareCommitment(activitySiteInfo);
|
||||
if (error != null) {
|
||||
return Result.error(error);
|
||||
}
|
||||
siteInfoService.update(activitySiteInfo);
|
||||
return null;
|
||||
}
|
||||
@@ -88,4 +110,25 @@ public class SiteManageController {
|
||||
public Object findOneSite(String 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.Ok;
|
||||
import org.nutz.mvc.annotation.Param;
|
||||
import org.nutz.trans.Trans;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
@@ -161,9 +162,25 @@ public class SiteReserveController {
|
||||
return fetch.getReserve_state() > stateCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建预约,面向分工会的场地必须先完成本次承诺书签署。
|
||||
* @param activitySiteReserve 预约表单
|
||||
* @param days 本次申请的预约日期
|
||||
* @param commitmentId 当前用户签署的场地承诺书凭证
|
||||
* @return 预约结果或资格、签署校验失败原因
|
||||
*/
|
||||
@At
|
||||
@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(
|
||||
activitySiteReserve.getSite_id(),
|
||||
@@ -183,22 +200,31 @@ public class SiteReserveController {
|
||||
Integer stateCode = activityCommonService.findStartStateCodeByModuleName(moduleName);
|
||||
|
||||
if (stateCode == null) {
|
||||
return null;
|
||||
return Result.error("场地尚未配置预约审核流程");
|
||||
}
|
||||
|
||||
for (String d : days) {
|
||||
activitySiteReserve.setReserve_day(d);
|
||||
//查询这个人是否已经预约这个时间段
|
||||
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) {
|
||||
return Result.error("您已预约该时间段!");
|
||||
}
|
||||
try {
|
||||
// 同一申请的多天预约和签署凭证一起提交,失败时不消耗签署凭证。
|
||||
Trans.exec(() -> {
|
||||
siteReserveService.bindCommitment(activitySiteReserve.getSite_id(), commitmentId, r);
|
||||
for (String d : days) {
|
||||
activitySiteReserve.setReserve_day(d);
|
||||
//查询这个人是否已经预约这个时间段
|
||||
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_person_id(ShiroUtil.getPrincipalProperty("id").toString());
|
||||
siteReserveService.insert(activitySiteReserve);
|
||||
activitySiteReserve.setReserve_state(stateCode);
|
||||
activitySiteReserve.setReserve_person_id(ShiroUtil.getPrincipalProperty("id").toString());
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询预约详情及本次申请签署的承诺书快照。
|
||||
* @param id 预约记录ID
|
||||
* @return 预约详情、审核记录和承诺书签署信息
|
||||
*/
|
||||
@At
|
||||
@ViReturn
|
||||
public Object findOne(String id) {
|
||||
ActivitySiteReserve fetch = siteReserveService.fetch(id);
|
||||
if (fetch == null) {
|
||||
return Result.error("预约记录不存在");
|
||||
}
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
asr.*,
|
||||
@@ -386,6 +420,7 @@ public class SiteReserveController {
|
||||
i.setCondition(cnd);
|
||||
List<Record> list1 = siteReserveService.list(i);
|
||||
record.set("site_info", list1);
|
||||
record.set("commitment", siteReserveService.findCommitment(fetch.getSqid()));
|
||||
|
||||
return record;
|
||||
}
|
||||
@@ -455,6 +490,13 @@ public class SiteReserveController {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定日期的场地预约时段,支持以24:00作为营业结束时间。
|
||||
*
|
||||
* @param siteId 场地ID
|
||||
* @param day 预约日期,格式为yyyy-MM-dd
|
||||
* @return 预约时段及不可预约状态,保留配置的结束时间用于匹配预约记录
|
||||
*/
|
||||
@At
|
||||
@ViReturn
|
||||
public Object getTimesByDay(String siteId, String day) {
|
||||
@@ -480,16 +522,19 @@ public class SiteReserveController {
|
||||
} else {
|
||||
// 构造起始时间
|
||||
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;
|
||||
while (DateUtil.compare(current, end) < 0) { // 当前时间 <= 结束时间
|
||||
DateTime next = DateUtil.offsetSecond(current, (int) (timeUnit * 3600));
|
||||
// 如果超过结束时间,则使用 endTime
|
||||
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;
|
||||
} 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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
private String introduce;
|
||||
|
||||
@Column
|
||||
@Comment("预约前承诺书标题")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 200)
|
||||
private String commitmentTitle;
|
||||
|
||||
@Column
|
||||
@Comment("预约前承诺书正文")
|
||||
@ColDefine(type = ColType.TEXT)
|
||||
private String commitmentContent;
|
||||
|
||||
@Column
|
||||
@Comment("可容纳人数")
|
||||
@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.ActivitySiteCommitment;
|
||||
import io.v.nutz.base.service.BaseService;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface SiteReserveService extends BaseService<ActivitySiteReserve> {
|
||||
|
||||
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.ActivitySiteCommitment;
|
||||
import io.v.nutz.activity.models.ActivitySiteReserve;
|
||||
import io.v.nutz.activity.models.ActivityType;
|
||||
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.modules.models.AuditState;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Chain;
|
||||
import org.nutz.dao.Dao;
|
||||
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.Map;
|
||||
@@ -28,6 +41,138 @@ public class SiteReserveServiceImpl extends BaseServiceImpl<ActivitySiteReserve>
|
||||
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
|
||||
public Map<Boolean, String> validateSiteReserve(String siteId, String day, String startTime, String endTime, Integer joinCount) {
|
||||
ActivitySiteInfo siteInfo = dao().fetch(ActivitySiteInfo.class, siteId);
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.nutz.lang.util.NutMap;
|
||||
import org.nutz.mvc.annotation.At;
|
||||
import org.nutz.mvc.annotation.Ok;
|
||||
import org.nutz.mvc.annotation.Param;
|
||||
import org.nutz.trans.Trans;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -234,10 +235,26 @@ public class SiteInfoMobileController {
|
||||
return hours;
|
||||
}
|
||||
|
||||
/**
|
||||
* 手机端预约入口,使用与电脑端相同的预约资格和承诺书校验。
|
||||
* @param siteId 场地ID
|
||||
* @param times 预约日期及时段JSON
|
||||
* @param message 预约事由
|
||||
* @param joinUser 参与人员
|
||||
* @param joinCount 预约人数
|
||||
* @param commitmentId 当前用户的本次承诺书签署凭证
|
||||
* @return 预约结果或签署校验失败原因
|
||||
*/
|
||||
@At("/reserveDo")
|
||||
@Ok("json:full")
|
||||
@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);
|
||||
Integer stateCode = activityCommonService.findStartStateCodeByModuleName(moduleName);
|
||||
@@ -272,7 +289,14 @@ public class SiteInfoMobileController {
|
||||
}
|
||||
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());
|
||||
}
|
||||
|
||||
//发送微信提醒审核人
|
||||
//查询状态码对应的审核人
|
||||
|
||||
@@ -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 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-form label-width="130px" style="padding: 20px 0">
|
||||
|
||||
@@ -166,6 +177,9 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
commitmentTime(value) {
|
||||
return moment(value).format('YYYY-MM-DD HH:mm:ss')
|
||||
},
|
||||
hasPane(name) {
|
||||
if (!this.handle) {
|
||||
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>
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<site-commitment ref="commitmentRecord"></site-commitment>
|
||||
|
||||
<m-page-loading v-if="mLoading"></m-page-loading>
|
||||
|
||||
@@ -357,6 +358,8 @@ layout("/mobile/platform.html"){
|
||||
</van-cell>
|
||||
</van-cell-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 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>
|
||||
@@ -399,6 +402,9 @@ layout("/mobile/platform.html"){
|
||||
const vue = new Vue({
|
||||
el: '#app',
|
||||
mixins: [mobileMixins],
|
||||
components: {
|
||||
'site-commitment': httpVueLoader('/components/activity/site/SiteCommitment.vue'),
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
desc: '',
|
||||
@@ -546,4 +552,4 @@ layout("/mobile/platform.html"){
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
#-->
|
||||
|
||||
@@ -291,8 +291,8 @@ layout("/mobile/platform.html"){
|
||||
this.show = true
|
||||
},
|
||||
openView(o) {
|
||||
if(o.reservetarget === 1 && "${@shiro.hasAnyRoles('gh01', 'H04')}" === 'false' && "${@shiro.hasRole('sysadmin')}" === 'false') {
|
||||
vant.Toast('该场地只能分工会主席预约')
|
||||
if(o.reservetarget === 1 && "${@shiro.hasAnyRoles('gh01', 'H04', 'H02')}" === 'false' && "${@shiro.hasRole('sysadmin')}" === 'false') {
|
||||
vant.Toast('该场地仅限分工会预约人员或协会负责人预约')
|
||||
return
|
||||
}
|
||||
this.siteId = o.id
|
||||
|
||||
@@ -155,12 +155,16 @@ layout("/mobile/platform.html"){
|
||||
right: 0;
|
||||
top: 10px;
|
||||
}
|
||||
.reservation-commitment-content img {
|
||||
max-width: 100%;
|
||||
}
|
||||
.van-cell__title {
|
||||
font-size: 16px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<site-commitment ref="commitmentRecord"></site-commitment>
|
||||
|
||||
<m-page-loading v-if="mLoading"></m-page-loading>
|
||||
|
||||
@@ -217,7 +221,7 @@ layout("/mobile/platform.html"){
|
||||
@load="onLoad">
|
||||
|
||||
<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="width: 74%">
|
||||
@@ -274,6 +278,12 @@ layout("/mobile/platform.html"){
|
||||
<van-button @click.stop="backOption(o)" class="cus_button"
|
||||
type="primary" size="small" color="#1867b0"
|
||||
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>
|
||||
@@ -288,6 +298,32 @@ layout("/mobile/platform.html"){
|
||||
</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-field
|
||||
v-model="option"
|
||||
@@ -308,6 +344,9 @@ layout("/mobile/platform.html"){
|
||||
const vue = new Vue({
|
||||
el: '#app',
|
||||
mixins: [mobileMixins],
|
||||
components: {
|
||||
'site-commitment': httpVueLoader('/components/activity/site/SiteCommitment.vue'),
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
time: new Date(),
|
||||
@@ -317,9 +356,32 @@ layout("/mobile/platform.html"){
|
||||
typeList: [],
|
||||
timeList: [],
|
||||
info: {},
|
||||
detailShow: false,
|
||||
detailLoading: false,
|
||||
detailError: '',
|
||||
reserveDetail: {},
|
||||
}
|
||||
},
|
||||
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() {
|
||||
if(this.pageForm.timeSwitch === true) {
|
||||
this.timeList = [{text: '全部', value: '全部'}]
|
||||
@@ -423,4 +485,4 @@ layout("/mobile/platform.html"){
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
#-->
|
||||
|
||||
@@ -3,6 +3,112 @@ layout("/mobile/platform.html"){
|
||||
#-->
|
||||
|
||||
<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 {
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
@@ -115,7 +221,33 @@ layout("/mobile/platform.html"){
|
||||
}
|
||||
</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
|
||||
:title="site.name"
|
||||
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
|
||||
type="info">关闭
|
||||
</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>
|
||||
</van-action-sheet>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
@@ -241,6 +374,9 @@ layout("/mobile/platform.html"){
|
||||
data() {
|
||||
return {
|
||||
isLoad: false,
|
||||
commitmentId: '',
|
||||
commitmentOpening: false,
|
||||
reserveSubmitting: false,
|
||||
formData: {},
|
||||
show: false,
|
||||
site_id: '',
|
||||
@@ -256,6 +392,9 @@ layout("/mobile/platform.html"){
|
||||
allowDayList: [],
|
||||
}
|
||||
},
|
||||
components: {
|
||||
'site-commitment': httpVueLoader('/components/activity/site/SiteCommitment.vue'),
|
||||
},
|
||||
computed: {
|
||||
getWeekTextByDate() {
|
||||
return (date) => {
|
||||
@@ -356,25 +495,40 @@ layout("/mobile/platform.html"){
|
||||
this.show = true
|
||||
},
|
||||
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, 'end_time', this.times[0].end_time)
|
||||
this.$set(this.formData, 'site_id', this.site_id)
|
||||
const resp = await $.post('/platform/activity/site/reserve/doAdd', {
|
||||
data: JSON.stringify(this.formData),
|
||||
days: JSON.stringify(this.times.map(o => o.day))
|
||||
})
|
||||
if (resp.code === 0) {
|
||||
vant.Toast('预约成功')
|
||||
this.reserveSubmitting = true
|
||||
try {
|
||||
const resp = await $.post('/platform/activity/site/reserve/doAdd', {
|
||||
data: JSON.stringify(this.formData),
|
||||
days: JSON.stringify(this.times.map(o => o.day)),
|
||||
commitmentId: this.commitmentId
|
||||
})
|
||||
if (resp.code === 0) {
|
||||
vant.Toast('预约成功')
|
||||
this.show = false
|
||||
this.times = []
|
||||
this.message = null
|
||||
if (this.site.reserveTarget === 1) {
|
||||
this.commitmentId = ''
|
||||
this.isLoad = false
|
||||
}
|
||||
await this.getMyReservation()
|
||||
await this.getReserve()
|
||||
await this.onLoad()
|
||||
} else {
|
||||
vant.Toast(resp.msg)
|
||||
}
|
||||
} else {
|
||||
vant.Toast(resp.msg)
|
||||
} catch (e) {
|
||||
vant.Toast('预约提交失败,请稍后重试')
|
||||
} finally {
|
||||
this.reserveSubmitting = false
|
||||
}
|
||||
},
|
||||
chooseTime(item) {
|
||||
@@ -448,17 +602,30 @@ layout("/mobile/platform.html"){
|
||||
})
|
||||
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>
|
||||
|
||||
|
||||
@@ -193,6 +193,19 @@ layout("/layouts/platform.html"){
|
||||
<el-form-item label="场地介绍">
|
||||
<text-editor v-model="formData.introduce"></text-editor>
|
||||
</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>
|
||||
<vi-title2 title="添加场次(温馨提醒:如果设置了预约时间单位,那么系统会在开始时间至结束时间范围内,按照设置的单位来拆分成多个时间段,如果未设置或者为0,则表示开始时间至结束时间是一个时间段)">
|
||||
<template #func>
|
||||
@@ -373,6 +386,8 @@ layout("/layouts/platform.html"){
|
||||
notApplyTimeList: [],
|
||||
reserveTarget: 2,
|
||||
openDayNum: 7,
|
||||
commitmentTitle: '教职工活动场地安全承诺书',
|
||||
commitmentContent: '',
|
||||
},
|
||||
pageForm: {
|
||||
searchName: "name",
|
||||
@@ -495,6 +510,7 @@ layout("/layouts/platform.html"){
|
||||
});
|
||||
},
|
||||
async operation() {
|
||||
if (this.subDis) return
|
||||
let method = this.formData.id ? "/doEdit" : "/doAdd"
|
||||
this.subDis = true
|
||||
this.$refs["addForm"].validate(async (valid) => {
|
||||
@@ -516,6 +532,8 @@ layout("/layouts/platform.html"){
|
||||
}
|
||||
loading.close()
|
||||
this.subDis = false
|
||||
} else {
|
||||
this.subDis = false
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -528,6 +546,12 @@ layout("/layouts/platform.html"){
|
||||
},
|
||||
async openEdit(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) {
|
||||
this.$set(this.formData, 'notApplyTimeList', [])
|
||||
}
|
||||
@@ -543,6 +567,8 @@ layout("/layouts/platform.html"){
|
||||
notApplyTimeList: [],
|
||||
reserveTarget: 2,
|
||||
openDayNum: 7,
|
||||
commitmentTitle: '教职工活动场地安全承诺书',
|
||||
commitmentContent: '',
|
||||
}
|
||||
this.$refs.guava.edit()
|
||||
|
||||
|
||||
@@ -77,6 +77,7 @@ layout("/layouts/platform.html"){
|
||||
}
|
||||
</style>
|
||||
<div id="app" v-cloak>
|
||||
<site-commitment ref="commitment"></site-commitment>
|
||||
<guava ref="guava">
|
||||
<template>
|
||||
<el-card shadow="never">
|
||||
@@ -383,6 +384,8 @@ layout("/layouts/platform.html"){
|
||||
data() {
|
||||
return {
|
||||
activityTypeList: [],
|
||||
commitmentId: '',
|
||||
commitmentOpening: false,
|
||||
drawer: false,
|
||||
siteList: [],
|
||||
restaurants: [],
|
||||
@@ -433,6 +436,7 @@ layout("/layouts/platform.html"){
|
||||
}
|
||||
},
|
||||
components: {
|
||||
'site-commitment': httpVueLoader('/components/activity/site/SiteCommitment.vue'),
|
||||
'guava': httpVueLoader('/components/plugins/Guava.vue'),
|
||||
'reserve-info': httpVueLoader('/components/activity/site/ReserveInfo.vue?v=1.0.0'),
|
||||
},
|
||||
@@ -511,23 +515,35 @@ layout("/layouts/platform.html"){
|
||||
this.addDialogVisible = false
|
||||
this.formData.end_time = ''
|
||||
},
|
||||
onReserve(data) {
|
||||
if(data.reservetarget === 1 && "${@shiro.hasAnyRoles('gh01', 'H04')}" === 'false' && "${@shiro.hasRole('sysadmin')}" === 'false') {
|
||||
this.$message.warning('该场地只能分工会预约')
|
||||
async onReserve(data) {
|
||||
if (this.commitmentOpening) return
|
||||
if(data.reservetarget === 1 && "${@shiro.hasAnyRoles('gh01', 'H04', 'H02')}" === 'false' && "${@shiro.hasRole('sysadmin')}" === 'false') {
|
||||
this.$message.warning('该场地仅限分工会预约人员或协会负责人预约')
|
||||
return
|
||||
}
|
||||
this.reverseData = data
|
||||
this.dayArray = []
|
||||
this.getAllowTime()
|
||||
if (data.sexlimit === 1 || data.sexlimit === 2) {
|
||||
const sex = data.sexlimit === 1 ? '男' : '女'
|
||||
if ("${@shiro.getPrincipalProperty('sex')}" === sex) {
|
||||
this.openReserve(data)
|
||||
} else {
|
||||
if ("${@shiro.getPrincipalProperty('sex')}" !== 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) {
|
||||
@@ -632,6 +648,7 @@ layout("/layouts/platform.html"){
|
||||
});
|
||||
},
|
||||
async doAdd() {
|
||||
if (this.subDis) return
|
||||
if (!this.formData.time) {
|
||||
this.$message.warning('未选择预约时间')
|
||||
return
|
||||
@@ -644,6 +661,7 @@ layout("/layouts/platform.html"){
|
||||
}
|
||||
this.$refs["addForm"].validate(async (valid) => {
|
||||
if (valid) {
|
||||
if (this.subDis) return
|
||||
this.subDis = true
|
||||
const loading = this.$loading({
|
||||
lock: true,
|
||||
@@ -651,20 +669,27 @@ layout("/layouts/platform.html"){
|
||||
spinner: 'el-icon-loading',
|
||||
background: 'rgba(0, 0, 0, 0.7)'
|
||||
});
|
||||
const {code, msg} = await $.post(location.href + "/doAdd", {
|
||||
data: JSON.stringify(this.formData),
|
||||
days: JSON.stringify(this.dayArray)
|
||||
})
|
||||
if (code === 0) {
|
||||
this.addDialogVisible = false
|
||||
this.$message.success(msg);
|
||||
this.pageData()
|
||||
this.$refs.guava.index()
|
||||
} else {
|
||||
this.$message.warning(msg)
|
||||
try {
|
||||
const {code, msg} = await $.post(location.href + "/doAdd", {
|
||||
data: JSON.stringify(this.formData),
|
||||
days: JSON.stringify(this.dayArray),
|
||||
commitmentId: this.commitmentId
|
||||
})
|
||||
if (code === 0) {
|
||||
this.commitmentId = ''
|
||||
this.addDialogVisible = false
|
||||
this.$message.success(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
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user