场地预约整改:分工会/协会审核-校工会审核
This commit is contained in:
+34
-148
@@ -1,164 +1,50 @@
|
|||||||
package io.v.nutz.zhgh.activity.controller.site;
|
package io.v.nutz.zhgh.activity.controller.site;
|
||||||
|
|
||||||
import cn.wizzer.framework.base.Result;
|
|
||||||
import io.v.nutz.zhgh.activity.models.ActivityType;
|
import io.v.nutz.zhgh.activity.models.ActivityType;
|
||||||
import io.v.nutz.zhgh.activity.services.ActivityCommonService;
|
|
||||||
import io.v.nutz.zhgh.activity.services.ActivityTypeService;
|
import io.v.nutz.zhgh.activity.services.ActivityTypeService;
|
||||||
import io.v.nutz.base.annontation.ViReturn;
|
import io.v.nutz.base.annontation.ViReturn;
|
||||||
import io.v.nutz.base.model.AuditState;
|
import org.apache.shiro.authz.annotation.*;
|
||||||
import io.v.nutz.base.model.AuditStateUser;
|
|
||||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
|
||||||
import org.nutz.dao.Cnd;
|
import org.nutz.dao.Cnd;
|
||||||
import org.nutz.dao.Dao;
|
import org.nutz.ioc.loader.annotation.*;
|
||||||
import org.nutz.dao.Sqls;
|
import org.nutz.ioc.aop.Aop;
|
||||||
import org.nutz.dao.sql.Sql;
|
import org.nutz.aop.interceptor.ioc.TransAop;
|
||||||
import org.nutz.dao.util.Daos;
|
|
||||||
import org.nutz.dao.util.cri.SqlExpressionGroup;
|
|
||||||
import org.nutz.ioc.loader.annotation.Inject;
|
|
||||||
import org.nutz.ioc.loader.annotation.IocBean;
|
|
||||||
import org.nutz.json.Json;
|
import org.nutz.json.Json;
|
||||||
import org.nutz.mvc.annotation.At;
|
import org.nutz.mvc.annotation.*;
|
||||||
import org.nutz.mvc.annotation.Ok;
|
|
||||||
import org.nutz.mvc.annotation.Param;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Author JyuHsin
|
|
||||||
* @Date 2022/5/11
|
|
||||||
* @Description
|
|
||||||
*/
|
|
||||||
@IocBean
|
@IocBean
|
||||||
@At("/platform/activity/site/type")
|
@At("/platform/activity/site/type")
|
||||||
|
@Ok("json:full")
|
||||||
public class ActivityTypeController {
|
public class ActivityTypeController {
|
||||||
|
@Inject private ActivityTypeService activityTypeService;
|
||||||
@Inject
|
@At("") @Ok("beetl:/platform/activity/site/SiteType.html") @RequiresPermissions("activity.site.type")
|
||||||
private Dao dao;
|
public void index() { }
|
||||||
|
/** enabledOnly=true 仅返回可预约类型;默认返回全部供历史筛选,结果为类型数组。 */
|
||||||
@Inject
|
@At @ViReturn @RequiresAuthentication
|
||||||
private ActivityTypeService activityTypeService;
|
public Object findAll(Boolean enabledOnly) {
|
||||||
|
Cnd c=Cnd.NEW(); c.asc("sortNum");
|
||||||
@Inject
|
if(Boolean.TRUE.equals(enabledOnly)) c.and("enabled","=",true);
|
||||||
private ActivityCommonService activityCommonService;
|
return activityTypeService.query(c);
|
||||||
|
|
||||||
@At("")
|
|
||||||
@Ok("beetl:/platform/activity/site/SiteType.html")
|
|
||||||
@RequiresPermissions("activity.site.type")
|
|
||||||
public void index(HttpServletRequest request) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@At("/findAll")
|
|
||||||
@Ok("json:full")
|
|
||||||
@ViReturn
|
|
||||||
public Object findAll() {
|
|
||||||
List<ActivityType> meetingTypeList = dao.query(ActivityType.class, Cnd.NEW().asc("sortNum"));
|
|
||||||
dao.fetchLinks(meetingTypeList, "^auditStateList$");
|
|
||||||
for (ActivityType ActivityType : meetingTypeList) {
|
|
||||||
List<AuditState> auditStateList = ActivityType.getAuditStateList();
|
|
||||||
dao.fetchLinks(auditStateList, "^auditStateUserList$");
|
|
||||||
}
|
|
||||||
return meetingTypeList;
|
|
||||||
}
|
|
||||||
|
|
||||||
@At
|
|
||||||
@Ok("json:full")
|
|
||||||
@ViReturn
|
|
||||||
@RequiresPermissions("activity.site.type")
|
|
||||||
public Object findOne(String module) {
|
|
||||||
|
|
||||||
List<AuditState> auditStateList = dao.query(AuditState.class, Cnd.where("module", "=", module));
|
|
||||||
auditStateList.forEach(v -> {
|
|
||||||
v.setAuditStateUserList(dao.query(AuditStateUser.class, Cnd.where("stateId", "=", v.getStateId())));
|
|
||||||
});
|
|
||||||
return auditStateList;
|
|
||||||
}
|
|
||||||
|
|
||||||
@At("/doHandle")
|
|
||||||
@Ok("json:full")
|
|
||||||
@ViReturn
|
|
||||||
@RequiresPermissions("activity.site.type")
|
|
||||||
public Object doHandle(@Param("data") String data) {
|
|
||||||
ActivityType activityType = Json.fromJson(ActivityType.class, data);
|
|
||||||
|
|
||||||
if (null == activityType.getId()) {
|
|
||||||
int count = dao.count(AuditState.class, Cnd.where("module", "=", activityType.getModuleName()));
|
|
||||||
if (count > 0) {
|
|
||||||
return Result.error("模块名称已存在,换一个试试");
|
|
||||||
}
|
|
||||||
activityTypeService.add(activityType);
|
|
||||||
} else {
|
|
||||||
activityTypeService.edit(activityType);
|
|
||||||
}
|
}
|
||||||
|
/** pageNumber/pageSize 为分页参数,返回 list/totalCount;复用页面表格 mixin。 */
|
||||||
|
@At @ViReturn @RequiresPermissions("activity.site.type")
|
||||||
|
public Object pageData(int pageNumber,int pageSize) { return activityTypeService.listPage(Math.max(1,pageNumber),Math.min(100,Math.max(1,pageSize)),Cnd.NEW().asc("sortNum")); }
|
||||||
|
/** data 为类型 JSON(id、code、meetingTypeName、enabled);返回 Object,由 @ViReturn 包装 code(0 成功)和 msg(结果说明)。 */
|
||||||
|
@At @ViReturn @RequiresPermissions("activity.site.type") @Aop(TransAop.READ_COMMITTED)
|
||||||
|
public Object doHandle(String data) {
|
||||||
|
ActivityType t=Json.fromJson(ActivityType.class,data);
|
||||||
|
if(t==null) throw new IllegalArgumentException("类型参数不能为空");
|
||||||
|
if(t.getId()==null) activityTypeService.add(t); else activityTypeService.edit(t);
|
||||||
|
// Object 返回类型使 @ViReturn 包装的 code/msg 能传递给 JSON 视图。
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
/** id 为类型主键,enabled 为是否启用;返回 Object,由 @ViReturn 包装 code(0 成功)和 msg(结果说明)。 */
|
||||||
@At("/delete/?")
|
@At @ViReturn @RequiresPermissions("activity.site.type") @Aop(TransAop.READ_COMMITTED)
|
||||||
@Ok("json:full")
|
public Object setEnabled(Integer id,Boolean enabled) {
|
||||||
@ViReturn
|
ActivityType t=activityTypeService.fetch(id);
|
||||||
@RequiresPermissions("activity.site.type")
|
if(t==null) throw new IllegalArgumentException("类型不存在");
|
||||||
public Object delete(Integer id) {
|
t.setEnabled(enabled); activityTypeService.edit(t);
|
||||||
if (id == 1) {
|
|
||||||
return Result.error("此记录不可删除");
|
|
||||||
}
|
|
||||||
ActivityType activityType = dao.fetch(ActivityType.class, id);
|
|
||||||
dao.clear(ActivityType.class, Cnd.where("id", "=", id));
|
|
||||||
dao.count(AuditState.class, Cnd.where("module", "=", activityType.getModuleName()));
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
/** id 为待删除类型主键,服务层检查场地引用;返回 Object,由 @ViReturn 包装 code(0 成功)和 msg(结果说明)。 */
|
||||||
@At("/findUserList")
|
@At("/delete/?") @ViReturn @RequiresPermissions("activity.site.type") @Aop(TransAop.READ_COMMITTED)
|
||||||
@Ok("json:full")
|
public Object delete(Integer id) { activityTypeService.delete(activityTypeService.fetch(id)); return null; }
|
||||||
@ViReturn
|
|
||||||
@RequiresPermissions("activity.site.type")
|
|
||||||
public Object findUserList(@Param("keyWords") String[] keyWords) {
|
|
||||||
Sql sql = Sqls.create("""
|
|
||||||
SELECT
|
|
||||||
id,
|
|
||||||
username,
|
|
||||||
loginname,
|
|
||||||
mobile,
|
|
||||||
sex,
|
|
||||||
unitname,
|
|
||||||
unionname,
|
|
||||||
unionid,
|
|
||||||
unitid
|
|
||||||
FROM
|
|
||||||
`user`
|
|
||||||
$condition
|
|
||||||
""");
|
|
||||||
Cnd cnd = Cnd.NEW();
|
|
||||||
SqlExpressionGroup seg = new SqlExpressionGroup();
|
|
||||||
for (String keyWord : keyWords) {
|
|
||||||
seg.andLike("loginname", keyWord);
|
|
||||||
seg.orLike("username", keyWord);
|
|
||||||
}
|
|
||||||
cnd.and(seg);
|
|
||||||
sql.setCondition(cnd);
|
|
||||||
return Daos.query(dao, sql.toString(), Sqls.callback.maps());
|
|
||||||
}
|
|
||||||
|
|
||||||
@At
|
|
||||||
@Ok("json:full")
|
|
||||||
@ViReturn
|
|
||||||
public Object findMaxStateId() {
|
|
||||||
return activityTypeService.findMaxStateId();
|
|
||||||
}
|
|
||||||
|
|
||||||
@At
|
|
||||||
@Ok("json:full")
|
|
||||||
@ViReturn
|
|
||||||
@RequiresPermissions("activity.site.type")
|
|
||||||
public Object checkStateId(@Param("stateId") Integer stateId, @Param("stateIndex") Integer stateIndex) {
|
|
||||||
if (stateId == null) {
|
|
||||||
return Result.error("参数错误");
|
|
||||||
}
|
|
||||||
int count = dao.count(AuditState.class, Cnd.where("stateId", "=", stateId));
|
|
||||||
if (count == 0) {
|
|
||||||
return Result.success().addData(stateId);
|
|
||||||
} else {
|
|
||||||
return Result.success(activityTypeService.findMaxStateId() + 10 * stateIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package io.v.nutz.zhgh.activity.controller.site;
|
||||||
|
import io.v.nutz.base.annontation.ViReturn;
|
||||||
|
import io.v.nutz.zhgh.activity.services.impl.SiteBookingService;
|
||||||
|
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||||
|
import org.nutz.ioc.loader.annotation.*;
|
||||||
|
import org.nutz.ioc.aop.Aop;
|
||||||
|
import org.nutz.aop.interceptor.ioc.TransAop;
|
||||||
|
import org.nutz.mvc.annotation.*;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
/** 协会审核:PC/H5 共享业务服务,入口固定节点,不能由请求绕过节点限制。 */
|
||||||
|
@IocBean @Ok("json:full") @At("/platform/activity/site/review/club")
|
||||||
|
@RequiresPermissions("activity.site.review.club")
|
||||||
|
public class SiteClubReviewController {
|
||||||
|
@Inject private SiteBookingService siteBookingService;
|
||||||
|
@At("") @Ok("beetl:/platform/activity/site/SiteReview.html")
|
||||||
|
public void index(HttpServletRequest request) {
|
||||||
|
request.setAttribute("reviewTitle","协会审核");
|
||||||
|
request.setAttribute("reviewApi","/platform/activity/site/review/club");
|
||||||
|
}
|
||||||
|
/** isAudit 为已审核/未审核;searchName 为场地名称或预约人字段,month 为 yyyy-MM,siteType 为类型 ID,返回 list/totalCount。 */
|
||||||
|
@At @ViReturn
|
||||||
|
public Object pageData(Boolean isAudit,String searchName,String searchKeyword,String month,String siteType,int pageNumber,int pageSize) {
|
||||||
|
return siteBookingService.reviewPage("club",Boolean.TRUE.equals(isAudit),searchName,searchKeyword,month,siteType,pageNumber,pageSize);
|
||||||
|
}
|
||||||
|
/** id 是预约记录主键,返回申请、时段及审核历史,服务层校验查看范围。 */
|
||||||
|
@At @ViReturn
|
||||||
|
public Object detail(String id) { return siteBookingService.detail(id); }
|
||||||
|
/** ids 为预约记录 ID 数组,isPass 为通过/拒绝,auditOpinion 为必填意见;返回 Object,由 @ViReturn 包装 code(0 成功)和 msg(结果说明)。 */
|
||||||
|
@At @ViReturn @Aop(TransAop.READ_COMMITTED)
|
||||||
|
public Object doReview(String[] ids,Boolean isPass,String auditOpinion) {
|
||||||
|
siteBookingService.review("club",ids,isPass,auditOpinion);
|
||||||
|
// Object 返回类型保留 @ViReturn 的统一结果,避免前端收到空响应。
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
/** id 为预约记录主键,撤回本人最近一次审核;返回统一 code/msg,成功后恢复当前节点待审。 */
|
||||||
|
@At @ViReturn @Aop(TransAop.READ_COMMITTED)
|
||||||
|
public Object doRevoke(String id) {
|
||||||
|
siteBookingService.revokeReview("club",id);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -64,7 +64,7 @@ public class SiteRecordController {
|
|||||||
LEFT JOIN
|
LEFT JOIN
|
||||||
activity_site_info asi ON asi.id = asr.site_id
|
activity_site_info asi ON asi.id = asr.site_id
|
||||||
LEFT JOIN
|
LEFT JOIN
|
||||||
audit_state ass ON ass.stateId = asr.reserve_state
|
(select state_id stateId,state_name stateName,state_color stateColor,'activity_site' module,case when state_id=4030 then 3 when state_id in (4040,4050) then 1 else 0 end stateAuditType from state where belong='activity_site') ass ON ass.stateId = asr.reserve_state
|
||||||
LEFT JOIN
|
LEFT JOIN
|
||||||
sys_user u on u.id = asr.reserve_person_id
|
sys_user u on u.id = asr.reserve_person_id
|
||||||
$condition
|
$condition
|
||||||
|
|||||||
@@ -32,6 +32,9 @@ import org.nutz.lang.Strings;
|
|||||||
import org.nutz.lang.random.R;
|
import org.nutz.lang.random.R;
|
||||||
import org.nutz.lang.util.NutMap;
|
import org.nutz.lang.util.NutMap;
|
||||||
import org.nutz.mvc.annotation.At;
|
import org.nutz.mvc.annotation.At;
|
||||||
|
import io.v.nutz.zhgh.activity.services.impl.SiteBookingService;
|
||||||
|
import org.nutz.ioc.aop.Aop;
|
||||||
|
import org.nutz.aop.interceptor.ioc.TransAop;
|
||||||
import org.nutz.mvc.annotation.Ok;
|
import org.nutz.mvc.annotation.Ok;
|
||||||
import org.nutz.mvc.annotation.Param;
|
import org.nutz.mvc.annotation.Param;
|
||||||
|
|
||||||
@@ -50,6 +53,8 @@ import java.util.stream.Collectors;
|
|||||||
@At("/platform/activity/site/reserve")
|
@At("/platform/activity/site/reserve")
|
||||||
public class SiteReserveController {
|
public class SiteReserveController {
|
||||||
|
|
||||||
|
@Inject private SiteBookingService siteBookingService;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private SiteInfoService siteInfoService;
|
private SiteInfoService siteInfoService;
|
||||||
|
|
||||||
@@ -101,7 +106,7 @@ public class SiteReserveController {
|
|||||||
LEFT JOIN
|
LEFT JOIN
|
||||||
activity_site_info asi ON asi.id = asr.site_id
|
activity_site_info asi ON asi.id = asr.site_id
|
||||||
LEFT JOIN
|
LEFT JOIN
|
||||||
audit_state ass ON ass.stateId = asr.reserve_state
|
(select state_id stateId,state_name stateName,state_color stateColor,'activity_site' module,case when state_id=4030 then 3 when state_id in (4040,4050) then 1 else 0 end stateAuditType from state where belong='activity_site') ass ON ass.stateId = asr.reserve_state
|
||||||
$condition
|
$condition
|
||||||
""");
|
""");
|
||||||
|
|
||||||
@@ -115,6 +120,7 @@ public class SiteReserveController {
|
|||||||
|
|
||||||
if (Strings.isNotBlank(siteId)) {
|
if (Strings.isNotBlank(siteId)) {
|
||||||
cnd.and("site_id", "=", siteId);
|
cnd.and("site_id", "=", siteId);
|
||||||
|
cnd.and("reserve_state", "not in", java.util.Arrays.asList(4040,4050));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Strings.isNotBlank(searchName) && Strings.isNotBlank(searchKeyword)) {
|
if (Strings.isNotBlank(searchName) && Strings.isNotBlank(searchKeyword)) {
|
||||||
@@ -140,124 +146,30 @@ public class SiteReserveController {
|
|||||||
@At
|
@At
|
||||||
@ViReturn
|
@ViReturn
|
||||||
@RequiresPermissions("activity.site.reserve")
|
@RequiresPermissions("activity.site.reserve")
|
||||||
|
@Aop(TransAop.READ_COMMITTED)
|
||||||
public Object doDelete(String id, Integer op) {
|
public Object doDelete(String id, Integer op) {
|
||||||
ActivitySiteReserve fetch = siteReserveService.fetch(id);
|
siteBookingService.cancel(id);
|
||||||
if (op == 1) {
|
|
||||||
siteReserveService.clear(Cnd.NEW().and("sqid", "=", fetch.getSqid()));
|
|
||||||
} else if (op == 2) {
|
|
||||||
//siteReserveService.update(Chain.make("reserve_state", 50), Cnd.where("id", "=", id));
|
|
||||||
siteReserveService.clear(Cnd.NEW().and("sqid", "=", fetch.getSqid()));
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@At
|
@At
|
||||||
@RequiresPermissions("activity.site.reserve")
|
@RequiresPermissions("activity.site.reserve")
|
||||||
public Object isCanRollBack(String id) {
|
public Object isCanRollBack(String id) {
|
||||||
//根据活动id查询第一个审核节点
|
ActivitySiteReserve b=siteReserveService.fetch(id);
|
||||||
ActivitySiteReserve fetch = siteReserveService.fetch(id);
|
if(b==null) return true;
|
||||||
String moduleName = activityCommonService.findModuleNameByActivityId(fetch.getSite_id());
|
return b.getReserve_state()!=siteBookingService.firstState(b.getReserve_type()) || b.getAuditList()!=null && !b.getAuditList().isEmpty();
|
||||||
Integer stateCode = activityCommonService.findStartStateCodeByModuleName(moduleName);
|
|
||||||
|
|
||||||
return fetch.getReserve_state() > stateCode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@At
|
@At
|
||||||
@ViReturn
|
@ViReturn
|
||||||
@RequiresPermissions("activity.site.reserve")
|
@RequiresPermissions("activity.site.reserve")
|
||||||
|
/** data 为场地、类型、协会及事由;days 为日期数组,起止时间取 data;返回新申请 sqid。 */
|
||||||
|
@Aop(TransAop.READ_COMMITTED)
|
||||||
public Object doAdd(@Param("data") ActivitySiteReserve activitySiteReserve, @Param("days") String[] days) {
|
public Object doAdd(@Param("data") ActivitySiteReserve activitySiteReserve, @Param("days") String[] days) {
|
||||||
|
List<NutMap> slots = new ArrayList<>();
|
||||||
if(activitySiteReserve.getReserve_type() == 1 && days.length > 1) {
|
if (activitySiteReserve == null || days == null) throw new IllegalArgumentException("请选择预约日期");
|
||||||
return Result.error("个人预约只支持预约明天的时间");
|
for (String day : days) slots.add(NutMap.NEW().setv("day",day).setv("start_time",activitySiteReserve.getStart_time()).setv("end_time",activitySiteReserve.getEnd_time()));
|
||||||
}
|
return siteBookingService.submit(activitySiteReserve,slots);
|
||||||
|
|
||||||
//主要来查询个人预约时的人数上限
|
|
||||||
ActivitySiteInfo siteInfo = siteInfoService.dao().fetch(ActivitySiteInfo.class, activitySiteReserve.getSite_id());
|
|
||||||
String moduleName = activityCommonService.findModuleNameByActivityId(activitySiteReserve.getSite_id());
|
|
||||||
Integer limitNum = siteInfo.getLimitNum();
|
|
||||||
|
|
||||||
for (String day : days) {
|
|
||||||
String time = day + " " + activitySiteReserve.getStart_time();
|
|
||||||
int compare = DateUtil.compare(new Date(), DateUtil.parse(time), "yyyy-MM-dd HH:mm");
|
|
||||||
if(compare > 0) {
|
|
||||||
return Result.error("您预约的【%s】时间已过".formatted(time));
|
|
||||||
}
|
|
||||||
if(activitySiteReserve.getReserve_type() == 1 && !day.equals(DateUtil.format(DateUtil.offsetDay(new Date(), 1), "yyyy-MM-dd"))) {
|
|
||||||
return Result.error("个人预约只支持预约明天的时间");
|
|
||||||
}
|
|
||||||
|
|
||||||
List<ActivitySiteReserve> list = siteReserveService.query(Cnd.NEW()
|
|
||||||
.and("reserve_day", "=", day)
|
|
||||||
.and("start_time", "=", activitySiteReserve.getStart_time())
|
|
||||||
.and("end_time", "=", activitySiteReserve.getEnd_time())
|
|
||||||
.and("site_id", "=", activitySiteReserve.getSite_id())
|
|
||||||
.and(new Static(" reserve_state in (select stateId from audit_state where stateAuditType != 1 and module = '%s')".formatted(moduleName))));
|
|
||||||
ActivitySiteReserve reserve = list.stream().filter(o -> o.getReserve_person_id().equals(ShiroUtil.getPrincipalProperty("id"))).findAny().orElse(null);
|
|
||||||
if (reserve != null) {
|
|
||||||
return Result.error("您已预约该时间段!");
|
|
||||||
}
|
|
||||||
if(activitySiteReserve.getReserve_type() == 1) {
|
|
||||||
if((list.size() + 1) > limitNum) {
|
|
||||||
return Result.error("【%s】时间段预约人数已满!".formatted(time));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if(list.size() > 0) {
|
|
||||||
return Result.error("【%s】时间段已有预约!".formatted(time));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String r = R.UU32();
|
|
||||||
activitySiteReserve.setSqid(r);
|
|
||||||
|
|
||||||
Integer stateCode = activityCommonService.findStartStateCodeByModuleName(moduleName);
|
|
||||||
|
|
||||||
if (stateCode == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (String d : days) {
|
|
||||||
activitySiteReserve.setReserve_day(d);
|
|
||||||
activitySiteReserve.setReserve_state(stateCode);
|
|
||||||
activitySiteReserve.setReserve_person_id(ShiroUtil.getPrincipalProperty("id").toString());
|
|
||||||
siteReserveService.insert(activitySiteReserve);
|
|
||||||
}
|
|
||||||
|
|
||||||
//发送微信提醒审核人
|
|
||||||
//查询状态码对应的审核人
|
|
||||||
Sql sql = Sqls.create("""
|
|
||||||
select userId,u.loginname,u.username,u.unitname from audit_state_user asu
|
|
||||||
left join `user` u on u.id = asu.userId left join audit_state s on s.stateId=asu.stateId $condition
|
|
||||||
""");
|
|
||||||
Cnd cnd = Cnd.NEW();
|
|
||||||
cnd.and("asu.stateId", "=", stateCode);
|
|
||||||
cnd.and("s.stateAuditType", "=", "0");
|
|
||||||
if ("infantRoom".equals(moduleName)) {
|
|
||||||
//获取当前登录人的单位id
|
|
||||||
String unitId = ShiroUtil.getPrincipalProperty("unitid").toString();
|
|
||||||
cnd.and("u.unitid", "=", unitId);
|
|
||||||
}
|
|
||||||
sql.setCondition(cnd);
|
|
||||||
List<Record> list = siteReserveService.list(sql);
|
|
||||||
|
|
||||||
list.forEach(item -> {
|
|
||||||
String content = "%s老师您好!%s老师预约%s日使用%s,请您通过门户网站或点击详情登录智慧工会平台进行审核。"
|
|
||||||
.formatted(item.getString("username"),
|
|
||||||
activitySiteReserve.getReserve_person(),
|
|
||||||
StringUtils.join(days, ","),
|
|
||||||
siteInfoService.fetch(activitySiteReserve.getSite_id()).getName());
|
|
||||||
|
|
||||||
Sys_user sys_user = sysUserService.fetch(item.getString("userId"));
|
|
||||||
NutMap map = new NutMap();
|
|
||||||
map.setv("keyword1", NutMap.NEW().setv("value", "场地预约"));
|
|
||||||
map.setv("keyword2", NutMap.NEW().setv("value", item.getString("username")
|
|
||||||
+ "(" + item.getString("loginname") + ")"));
|
|
||||||
map.setv("keyword3", NutMap.NEW().setv("value", item.getString("unitname")));
|
|
||||||
map.setv("keyword4", NutMap.NEW().setv("value", content));
|
|
||||||
map.setv("keyword5", NutMap.NEW().setv("value", DateUtil.now()));
|
|
||||||
//msgApi.sendMsg(sys_user.getWeAppOpenid(), msgApi.DSH_TEMPLATE_ID, Globals.AppDomain + "/mobile/activity/site/audit", map);
|
|
||||||
});
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@At
|
@At
|
||||||
@@ -267,7 +179,7 @@ public class SiteReserveController {
|
|||||||
@Param(value = "siteType",required = false) String siteType) {
|
@Param(value = "siteType",required = false) String siteType) {
|
||||||
String sb = "SELECT " +
|
String sb = "SELECT " +
|
||||||
"asi.*," +
|
"asi.*," +
|
||||||
"( SELECT count( 1 ) FROM activity_site_reserve WHERE site_id = asi.id AND reserve_state = (select stateId from audit_state where module = (select moduleName from activity_type where id = asi.typeId) and stateAuditType = 3) ";
|
"( SELECT count( 1 ) FROM activity_site_reserve WHERE site_id = asi.id AND reserve_state = 4030 ";
|
||||||
if (StrUtil.isNotBlank(time)) {
|
if (StrUtil.isNotBlank(time)) {
|
||||||
sb += " AND left(reserve_day, 7) = '" + time + "'";
|
sb += " AND left(reserve_day, 7) = '" + time + "'";
|
||||||
}
|
}
|
||||||
@@ -276,6 +188,7 @@ public class SiteReserveController {
|
|||||||
Sql sql = Sqls.create(sb);
|
Sql sql = Sqls.create(sb);
|
||||||
Cnd cnd = Cnd.NEW();
|
Cnd cnd = Cnd.NEW();
|
||||||
cnd.and("state", "=", true);
|
cnd.and("state", "=", true);
|
||||||
|
cnd.and("typeId", "in", Sqls.create("select id from activity_type where enabled=1"));
|
||||||
if (StrUtil.isNotBlank(siteType)) {
|
if (StrUtil.isNotBlank(siteType)) {
|
||||||
cnd.and("typeId", "=", siteType);
|
cnd.and("typeId", "=", siteType);
|
||||||
}
|
}
|
||||||
@@ -302,28 +215,14 @@ public class SiteReserveController {
|
|||||||
@RequiresPermissions("activity.site.reserve")
|
@RequiresPermissions("activity.site.reserve")
|
||||||
public Object findReserveInfo(@Param(value = "siteId",required = false) String siteId,
|
public Object findReserveInfo(@Param(value = "siteId",required = false) String siteId,
|
||||||
@Param(value = "day",required = false) String day) {
|
@Param(value = "day",required = false) String day) {
|
||||||
Sql sql = Sqls.create("SELECT ar.*,`as`.stateAuditType FROM activity_site_reserve ar left join audit_state `as` on ar.reserve_state=`as`.stateId $condition");
|
Sql sql = Sqls.create("SELECT ar.*,`as`.stateAuditType FROM activity_site_reserve ar left join (select state_id stateId,state_name stateName,state_color stateColor,'activity_site' module,case when state_id=4030 then 3 when state_id in (4040,4050) then 1 else 0 end stateAuditType from state where belong='activity_site') `as` on ar.reserve_state=`as`.stateId $condition");
|
||||||
|
|
||||||
String moduleName = activityCommonService.findModuleNameByActivityId(siteId);
|
|
||||||
//根据moduleName找审核失败的数据
|
|
||||||
Sql s = Sqls.create("""
|
|
||||||
select
|
|
||||||
stateId
|
|
||||||
from
|
|
||||||
audit_state
|
|
||||||
where
|
|
||||||
stateAuditType = 1 and
|
|
||||||
module = @module
|
|
||||||
""").setParam("module", moduleName);
|
|
||||||
|
|
||||||
List<Record> list = siteReserveService.list(s);
|
|
||||||
List<String> stateList = list.stream().map(o -> o.getString("stateId")).collect(Collectors.toList());
|
|
||||||
|
|
||||||
Cnd cnd = Cnd.NEW();
|
Cnd cnd = Cnd.NEW();
|
||||||
cnd.and("site_id", "=", siteId);
|
cnd.and("site_id", "=", siteId);
|
||||||
|
cnd.and("reserve_state", "not in", java.util.Arrays.asList(4040,4050));
|
||||||
if (StrUtil.isNotBlank(day)) {
|
if (StrUtil.isNotBlank(day)) {
|
||||||
cnd.and("reserve_day", "=", day);
|
cnd.and("reserve_day", "=", day);
|
||||||
cnd.and("reserve_state", "not in ", stateList);
|
|
||||||
}
|
}
|
||||||
sql.setCondition(cnd);
|
sql.setCondition(cnd);
|
||||||
return siteReserveService.list(sql);
|
return siteReserveService.list(sql);
|
||||||
@@ -335,7 +234,7 @@ public class SiteReserveController {
|
|||||||
public Object checkReserve(String reserve_day, String start_time, String end_time, String site_id) {
|
public Object checkReserve(String reserve_day, String start_time, String end_time, String site_id) {
|
||||||
Cnd cnd = Cnd.NEW();
|
Cnd cnd = Cnd.NEW();
|
||||||
cnd.and("site_id", "=", site_id)
|
cnd.and("site_id", "=", site_id)
|
||||||
.and("reserve_state", "!=", 40)
|
.and("reserve_state", "not in", java.util.Arrays.asList(4040,4050))
|
||||||
.and("reserve_day", "=", reserve_day);
|
.and("reserve_day", "=", reserve_day);
|
||||||
SqlExpressionGroup group = new SqlExpressionGroup();
|
SqlExpressionGroup group = new SqlExpressionGroup();
|
||||||
group.or("start_time", "<=", start_time).and("end_time", ">=", start_time);
|
group.or("start_time", "<=", start_time).and("end_time", ">=", start_time);
|
||||||
@@ -348,84 +247,10 @@ public class SiteReserveController {
|
|||||||
@ViReturn
|
@ViReturn
|
||||||
@RequiresPermissions("activity.site.reserve")
|
@RequiresPermissions("activity.site.reserve")
|
||||||
public Object findOne(String id) {
|
public Object findOne(String id) {
|
||||||
ActivitySiteReserve fetch = siteReserveService.fetch(id);
|
return siteBookingService.detail(id);
|
||||||
Sql sql = Sqls.create("""
|
|
||||||
SELECT
|
|
||||||
asr.*,
|
|
||||||
asi.`name` site_name,
|
|
||||||
ass.`stateName` state_name,
|
|
||||||
ass.stateAuditType,
|
|
||||||
su.username,
|
|
||||||
sm.username smusername,
|
|
||||||
su.sex,
|
|
||||||
su.loginname,
|
|
||||||
count(sqid) days,
|
|
||||||
GROUP_CONCAT(reserve_day order by reserve_day asc) concat_day
|
|
||||||
FROM
|
|
||||||
activity_site_reserve asr
|
|
||||||
LEFT JOIN activity_site_info asi ON asi.id = asr.site_id
|
|
||||||
LEFT JOIN audit_state ass ON ass.stateId = asr.reserve_state
|
|
||||||
LEFT JOIN sys_user su ON su.id = asr.reserve_person_id
|
|
||||||
LEFT JOIN sys_user sm ON sm.id = asr.site_manager_id
|
|
||||||
WHERE
|
|
||||||
asr.sqid = @id
|
|
||||||
group by sqid
|
|
||||||
""").setParam("id", fetch.getSqid());
|
|
||||||
NutMap record = (NutMap) Daos.query(siteReserveService.dao(), sql.toString(), Sqls.callback.map());
|
|
||||||
String auditList = record.getString("auditList");
|
|
||||||
if (auditList != null) {
|
|
||||||
List<NutMap> nutMaps = Json.fromJsonAsList(NutMap.class, auditList);
|
|
||||||
nutMaps.forEach(item -> {
|
|
||||||
item.put("auditListName", item.getBoolean("auditState") ? "审核通过" : "审核拒绝");
|
|
||||||
Sys_user auditUSer = sysUserService.fetch(item.getString("auditUser"));
|
|
||||||
item.put("auditUserName", auditUSer.getUsername());
|
|
||||||
item.put("auditUserUnionName", sysUnitService.fetch(auditUSer.getUnitid()).getName());
|
|
||||||
Sql sqlStr = Sqls.create("select stateName from audit_state where stateId = '" + item.getString("stateCode") + "'");
|
|
||||||
Record re = sysUserService.list(sqlStr).get(0);
|
|
||||||
item.put("auditStateName", re.getString("stateName"));
|
|
||||||
});
|
|
||||||
record.put("auditListTable", nutMaps);
|
|
||||||
} else {
|
|
||||||
record.put("auditListTable", new ArrayList<>());
|
|
||||||
}
|
|
||||||
|
|
||||||
Cnd cnd = Cnd.NEW();
|
|
||||||
String site_id = record.getString("site_id");
|
|
||||||
String reserve_day = record.getString("reserve_day");
|
|
||||||
|
|
||||||
String moduleName = activityCommonService.findModuleNameByActivityId(record.getString("site_id"));
|
|
||||||
//根据moduleName找审核失败的数据
|
|
||||||
Sql s = Sqls.create("""
|
|
||||||
select
|
|
||||||
stateId
|
|
||||||
from
|
|
||||||
audit_state
|
|
||||||
where
|
|
||||||
stateAuditType = 1 and
|
|
||||||
module = @module
|
|
||||||
""").setParam("module", moduleName);
|
|
||||||
|
|
||||||
List<NutMap> list = siteReserveService.listMap(s);
|
|
||||||
List<String> stateList = list.stream().map(o -> o.getString("stateId")).collect(Collectors.toList());
|
|
||||||
|
|
||||||
Sql i = Sqls.create("""
|
|
||||||
SELECT
|
|
||||||
ar.*,
|
|
||||||
`as`.stateAuditType
|
|
||||||
FROM
|
|
||||||
activity_site_reserve ar
|
|
||||||
left join audit_state `as` on `as`.stateId = ar.reserve_state
|
|
||||||
$condition
|
|
||||||
""");
|
|
||||||
cnd.and("site_id", "=", site_id)
|
|
||||||
//.and("reserve_day", "=", reserve_day)
|
|
||||||
.and("reserve_state", "not in", stateList);
|
|
||||||
|
|
||||||
i.setCondition(cnd);
|
|
||||||
List<NutMap> list1 = siteReserveService.listMap(i);
|
|
||||||
record.put("site_info", list1);
|
|
||||||
|
|
||||||
return record;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 返回当前用户有效协会数组(id/name),供个人所属协会展示和协会预约选择。 */
|
||||||
|
@At @ViReturn @org.apache.shiro.authz.annotation.RequiresAuthentication
|
||||||
|
public Object myClubs() { return siteBookingService.myClubs(); }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,258 +1,10 @@
|
|||||||
package io.v.nutz.zhgh.activity.controller.site;
|
package io.v.nutz.zhgh.activity.controller.site;
|
||||||
|
|
||||||
|
|
||||||
import cn.hutool.core.date.DateUtil;
|
|
||||||
import io.v.nutz.zhgh.activity.models.ActivitySiteInfo;
|
|
||||||
import io.v.nutz.zhgh.activity.models.ActivitySiteReserve;
|
|
||||||
import io.v.nutz.zhgh.activity.services.ActivityCommonService;
|
|
||||||
import io.v.nutz.zhgh.activity.services.SiteInfoService;
|
|
||||||
import io.v.nutz.zhgh.activity.services.SiteReserveService;
|
|
||||||
import io.v.nutz.base.annontation.ViReturn;
|
|
||||||
import io.v.nutz.base.utils.MsgApi;
|
|
||||||
import io.v.nutz.base.utils.PageUtil;
|
|
||||||
|
|
||||||
import io.v.nutz.base.model.Audit;
|
|
||||||
import io.v.nutz.base.model.AuditState;
|
|
||||||
import io.v.nutz.zhgh.msgNotify.service.MsgNotifyService;
|
|
||||||
import io.v.nutz.sys.models.Sys_user;
|
|
||||||
import io.v.nutz.sys.services.SysUserService;
|
|
||||||
import io.v.nutz.web.commons.utils.ShiroUtil;
|
|
||||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
|
||||||
import org.nutz.dao.Cnd;
|
|
||||||
import org.nutz.dao.Sqls;
|
|
||||||
import org.nutz.dao.entity.Record;
|
|
||||||
import org.nutz.dao.sql.Sql;
|
|
||||||
import org.nutz.dao.util.cri.SqlExpressionGroup;
|
|
||||||
import org.nutz.dao.util.cri.Static;
|
|
||||||
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.mvc.annotation.*;
|
||||||
import org.nutz.lang.Strings;
|
import org.apache.shiro.authz.annotation.RequiresAuthentication;
|
||||||
import org.nutz.lang.util.NutMap;
|
/** 旧审核入口仅展示三个新入口,不再提供旧审核提交接口。 */
|
||||||
import org.nutz.mvc.annotation.At;
|
@IocBean @At("/platform/activity/site/review") @RequiresAuthentication
|
||||||
import org.nutz.mvc.annotation.Ok;
|
|
||||||
import org.nutz.mvc.annotation.Param;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author: Aaron
|
|
||||||
* @create: 2020-12-15 13:52
|
|
||||||
* @description: 预约审核
|
|
||||||
**/
|
|
||||||
@IocBean
|
|
||||||
@Ok("json:full")
|
|
||||||
@At("/platform/activity/site/review")
|
|
||||||
public class SiteReviewController {
|
public class SiteReviewController {
|
||||||
|
@At("") @Ok("beetl:/platform/activity/site/ReviewEntries.html")
|
||||||
@Inject
|
public void index() { }
|
||||||
private SiteInfoService siteInfoService;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
private SiteReserveService siteReserveService;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
private ActivityCommonService activityCommonService;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
private MsgApi msgApi;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
private MsgNotifyService msgNotifyService;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
private SysUserService sysUserService;
|
|
||||||
|
|
||||||
@At("")
|
|
||||||
@Ok("beetl:platform/activity/site/SiteReview.html")
|
|
||||||
@RequiresPermissions("activity.site.review")
|
|
||||||
public void index() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@At
|
|
||||||
@ViReturn
|
|
||||||
@RequiresPermissions("activity.site.review")
|
|
||||||
public Object pageData(@Param(value = "month",required = false) String month,
|
|
||||||
@Param(value = "siteType",required = false) String siteType,
|
|
||||||
@Param(value = "searchName",required = false) String searchName,
|
|
||||||
@Param(value = "searchKeyword",required = false) String searchKeyword,
|
|
||||||
@Param("pageNumber") int pageNumber,@Param("pageSize") int pageSize,
|
|
||||||
@Param(value = "pageOrderName",required = false) String pageOrderName,
|
|
||||||
@Param(value = "pageOrderBy",required = false) String pageOrderBy,
|
|
||||||
@Param(value = "isAudit",required = false) Boolean isAudit) {
|
|
||||||
Cnd cnd = Cnd.NEW();
|
|
||||||
|
|
||||||
Sql sql = Sqls.create("""
|
|
||||||
SELECT
|
|
||||||
asr.*,
|
|
||||||
asi.`name` site_name,
|
|
||||||
ass.`stateName` state_name,
|
|
||||||
ass.`stateColor` state_color,
|
|
||||||
ass.stateAuditType,
|
|
||||||
count(sqid) days,
|
|
||||||
GROUP_CONCAT(reserve_day order by reserve_day asc) concat_day
|
|
||||||
FROM
|
|
||||||
activity_site_reserve asr
|
|
||||||
LEFT JOIN
|
|
||||||
activity_site_info asi ON asi.id = asr.site_id
|
|
||||||
LEFT JOIN
|
|
||||||
audit_state ass ON ass.stateId = asr.reserve_state
|
|
||||||
LEFT JOIN
|
|
||||||
`user` u on u.id = asr.reserve_person_id
|
|
||||||
$condition
|
|
||||||
""");
|
|
||||||
|
|
||||||
/*if (ShiroUtil.hasAnyRoles(new String[]{"gh10"})) {
|
|
||||||
Sys_user user = (Sys_user) ShiroUtil.getPrincipal();
|
|
||||||
cnd.and("u.unitid", "=", user.getUnitid());
|
|
||||||
}*/
|
|
||||||
|
|
||||||
/*if (!ShiroUtil.hasAnyRoles(new String[]{"sysadmin", "A06"})) {
|
|
||||||
cnd.and(new Static("if(asi.typeId=1, u.unionId = '" + Vi.getUnionId() + "', 1=1)"));
|
|
||||||
}*/
|
|
||||||
|
|
||||||
//查询未审核,获取当前用户可以审核的节点
|
|
||||||
Sql s = Sqls.create("""
|
|
||||||
select stateId from audit_state_user where userId=@userId
|
|
||||||
""").setParam("userId", ShiroUtil.getPrincipalProperty("id"));
|
|
||||||
List<Record> sList = siteReserveService.list(s);
|
|
||||||
List<String> stateList = sList.stream().map(o -> o.getString("stateId")).collect(Collectors.toList());
|
|
||||||
|
|
||||||
List<AuditState> audit = siteInfoService.dao().query(AuditState.class, Cnd.where("stateId", "in", stateList));
|
|
||||||
SqlExpressionGroup sqlExpressionGroup = new SqlExpressionGroup();
|
|
||||||
audit.forEach(v->{
|
|
||||||
if (Strings.isNotBlank(v.getAuditAfterType())){
|
|
||||||
// CustomAuditTypeHandle instance = Enum.instance(CustomAuditTypeHandle.class, v.getAuditAfterType());
|
|
||||||
// if(instance != null) {
|
|
||||||
// instance.next(sqlExpressionGroup);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (!sqlExpressionGroup.isEmpty()) {
|
|
||||||
cnd.and(sqlExpressionGroup);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isAudit == null) {
|
|
||||||
SqlExpressionGroup group = new SqlExpressionGroup();
|
|
||||||
group.and(new Static("JSON_CONTAINS(auditList, JSON_OBJECT('auditUser', '" + ShiroUtil.getPrincipalProperty("id") + "'))"))
|
|
||||||
.or("reserve_state", "in", stateList);
|
|
||||||
cnd.and(group);
|
|
||||||
} else if (isAudit) {
|
|
||||||
//查询已审核
|
|
||||||
cnd.and(new Static("JSON_CONTAINS(auditList, JSON_OBJECT('auditUser', '" + ShiroUtil.getPrincipalProperty("id") + "'))"));
|
|
||||||
} else {
|
|
||||||
cnd.and("reserve_state", "in", stateList);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Strings.isNotBlank(siteType)) {
|
|
||||||
cnd.and("asi.typeId", "=", siteType);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Strings.isNotBlank(month)) {
|
|
||||||
cnd.and("left(reserve_day, 7)", "=", month);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Strings.isNotBlank(searchName) && Strings.isNotBlank(searchKeyword)) {
|
|
||||||
cnd.where().andLike(searchName, searchKeyword);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Strings.isNotBlank(pageOrderName) && Strings.isNotBlank(pageOrderName)) {
|
|
||||||
cnd.orderBy(pageOrderName, PageUtil.getOrder(pageOrderBy));
|
|
||||||
} else {
|
|
||||||
cnd.asc("asr.reserve_state").desc("u.unitid");
|
|
||||||
}
|
|
||||||
|
|
||||||
cnd.groupBy("asi.id");
|
|
||||||
|
|
||||||
sql.setCondition(cnd);
|
|
||||||
return siteReserveService.listPage(pageNumber, pageSize, sql);
|
|
||||||
}
|
|
||||||
|
|
||||||
@At
|
|
||||||
@ViReturn
|
|
||||||
@RequiresPermissions("activity.site.review")
|
|
||||||
public Object doReview(String[] id, Audit audit, Boolean isPass) {
|
|
||||||
siteReserveService.insert(audit);
|
|
||||||
|
|
||||||
for (String s : id) {
|
|
||||||
ActivitySiteReserve fetch = siteReserveService.fetch(s);
|
|
||||||
|
|
||||||
Integer stateCode = fetch.getReserve_state();
|
|
||||||
Integer afterStateCode = activityCommonService.findAfterStateCode(stateCode, isPass);
|
|
||||||
|
|
||||||
List<ActivitySiteReserve> reserves = siteReserveService.query(Cnd.NEW().and("sqid", "=", fetch.getSqid()));
|
|
||||||
String days = "";
|
|
||||||
for (ActivitySiteReserve siteReserve : reserves) {
|
|
||||||
|
|
||||||
siteReserve.setReserve_state(afterStateCode);
|
|
||||||
|
|
||||||
List<NutMap> auditList = siteReserve.getAuditList();
|
|
||||||
if (Lang.isEmpty(auditList)) {
|
|
||||||
auditList = new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
auditList.add(NutMap.NEW().addv("stateCode", stateCode).addv("auditId", audit.getId())
|
|
||||||
.addv("auditUser", ShiroUtil.getPrincipalProperty("id"))
|
|
||||||
.addv("auditState", isPass)
|
|
||||||
.addv("auditOption", audit.getAuditOpinion())
|
|
||||||
);
|
|
||||||
siteReserve.setAuditList(auditList);
|
|
||||||
|
|
||||||
days += siteReserve.getReserve_day() + ",";
|
|
||||||
siteReserveService.update(siteReserve);
|
|
||||||
}
|
|
||||||
days = days.substring(0, days.length() - 1);
|
|
||||||
|
|
||||||
Integer successCode = activityCommonService.findSuccessStateCode(fetch.getSite_id());
|
|
||||||
ActivitySiteInfo siteInfo = siteInfoService.fetch(fetch.getSite_id());
|
|
||||||
|
|
||||||
if (afterStateCode.equals(successCode) && siteInfo.getTypeId() == 1) {
|
|
||||||
String content = "%s老师您好!您提交的%s使用申请已通过审批,如有疑问,欢迎咨询校工会。"
|
|
||||||
.formatted(fetch.getReserve_person(), siteInfo.getName());
|
|
||||||
Sys_user user = sysUserService.dao().fetch(Sys_user.class, fetch.getReserve_person_id());
|
|
||||||
// msgApi.sendMsg(content, List.of(user.getLoginname()));
|
|
||||||
} else if (!afterStateCode.equals(successCode)) {
|
|
||||||
//发送微信提醒审核人
|
|
||||||
//查询状态码对应的审核人
|
|
||||||
Sql sql = Sqls.create("""
|
|
||||||
select userId,u.loginname,u.username,u.unitname from audit_state_user asu
|
|
||||||
left join `user` u on u.id = asu.userId left join audit_state s on s.stateId=asu.stateId $condition
|
|
||||||
""");
|
|
||||||
Cnd cnd = Cnd.NEW();
|
|
||||||
cnd.and("asu.stateId", "=", afterStateCode);
|
|
||||||
cnd.and("s.stateAuditType", "=", "0");
|
|
||||||
String moduleName = activityCommonService.findModuleNameByActivityId(fetch.getSite_id());
|
|
||||||
if ("infantRoom".equals(moduleName)) {
|
|
||||||
//获取当前登录人的单位id
|
|
||||||
String unitId = ShiroUtil.getPrincipalProperty("unitid").toString();
|
|
||||||
cnd.and("u.unitid", "=", unitId);
|
|
||||||
}
|
|
||||||
sql.setCondition(cnd);
|
|
||||||
List<Record> list = siteReserveService.list(sql);
|
|
||||||
|
|
||||||
for (Record item : list) {
|
|
||||||
String content = "%s老师您好!%s老师预约%s日使用%s,请您通过门户网站或点击详情登录智慧工会平台进行审核。"
|
|
||||||
.formatted(item.getString("username"),
|
|
||||||
fetch.getReserve_person(),
|
|
||||||
days,
|
|
||||||
siteInfo.getName());
|
|
||||||
Sys_user sys_user = sysUserService.fetch(item.getString("userId"));
|
|
||||||
NutMap map = new NutMap();
|
|
||||||
map.setv("keyword1", NutMap.NEW().setv("value", "场地预约"));
|
|
||||||
map.setv("keyword2", NutMap.NEW().setv("value", item.getString("username")
|
|
||||||
+ "(" + item.getString("loginname") + ")"));
|
|
||||||
map.setv("keyword3", NutMap.NEW().setv("value", item.getString("unitname")));
|
|
||||||
map.setv("keyword4", NutMap.NEW().setv("value", content));
|
|
||||||
map.setv("keyword5", NutMap.NEW().setv("value", DateUtil.now()));
|
|
||||||
// msgApi.sendMsg(sys_user.getWeAppOpenid(), msgApi.DSH_TEMPLATE_ID, Globals.AppDomain + "/mobile/activity/site/audit", map);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package io.v.nutz.zhgh.activity.controller.site;
|
||||||
|
import io.v.nutz.base.annontation.ViReturn;
|
||||||
|
import io.v.nutz.zhgh.activity.services.impl.SiteBookingService;
|
||||||
|
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||||
|
import org.nutz.ioc.loader.annotation.*;
|
||||||
|
import org.nutz.ioc.aop.Aop;
|
||||||
|
import org.nutz.aop.interceptor.ioc.TransAop;
|
||||||
|
import org.nutz.mvc.annotation.*;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
/** 校工会审核:PC/H5 共享业务服务,入口固定节点,不能由请求绕过节点限制。 */
|
||||||
|
@IocBean @Ok("json:full") @At("/platform/activity/site/review/school")
|
||||||
|
@RequiresPermissions("activity.site.review.school")
|
||||||
|
public class SiteSchoolReviewController {
|
||||||
|
@Inject private SiteBookingService siteBookingService;
|
||||||
|
@At("") @Ok("beetl:/platform/activity/site/SiteReview.html")
|
||||||
|
public void index(HttpServletRequest request) {
|
||||||
|
request.setAttribute("reviewTitle","校工会审核");
|
||||||
|
request.setAttribute("reviewApi","/platform/activity/site/review/school");
|
||||||
|
}
|
||||||
|
/** isAudit 为已审核/未审核;searchName 为场地名称或预约人字段,month 为 yyyy-MM,siteType 为类型 ID,返回 list/totalCount。 */
|
||||||
|
@At @ViReturn
|
||||||
|
public Object pageData(Boolean isAudit,String searchName,String searchKeyword,String month,String siteType,int pageNumber,int pageSize) {
|
||||||
|
return siteBookingService.reviewPage("school",Boolean.TRUE.equals(isAudit),searchName,searchKeyword,month,siteType,pageNumber,pageSize);
|
||||||
|
}
|
||||||
|
/** id 是预约记录主键,返回申请、时段及审核历史,服务层校验查看范围。 */
|
||||||
|
@At @ViReturn
|
||||||
|
public Object detail(String id) { return siteBookingService.detail(id); }
|
||||||
|
/** ids 为预约记录 ID 数组,isPass 为通过/拒绝,auditOpinion 为必填意见;返回 Object,由 @ViReturn 包装 code(0 成功)和 msg(结果说明)。 */
|
||||||
|
@At @ViReturn @Aop(TransAop.READ_COMMITTED)
|
||||||
|
public Object doReview(String[] ids,Boolean isPass,String auditOpinion) {
|
||||||
|
siteBookingService.review("school",ids,isPass,auditOpinion);
|
||||||
|
// Object 返回类型保留 @ViReturn 的统一结果,避免前端收到空响应。
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
/** id 为预约记录主键,撤回本人最近一次审核;返回统一 code/msg,成功后恢复当前节点待审。 */
|
||||||
|
@At @ViReturn @Aop(TransAop.READ_COMMITTED)
|
||||||
|
public Object doRevoke(String id) {
|
||||||
|
siteBookingService.revokeReview("school",id);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package io.v.nutz.zhgh.activity.controller.site;
|
||||||
|
import io.v.nutz.base.annontation.ViReturn;
|
||||||
|
import io.v.nutz.zhgh.activity.services.impl.SiteBookingService;
|
||||||
|
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||||
|
import org.nutz.ioc.loader.annotation.*;
|
||||||
|
import org.nutz.ioc.aop.Aop;
|
||||||
|
import org.nutz.aop.interceptor.ioc.TransAop;
|
||||||
|
import org.nutz.mvc.annotation.*;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
/** 分工会审核:PC/H5 共享业务服务,入口固定节点,不能由请求绕过节点限制。 */
|
||||||
|
@IocBean @Ok("json:full") @At("/platform/activity/site/review/union")
|
||||||
|
@RequiresPermissions("activity.site.review.union")
|
||||||
|
public class SiteUnionReviewController {
|
||||||
|
@Inject private SiteBookingService siteBookingService;
|
||||||
|
@At("") @Ok("beetl:/platform/activity/site/SiteReview.html")
|
||||||
|
public void index(HttpServletRequest request) {
|
||||||
|
request.setAttribute("reviewTitle","分工会审核");
|
||||||
|
request.setAttribute("reviewApi","/platform/activity/site/review/union");
|
||||||
|
}
|
||||||
|
/** isAudit 为已审核/未审核;searchName 为场地名称或预约人字段,month 为 yyyy-MM,siteType 为类型 ID,返回 list/totalCount。 */
|
||||||
|
@At @ViReturn
|
||||||
|
public Object pageData(Boolean isAudit,String searchName,String searchKeyword,String month,String siteType,int pageNumber,int pageSize) {
|
||||||
|
return siteBookingService.reviewPage("union",Boolean.TRUE.equals(isAudit),searchName,searchKeyword,month,siteType,pageNumber,pageSize);
|
||||||
|
}
|
||||||
|
/** id 是预约记录主键,返回申请、时段及审核历史,服务层校验查看范围。 */
|
||||||
|
@At @ViReturn
|
||||||
|
public Object detail(String id) { return siteBookingService.detail(id); }
|
||||||
|
/** ids 为预约记录 ID 数组,isPass 为通过/拒绝,auditOpinion 为必填意见;返回 Object,由 @ViReturn 包装 code(0 成功)和 msg(结果说明)。 */
|
||||||
|
@At @ViReturn @Aop(TransAop.READ_COMMITTED)
|
||||||
|
public Object doReview(String[] ids,Boolean isPass,String auditOpinion) {
|
||||||
|
siteBookingService.review("union",ids,isPass,auditOpinion);
|
||||||
|
// Object 返回类型保留 @ViReturn 的统一结果,避免前端收到空响应。
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
/** id 为预约记录主键,撤回本人最近一次审核;返回统一 code/msg,成功后恢复当前节点待审。 */
|
||||||
|
@At @ViReturn @Aop(TransAop.READ_COMMITTED)
|
||||||
|
public Object doRevoke(String id) {
|
||||||
|
siteBookingService.revokeReview("union",id);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,6 +19,16 @@ import java.util.List;
|
|||||||
@Comment("活动场地预约信息")
|
@Comment("活动场地预约信息")
|
||||||
public class ActivitySiteReserve extends BaseModel {
|
public class ActivitySiteReserve extends BaseModel {
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@Comment("协会预约所属协会,提交时校验申请人的有效成员关系")
|
||||||
|
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||||
|
private String clubId;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@Comment("提交时所属分工会快照,用于分工会主席审核范围")
|
||||||
|
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||||
|
private String reserve_person_unionid;
|
||||||
|
|
||||||
@Column
|
@Column
|
||||||
@Name
|
@Name
|
||||||
@Comment("ID")
|
@Comment("ID")
|
||||||
@@ -127,7 +137,7 @@ public class ActivitySiteReserve extends BaseModel {
|
|||||||
private String joinUser;
|
private String joinUser;
|
||||||
|
|
||||||
@Column
|
@Column
|
||||||
@Comment("预约类型(1.个人预约,2.单位预约)")
|
@Comment("预约类型(1.个人预约,2.单位预约,3.协会预约)")
|
||||||
@ColDefine(type = ColType.INT, width = 10)
|
@ColDefine(type = ColType.INT, width = 10)
|
||||||
private Integer reserve_type;
|
private Integer reserve_type;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,16 @@ import java.util.List;
|
|||||||
@Comment("活动场地类型")
|
@Comment("活动场地类型")
|
||||||
public class ActivityType {
|
public class ActivityType {
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@Comment("类型编码,字符串保留前导零")
|
||||||
|
@ColDefine(type = ColType.VARCHAR, width = 50)
|
||||||
|
private String code;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@Comment("是否启用;停用后不可新增预约")
|
||||||
|
@Default("1")
|
||||||
|
private Boolean enabled;
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@Comment("ID")
|
@Comment("ID")
|
||||||
@ColDefine(type = ColType.INT)
|
@ColDefine(type = ColType.INT)
|
||||||
|
|||||||
@@ -1,103 +1,47 @@
|
|||||||
package io.v.nutz.zhgh.activity.services.impl;
|
package io.v.nutz.zhgh.activity.services.impl;
|
||||||
|
|
||||||
import io.v.nutz.zhgh.activity.models.ActivityType;
|
import io.v.nutz.zhgh.activity.models.ActivityType;
|
||||||
import io.v.nutz.zhgh.activity.services.ActivityCommonService;
|
import io.v.nutz.zhgh.activity.models.ActivitySiteInfo;
|
||||||
import io.v.nutz.zhgh.activity.services.ActivityTypeService;
|
import io.v.nutz.zhgh.activity.services.ActivityTypeService;
|
||||||
import io.v.nutz.base.model.AuditState;
|
|
||||||
import io.v.nutz.base.model.AuditStateUser;
|
|
||||||
import io.v.nutz.base.service.impl.ViServiceImpl;
|
import io.v.nutz.base.service.impl.ViServiceImpl;
|
||||||
import org.nutz.aop.interceptor.ioc.TransAop;
|
import org.nutz.dao.*;
|
||||||
import org.nutz.dao.Cnd;
|
|
||||||
import org.nutz.dao.Dao;
|
|
||||||
import org.nutz.dao.Sqls;
|
|
||||||
import org.nutz.dao.sql.Sql;
|
|
||||||
import org.nutz.dao.util.Daos;
|
|
||||||
import org.nutz.ioc.aop.Aop;
|
|
||||||
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.ioc.aop.Aop;
|
||||||
|
import org.nutz.aop.interceptor.ioc.TransAop;
|
||||||
import java.util.List;
|
import org.nutz.lang.Strings;
|
||||||
|
|
||||||
|
/** 类型仅管理分类资料;保留旧模块字段,不再增删审核节点。 */
|
||||||
@IocBean(args = {"refer:dao"})
|
@IocBean(args = {"refer:dao"})
|
||||||
public class ActivityTypeServiceImpl extends ViServiceImpl<ActivityType> implements ActivityTypeService {
|
public class ActivityTypeServiceImpl extends ViServiceImpl<ActivityType> implements ActivityTypeService {
|
||||||
|
public ActivityTypeServiceImpl(Dao dao) { super(dao); }
|
||||||
@Inject
|
/** 旧接口兼容,不用于新预约流程。 */
|
||||||
private ActivityCommonService activityCommonService;
|
public int findMaxStateId() { return dao().func("audit_state", "max", "stateId"); }
|
||||||
|
/** 校验编码/名称/启用状态,编码须唯一且保留前导零。 */
|
||||||
public ActivityTypeServiceImpl(Dao dao) {
|
private void validate(ActivityType t) {
|
||||||
super(dao);
|
if(t == null || Strings.isBlank(t.getCode()) || Strings.isBlank(t.getMeetingTypeName()) || t.getEnabled()==null)
|
||||||
|
throw new IllegalArgumentException("请填写类型编码、类型名称和是否启用");
|
||||||
|
t.setCode(t.getCode().trim()); t.setMeetingTypeName(t.getMeetingTypeName().trim());
|
||||||
|
if(t.getCode().length()>50 || t.getMeetingTypeName().length()>50) throw new IllegalArgumentException("编码和名称不能超过50字");
|
||||||
|
Cnd c=Cnd.where("code","=",t.getCode());
|
||||||
|
if(t.getId()!=null) c.and("id","!=",t.getId());
|
||||||
|
if(count(c)>0) throw new IllegalArgumentException("类型编码已存在");
|
||||||
}
|
}
|
||||||
|
/** 新增类型,不生成审核节点。 */
|
||||||
@Override
|
|
||||||
public int findMaxStateId() {
|
|
||||||
return dao().func(AuditState.class, "max", "stateId");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据模块名称查找stateId
|
|
||||||
*
|
|
||||||
* @param moduleName
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private String[] findStateIdArray(String moduleName) {
|
|
||||||
//查询该类型原来关联的stateId
|
|
||||||
Sql stateSql = Sqls.create("select stateId from audit_state where module = @moduleName").setParam("moduleName", moduleName);
|
|
||||||
return (String[]) Daos.query(dao(), stateSql.toString(), Sqls.callback.strs());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 添加流程信息
|
|
||||||
*
|
|
||||||
* @param activityType
|
|
||||||
*/
|
|
||||||
private void insertAuditInfo(ActivityType activityType) {
|
|
||||||
List<AuditState> auditStateList = activityType.getAuditStateList();
|
|
||||||
if (Lang.isNotEmpty(auditStateList)) {
|
|
||||||
for (int i = 0; i < auditStateList.size(); i++) {
|
|
||||||
AuditState state = auditStateList.get(i);
|
|
||||||
state.setModule(activityType.getModuleName());
|
|
||||||
state.setMeetingTypeId(activityType.getId());
|
|
||||||
dao().insertWith(state, "^auditStateUserList$");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Aop(TransAop.READ_COMMITTED)
|
@Aop(TransAop.READ_COMMITTED)
|
||||||
public void add(ActivityType activityType) {
|
public void add(ActivityType t) { validate(t); t.setSortNum(count()+1); t.setModuleName(t.getCode()); dao().insert(t); }
|
||||||
dao().insert(activityType);
|
/** 更新分类资料,保留历史 moduleName 和关联节点。 */
|
||||||
insertAuditInfo(activityType);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Aop(TransAop.READ_COMMITTED)
|
@Aop(TransAop.READ_COMMITTED)
|
||||||
public void edit(ActivityType activityType) {
|
public void edit(ActivityType t) {
|
||||||
dao().update(activityType);
|
validate(t);
|
||||||
|
ActivityType old=fetch(t.getId());
|
||||||
//查询该类型原来关联的stateId
|
if(old==null) throw new IllegalArgumentException("类型不存在");
|
||||||
String[] stateIdStr = findStateIdArray(activityType.getModuleName());
|
old.setCode(t.getCode()).setMeetingTypeName(t.getMeetingTypeName()).setEnabled(t.getEnabled()); dao().update(old);
|
||||||
|
|
||||||
//删除原来审核状态下的审核人员
|
|
||||||
dao().clear(AuditStateUser.class, Cnd.where("stateId", "in", stateIdStr));
|
|
||||||
//删除原来的审核状态
|
|
||||||
dao().clear(AuditState.class, Cnd.where("stateId", "in", stateIdStr));
|
|
||||||
|
|
||||||
//添加新的审核流程
|
|
||||||
insertAuditInfo(activityType);
|
|
||||||
}
|
}
|
||||||
|
/** 被场地使用的类型不可删除,防止预约历史失去分类。 */
|
||||||
@Override
|
|
||||||
@Aop(TransAop.READ_COMMITTED)
|
@Aop(TransAop.READ_COMMITTED)
|
||||||
public void delete(ActivityType activityType) {
|
public void delete(ActivityType t) {
|
||||||
//查询该类型原来关联的stateId
|
if(t==null) throw new IllegalArgumentException("类型不存在");
|
||||||
String[] stateIdStr = findStateIdArray(activityType.getModuleName());
|
if(dao().count(ActivitySiteInfo.class,Cnd.where("typeId","=",t.getId()))>0) throw new IllegalArgumentException("该类型已被场地使用,请停用而非删除");
|
||||||
|
dao().delete(t);
|
||||||
//删除原来审核状态下的审核人员
|
|
||||||
dao().clear(AuditStateUser.class, Cnd.where("stateId", "in", stateIdStr));
|
|
||||||
//删除原来的审核状态
|
|
||||||
dao().clear(AuditState.class, Cnd.where("stateId", "in", stateIdStr));
|
|
||||||
|
|
||||||
dao().delete(activityType);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,402 @@
|
|||||||
|
package io.v.nutz.zhgh.activity.services.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.date.DateUtil;
|
||||||
|
import io.v.nutz.base.model.Audit;
|
||||||
|
import io.v.nutz.base.service.impl.BaseServiceImpl;
|
||||||
|
import io.v.nutz.sys.models.Sys_user;
|
||||||
|
import io.v.nutz.sys.models.Sys_local_process_instance;
|
||||||
|
import io.v.nutz.sys.models.Sys_local_process_instance_task;
|
||||||
|
import io.v.nutz.base.page.Pagination;
|
||||||
|
import io.v.nutz.sys.services.SysLocalProcessService;
|
||||||
|
import io.v.nutz.web.commons.utils.ShiroUtil;
|
||||||
|
import io.v.nutz.zhgh.activity.models.*;
|
||||||
|
import org.nutz.aop.interceptor.ioc.TransAop;
|
||||||
|
import org.nutz.dao.*;
|
||||||
|
import org.nutz.dao.sql.Sql;
|
||||||
|
import org.nutz.dao.entity.Record;
|
||||||
|
import org.nutz.ioc.aop.Aop;
|
||||||
|
import org.nutz.ioc.loader.annotation.*;
|
||||||
|
import org.nutz.json.Json;
|
||||||
|
import org.nutz.lang.Strings;
|
||||||
|
import org.nutz.lang.util.NutMap;
|
||||||
|
import java.time.*;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 场地预约固定状态流程。一次 sqid 对应多条时段和一个待办实例;
|
||||||
|
* 状态名称取自 state,路由由预约类型决定,与场地类型配置无关。
|
||||||
|
*/
|
||||||
|
@IocBean(args = {"refer:dao"})
|
||||||
|
public class SiteBookingService extends BaseServiceImpl<ActivitySiteReserve> {
|
||||||
|
public static final int UNION = 4000, CLUB = 4010, SCHOOL = 4020, PASS = 4030, REJECT = 4040, CANCEL = 4050;
|
||||||
|
/** 兼容现有日历组件的状态字段;名称统一读取 state,不再读取 audit_state。 */
|
||||||
|
public static final String STATES = "(select state_id stateId,state_name stateName,state_color stateColor,"
|
||||||
|
+ "case when state_id=4030 then 3 when state_id in (4040,4050) then 1 else 0 end stateAuditType "
|
||||||
|
+ "from state where belong='activity_site')";
|
||||||
|
@Inject private SysLocalProcessService sysLocalProcessService;
|
||||||
|
|
||||||
|
public SiteBookingService(Dao dao) { super(dao); }
|
||||||
|
|
||||||
|
/** 返回当前登录人 ID,不接受前端冒用申请人或审核人。 */
|
||||||
|
private String uid() { return String.valueOf(ShiroUtil.getPrincipalProperty("id")); }
|
||||||
|
|
||||||
|
/** 返回已通过入会且正常在会的协会,元素为 id、name;不按会长角色限制申请资格。 */
|
||||||
|
public List<NutMap> myClubs() {
|
||||||
|
return listMap(Sqls.create("select distinct c.id,c.name from sys_club_user m join sys_club c on c.id=m.clubid "
|
||||||
|
+ "where m.userid=@uid and m.status=5 and m.isNormal=1 and coalesce(m.delFlag,0)=0 "
|
||||||
|
+ "order by c.name").setParam("uid", uid()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 预约类型 1 个人、2 单位、3 协会,返回应进入的首个状态 ID。 */
|
||||||
|
public int firstState(Integer type) {
|
||||||
|
if (Integer.valueOf(1).equals(type)) return UNION;
|
||||||
|
if (Integer.valueOf(2).equals(type)) return SCHOOL;
|
||||||
|
if (Integer.valueOf(3).equals(type)) return CLUB;
|
||||||
|
throw new IllegalArgumentException("请选择有效的预约类型");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 按节点角色及组织范围查实际审核人;不允许空组织退化为全校范围。 */
|
||||||
|
private List<NutMap> reviewers(int node, ActivitySiteReserve booking) {
|
||||||
|
String role = node == UNION ? "gh01" : node == CLUB ? "club01" : "sysadmin";
|
||||||
|
String filter = "";
|
||||||
|
if (node == UNION) {
|
||||||
|
if (Strings.isBlank(booking.getReserve_person_unionid())) throw new IllegalArgumentException("申请人未关联分工会,请先完善所属分工会");
|
||||||
|
filter = " and ur.unionid=@org";
|
||||||
|
} else if (node == CLUB) {
|
||||||
|
if (Strings.isBlank(booking.getClubId())) throw new IllegalArgumentException("请选择所属协会");
|
||||||
|
filter = " and ur.stid=@org";
|
||||||
|
} else if (node != SCHOOL) throw new IllegalArgumentException("当前节点不允许审核");
|
||||||
|
return listMap(Sqls.create("select distinct u.id,u.loginname from sys_user_role ur join sys_role r on r.id=ur.roleId "
|
||||||
|
+ "join sys_user u on u.id=ur.userId where r.code=@role and coalesce(u.disabled,0)=0 "
|
||||||
|
+ "and coalesce(u.delFlag,0)=0" + filter).setParam("role", role)
|
||||||
|
.setParam("org", node == UNION ? booking.getReserve_person_unionid() : booking.getClubId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 返回非空审核人工号名单,缺少节点负责人时中止事务并提示具体节点。 */
|
||||||
|
private List<String> assignments(int node, ActivitySiteReserve booking) {
|
||||||
|
List<String> names = reviewers(node, booking).stream().map(v -> v.getString("loginname"))
|
||||||
|
.filter(Strings::isNotBlank).distinct().collect(Collectors.toList());
|
||||||
|
if (names.isEmpty()) throw new IllegalArgumentException(node == UNION ? "所属分工会未配置可用的分工会主席"
|
||||||
|
: node == CLUB ? "所选协会未配置可用的协会会长" : "未配置可用的校工会审核人(系统管理员)");
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** stages 是后端固定入口 union/club/school,返回节点 ID,禁止任意传入状态码。 */
|
||||||
|
public int node(String stage) {
|
||||||
|
if ("union".equals(stage)) return UNION;
|
||||||
|
if ("club".equals(stage)) return CLUB;
|
||||||
|
if ("school".equals(stage)) return SCHOOL;
|
||||||
|
throw new IllegalArgumentException("无效的审核入口");
|
||||||
|
}
|
||||||
|
|
||||||
|
private String stage(int node) { return node == UNION ? "union" : node == CLUB ? "club" : "school"; }
|
||||||
|
private String title(int node) { return node == UNION ? "分工会审核" : node == CLUB ? "协会审核" : "校工会审核"; }
|
||||||
|
private String process(ActivitySiteReserve b) { return "activity_site@" + b.getSqid(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存一次预约。form 提供场地、事由、类型和协会;slots 提供 day/start_time/end_time。
|
||||||
|
* 返回新申请 sqid;人员身份、组织、状态在后端生成。锁定场地避免并发超额预约。
|
||||||
|
*/
|
||||||
|
@Aop(TransAop.READ_COMMITTED)
|
||||||
|
public String submit(ActivitySiteReserve form, List<NutMap> slots) {
|
||||||
|
if (form == null || Strings.isBlank(form.getSite_id())) throw new IllegalArgumentException("请选择场地");
|
||||||
|
int first = firstState(form.getReserve_type());
|
||||||
|
if (slots == null || slots.isEmpty()) throw new IllegalArgumentException("请选择预约时段");
|
||||||
|
listMap(Sqls.create("select id from activity_site_info where id=@id for update").setParam("id", form.getSite_id()));
|
||||||
|
ActivitySiteInfo site = dao().fetch(ActivitySiteInfo.class, form.getSite_id());
|
||||||
|
if (site == null || !Boolean.TRUE.equals(site.getState())) throw new IllegalArgumentException("场地不存在或已停用");
|
||||||
|
ActivityType type = dao().fetch(ActivityType.class, site.getTypeId());
|
||||||
|
if (type == null || !Boolean.TRUE.equals(type.getEnabled())) throw new IllegalArgumentException("场地类型已停用");
|
||||||
|
if (Strings.isBlank(form.getReserve_cause())) throw new IllegalArgumentException("请填写预约事由");
|
||||||
|
Sys_user user = dao().fetch(Sys_user.class, uid());
|
||||||
|
form.setReserve_person_id(uid());
|
||||||
|
form.setReserve_person(user.getUsername());
|
||||||
|
form.setReserve_person_phone(user.getMobile());
|
||||||
|
// user 视图按特殊人员工会关系、所属单位计算有效分工会;原表 unionid 可能为空或为历史值。
|
||||||
|
// 提交时保存有效组织快照,后续待办和审核范围均使用该快照匹配主席角色。
|
||||||
|
List<NutMap> profile=listMap(Sqls.create("select unionid,unitname from `user` where id=@id").setParam("id",uid()));
|
||||||
|
form.setReserve_person_unionid(profile.isEmpty() ? null : profile.get(0).getString("unionid"));
|
||||||
|
form.setReserve_person_unit(profile.isEmpty() ? "" : profile.get(0).getString("unitname"));
|
||||||
|
if (first == CLUB && myClubs().stream().noneMatch(v -> v.getString("id").equals(form.getClubId())))
|
||||||
|
throw new IllegalArgumentException("您不是所选协会的有效成员,请重新选择所属协会");
|
||||||
|
if (first != CLUB) form.setClubId(null);
|
||||||
|
// 提交前同时检查首节点和最终节点,避免申请进入流程后无审核人可处理。
|
||||||
|
assignments(first, form);
|
||||||
|
assignments(SCHOOL, form);
|
||||||
|
List<NutMap> open = Json.fromJsonAsList(NutMap.class, Json.toJson(site.getOpen_hours()));
|
||||||
|
Set<String> distinct = new HashSet<>();
|
||||||
|
for (NutMap slot : slots) {
|
||||||
|
String day = slot.getString("day"), start = slot.getString("start_time"), end = slot.getString("end_time");
|
||||||
|
if (Strings.isBlank(day) || Strings.isBlank(start) || Strings.isBlank(end)) throw new IllegalArgumentException("预约日期和起止时间不能为空");
|
||||||
|
LocalDate date = LocalDate.parse(day);
|
||||||
|
LocalTime startTime = LocalTime.parse(start), endTime = "24:00".equals(end) ? LocalTime.MIDNIGHT : LocalTime.parse(end);
|
||||||
|
if (!"24:00".equals(end) && !endTime.isAfter(startTime)) throw new IllegalArgumentException("结束时间必须晚于开始时间");
|
||||||
|
if (!LocalDateTime.of(date, startTime).isAfter(LocalDateTime.now())) throw new IllegalArgumentException("预约时段已过期");
|
||||||
|
if (first == UNION && !date.equals(LocalDate.now().plusDays(1))) throw new IllegalArgumentException("个人预约只支持预约明天的时间");
|
||||||
|
if (Boolean.TRUE.equals(site.getWorkday()) && date.getDayOfWeek().getValue() >= 6) throw new IllegalArgumentException("该场地仅支持工作日预约");
|
||||||
|
if (open == null || open.stream().noneMatch(v -> start.equals(v.getString("start_time")) && end.equals(v.getString("end_time"))))
|
||||||
|
throw new IllegalArgumentException("所选时段不在场地开放场次中,请刷新重选");
|
||||||
|
if (!distinct.add(day + " " + start + " " + end)) throw new IllegalArgumentException("同一申请不能重复选择时段");
|
||||||
|
List<ActivitySiteReserve> existing = query(Cnd.where("site_id", "=", site.getId()).and("reserve_day", "=", day)
|
||||||
|
.and("start_time", "<", end).and("end_time", ">", start).and("reserve_state", "not in", Arrays.asList(REJECT, CANCEL)));
|
||||||
|
if (existing.stream().anyMatch(v -> uid().equals(v.getReserve_person_id()))) throw new IllegalArgumentException("您已预约该时间段");
|
||||||
|
if (first != UNION && !existing.isEmpty()) throw new IllegalArgumentException("该时段已有预约,协会或单位预约需要空闲时段");
|
||||||
|
if (first == UNION && (existing.stream().anyMatch(v -> !Integer.valueOf(1).equals(v.getReserve_type()))
|
||||||
|
|| site.getLimitNum() == null || existing.size() >= site.getLimitNum())) throw new IllegalArgumentException("该时段预约人数已满或已被单位/协会预约");
|
||||||
|
}
|
||||||
|
String sqid = UUID.randomUUID().toString().replace("-", "");
|
||||||
|
for (NutMap slot : slots) {
|
||||||
|
ActivitySiteReserve row = new ActivitySiteReserve();
|
||||||
|
row.setSqid(sqid); row.setSite_id(site.getId()); row.setReserve_type(form.getReserve_type());
|
||||||
|
row.setClubId(form.getClubId()); row.setReserve_person_unionid(form.getReserve_person_unionid());
|
||||||
|
row.setReserve_person_id(uid()); row.setReserve_person(user.getUsername());
|
||||||
|
row.setReserve_person_phone(user.getMobile()); row.setReserve_person_unit(form.getReserve_person_unit());
|
||||||
|
row.setReserve_cause(form.getReserve_cause()); row.setReserve_state(first);
|
||||||
|
row.setReserve_day(slot.getString("day")); row.setStart_time(slot.getString("start_time")); row.setEnd_time(slot.getString("end_time"));
|
||||||
|
dao().insert(row);
|
||||||
|
}
|
||||||
|
form.setSqid(sqid);
|
||||||
|
sysLocalProcessService.startProcess("场地预约:" + site.getName(), process(form), title(first), uid(),
|
||||||
|
"/platform/activity/site/reserve", "/mobile/activity/site/info/my");
|
||||||
|
createTask(first, form);
|
||||||
|
return sqid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 创建当前节点待办,所有候选人共用一个任务,一人处理即完成该节点。 */
|
||||||
|
@Aop(TransAop.READ_COMMITTED)
|
||||||
|
public void createTask(int node, ActivitySiteReserve booking) {
|
||||||
|
String url = "/platform/activity/site/review/" + stage(node);
|
||||||
|
String mobile = "/mobile/activity/site/audit/" + stage(node);
|
||||||
|
sysLocalProcessService.createTask(process(booking), stage(node), title(node), uid(), assignments(node, booking),
|
||||||
|
url, url, mobile, mobile);
|
||||||
|
sysLocalProcessService.updateProcessNodeName(process(booking), title(node));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 返回指定审核入口的数据范围,分工会/协会使用角色关系上的组织字段。 */
|
||||||
|
private String scope(int node) {
|
||||||
|
if (node == SCHOOL) return "exists(select 1 from sys_user_role ur join sys_role r on r.id=ur.roleId where ur.userId=@uid and r.code='sysadmin')";
|
||||||
|
return "exists(select 1 from sys_user_role ur join sys_role r on r.id=ur.roleId where ur.userId=@uid and r.code='"
|
||||||
|
+ (node == UNION ? "gh01" : "club01") + "' and "
|
||||||
|
+ (node == UNION ? "ur.unionid=b.reserve_person_unionid" : "ur.stid=b.clubId") + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询节点待审/已审申请。isAudit=null 为两者合并;返回标准分页 list/totalCount。
|
||||||
|
* 以每个 sqid 的代表行展示,避免按场地分组混合不同申请。
|
||||||
|
*/
|
||||||
|
public Object reviewPage(String stage, Boolean isAudit, String keyword, String month, String typeId, int page, int size) {
|
||||||
|
return reviewPage(stage, isAudit, "", keyword, month, typeId, page, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PC 指定 searchName=asi.name(场地名称)或 asr.reserve_person(预约人);空值兼容手机端组合搜索。
|
||||||
|
* keyword 为关键词,month 为 yyyy-MM,typeId 为场地类型主键;返回 list/totalCount 标准分页结果。
|
||||||
|
* 查询字段只映射固定白名单,不把请求提供的字段名拼入 SQL。
|
||||||
|
*/
|
||||||
|
public Object reviewPage(String stage, Boolean isAudit, String searchName, String keyword, String month, String typeId, int page, int size) {
|
||||||
|
String keywordFilter;
|
||||||
|
if (Strings.isBlank(searchName)) keywordFilter = "s.name like @like or b.reserve_person like @like";
|
||||||
|
else if ("asi.name".equals(searchName)) keywordFilter = "s.name like @like";
|
||||||
|
else if ("asr.reserve_person".equals(searchName)) keywordFilter = "b.reserve_person like @like";
|
||||||
|
else throw new IllegalArgumentException("请选择有效的查询类型");
|
||||||
|
int node = node(stage);
|
||||||
|
String pending = "(b.reserve_state=" + node + " and " + scope(node) + ")";
|
||||||
|
String done = "JSON_CONTAINS(coalesce(b.auditList,'[]'),JSON_OBJECT('auditUser',@uid,'stateCode'," + node + "))";
|
||||||
|
String selected = isAudit == null ? "(" + pending + " or " + done + ")" : isAudit ? done : pending;
|
||||||
|
Sql sql = Sqls.create(baseSelect() + " where b.id=(select min(x.id) from activity_site_reserve x where x.sqid=b.sqid) and "
|
||||||
|
+ selected + " and (@keyword='' or " + keywordFilter + ") "
|
||||||
|
+ "and (@month='' or left(b.reserve_day,7)=@month) and (@typeId='' or s.typeId=@typeId) order by b.opAt desc,b.id")
|
||||||
|
.setParam("uid", uid()).setParam("keyword", Strings.sNull(keyword)).setParam("like", "%" + Strings.sNull(keyword) + "%")
|
||||||
|
.setParam("month", Strings.sNull(month)).setParam("typeId", Strings.sNull(typeId));
|
||||||
|
Pagination result = listPageMap(Math.max(1,page), Math.min(100,Math.max(1,size)), sql);
|
||||||
|
// 两端共用后端资格判断;真正撤回仍在加锁后复查,不能信任列表中的旧状态。
|
||||||
|
for (Map<String,Object> row : result.<Map<String,Object>>getList()) {
|
||||||
|
ActivitySiteReserve booking = fetch(String.valueOf(row.get("id")));
|
||||||
|
String reason = revokeReason(node, booking);
|
||||||
|
row.put("canRevoke", reason == null);
|
||||||
|
row.put("revokeReason", reason);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 公共展示字段,保留既有组件所需别名,并显示预约类型和所属协会。 */
|
||||||
|
private String baseSelect() {
|
||||||
|
return "select b.*,u.loginname,u.sex,s.name site_name,c.name club_name,un.unionname union_name,st.stateName state_name,st.stateColor state_color,st.stateAuditType,"
|
||||||
|
+ "(select count(*) from activity_site_reserve x where x.sqid=b.sqid) days,"
|
||||||
|
+ "(select group_concat(concat(x.reserve_day,' ',x.start_time,'-',x.end_time) order by x.reserve_day,x.start_time separator ';') from activity_site_reserve x where x.sqid=b.sqid) concat_day "
|
||||||
|
+ "from activity_site_reserve b left join sys_user u on u.id=b.reserve_person_id left join activity_site_info s on s.id=b.site_id left join sys_club c on c.id=b.clubId "
|
||||||
|
+ "left join sys_union un on un.id=b.reserve_person_unionid "
|
||||||
|
+ "left join " + STATES + " st on st.stateId=b.reserve_state";
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 详情仅允许申请人或对应组织审核人查看,已处理者可以继续查看自己的历史。 */
|
||||||
|
public NutMap detail(String id) {
|
||||||
|
ActivitySiteReserve b = fetch(id);
|
||||||
|
if (b == null) throw new IllegalArgumentException("预约不存在");
|
||||||
|
boolean allowed = uid().equals(b.getReserve_person_id());
|
||||||
|
for (int node : new java.util.LinkedHashSet<>(Arrays.asList(firstState(b.getReserve_type()), SCHOOL))) {
|
||||||
|
if (node == UNION && Strings.isBlank(b.getReserve_person_unionid()) || node == CLUB && Strings.isBlank(b.getClubId())) continue;
|
||||||
|
allowed |= reviewers(node,b).stream().anyMatch(v -> uid().equals(v.getString("id")));
|
||||||
|
}
|
||||||
|
List<NutMap> history = b.getAuditList() == null ? new ArrayList<>() : b.getAuditList();
|
||||||
|
allowed |= history.stream().anyMatch(v -> uid().equals(v.getString("auditUser")));
|
||||||
|
if (!allowed) throw new IllegalArgumentException("无权查看该预约");
|
||||||
|
NutMap result = listMap(Sqls.create(baseSelect() + " where b.id=@id").setParam("id",id)).get(0);
|
||||||
|
result.put("auditListTable",history);
|
||||||
|
// 日历仅携带本次申请的时段,避免通过详情获取其他申请人的资料。
|
||||||
|
result.put("site_info", listMap(Sqls.create("select b.*,st.stateAuditType from activity_site_reserve b left join "
|
||||||
|
+ STATES + " st on st.stateId=b.reserve_state where b.sqid=@sqid").setParam("sqid",b.getSqid())));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 审核 ids 中的申请;pass 为明确的通过/拒绝,opinion 为必填意见。
|
||||||
|
* 返回 void;事务内锁定申请并检查节点、角色及组织,重复/越权审核不产生历史或待办。
|
||||||
|
*/
|
||||||
|
@Aop(TransAop.READ_COMMITTED)
|
||||||
|
public void review(String stage, String[] ids, Boolean pass, String opinion) {
|
||||||
|
int node = node(stage);
|
||||||
|
if (ids == null || ids.length == 0 || pass == null) throw new IllegalArgumentException("请选择申请及审核结果");
|
||||||
|
if (Strings.isBlank(opinion) || opinion.length() > 500) throw new IllegalArgumentException("请填写不超过500字的审核意见");
|
||||||
|
Set<String> handled = new HashSet<>();
|
||||||
|
// 固定加锁顺序降低批量审核之间的死锁风险。
|
||||||
|
List<String> ordered = Arrays.stream(ids).sorted().collect(Collectors.toList());
|
||||||
|
for (String id : ordered) {
|
||||||
|
ActivitySiteReserve b = fetch(id);
|
||||||
|
if (b == null) throw new IllegalArgumentException("预约不存在,请刷新列表");
|
||||||
|
if (!handled.add(b.getSqid())) continue;
|
||||||
|
listMap(Sqls.create("select id from activity_site_reserve where sqid=@sqid order by id for update").setParam("sqid",b.getSqid()));
|
||||||
|
b = fetch(id);
|
||||||
|
if (!Integer.valueOf(node).equals(b.getReserve_state())) throw new IllegalArgumentException("申请已处理或不属于当前审核节点,请刷新列表");
|
||||||
|
if (reviewers(node,b).stream().noneMatch(v -> uid().equals(v.getString("id")))) throw new IllegalArgumentException("无权审核该分工会或协会的申请");
|
||||||
|
int next = !pass ? REJECT : node == SCHOOL ? PASS : SCHOOL;
|
||||||
|
if (next == SCHOOL) assignments(SCHOOL,b);
|
||||||
|
Audit audit = new Audit();
|
||||||
|
audit.setAuditor(uid()); audit.setUsername(String.valueOf(ShiroUtil.getPrincipalProperty("username"))); audit.setLoginname(String.valueOf(ShiroUtil.getPrincipalProperty("loginname")));
|
||||||
|
audit.setAuditTime(new Date()); audit.setAuditPass(pass); audit.setAuditType(pass ? 1 : 2); audit.setAuditOpinion(opinion);
|
||||||
|
dao().insert(audit);
|
||||||
|
List<NutMap> history = b.getAuditList() == null ? new ArrayList<>() : b.getAuditList();
|
||||||
|
history.add(NutMap.NEW().setv("stateCode",node).setv("auditId",audit.getId()).setv("auditUser",uid())
|
||||||
|
.setv("auditState",pass).setv("auditOption",opinion).setv("auditUserName",audit.getUsername())
|
||||||
|
.setv("auditStateName",title(node)).setv("auditListName",pass ? "审核通过" : "审核拒绝")
|
||||||
|
.setv("auditTime",DateUtil.now()).setv("auditUserUnionName",""));
|
||||||
|
for (ActivitySiteReserve row : query(Cnd.where("sqid","=",b.getSqid()))) {
|
||||||
|
row.setReserve_state(next); row.setAuditList(history); dao().update(row);
|
||||||
|
}
|
||||||
|
sysLocalProcessService.completeTask(stage,process(b),uid(),opinion);
|
||||||
|
if (next == SCHOOL) createTask(SCHOOL,b);
|
||||||
|
else if (next == REJECT) sysLocalProcessService.refuseProcess(process(b),"审核拒绝,流程结束");
|
||||||
|
else sysLocalProcessService.completeProcess(process(b));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 返回不可撤回原因;仅最近一次有效审核的本人且仍拥有该组织审核资格可以撤回。 */
|
||||||
|
private String revokeReason(int node, ActivitySiteReserve booking) {
|
||||||
|
if (booking == null) return "预约不存在";
|
||||||
|
List<NutMap> history = booking.getAuditList();
|
||||||
|
if (history == null || history.isEmpty()) return "申请尚未审核或已撤回";
|
||||||
|
NutMap last = history.get(history.size() - 1);
|
||||||
|
if (last.getInt("stateCode") != node) return "下一级已审核,不能撤回当前节点";
|
||||||
|
if (!uid().equals(last.getString("auditUser"))) return "只能撤回本人最近一次审核";
|
||||||
|
int expected = last.getBoolean("auditState") ? node == SCHOOL ? PASS : SCHOOL : REJECT;
|
||||||
|
if (!Integer.valueOf(expected).equals(booking.getReserve_state())) return "申请状态已变化,请刷新列表";
|
||||||
|
if (reviewers(node,booking).stream().noneMatch(user -> uid().equals(user.getString("id"))))
|
||||||
|
return "已无该组织的审核权限,不能撤回";
|
||||||
|
for (ActivitySiteReserve slot : query(Cnd.where("sqid","=",booking.getSqid()))) {
|
||||||
|
if (!LocalDateTime.of(LocalDate.parse(slot.getReserve_day()),LocalTime.parse(slot.getStart_time())).isAfter(LocalDateTime.now()))
|
||||||
|
return "预约时段已经开始,不能撤回";
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 撤回审核:stage 为固定入口,id 为预约记录主键;恢复同一 sqid 全部时段及当前节点待办。
|
||||||
|
* 仅撤回最近一次本人的有效审核,拒绝后恢复须重新校验占用;返回 void,由 Controller 包装结果。
|
||||||
|
*/
|
||||||
|
@Aop(TransAop.READ_COMMITTED)
|
||||||
|
public void revokeReview(String stage, String id) {
|
||||||
|
int node = node(stage);
|
||||||
|
if (Strings.isBlank(id)) throw new IllegalArgumentException("请选择需要撤回的申请");
|
||||||
|
ActivitySiteReserve booking = fetch(id);
|
||||||
|
if (booking == null) throw new IllegalArgumentException("预约不存在,请刷新列表");
|
||||||
|
// 与提交保持先锁场地再锁申请的顺序,避免拒绝后恢复与新预约抢占同一时段。
|
||||||
|
listMap(Sqls.create("select id from activity_site_info where id=@id for update").setParam("id",booking.getSite_id()));
|
||||||
|
listMap(Sqls.create("select id from activity_site_reserve where sqid=@sqid order by id for update").setParam("sqid",booking.getSqid()));
|
||||||
|
booking = fetch(id);
|
||||||
|
String reason = revokeReason(node,booking);
|
||||||
|
if (reason != null) throw new IllegalArgumentException(reason);
|
||||||
|
ActivitySiteInfo site = dao().fetch(ActivitySiteInfo.class,booking.getSite_id());
|
||||||
|
ActivityType type = site == null ? null : dao().fetch(ActivityType.class,site.getTypeId());
|
||||||
|
if (site == null || !Boolean.TRUE.equals(site.getState()) || type == null || !Boolean.TRUE.equals(type.getEnabled()))
|
||||||
|
throw new IllegalArgumentException("场地或场地类型已停用,不能恢复待审核预约");
|
||||||
|
assignments(node,booking);
|
||||||
|
List<ActivitySiteReserve> slots = query(Cnd.where("sqid","=",booking.getSqid()));
|
||||||
|
if (Integer.valueOf(REJECT).equals(booking.getReserve_state())) {
|
||||||
|
for (ActivitySiteReserve slot : slots) {
|
||||||
|
List<ActivitySiteReserve> occupied = query(Cnd.where("site_id","=",booking.getSite_id())
|
||||||
|
.and("sqid","!=",booking.getSqid()).and("reserve_day","=",slot.getReserve_day())
|
||||||
|
.and("start_time","<",slot.getEnd_time()).and("end_time",">",slot.getStart_time())
|
||||||
|
.and("reserve_state","not in",Arrays.asList(REJECT,CANCEL)));
|
||||||
|
boolean conflict = Integer.valueOf(1).equals(booking.getReserve_type())
|
||||||
|
? occupied.stream().anyMatch(row -> !Integer.valueOf(1).equals(row.getReserve_type())
|
||||||
|
|| row.getReserve_person_id().equals(slot.getReserve_person_id()))
|
||||||
|
|| site.getLimitNum() == null || occupied.size() >= site.getLimitNum()
|
||||||
|
: !occupied.isEmpty();
|
||||||
|
if (conflict) throw new IllegalArgumentException("原预约时段已被占用或人数已满,不能撤回拒绝结果");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String processId = process(booking);
|
||||||
|
Sys_local_process_instance instance = dao().fetch(Sys_local_process_instance.class,
|
||||||
|
Cnd.where("processUniqueId","=",processId).and("processDeleteFlag","=",false).and("delFlag","=",false));
|
||||||
|
Sys_local_process_instance_task task = dao().fetch(Sys_local_process_instance_task.class,
|
||||||
|
Cnd.where("processUniqueId","=",processId).and("taskUniqueId","=",stage)
|
||||||
|
.and("status","=",2).and("taskDeleteFlag","=",false).and("delFlag","=",false).desc("id"));
|
||||||
|
if (instance == null || task == null || !uid().equals(task.getActualOwnerId()))
|
||||||
|
throw new IllegalArgumentException("审核待办记录不完整,不能撤回,请联系管理员");
|
||||||
|
List<NutMap> history = new ArrayList<>(booking.getAuditList());
|
||||||
|
NutMap last = history.remove(history.size()-1);
|
||||||
|
Audit audit = dao().fetch(Audit.class,last.getString("auditId"));
|
||||||
|
if (audit == null) throw new IllegalArgumentException("审核记录不存在,不能撤回");
|
||||||
|
// 原审核保留在 audit,扩展信息记录撤回人、时间及申请关联,不物理删除审核痕迹。
|
||||||
|
cn.hutool.json.JSONObject ext = audit.getExt() == null ? new cn.hutool.json.JSONObject() : audit.getExt();
|
||||||
|
ext.set("siteRevoke",NutMap.NEW().setv("sqid",booking.getSqid()).setv("node",node)
|
||||||
|
.setv("userId",uid()).setv("time",DateUtil.now()));
|
||||||
|
audit.setExt(ext);
|
||||||
|
dao().update(audit,"ext");
|
||||||
|
for (ActivitySiteReserve slot : slots) {
|
||||||
|
slot.setReserve_state(node);
|
||||||
|
slot.setAuditList(history);
|
||||||
|
dao().update(slot);
|
||||||
|
}
|
||||||
|
// 失效本次已办及其后续未办记录,保留前级已办;新建本节点待办,兼容首节点拒绝和终审。
|
||||||
|
dao().update(Sys_local_process_instance_task.class,Chain.make("taskDeleteFlag",true).add("delFlag",true),
|
||||||
|
Cnd.where("processUniqueId","=",processId).and("id",">=",task.getId()).and("taskDeleteFlag","=",false));
|
||||||
|
instance.setProcessInstanceStatus(1);
|
||||||
|
instance.setNodeName(title(node));
|
||||||
|
dao().update(instance);
|
||||||
|
createTask(node,booking);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 仅申请人可撤销尚未处理的首节点申请;保留取消状态及流程记录。 */
|
||||||
|
@Aop(TransAop.READ_COMMITTED)
|
||||||
|
public void cancel(String id) {
|
||||||
|
ActivitySiteReserve b = fetch(id);
|
||||||
|
if (b == null || !uid().equals(b.getReserve_person_id())) throw new IllegalArgumentException("只能撤销本人的申请");
|
||||||
|
listMap(Sqls.create("select id from activity_site_reserve where sqid=@sqid order by id for update").setParam("sqid",b.getSqid()));
|
||||||
|
b=fetch(id);
|
||||||
|
if (b.getReserve_state() != firstState(b.getReserve_type()) || b.getAuditList()!=null && !b.getAuditList().isEmpty())
|
||||||
|
throw new IllegalArgumentException("申请已处理,不能撤销");
|
||||||
|
dao().update(ActivitySiteReserve.class,Chain.make("reserve_state",CANCEL),Cnd.where("sqid","=",b.getSqid()));
|
||||||
|
sysLocalProcessService.refuseProcess(process(b),"申请人已撤销");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 反馈只更新本人申请,option 为不超过200字的使用反馈。 */
|
||||||
|
@Aop(TransAop.READ_COMMITTED)
|
||||||
|
public void feedback(String id,String option) {
|
||||||
|
ActivitySiteReserve b=fetch(id);
|
||||||
|
if(b==null || !uid().equals(b.getReserve_person_id())) throw new IllegalArgumentException("只能反馈本人的预约");
|
||||||
|
if(option!=null && option.length()>200) throw new IllegalArgumentException("反馈不能超过200字");
|
||||||
|
dao().update(ActivitySiteReserve.class,Chain.make("back_option",option),Cnd.where("sqid","=",b.getSqid()));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,310 +1,10 @@
|
|||||||
package io.v.nutz.zhgh.mobile.activity.site;
|
package io.v.nutz.zhgh.mobile.activity.site;
|
||||||
|
|
||||||
import cn.hutool.core.date.DateUtil;
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
|
||||||
import cn.wizzer.framework.page.Pagination;
|
|
||||||
import io.v.nutz.zhgh.activity.models.ActivitySiteInfo;
|
|
||||||
import io.v.nutz.zhgh.activity.models.ActivitySiteReserve;
|
|
||||||
import io.v.nutz.zhgh.activity.services.ActivityCommonService;
|
|
||||||
import io.v.nutz.zhgh.activity.services.SiteInfoService;
|
|
||||||
import io.v.nutz.zhgh.activity.services.SiteReserveService;
|
|
||||||
import io.v.nutz.base.annontation.ViReturn;
|
|
||||||
import io.v.nutz.base.utils.MsgApi;
|
|
||||||
|
|
||||||
import io.v.nutz.base.utils.Vi;
|
|
||||||
import io.v.nutz.base.enums.AuditTypeEnum;
|
|
||||||
import io.v.nutz.base.dao.CndPlus;
|
|
||||||
import io.v.nutz.base.model.Audit;
|
|
||||||
import io.v.nutz.base.query.PageForm;
|
|
||||||
import io.v.nutz.sys.models.Sys_user;
|
|
||||||
import io.v.nutz.sys.services.SysUserService;
|
|
||||||
import io.v.nutz.web.commons.utils.ShiroUtil;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.nutz.dao.Cnd;
|
|
||||||
import org.nutz.dao.Dao;
|
|
||||||
import org.nutz.dao.Sqls;
|
|
||||||
import org.nutz.dao.entity.Record;
|
|
||||||
import org.nutz.dao.sql.Sql;
|
|
||||||
import org.nutz.dao.util.cri.SqlExpressionGroup;
|
|
||||||
import org.nutz.dao.util.cri.Static;
|
|
||||||
import org.nutz.ioc.loader.annotation.Inject;
|
|
||||||
import org.nutz.ioc.loader.annotation.IocBean;
|
import org.nutz.ioc.loader.annotation.IocBean;
|
||||||
import org.nutz.json.Json;
|
import org.nutz.mvc.annotation.*;
|
||||||
import org.nutz.lang.Lang;
|
import org.apache.shiro.authz.annotation.RequiresAuthentication;
|
||||||
import org.nutz.lang.util.NutMap;
|
/** 旧审核入口仅展示三个新入口,不再提供旧审核提交接口。 */
|
||||||
import org.nutz.mvc.annotation.At;
|
@IocBean @At("/mobile/activity/site/audit") @RequiresAuthentication
|
||||||
import org.nutz.mvc.annotation.Ok;
|
|
||||||
import org.nutz.mvc.annotation.Param;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Author JyuHsin
|
|
||||||
* @Date 2022/5/13
|
|
||||||
* @Description
|
|
||||||
*/
|
|
||||||
@IocBean
|
|
||||||
@Ok("json:full")
|
|
||||||
@At("/mobile/activity/site/audit")
|
|
||||||
public class SiteAuditMobileController {
|
public class SiteAuditMobileController {
|
||||||
|
@At("") @Ok("beetl:/platform/activity/site/ReviewEntries.html")
|
||||||
@Inject
|
public void index() { }
|
||||||
private SiteInfoService siteInfoService;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
private SiteReserveService siteReserveService;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
private SysUserService sysUserService;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
private ActivityCommonService activityCommonService;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
private Dao dao;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
private MsgApi msgApi;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@At("")
|
|
||||||
@Ok("beetl:mobile/activity/site/audit.html")
|
|
||||||
public void index() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@At("/pageData")
|
|
||||||
@Ok("json:full")
|
|
||||||
@ViReturn
|
|
||||||
public Object pageData(PageForm pageForm, @Param(value = "typeId", required = false) String typeId) {
|
|
||||||
|
|
||||||
Sql sql = Sqls.create("""
|
|
||||||
SELECT
|
|
||||||
info.*,
|
|
||||||
type.meetingTypeName,
|
|
||||||
type.moduleName,
|
|
||||||
(select unitname from `user` where username = info.contact_person) as unitname,
|
|
||||||
(select count(DISTINCT sqid) from activity_site_reserve where site_id=info.id and JSON_CONTAINS(auditList, JSON_OBJECT('auditUser', @userid))) as audit
|
|
||||||
FROM
|
|
||||||
activity_site_info info
|
|
||||||
LEFT JOIN activity_type type ON type.id = info.typeId
|
|
||||||
LEFT JOIN audit_state state ON state.module = type.moduleName
|
|
||||||
$condition
|
|
||||||
""").setParam("userid", ShiroUtil.getPrincipalProperty("id"));
|
|
||||||
CndPlus cnd = CndPlus.create();
|
|
||||||
|
|
||||||
cnd.andEX("state.stateId", "in", activityCommonService.findStateIdForMeCanAudit());
|
|
||||||
cnd.and("info.state", "=", true);
|
|
||||||
if (StringUtils.isNotBlank(pageForm.getSearchKeyword())) {
|
|
||||||
SqlExpressionGroup seg = new SqlExpressionGroup();
|
|
||||||
seg.orLike("info.name", pageForm.getSearchKeyword());
|
|
||||||
seg.orLike("info.address", pageForm.getSearchKeyword());
|
|
||||||
cnd.and(seg);
|
|
||||||
}
|
|
||||||
if (StrUtil.isNotBlank(typeId)) {
|
|
||||||
cnd.andEX("info.typeId", "=", typeId);
|
|
||||||
}
|
|
||||||
cnd.groupBy("info.id");
|
|
||||||
|
|
||||||
sql.setCondition(cnd);
|
|
||||||
Pagination pagination = siteInfoService.listPage(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
|
|
||||||
|
|
||||||
List<Object> list = pagination.getList();
|
|
||||||
list.forEach(item -> {
|
|
||||||
Record map = (Record) item;
|
|
||||||
List<NutMap> canAuditUserByLoginUser = this.getCanAuditUserByLoginUser(map.getString("id"), map.getString("moduleName"));
|
|
||||||
map.put("no_audit", canAuditUserByLoginUser.size());
|
|
||||||
});
|
|
||||||
return pagination;
|
|
||||||
}
|
|
||||||
|
|
||||||
@At("/getLeaveUser")
|
|
||||||
@Ok("json:full")
|
|
||||||
@ViReturn
|
|
||||||
public Object getLeaveUser(@Param(value = "siteId", required = false) String siteId,
|
|
||||||
@Param(value = "moduleName", required = false) String moduleName,
|
|
||||||
@Param(value = "auditType", required = false)String auditType) {
|
|
||||||
|
|
||||||
List<NutMap> list = "canAudit".equals(auditType) ? this.getCanAuditUserByLoginUser(siteId, moduleName) : this.getHasAuditUserByLoginUser(siteId);
|
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*根据当前登录用户获取可以审核的用户*/
|
|
||||||
public List<NutMap> getCanAuditUserByLoginUser(String siteId, String moduleName) {
|
|
||||||
Sql sql = Sqls.create("""
|
|
||||||
SELECT
|
|
||||||
ar.*,
|
|
||||||
u.username,
|
|
||||||
u.loginname,
|
|
||||||
u.sex,
|
|
||||||
u.unitname,
|
|
||||||
state.stateName,
|
|
||||||
count(sqid) days,
|
|
||||||
GROUP_CONCAT(reserve_day order by reserve_day asc) concat_day
|
|
||||||
FROM
|
|
||||||
activity_site_reserve ar
|
|
||||||
LEFT JOIN activity_site_info asi ON asi.id = ar.site_id
|
|
||||||
left join `user` u on ar.reserve_person_id=u.id
|
|
||||||
LEFT JOIN audit_state state on state.stateId = ar.reserve_state
|
|
||||||
$condition
|
|
||||||
""");
|
|
||||||
Cnd cnd = Cnd.NEW();
|
|
||||||
cnd.andEX("ar.site_id", "=", siteId);
|
|
||||||
cnd.and("u.username", "is not", null);
|
|
||||||
cnd.and("u.username", "!=", "");
|
|
||||||
cnd.and("ar.reserve_state", "in", Sqls.createf("SELECT stateId FROM audit_state_user WHERE userid = '%s'", ShiroUtil.getPrincipalProperty("id")));
|
|
||||||
cnd.and("state.stateAuditType", "in", Lang.list(AuditTypeEnum.AUDIT.getValue()));
|
|
||||||
|
|
||||||
/*if(!ShiroUtil.hasAnyRoles(new String[]{"xghng","A06","sysadmin"})) {
|
|
||||||
if (ShiroUtil.hasAnyRoles(new String[]{"gh10"})) {
|
|
||||||
Sys_user user = (Sys_user) ShiroUtil.getPrincipal();
|
|
||||||
cnd.and("u.unitid", "=", user.getUnitid());
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
if (!io.v.nutz.web.commons.utils.ShiroUtil.hasAnyRoles(new String[]{"sysadmin"})) {
|
|
||||||
cnd.and(new Static("if(asi.typeId=1, u.unitid = '" + Vi.getUnit().getId() + "', 1=1)"));
|
|
||||||
}
|
|
||||||
|
|
||||||
cnd.groupBy("sqid");
|
|
||||||
|
|
||||||
sql.setCondition(cnd);
|
|
||||||
|
|
||||||
return listMap(sql);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*根据当前登录用户获取已经审核的用户*/
|
|
||||||
public List<NutMap> getHasAuditUserByLoginUser(String siteId) {
|
|
||||||
Sql sql = Sqls.create("""
|
|
||||||
select
|
|
||||||
ar.*,
|
|
||||||
u.username,
|
|
||||||
u.loginname,
|
|
||||||
u.unitname,
|
|
||||||
u.sex,
|
|
||||||
state.stateName,
|
|
||||||
count(sqid) days,
|
|
||||||
GROUP_CONCAT(reserve_day order by reserve_day asc) concat_day
|
|
||||||
from
|
|
||||||
activity_site_reserve ar
|
|
||||||
left join
|
|
||||||
`user` u on ar.reserve_person_id=u.id
|
|
||||||
LEFT JOIN
|
|
||||||
audit_state state on state.stateId = ar.reserve_state
|
|
||||||
$condition
|
|
||||||
""");
|
|
||||||
Cnd cnd = Cnd.NEW();
|
|
||||||
cnd.and("site_id", "=", siteId);
|
|
||||||
cnd.and(new Static("JSON_CONTAINS(auditList, JSON_OBJECT('auditUser', '" + ShiroUtil.getPrincipalProperty("id") + "'))"));
|
|
||||||
|
|
||||||
cnd.groupBy("sqid");
|
|
||||||
|
|
||||||
sql.setCondition(cnd);
|
|
||||||
List<NutMap> list = listMap(sql);
|
|
||||||
list.forEach(item -> {
|
|
||||||
List<NutMap> listMap = Json.fromJsonAsList(NutMap.class, item.getString("auditList"));
|
|
||||||
Boolean auditState = false;
|
|
||||||
for (NutMap map : listMap) {
|
|
||||||
if (map.getString("auditUser").equals(ShiroUtil.getPrincipalProperty("id"))) {
|
|
||||||
auditState = map.get("auditState") == null ? null : map.getBoolean("auditState");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
item.put("auditState", auditState);
|
|
||||||
});
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
@At("/audit")
|
|
||||||
@Ok("json:full")
|
|
||||||
@ViReturn
|
|
||||||
public Object audit(@Param(value = "ids", required = false) String[] ids, Audit audit,
|
|
||||||
@Param(value = "isPass", required = false) boolean isPass) {
|
|
||||||
dao.insert(audit);
|
|
||||||
for (String id : ids) {
|
|
||||||
|
|
||||||
ActivitySiteReserve fetch = siteReserveService.fetch(id);
|
|
||||||
|
|
||||||
Integer stateCode = fetch.getReserve_state();
|
|
||||||
Integer afterStateCode = activityCommonService.findAfterStateCode(stateCode, isPass);
|
|
||||||
|
|
||||||
List<ActivitySiteReserve> reserves = siteReserveService.query(Cnd.NEW().and("sqid", "=", fetch.getSqid()));
|
|
||||||
String days = "";
|
|
||||||
|
|
||||||
for (ActivitySiteReserve siteReserve : reserves) {
|
|
||||||
siteReserve.setReserve_state(afterStateCode);
|
|
||||||
|
|
||||||
List<NutMap> auditIdList = siteReserve.getAuditList();
|
|
||||||
if (Lang.isEmpty(auditIdList)) {
|
|
||||||
auditIdList = new ArrayList<>();
|
|
||||||
}
|
|
||||||
auditIdList.add(NutMap.NEW().addv("stateCode", stateCode)
|
|
||||||
.addv("auditId", audit.getId())
|
|
||||||
.addv("auditUser", ShiroUtil.getPrincipalProperty("id"))
|
|
||||||
.addv("auditState", isPass)
|
|
||||||
.addv("auditOption", audit.getAuditOpinion()));
|
|
||||||
siteReserve.setAuditList(auditIdList);
|
|
||||||
|
|
||||||
days += siteReserve.getReserve_day() + ",";
|
|
||||||
dao.update(siteReserve);
|
|
||||||
}
|
|
||||||
days = days.substring(0, days.length() - 1);
|
|
||||||
|
|
||||||
Integer successCode = activityCommonService.findSuccessStateCode(fetch.getSite_id());
|
|
||||||
ActivitySiteInfo siteInfo = siteInfoService.fetch(fetch.getSite_id());
|
|
||||||
if (afterStateCode.equals(successCode) && siteInfo.getTypeId() == 1) {
|
|
||||||
String content = "%s老师您好!您提交的%s使用申请已通过审批,在预约使用期间可刷校园卡进入母婴室,如有疑问,欢迎咨询校工会。"
|
|
||||||
.formatted(fetch.getReserve_person(), siteInfo.getName());
|
|
||||||
Sys_user user = sysUserService.fetch(fetch.getReserve_person_id());
|
|
||||||
|
|
||||||
ArrayList<Map> list2 = new ArrayList<>();
|
|
||||||
list2.add(Map.of("type", "User", "userId", user.getLoginname(), "name", user.getUsername()));
|
|
||||||
// msgApi.sendMsg(content, list2, "场地预约", "WeChat", MsgApi.sendMode.normal.name());
|
|
||||||
} else if (!afterStateCode.equals(successCode)) {
|
|
||||||
//发送微信提醒审核人
|
|
||||||
//查询状态码对应的审核人
|
|
||||||
Sql sql = Sqls.create("""
|
|
||||||
select userId,u.loginname,u.username,u.unitname from audit_state_user asu
|
|
||||||
left join `user` u on u.id = asu.userId left join audit_state s on s.stateId=asu.stateId $condition
|
|
||||||
""");
|
|
||||||
Cnd cnd = Cnd.NEW();
|
|
||||||
cnd.and("asu.stateId", "=", afterStateCode);
|
|
||||||
cnd.and("s.stateAuditType", "=", "0");
|
|
||||||
String moduleName = activityCommonService.findModuleNameByActivityId(fetch.getSite_id());
|
|
||||||
if ("infantRoom".equals(moduleName)) {
|
|
||||||
//获取当前登录人的单位id
|
|
||||||
String unitId = io.v.nutz.web.commons.utils.ShiroUtil.getPrincipalProperty("unitid").toString();
|
|
||||||
cnd.and("u.unitid", "=", unitId);
|
|
||||||
}
|
|
||||||
sql.setCondition(cnd);
|
|
||||||
List<Record> list = siteReserveService.list(sql);
|
|
||||||
|
|
||||||
for (Record item : list) {
|
|
||||||
String content = "%s老师您好!%s老师预约%s日使用%s,请您通过门户网站或点击详情登录智慧工会平台进行审核。"
|
|
||||||
.formatted(item.getString("username"),
|
|
||||||
fetch.getReserve_person(),
|
|
||||||
days,
|
|
||||||
siteInfo.getName());
|
|
||||||
Sys_user sys_user = sysUserService.fetch(item.getString("userId"));
|
|
||||||
NutMap map = new NutMap();
|
|
||||||
map.setv("keyword1", NutMap.NEW().setv("value", "场地预约"));
|
|
||||||
map.setv("keyword2", NutMap.NEW().setv("value", item.getString("username")
|
|
||||||
+ "(" + item.getString("loginname") + ")"));
|
|
||||||
map.setv("keyword3", NutMap.NEW().setv("value", item.getString("unitname")));
|
|
||||||
map.setv("keyword4", NutMap.NEW().setv("value", content));
|
|
||||||
map.setv("keyword5", NutMap.NEW().setv("value", DateUtil.now()));
|
|
||||||
// msgApi.sendMsg(sys_user.getWeAppOpenid(), msgApi.DSH_TEMPLATE_ID, Globals.AppDomain + "/mobile/activity/site/audit", map);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<NutMap> listMap(Sql sql) {
|
|
||||||
sql.setCallback(Sqls.callback.maps());
|
|
||||||
dao.execute(sql);
|
|
||||||
return sql.getList(NutMap.class);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package io.v.nutz.zhgh.mobile.activity.site;
|
||||||
|
import io.v.nutz.base.annontation.ViReturn;
|
||||||
|
import io.v.nutz.zhgh.activity.services.impl.SiteBookingService;
|
||||||
|
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||||
|
import org.nutz.ioc.loader.annotation.*;
|
||||||
|
import org.nutz.ioc.aop.Aop;
|
||||||
|
import org.nutz.aop.interceptor.ioc.TransAop;
|
||||||
|
import org.nutz.mvc.annotation.*;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
/** 协会审核:PC/H5 共享业务服务,入口固定节点,不能由请求绕过节点限制。 */
|
||||||
|
@IocBean @Ok("json:full") @At("/mobile/activity/site/audit/club")
|
||||||
|
@RequiresPermissions("activity.site.review.club")
|
||||||
|
public class SiteClubAuditMobileController {
|
||||||
|
@Inject private SiteBookingService siteBookingService;
|
||||||
|
@At("") @Ok("beetl:/mobile/activity/site/audit.html")
|
||||||
|
public void index(HttpServletRequest request) {
|
||||||
|
request.setAttribute("reviewTitle","协会审核");
|
||||||
|
request.setAttribute("reviewApi","/mobile/activity/site/audit/club");
|
||||||
|
}
|
||||||
|
/** isAudit 为未审/已审/null全部;keyword/month/typeId 为筛选条件,返回分页 list/totalCount。 */
|
||||||
|
@At @ViReturn
|
||||||
|
public Object pageData(Boolean isAudit,String searchKeyword,String month,String typeId,int pageNumber,int pageSize) {
|
||||||
|
return siteBookingService.reviewPage("club",isAudit,searchKeyword,month,typeId,pageNumber,pageSize);
|
||||||
|
}
|
||||||
|
/** id 是预约记录主键,返回申请、时段及审核历史,服务层校验查看范围。 */
|
||||||
|
@At @ViReturn
|
||||||
|
public Object detail(String id) { return siteBookingService.detail(id); }
|
||||||
|
/** ids 为预约记录 ID 数组,isPass 为通过/拒绝,auditOpinion 为必填意见;返回 Object,由 @ViReturn 包装 code(0 成功)、msg(结果说明)。 */
|
||||||
|
@At @ViReturn @Aop(TransAop.READ_COMMITTED)
|
||||||
|
public Object doReview(String[] ids,Boolean isPass,String auditOpinion) {
|
||||||
|
siteBookingService.review("club",ids,isPass,auditOpinion);
|
||||||
|
// 非 void 签名保留拦截器生成的统一结果,避免审核成功却返回空响应。
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
/** id 为预约记录主键,撤回本人最近一次审核;返回统一 code/msg,成功后恢复当前节点待审。 */
|
||||||
|
@At @ViReturn @Aop(TransAop.READ_COMMITTED)
|
||||||
|
public Object doRevoke(String id) {
|
||||||
|
siteBookingService.revokeReview("club",id);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,6 +31,9 @@ import org.nutz.json.Json;
|
|||||||
import org.nutz.lang.random.R;
|
import org.nutz.lang.random.R;
|
||||||
import org.nutz.lang.util.NutMap;
|
import org.nutz.lang.util.NutMap;
|
||||||
import org.nutz.mvc.annotation.At;
|
import org.nutz.mvc.annotation.At;
|
||||||
|
import io.v.nutz.zhgh.activity.services.impl.SiteBookingService;
|
||||||
|
import org.nutz.ioc.aop.Aop;
|
||||||
|
import org.nutz.aop.interceptor.ioc.TransAop;
|
||||||
import org.nutz.mvc.annotation.Ok;
|
import org.nutz.mvc.annotation.Ok;
|
||||||
import org.nutz.mvc.annotation.Param;
|
import org.nutz.mvc.annotation.Param;
|
||||||
|
|
||||||
@@ -45,9 +48,12 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
@IocBean
|
@IocBean
|
||||||
@Ok("json:full")
|
@Ok("json:full")
|
||||||
|
@org.apache.shiro.authz.annotation.RequiresPermissions("activity.site.reserve")
|
||||||
@At("/mobile/activity/site/info")
|
@At("/mobile/activity/site/info")
|
||||||
public class SiteInfoMobileController {
|
public class SiteInfoMobileController {
|
||||||
|
|
||||||
|
@Inject private SiteBookingService siteBookingService;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private SiteInfoService siteInfoService;
|
private SiteInfoService siteInfoService;
|
||||||
|
|
||||||
@@ -92,16 +98,16 @@ public class SiteInfoMobileController {
|
|||||||
type.meetingTypeName,
|
type.meetingTypeName,
|
||||||
type.moduleName,
|
type.moduleName,
|
||||||
(select unitname from `user` where username = info.contact_person) as unitname,
|
(select unitname from `user` where username = info.contact_person) as unitname,
|
||||||
( SELECT count( 1 ) FROM activity_site_reserve WHERE site_id = info.id AND reserve_state = (select stateId from audit_state where module = (select moduleName from activity_type where id = info.typeId) and stateAuditType = 3)) count
|
( SELECT count( 1 ) FROM activity_site_reserve WHERE site_id = info.id AND reserve_state = 4030) count
|
||||||
FROM
|
FROM
|
||||||
activity_site_info info
|
activity_site_info info
|
||||||
LEFT JOIN activity_type type ON type.id = info.typeId
|
LEFT JOIN activity_type type ON type.id = info.typeId
|
||||||
LEFT JOIN audit_state state ON state.module = type.moduleName
|
|
||||||
$condition
|
$condition
|
||||||
""");
|
""");
|
||||||
Cnd cnd = Cnd.NEW();
|
Cnd cnd = Cnd.NEW();
|
||||||
|
|
||||||
cnd.and("info.state", "=", true);
|
cnd.and("info.state", "=", true);
|
||||||
|
cnd.and("type.enabled","=",true);
|
||||||
if (StringUtils.isNotBlank(pageForm.getSearchKeyword())) {
|
if (StringUtils.isNotBlank(pageForm.getSearchKeyword())) {
|
||||||
SqlExpressionGroup seg = new SqlExpressionGroup();
|
SqlExpressionGroup seg = new SqlExpressionGroup();
|
||||||
seg.orLike("info.name", pageForm.getSearchKeyword());
|
seg.orLike("info.name", pageForm.getSearchKeyword());
|
||||||
@@ -147,7 +153,7 @@ public class SiteInfoMobileController {
|
|||||||
from
|
from
|
||||||
activity_site_reserve ar
|
activity_site_reserve ar
|
||||||
left join activity_site_info info on info.id = ar.site_id
|
left join activity_site_info info on info.id = ar.site_id
|
||||||
left join audit_state `as` on ar.reserve_state=`as`.stateId
|
left join (select state_id stateId,state_name stateName,state_color stateColor,'activity_site' module,case when state_id=4030 then 3 when state_id in (4040,4050) then 1 else 0 end stateAuditType from state where belong='activity_site') `as` on ar.reserve_state=`as`.stateId
|
||||||
$condition
|
$condition
|
||||||
""");
|
""");
|
||||||
|
|
||||||
@@ -161,7 +167,7 @@ public class SiteInfoMobileController {
|
|||||||
if (StrUtil.isNotBlank(typeId)) {
|
if (StrUtil.isNotBlank(typeId)) {
|
||||||
cnd.andEX("info.typeId", "=", typeId);
|
cnd.andEX("info.typeId", "=", typeId);
|
||||||
}
|
}
|
||||||
if (!timeSwitch && StrUtil.isNotBlank(time)) {
|
if (!Boolean.TRUE.equals(timeSwitch) && StrUtil.isNotBlank(time)) {
|
||||||
cnd.and("left(ar.reserve_day, 7)", "=", time);
|
cnd.and("left(ar.reserve_day, 7)", "=", time);
|
||||||
}
|
}
|
||||||
cnd.and("reserve_person_id", "=", ShiroUtil.getPrincipalProperty("id"));
|
cnd.and("reserve_person_id", "=", ShiroUtil.getPrincipalProperty("id"));
|
||||||
@@ -183,26 +189,18 @@ public class SiteInfoMobileController {
|
|||||||
@At("/rollback")
|
@At("/rollback")
|
||||||
@Ok("json:full")
|
@Ok("json:full")
|
||||||
@ViReturn
|
@ViReturn
|
||||||
|
@Aop(TransAop.READ_COMMITTED)
|
||||||
public Object rollback(String id) {
|
public Object rollback(String id) {
|
||||||
//根据活动id查询第一个审核节点
|
siteBookingService.cancel(id);
|
||||||
ActivitySiteReserve fetch = siteReserveService.fetch(id);
|
|
||||||
String moduleName = activityCommonService.findModuleNameByActivityId(fetch.getSite_id());
|
|
||||||
Integer stateCode = activityCommonService.findStartStateCodeByModuleName(moduleName);
|
|
||||||
|
|
||||||
if (fetch.getReserve_state() > stateCode) {
|
|
||||||
return Result.error("当前状态不能进行撤销操作");
|
|
||||||
}
|
|
||||||
ActivitySiteReserve siteReserve = siteReserveService.fetch(id);
|
|
||||||
siteReserveService.clear(Cnd.NEW().and("sqid", "=", siteReserve.getSqid()));
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@At("/backOption")
|
@At("/backOption")
|
||||||
@Ok("json:full")
|
@Ok("json:full")
|
||||||
@ViReturn
|
@ViReturn
|
||||||
|
@Aop(TransAop.READ_COMMITTED)
|
||||||
public Object backOption(String id, String option) {
|
public Object backOption(String id, String option) {
|
||||||
ActivitySiteReserve fetch = siteReserveService.fetch(id);
|
siteBookingService.feedback(id,option);
|
||||||
siteReserveService.update(Chain.make("back_option", option), Cnd.NEW().and("sqid", "=", fetch.getSqid()));
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,7 +211,6 @@ public class SiteInfoMobileController {
|
|||||||
|
|
||||||
//获取场地的开放时间
|
//获取场地的开放时间
|
||||||
ActivitySiteInfo fetch = siteInfoService.fetch(siteId);
|
ActivitySiteInfo fetch = siteInfoService.fetch(siteId);
|
||||||
String moduleName = activityCommonService.findModuleNameByActivityId(siteId);
|
|
||||||
List<NutMap> hours = fetch.getOpen_hours();
|
List<NutMap> hours = fetch.getOpen_hours();
|
||||||
Integer limitNum = fetch.getLimitNum();
|
Integer limitNum = fetch.getLimitNum();
|
||||||
|
|
||||||
@@ -240,10 +237,10 @@ public class SiteInfoMobileController {
|
|||||||
.and("start_time", "=", startTime)
|
.and("start_time", "=", startTime)
|
||||||
.and("end_time", "=", endTime)
|
.and("end_time", "=", endTime)
|
||||||
.and("site_id", "=", siteId)
|
.and("site_id", "=", siteId)
|
||||||
.and(new Static(" reserve_state in (select stateId from audit_state where stateAuditType != 1 and module = '%s')".formatted(moduleName))));
|
.and("reserve_state", "not in", java.util.Arrays.asList(4040,4050)));
|
||||||
|
|
||||||
//如果这个时间段被单位预约了,直接显示约满
|
//如果这个时间段被单位预约了,直接显示约满
|
||||||
ActivitySiteReserve reserve1 = list.stream().filter(o -> o.getReserve_type() == 2).findAny().orElse(null);
|
ActivitySiteReserve reserve1 = list.stream().filter(o -> o.getReserve_type() != 1).findAny().orElse(null);
|
||||||
if(reserve1 != null) {
|
if(reserve1 != null) {
|
||||||
item.put("code", -2);
|
item.put("code", -2);
|
||||||
item.put("msg", "已约满");
|
item.put("msg", "已约满");
|
||||||
@@ -274,111 +271,11 @@ public class SiteInfoMobileController {
|
|||||||
@At("/reserveDo")
|
@At("/reserveDo")
|
||||||
@Ok("json:full")
|
@Ok("json:full")
|
||||||
@ViReturn
|
@ViReturn
|
||||||
public Object reserveDo(String siteId, String times, String message, Integer reserve_type) {
|
/** siteId 为场地;times 为 day/start_time/end_time 数组 JSON;message 为事由;reserve_type 为1/2/3,clubId 为协会预约所属协会;返回 sqid。 */
|
||||||
String moduleName = activityCommonService.findModuleNameByActivityId(siteId);
|
@Aop(TransAop.READ_COMMITTED)
|
||||||
Integer stateCode = activityCommonService.findStartStateCodeByModuleName(moduleName);
|
public Object reserveDo(String siteId, String times, String message, Integer reserve_type, String clubId) {
|
||||||
|
ActivitySiteReserve form=new ActivitySiteReserve();
|
||||||
if (stateCode == null) {
|
form.setSite_id(siteId);form.setReserve_cause(message);form.setReserve_type(reserve_type);form.setClubId(clubId);
|
||||||
return null;
|
return siteBookingService.submit(form,Json.fromJsonAsList(NutMap.class,times));
|
||||||
}
|
|
||||||
|
|
||||||
Sys_user user = (Sys_user) ShiroUtil.getPrincipal();
|
|
||||||
List<ActivitySiteReserve> li = new ArrayList<>();
|
|
||||||
|
|
||||||
List<NutMap> nutMaps = Json.fromJsonAsList(NutMap.class, times);
|
|
||||||
|
|
||||||
//主要来查询个人预约时的人数上限
|
|
||||||
ActivitySiteInfo siteInfo = siteInfoService.dao().fetch(ActivitySiteInfo.class, siteId);
|
|
||||||
Integer limitNum = siteInfo.getLimitNum();
|
|
||||||
|
|
||||||
for (NutMap item : nutMaps) {
|
|
||||||
String time = item.getString("day") + " " + item.getString("start_time");
|
|
||||||
int compare = DateUtil.compare(new Date(), DateUtil.parse(time), "yyyy-MM-dd HH:mm");
|
|
||||||
if(compare > 0) {
|
|
||||||
return Result.error("您预约的【%s】时间已过".formatted(time));
|
|
||||||
}
|
|
||||||
if(reserve_type == 1 && !item.getString("day").equals(DateUtil.format(DateUtil.offsetDay(new Date(), 1), "yyyy-MM-dd"))) {
|
|
||||||
return Result.error("个人预约只支持预约明天的时间");
|
|
||||||
}
|
|
||||||
|
|
||||||
List<ActivitySiteReserve> list = siteReserveService.query(Cnd.NEW()
|
|
||||||
.and("reserve_day", "=", item.getString("day"))
|
|
||||||
.and("start_time", "=", item.getString("start_time"))
|
|
||||||
.and("end_time", "=", item.getString("end_time"))
|
|
||||||
.and("site_id", "=", siteId)
|
|
||||||
.and(new Static(" reserve_state in (select stateId from audit_state where stateAuditType != 1 and module = '%s')".formatted(moduleName))));
|
|
||||||
ActivitySiteReserve reserve = list.stream().filter(o -> o.getReserve_person_id().equals(ShiroUtil.getPrincipalProperty("id"))).findAny().orElse(null);
|
|
||||||
if (reserve != null) {
|
|
||||||
return Result.error("您已预约该时间段!");
|
|
||||||
}
|
|
||||||
if(reserve_type == 1) {
|
|
||||||
if((list.size() + 1) > limitNum) {
|
|
||||||
return Result.error("【%s】时间段预约人数已满!".formatted(time));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if(list.size() > 0) {
|
|
||||||
return Result.error("【%s】时间段已有预约!".formatted(time));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String str = "";
|
|
||||||
String r = R.UU32();
|
|
||||||
for (NutMap item : nutMaps) {
|
|
||||||
ActivitySiteReserve as = new ActivitySiteReserve();
|
|
||||||
as.setSqid(r);
|
|
||||||
as.setSite_id(siteId);
|
|
||||||
as.setReserve_person(user.getUsername());
|
|
||||||
as.setReserve_person_id(user.getId());
|
|
||||||
as.setReserve_person_unit(user.getUnit() == null ? null : user.getUnit().getName());
|
|
||||||
as.setReserve_person_phone(user.getMobile());
|
|
||||||
as.setReserve_cause(message);
|
|
||||||
as.setReserve_state(stateCode);
|
|
||||||
as.setReserve_day(item.getString("day"));
|
|
||||||
as.setStart_time(item.getString("start_time"));
|
|
||||||
as.setEnd_time(item.getString("end_time"));
|
|
||||||
as.setReserve_type(reserve_type);
|
|
||||||
str += item.getString("day") + ",";
|
|
||||||
li.add(as);
|
|
||||||
}
|
|
||||||
str = str.substring(0, str.length() - 1);
|
|
||||||
|
|
||||||
siteReserveService.insert(li);
|
|
||||||
|
|
||||||
//发送微信提醒审核人
|
|
||||||
//查询状态码对应的审核人
|
|
||||||
Sql sql = Sqls.create("""
|
|
||||||
select userId,u.loginname,u.username,u.unitname from audit_state_user asu
|
|
||||||
left join `user` u on u.id = asu.userId left join audit_state s on s.stateId=asu.stateId $condition
|
|
||||||
""");
|
|
||||||
Cnd cnd = Cnd.NEW();
|
|
||||||
cnd.and("asu.stateId", "=", stateCode);
|
|
||||||
cnd.and("s.stateAuditType", "=", "0");
|
|
||||||
if ("infantRoom".equals(moduleName)) {
|
|
||||||
//获取当前登录人的单位id
|
|
||||||
String unitId = ShiroUtil.getPrincipalProperty("unitid").toString();
|
|
||||||
cnd.and("u.unitid", "=", unitId);
|
|
||||||
}
|
|
||||||
sql.setCondition(cnd);
|
|
||||||
List<Record> list = siteReserveService.list(sql);
|
|
||||||
|
|
||||||
for (Record item : list) {
|
|
||||||
String content = "%s老师您好!%s老师预约%s日使用%s,请您通过门户网站或点击详情登录智慧工会平台进行审核。"
|
|
||||||
.formatted(item.getString("username"),
|
|
||||||
user.getUsername(),
|
|
||||||
str,
|
|
||||||
siteInfoService.fetch(siteId).getName());
|
|
||||||
|
|
||||||
Sys_user sys_user = sysUserService.fetch(item.getString("userId"));
|
|
||||||
NutMap map = new NutMap();
|
|
||||||
map.setv("keyword1", NutMap.NEW().setv("value", "场地预约"));
|
|
||||||
map.setv("keyword2", NutMap.NEW().setv("value", item.getString("username")
|
|
||||||
+ "(" + item.getString("loginname") + ")"));
|
|
||||||
map.setv("keyword3", NutMap.NEW().setv("value", item.getString("unitname")));
|
|
||||||
map.setv("keyword4", NutMap.NEW().setv("value", content));
|
|
||||||
map.setv("keyword5", NutMap.NEW().setv("value", DateUtil.now()));
|
|
||||||
// msgApi.sendMsg(sys_user.getWeAppOpenid(), msgApi.DSH_TEMPLATE_ID, Globals.AppDomain + "/mobile/activity/site/audit", map);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
package io.v.nutz.zhgh.mobile.activity.site;
|
||||||
|
import io.v.nutz.base.annontation.ViReturn;
|
||||||
|
import io.v.nutz.zhgh.activity.services.impl.SiteBookingService;
|
||||||
|
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||||
|
import org.nutz.ioc.loader.annotation.*;
|
||||||
|
import org.nutz.ioc.aop.Aop;
|
||||||
|
import org.nutz.aop.interceptor.ioc.TransAop;
|
||||||
|
import org.nutz.mvc.annotation.*;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
/** 校工会审核:PC/H5 共享业务服务,入口固定节点,不能由请求绕过节点限制。 */
|
||||||
|
@IocBean @Ok("json:full") @At("/mobile/activity/site/audit/school")
|
||||||
|
@RequiresPermissions("activity.site.review.school")
|
||||||
|
public class SiteSchoolAuditMobileController {
|
||||||
|
@Inject private SiteBookingService siteBookingService;
|
||||||
|
@At("") @Ok("beetl:/mobile/activity/site/audit.html")
|
||||||
|
public void index(HttpServletRequest request) {
|
||||||
|
request.setAttribute("reviewTitle","校工会审核");
|
||||||
|
request.setAttribute("reviewApi","/mobile/activity/site/audit/school");
|
||||||
|
}
|
||||||
|
/** isAudit 为未审/已审/null全部;keyword/month/typeId 为筛选条件,返回分页 list/totalCount。 */
|
||||||
|
@At @ViReturn
|
||||||
|
public Object pageData(Boolean isAudit,String searchKeyword,String month,String typeId,int pageNumber,int pageSize) {
|
||||||
|
return siteBookingService.reviewPage("school",isAudit,searchKeyword,month,typeId,pageNumber,pageSize);
|
||||||
|
}
|
||||||
|
/** id 是预约记录主键,返回申请、时段及审核历史,服务层校验查看范围。 */
|
||||||
|
@At @ViReturn
|
||||||
|
public Object detail(String id) { return siteBookingService.detail(id); }
|
||||||
|
/** ids 为预约记录 ID 数组,isPass 为通过/拒绝,auditOpinion 为必填意见;返回 Object,由 @ViReturn 包装 code(0 成功)、msg(结果说明)。 */
|
||||||
|
@At @ViReturn @Aop(TransAop.READ_COMMITTED)
|
||||||
|
public Object doReview(String[] ids,Boolean isPass,String auditOpinion) {
|
||||||
|
siteBookingService.review("school",ids,isPass,auditOpinion);
|
||||||
|
// 非 void 签名保留拦截器生成的统一结果,避免审核成功却返回空响应。
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
/** id 为预约记录主键,撤回本人最近一次审核;返回统一 code/msg,成功后恢复当前节点待审。 */
|
||||||
|
@At @ViReturn @Aop(TransAop.READ_COMMITTED)
|
||||||
|
public Object doRevoke(String id) {
|
||||||
|
siteBookingService.revokeReview("school",id);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package io.v.nutz.zhgh.mobile.activity.site;
|
||||||
|
import io.v.nutz.base.annontation.ViReturn;
|
||||||
|
import io.v.nutz.zhgh.activity.services.impl.SiteBookingService;
|
||||||
|
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||||
|
import org.nutz.ioc.loader.annotation.*;
|
||||||
|
import org.nutz.ioc.aop.Aop;
|
||||||
|
import org.nutz.aop.interceptor.ioc.TransAop;
|
||||||
|
import org.nutz.mvc.annotation.*;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
/** 分工会审核:PC/H5 共享业务服务,入口固定节点,不能由请求绕过节点限制。 */
|
||||||
|
@IocBean @Ok("json:full") @At("/mobile/activity/site/audit/union")
|
||||||
|
@RequiresPermissions("activity.site.review.union")
|
||||||
|
public class SiteUnionAuditMobileController {
|
||||||
|
@Inject private SiteBookingService siteBookingService;
|
||||||
|
@At("") @Ok("beetl:/mobile/activity/site/audit.html")
|
||||||
|
public void index(HttpServletRequest request) {
|
||||||
|
request.setAttribute("reviewTitle","分工会审核");
|
||||||
|
request.setAttribute("reviewApi","/mobile/activity/site/audit/union");
|
||||||
|
}
|
||||||
|
/** isAudit 为未审/已审/null全部;keyword/month/typeId 为筛选条件,返回分页 list/totalCount。 */
|
||||||
|
@At @ViReturn
|
||||||
|
public Object pageData(Boolean isAudit,String searchKeyword,String month,String typeId,int pageNumber,int pageSize) {
|
||||||
|
return siteBookingService.reviewPage("union",isAudit,searchKeyword,month,typeId,pageNumber,pageSize);
|
||||||
|
}
|
||||||
|
/** id 是预约记录主键,返回申请、时段及审核历史,服务层校验查看范围。 */
|
||||||
|
@At @ViReturn
|
||||||
|
public Object detail(String id) { return siteBookingService.detail(id); }
|
||||||
|
/** ids 为预约记录 ID 数组,isPass 为通过/拒绝,auditOpinion 为必填意见;返回 Object,由 @ViReturn 包装 code(0 成功)、msg(结果说明)。 */
|
||||||
|
@At @ViReturn @Aop(TransAop.READ_COMMITTED)
|
||||||
|
public Object doReview(String[] ids,Boolean isPass,String auditOpinion) {
|
||||||
|
siteBookingService.review("union",ids,isPass,auditOpinion);
|
||||||
|
// 非 void 签名保留拦截器生成的统一结果,避免审核成功却返回空响应。
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
/** id 为预约记录主键,撤回本人最近一次审核;返回统一 code/msg,成功后恢复当前节点待审。 */
|
||||||
|
@At @ViReturn @Aop(TransAop.READ_COMMITTED)
|
||||||
|
public Object doRevoke(String id) {
|
||||||
|
siteBookingService.revokeReview("union",id);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
// 所有接入页面共用一个弹框历史栈;只注册一次 popstate,系统返回按后进先出关闭。
|
||||||
|
window.popupHistory = window.popupHistory || (() => {
|
||||||
|
let stack = [];
|
||||||
|
let pending = null;
|
||||||
|
let navigating = false;
|
||||||
|
const queue = [];
|
||||||
|
const owners = new Map();
|
||||||
|
const finish = () => {
|
||||||
|
const callback = pending;
|
||||||
|
pending = null;
|
||||||
|
if (callback) callback();
|
||||||
|
};
|
||||||
|
window.addEventListener('popstate', (event) => {
|
||||||
|
const marker = event.state && event.state.popupHistoryId;
|
||||||
|
const index = stack.findIndex((entry) => entry.id === marker);
|
||||||
|
const removed = stack.splice(index + 1);
|
||||||
|
removed.reverse().forEach((entry) => {
|
||||||
|
const callback = owners.get(entry.owner);
|
||||||
|
if (callback) callback(entry.key);
|
||||||
|
});
|
||||||
|
navigating = false;
|
||||||
|
finish();
|
||||||
|
// 无需回退的排队操作也继续消费;发生下一次历史回退时等待对应 popstate。
|
||||||
|
while (!navigating && queue.length) queue.shift()();
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
register(owner, callback) { owners.set(owner, callback) },
|
||||||
|
push(owner, key) {
|
||||||
|
if (navigating) { queue.push(() => this.push(owner, key)); return; }
|
||||||
|
if (stack.some((entry) => entry.owner === owner && entry.key === key)) return;
|
||||||
|
const id = Date.now().toString() + Math.random().toString(16).slice(2);
|
||||||
|
stack.push({owner, key, id});
|
||||||
|
window.history.pushState(Object.assign({}, window.history.state, {popupHistoryId:id}), '');
|
||||||
|
},
|
||||||
|
close(owner, key) {
|
||||||
|
if (navigating) { queue.push(() => this.close(owner, key)); return; }
|
||||||
|
const top = stack[stack.length - 1];
|
||||||
|
if (top && top.owner === owner && top.key === key) { navigating = true; window.history.back(); }
|
||||||
|
},
|
||||||
|
clear(owner, callback) {
|
||||||
|
if (navigating) { queue.push(() => this.clear(owner, callback)); return; }
|
||||||
|
const count = stack.filter((entry) => entry.owner === owner).length;
|
||||||
|
if (!count) { if (callback) callback(); return; }
|
||||||
|
pending = callback || null;
|
||||||
|
navigating = true;
|
||||||
|
window.history.go(-count);
|
||||||
|
},
|
||||||
|
unregister(owner) {
|
||||||
|
owners.delete(owner);
|
||||||
|
this.clear(owner);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
// 页面声明 historyPopupKeys 即可同步 Popup/Dialog/ActionSheet;无需页面重复监听浏览器事件。
|
||||||
|
window.popupHistoryMixin = {
|
||||||
|
mounted() {
|
||||||
|
this._popupOwner = 'page-' + this._uid;
|
||||||
|
this._popupFromHistory = new Set();
|
||||||
|
this._popupUnwatch = [];
|
||||||
|
window.popupHistory.register(this._popupOwner, (key) => {
|
||||||
|
this._popupFromHistory.add(key);
|
||||||
|
this.$set(this, key, false);
|
||||||
|
this.$nextTick(() => this._popupFromHistory.delete(key));
|
||||||
|
});
|
||||||
|
(this.historyPopupKeys || []).forEach((key) => {
|
||||||
|
this._popupUnwatch.push(this.$watch(key, (value) => {
|
||||||
|
if (this._popupFromHistory.has(key)) return;
|
||||||
|
if (value) window.popupHistory.push(this._popupOwner, key);
|
||||||
|
else window.popupHistory.close(this._popupOwner, key);
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
(this._popupUnwatch || []).forEach((unwatch) => unwatch());
|
||||||
|
window.popupHistory.unregister(this._popupOwner);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 成功提交或页面跳转前清空本页弹框历史,等待回退完成再执行回调。
|
||||||
|
popupBack() { window.history.back() },
|
||||||
|
clearPopupHistory(callback) { window.popupHistory.clear(this._popupOwner, callback) }
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -95,16 +95,18 @@ const initTableMixins = {
|
|||||||
const address = url ? url : loc() + "/pageData"
|
const address = url ? url : loc() + "/pageData"
|
||||||
sublime.showLoadingbar();
|
sublime.showLoadingbar();
|
||||||
this.tableLoading = true
|
this.tableLoading = true
|
||||||
$.post(address, data ? data : this.pageForm, (data) => {
|
// 公共查询统一在 always 收尾;分页及查询入口继续由各页面复用。
|
||||||
sublime.closeLoadingbar();
|
return $.post(address, data ? data : this.pageForm).then((res) => {
|
||||||
this.tableLoading = false
|
if (res.code === 0) {
|
||||||
if (data.code === 0) {
|
this.tableData = res.data.list;
|
||||||
this.tableData = data.data.list;
|
this.pageForm.totalCount = res.data.totalCount;
|
||||||
this.pageForm.totalCount = data.data.totalCount;
|
|
||||||
} else {
|
} else {
|
||||||
this.$message.error(data.msg);
|
this.$message.error(res.msg);
|
||||||
}
|
}
|
||||||
}, "json");
|
}).always(() => {
|
||||||
|
sublime.closeLoadingbar();
|
||||||
|
this.tableLoading = false;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
notifySuccess(msg) {
|
notifySuccess(msg) {
|
||||||
this.$notify({
|
this.$notify({
|
||||||
|
|||||||
@@ -33,6 +33,8 @@
|
|||||||
|
|
||||||
<el-tab-pane label="预约基本信息" name="2">
|
<el-tab-pane label="预约基本信息" name="2">
|
||||||
<el-descriptions class="margin-top" :column="3" border>
|
<el-descriptions class="margin-top" :column="3" border>
|
||||||
|
<el-descriptions-item label="预约类型">{{ {1:'个人预约',2:'单位预约',3:'协会预约'}[viewData.reserve_type] }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="所属协会">{{viewData.club_name || '—'}}</el-descriptions-item>
|
||||||
<el-descriptions-item label="预约人">{{ viewData.reserve_person }}</el-descriptions-item>
|
<el-descriptions-item label="预约人">{{ viewData.reserve_person }}</el-descriptions-item>
|
||||||
<el-descriptions-item :span="2" label="场地名称"> {{ viewData.site_name }}</el-descriptions-item>
|
<el-descriptions-item :span="2" label="场地名称"> {{ viewData.site_name }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="性别">{{ viewData.sex }}</el-descriptions-item>
|
<el-descriptions-item label="性别">{{ viewData.sex }}</el-descriptions-item>
|
||||||
@@ -123,22 +125,18 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
return this.panes.includes(name)
|
return this.panes.includes(name)
|
||||||
},
|
},
|
||||||
async getSiteReserveInfo(id) {
|
// id 为预约记录主键;返回包含申请、时段和审核历史的 data。
|
||||||
const {data} = await $.get(base + "/platform/activity/site/reserve/findOne", {id})
|
openView(id) {
|
||||||
return data
|
this.loading = true;
|
||||||
},
|
$.post(base + '/platform/activity/site/reserve/findOne', {id}).then((res) => {
|
||||||
async openView(id) {
|
if (res.code === 0) {
|
||||||
this.loading = true
|
this.viewData = res.data;
|
||||||
this.$forceUpdate()
|
this.activeName = this.role === true ? '1' : '2';
|
||||||
this.viewData = await this.getSiteReserveInfo(id)
|
|
||||||
if (this.viewData) {
|
|
||||||
this.activeName = this.role === true ? "1" : "2"
|
|
||||||
} else {
|
} else {
|
||||||
this.viewData = {}
|
this.viewData = {};
|
||||||
this.$message.error("获取场地预约信息失败");
|
this.$message.error(res.msg);
|
||||||
}
|
}
|
||||||
this.$forceUpdate()
|
}).always(() => { this.loading = false });
|
||||||
this.loading = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,551 +1,258 @@
|
|||||||
<!--#
|
<!--# layout("/mobile/platform.html"){ #-->
|
||||||
layout("/mobile/platform.html"){
|
|
||||||
#-->
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
.site-audit .van-doc-card { margin:14px; padding:12px; background:#fff; border-radius:10px; box-shadow:0 8px 12px #ebedf0; line-height:20px; font-size:15px; position:relative; }
|
||||||
.van-hairline--top-bottom::after, .van-hairline-unset--top-bottom::after {
|
.site-audit .card-title { font-size:18px; font-weight:bold; color:#1867b0; padding-left:4px; }
|
||||||
border: 0;
|
.site-audit .title-mark { position:absolute; left:0; top:12px; color:#1867b0; font-weight:bold; }
|
||||||
}
|
.site-audit .card-fields { margin-top:4px; line-height:30px; overflow-wrap:anywhere; }
|
||||||
|
.site-audit .field-label { color:grey; }
|
||||||
.van-index-bar__sidebar {
|
.site-audit .van-divider { margin:6px 0; }
|
||||||
display: none;
|
.site-audit .card-footer { display:flex; justify-content:space-between; align-items:center; flex-wrap:wrap; gap:8px; }
|
||||||
}
|
.site-audit .card-footer .van-button { border-radius:8px; height:26px; font-size:16px; margin-left:4px; }
|
||||||
|
.site-audit .process-title { color:#1867b0; font-size:15px; font-weight:600; margin:10px 7px; padding:10px; }
|
||||||
.van-doc-card {
|
.site-audit .audit-actions { display:flex; gap:10px; padding:10px; }
|
||||||
margin: 14px;
|
.site-audit .site-audit-section { color:#1867b0; font-weight:bold; font-size:16px; }
|
||||||
padding: 12px 12px 12px 12px;
|
.site-audit .site-audit-loading { padding:24px; text-align:center; }
|
||||||
background-color: #fff;
|
.site-audit .site-audit-text .van-cell__label { white-space:pre-wrap; overflow-wrap:anywhere; }
|
||||||
border-radius: 10px;
|
.site-audit .detail-popup { background:#f7f8fa; }
|
||||||
box-shadow: 0 8px 12px #ebedf0;
|
|
||||||
line-height: 20px;
|
|
||||||
font-size: 12px;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.in-sheet-card {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
line-height: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.van-divider {
|
|
||||||
margin: 6px 0 6px 0px;
|
|
||||||
border-color: lightgray;
|
|
||||||
}
|
|
||||||
|
|
||||||
.van-button--small {
|
|
||||||
border-radius: revert;
|
|
||||||
width: 40%;
|
|
||||||
height: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.van-col {
|
|
||||||
line-height: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title_span {
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: bold;
|
|
||||||
position: absolute;
|
|
||||||
left: -1px;
|
|
||||||
top: 11px;
|
|
||||||
color: #1867b0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: bold;
|
|
||||||
flex: 1;
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cus_overflow {
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
.showMore {
|
|
||||||
color: #1867b0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cus_popup {
|
|
||||||
width: 70%;
|
|
||||||
height: 50%;
|
|
||||||
/*border-radius: 10px;*/
|
|
||||||
padding: 10px 14px;
|
|
||||||
font-size: 15px;
|
|
||||||
/*background-color: #E5E7E9;
|
|
||||||
background-image: url("https://www.transparenttextures.com/patterns/green-cup.png");*/
|
|
||||||
}
|
|
||||||
|
|
||||||
.cus_icon {
|
|
||||||
z-index: 10000;
|
|
||||||
color: white;
|
|
||||||
font-size: 35px;
|
|
||||||
position: fixed;
|
|
||||||
top: 81%;
|
|
||||||
left: 44%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.userPopup {
|
|
||||||
max-height: 96%;
|
|
||||||
height: 96%;
|
|
||||||
background-color: #f6f7f9;
|
|
||||||
font-size: 14px;
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
|
|
||||||
.van-row div {
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
.firstName {
|
|
||||||
width: 45px;
|
|
||||||
background-color: lightgrey;
|
|
||||||
border-radius: 45px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: 24px;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
height: 86%;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.no_content {
|
|
||||||
height: 100%;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.van-action-sheet__content {
|
|
||||||
height: 88%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.van-dialog__header {
|
|
||||||
padding-top: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.van-dialog__confirm {
|
|
||||||
color: #1867b0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.van-field__label {
|
|
||||||
width: 6em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cus_cell {
|
|
||||||
padding: 4px 0px;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.van-checkbox-group {
|
|
||||||
max-height: 260px;
|
|
||||||
overflow-y: auto;
|
|
||||||
margin-top: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.unit {
|
|
||||||
display: inline-block;
|
|
||||||
max-width: 160px;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cus_button {
|
|
||||||
border-top-right-radius: 16px;
|
|
||||||
border-bottom-left-radius: 16px;
|
|
||||||
height: 26px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.van-dropdown-menu__item {
|
|
||||||
flex: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.audit_icon {
|
|
||||||
font-size: 24px;
|
|
||||||
/*position: absolute;*/
|
|
||||||
font-weight: bolder;
|
|
||||||
right: 0;
|
|
||||||
top: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.van-cell__value--alone {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
<div id="app" class="site-audit" v-cloak>
|
||||||
<div id="app" v-cloak>
|
<van-sticky>
|
||||||
|
<van-nav-bar title="${reviewTitle}" left-text="返回" left-arrow @click-left="goBack"></van-nav-bar>
|
||||||
<m-page-loading v-if="mLoading"></m-page-loading>
|
<van-search v-model="keyword" placeholder="请输入场地名称或申请人" @search="search" @clear="search"></van-search>
|
||||||
|
<van-dropdown-menu :close-on-click-outside="false" :close-on-click-overlay="false">
|
||||||
<!--top栏-->
|
<van-dropdown-item ref="auditFilter" v-model="isAudit" :options="auditOptions" @change="search"
|
||||||
<van-nav-bar
|
@open="filterOpened" @close="filterClosed"></van-dropdown-item>
|
||||||
title="场地预约审核"
|
|
||||||
left-arrow
|
|
||||||
placeholder
|
|
||||||
fixed
|
|
||||||
@click-left="pjaxReplace('/mobile/index')"
|
|
||||||
safe-area-inset-top
|
|
||||||
></van-nav-bar>
|
|
||||||
|
|
||||||
<!--筛选框-->
|
|
||||||
<div class="search-fixed">
|
|
||||||
<van-search
|
|
||||||
v-model="pageForm.searchKeyword"
|
|
||||||
shape="round"
|
|
||||||
maxlength="10"
|
|
||||||
@search="doSearch"
|
|
||||||
placeholder="请输入场地名称、场地地点进行查询"
|
|
||||||
>
|
|
||||||
</van-search>
|
|
||||||
<van-dropdown-menu>
|
|
||||||
<!--<van-dropdown-item v-model="pageForm.meetingTime" :options="meetingTimeList"
|
|
||||||
@change="doSearch"></van-dropdown-item>-->
|
|
||||||
<van-dropdown-item v-model="pageForm.typeId" :options="typeList"
|
|
||||||
@change="doSearch"></van-dropdown-item>
|
|
||||||
</van-dropdown-menu>
|
</van-dropdown-menu>
|
||||||
|
</van-sticky>
|
||||||
|
<van-list v-model="loading" :finished="finished" :error.sync="listError" error-text="加载失败,点击重试"
|
||||||
|
:immediate-check="false" :finished-text="rows.length ? '没有更多了' : ''" @load="loadMore">
|
||||||
|
<div v-for="row in rows" :key="row.id" class="van-doc-card">
|
||||||
|
<div class="card-title van-ellipsis"><span class="title-mark">|</span>{{row.reserve_person}}({{row.loginname}})</div>
|
||||||
|
<div class="card-fields">
|
||||||
|
<div><span class="field-label">场地名称:</span>{{row.site_name}}</div>
|
||||||
|
<div><span class="field-label">所属单位:</span>{{row.reserve_person_unit || '—'}}</div>
|
||||||
|
<div><span class="field-label">预约类型:</span>{{typeName(row.reserve_type)}}</div>
|
||||||
|
<div v-if="row.club_name"><span class="field-label">所属协会:</span>{{row.club_name}}</div>
|
||||||
|
<div><span class="field-label">预约时段:</span>{{row.concat_day}}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!--列表-->
|
|
||||||
<div style="margin-top: 116px">
|
|
||||||
<van-list
|
|
||||||
v-model="loading"
|
|
||||||
:finished="finished" :immediate-check="false"
|
|
||||||
:finished-text="tableData.length>0?'没有更多了':''"
|
|
||||||
@load="onLoad">
|
|
||||||
|
|
||||||
<div class="van-doc-card" v-for="o in tableData">
|
|
||||||
<div @click="openView(o.id, o.modulename)">
|
|
||||||
|
|
||||||
<div style="display: flex; justify-content: space-between">
|
|
||||||
<div style="width: 74%">
|
|
||||||
<div class="van-ellipsis title">
|
|
||||||
<span class="title_span">|</span>
|
|
||||||
<span>{{o.name}}</span>
|
|
||||||
</div>
|
|
||||||
<div style="color: grey">{{o.address}}</div>
|
|
||||||
</div>
|
|
||||||
<div style="color: #1867b0;">
|
|
||||||
{{o.meetingtypename}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<van-divider></van-divider>
|
<van-divider></van-divider>
|
||||||
|
<div class="card-footer">
|
||||||
<div style="margin-top: 10px">
|
<!-- 仅状态内容绑定配置颜色,保留字段标签原有灰色。 -->
|
||||||
<van-row>
|
<div><span class="field-label">当前状态:</span><span :style="{color: row.state_color || null}">{{row.state_name}}</span></div>
|
||||||
<van-col span="12"><span style="color: grey"> 联系人:</span>{{o.unitname ? (o.unitname +
|
|
||||||
'') : '' + '' + o.contact_person}}
|
|
||||||
</van-col>
|
|
||||||
</van-row>
|
|
||||||
<van-row>
|
|
||||||
<van-col span="12"><span style="color: grey">联系方式:</span>{{o.contact_phone}}</van-col>
|
|
||||||
</van-row>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="display: flex; justify-content: space-between">
|
|
||||||
<!--<div class="cus_overflow" style="line-height: 20px; width: 83%">
|
|
||||||
<span style="color: grey">开放时段:</span>
|
|
||||||
{{o.meetingdescription}}
|
|
||||||
</div>-->
|
|
||||||
<!--<span class="showMore" @click.stop="desc = o.meetingdescription; popupShow = true">查看更多</span>-->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="display: flex; justify-content: flex-end; margin-top: 4px">
|
|
||||||
<div>
|
<div>
|
||||||
<van-button @click.stop="getAuditList(o)" class="cus_button"
|
<van-button size="small" type="info" @click="openDetail(row,false)">查看</van-button>
|
||||||
type="primary" size="small" color="#1867b0"
|
<van-button v-if="canReview(row)" size="small" type="primary" @click="openDetail(row,true)">审核</van-button>
|
||||||
style="margin-right: 8px; width: auto;">已审{{o.audit}}
|
<van-button v-if="isAudit===true" size="small" type="danger" :disabled="!row.canRevoke || formLoading"
|
||||||
</van-button>
|
:loading="formLoading && revokeId===row.id" @click="openRevoke(row)">撤回</van-button>
|
||||||
<van-button @click.stop="openView(o.id, o.modulename)" class="cus_button"
|
|
||||||
type="primary" size="small" color="#1867b0"
|
|
||||||
style="margin-right: 8px; width: auto;">未审{{o.no_audit}}
|
|
||||||
</van-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</van-list>
|
</van-list>
|
||||||
<van-empty
|
<van-empty v-if="!loading && !listError && !rows.length" image="/none.svg" description="暂无数据"></van-empty>
|
||||||
v-if="mLoading==false&&tableData.length==0"
|
<van-popup v-model="detailShow" position="right" :style="{height:'100%',width:'100%'}" class="detail-popup" safe-area-inset-bottom>
|
||||||
class="custom-image"
|
<van-sticky>
|
||||||
image="/none.svg"
|
<van-nav-bar title="申请详情" left-text="返回" left-arrow @click-left="goBack"></van-nav-bar>
|
||||||
description="暂无数据"
|
</van-sticky>
|
||||||
></van-empty>
|
<site-audit-info ref="infoRef" :review-api="reviewApi" @loaded="onDetailLoaded"></site-audit-info>
|
||||||
|
<div v-if="showApprovalForm">
|
||||||
|
<div class="process-title">${reviewTitle}</div>
|
||||||
|
<van-form @submit="openConfirm(true)">
|
||||||
|
<van-field label="审核人员" readonly v-model="formData.username"></van-field>
|
||||||
|
<van-field label="当前时间" readonly v-model="formData.auditTime"></van-field>
|
||||||
|
<van-field label="审核意见" type="textarea" maxlength="500" show-word-limit required
|
||||||
|
v-model="formData.auditOpinion" placeholder="请输入审核意见"></van-field>
|
||||||
|
</van-form>
|
||||||
|
<div class="audit-actions">
|
||||||
|
<van-button block type="default" :disabled="formLoading" @click="goBack">取消</van-button>
|
||||||
|
<van-button block type="danger" :disabled="!auditReady || formLoading" :loading="formLoading && !pendingPass" @click="openConfirm(false)">拒绝</van-button>
|
||||||
|
<van-button block type="primary" :disabled="!auditReady || formLoading" :loading="formLoading && pendingPass" @click="openConfirm(true)">通过</van-button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!--弹出框-->
|
|
||||||
<van-popup v-model:show="popupShow" class="cus_popup">
|
|
||||||
<pre style="margin: 0; white-space: break-spaces">{{desc}}</pre>
|
|
||||||
</van-popup>
|
</van-popup>
|
||||||
|
<van-dialog v-model="confirmShow" title="提示" show-cancel-button :before-close="beforeConfirmClose"
|
||||||
<van-icon @click="popupShow = false" class="cus_icon" v-if="popupShow" name="close"></van-icon>
|
:message="pendingPass ? '确定通过该申请吗?' : '确定拒绝该申请吗?拒绝后流程结束。'">
|
||||||
|
|
||||||
<!--审核人员-->
|
|
||||||
<van-action-sheet v-model:show="userShow" :title="queryType === 'notAudit' ? '预约列表' : '预约列表'"
|
|
||||||
class="userPopup" @close="popClose">
|
|
||||||
<div :class="queryType == 'notAudit' ? 'content' : 'no_content'">
|
|
||||||
<div v-if="queryType === 'notAudit'" style="text-align: right; width: 96%">
|
|
||||||
<van-tag color="#1867b0" style="margin-right: 10px" @click="cancelAll" size="large" type="primary">
|
|
||||||
取消选中
|
|
||||||
</van-tag>
|
|
||||||
<van-tag color="#1867b0" @click="allIn" size="large" type="primary">全选</van-tag>
|
|
||||||
</div>
|
|
||||||
<div class="van-doc-card in-sheet-card" v-for="(item, i) in userList">
|
|
||||||
<!--<div style="display: flex; justify-content: space-between">
|
|
||||||
<div style="width: 80%">
|
|
||||||
<span>{{moment(item.startTime).format('YYYY-MM-DD HH:mm') + ' ~ ' + moment(item.endTime).format('YYYY-MM-DD HH:mm')}}</span>
|
|
||||||
</div>
|
|
||||||
<div v-if="queryType === 'notAudit'">
|
|
||||||
<span @click="checkAll(i)">全选</span>
|
|
||||||
<span @click="toggleAll(i)" style="margin-left: 4px">反选</span>
|
|
||||||
</div>
|
|
||||||
</div>-->
|
|
||||||
<div style="width: 90%">
|
|
||||||
<div>
|
|
||||||
<span style="display: inline-block; min-width: 40px; max-width: 76px">{{item.username}}</span>
|
|
||||||
<span style="display: inline-block; width: 20px">{{item.sex}}</span>
|
|
||||||
<span style="display: inline-block; width: 76px">{{item.loginname}}</span>
|
|
||||||
<span class="unit">{{item.unitname}}</span>
|
|
||||||
</div>
|
|
||||||
<div style="width: 94%; display: flex">
|
|
||||||
<div style="color: grey;">预约时间:</div>
|
|
||||||
<div>
|
|
||||||
<div v-for="(d,index) in item.concat_day.split(',')">{{getTime(item, d)}}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style="width: 94%;">
|
|
||||||
<span style="color: grey">参加人员:</span><span>{{item.joinUser}}</span>
|
|
||||||
</div>
|
|
||||||
<div style="width: 94%;">
|
|
||||||
<span style="color: grey">预约事由:</span><span>{{item.reserve_cause}}</span>
|
|
||||||
</div>
|
|
||||||
<div style="width: 94%;">
|
|
||||||
<span style="color: grey">审核状态:</span><span>{{item.stateName}}</span>
|
|
||||||
</div>
|
|
||||||
<div v-if="${@shiro.hasRole('sysadmin')||@shiro.hasRole('xghng')||@shiro.hasRole('A06')}"
|
|
||||||
style="width: 94%;">
|
|
||||||
<span style="color: grey">反馈意见:</span><span>{{item.back_option}}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<van-checkbox-group v-model="result" ref="checkboxGroup">
|
|
||||||
<van-cell-group>
|
|
||||||
<van-cell
|
|
||||||
clickable class="cus_cell"
|
|
||||||
:key="item.id"
|
|
||||||
@click="toggle(i)">
|
|
||||||
|
|
||||||
<van-icon class="audit_icon" color="green"
|
|
||||||
v-if="queryType === 'hasAudit' && item.auditState == true"
|
|
||||||
name="passed"></van-icon>
|
|
||||||
<van-icon class="audit_icon" color="grey"
|
|
||||||
v-if="queryType === 'hasAudit' && item.auditState == false"
|
|
||||||
name="close"></van-icon>
|
|
||||||
<template #right-icon v-if="queryType === 'notAudit'">
|
|
||||||
<van-checkbox :name="item.id" ref="checkboxes"></van-checkbox>
|
|
||||||
</template>
|
|
||||||
</van-cell>
|
|
||||||
</van-cell-group>
|
|
||||||
</van-checkbox-group>
|
|
||||||
</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>
|
|
||||||
<van-button @click="audit('all', 'pass')" color="#1867b0" size="small" style="height: 38px">一键全部通过</van-button>
|
|
||||||
</div>-->
|
|
||||||
</div>
|
|
||||||
<div style="position: absolute; bottom: 20px; width: 100%;z-index: 9999">
|
|
||||||
<div v-if="queryType === 'notAudit'" style="display: flex; justify-content: center">
|
|
||||||
<van-button @click="audit('many', 'reject')" color="#ff976a" size="small"
|
|
||||||
style="height: 38px;border-radius: 10px;margin-right: 20px">驳回
|
|
||||||
</van-button>
|
|
||||||
<van-button @click="audit('many', 'pass')" color="#1867b0" size="small"
|
|
||||||
style="height: 38px;border-radius: 10px">通过
|
|
||||||
</van-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</van-action-sheet>
|
|
||||||
|
|
||||||
<!--审核弹框-->
|
|
||||||
<van-dialog v-model:show="auditShow" @confirm="auditDo" title="温馨提示" show-cancel-button>
|
|
||||||
<div style="padding-top: 8px; text-align: center; font-size: 14px; color: #646566">{{str}}</div>
|
|
||||||
<van-divider style="margin: 12px 0 1px 0px;"></van-divider>
|
|
||||||
<van-field
|
|
||||||
v-model="reason"
|
|
||||||
rows="2"
|
|
||||||
label="审核意见:"
|
|
||||||
autosize
|
|
||||||
type="textarea"
|
|
||||||
maxlength="50"
|
|
||||||
placeholder="请输入审核意见"
|
|
||||||
show-word-limit
|
|
||||||
></van-field>
|
|
||||||
</van-dialog>
|
</van-dialog>
|
||||||
|
<van-dialog v-model="revokeShow" title="撤回审核" show-cancel-button :before-close="beforeRevokeClose"
|
||||||
|
message="确定撤回本次审核吗?撤回后申请恢复到当前节点待审核。"></van-dialog>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
<!--# include("/platform/activity/includeJs/activityUtil.js"){} #-->
|
(() => {
|
||||||
const siteTypeUtil = new typeUtil()
|
// PJAX 先执行内联脚本;捕获本次页面,依赖加载完成后才允许创建 Vue。
|
||||||
const vue = new Vue({
|
const pageRoot = document.getElementById('app')
|
||||||
|
const startPage = () => {
|
||||||
|
if (document.getElementById('app') !== pageRoot || !pageRoot.isConnected) return
|
||||||
|
<!--# include('./common/info.js'){} #-->
|
||||||
|
new Vue({
|
||||||
el: '#app',
|
el: '#app',
|
||||||
mixins: [mobileMixins],
|
mixins: [window.popupHistoryMixin],
|
||||||
|
components: {'site-audit-info': SITE_AUDIT_INFO},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
desc: '',
|
historyPopupKeys: ['filterShow','detailShow','confirmShow','revokeShow'],
|
||||||
list: ['a', 'b'],
|
revokeShow:false, revokeId:'',
|
||||||
result: [],
|
reviewApi: '${reviewApi}',
|
||||||
reason: '',
|
auditOptions: [{text:'已审核',value:true},{text:'未审核',value:false}],
|
||||||
auditShow: false,
|
isAudit: false, keyword: '', rows: [], page: 1,
|
||||||
popupShow: false,
|
loading: false, finished: false, listError: false, requestVersion: 0, requesting: false,
|
||||||
userShow: false,
|
filterShow: false, detailShow: false, confirmShow: false,
|
||||||
userList: [],
|
showApprovalForm: false, auditReady: false, pendingPass: false, formLoading: false,
|
||||||
userClickList: [],
|
formData: {id:'',username:'',auditTime:'',auditOpinion:''}
|
||||||
str: '',
|
|
||||||
type: '',
|
|
||||||
auditType: '',
|
|
||||||
queryType: '',
|
|
||||||
typeList: [],
|
|
||||||
meetingTimeList: [{text: '全部时间', value: null}, {text: '即将开始', value: 0}, {text: '已结束', value: 1}],
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
// DropdownItem 没有弹层 v-model,通过全局历史状态同步关闭内部下拉层。
|
||||||
|
filterShow(value) { if (!value && this.$refs.auditFilter) this.$refs.auditFilter.toggle(false) }
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getTime(o, day) {
|
openRevoke(row) {
|
||||||
const week = new Date(day).getDay()
|
if (this.formLoading || !row.canRevoke) return
|
||||||
const arr = ['日', '一', '二', '三', '四', '五', '六']
|
this.$set(this, 'revokeId', row.id)
|
||||||
return moment(day).format('MM月DD日') + ' 周' + arr[week] + ' ' + o.start_time + '-' + o.end_time
|
this.$set(this, 'revokeShow', true)
|
||||||
},
|
},
|
||||||
async getAuditList(o) {
|
// 确认框也进入历史栈;失败保留弹框,请求结束复位 Vant 内部 loading。
|
||||||
this.queryType = 'hasAudit'
|
beforeRevokeClose(action, done) {
|
||||||
const resp = await $.post('/mobile/activity/site/audit/getLeaveUser', {
|
if (this.formLoading) { done(false); return }
|
||||||
siteId: o.id,
|
if (action!=='confirm') { done(false); this.goBack(); return }
|
||||||
auditType: 'hasAudit',
|
this.$set(this, 'formLoading', true)
|
||||||
|
$.post(this.reviewApi + '/doRevoke', {id:this.revokeId}).then((res) => {
|
||||||
|
if (res && res.code===0) {
|
||||||
|
this.clearPopupHistory(() => { this.$toast.success('已撤回审核'); this.search() })
|
||||||
|
} else { this.$toast.fail(res && res.msg || '响应异常,请刷新核对申请状态') }
|
||||||
|
}, () => { this.$toast.fail('请求失败,请刷新核对申请状态') }).always(() => {
|
||||||
|
this.$set(this, 'formLoading', false)
|
||||||
|
done(false)
|
||||||
})
|
})
|
||||||
if (resp.code === 0) {
|
|
||||||
this.userList = resp.data
|
|
||||||
if (this.userList.length === 0) {
|
|
||||||
vant.Toast('暂无数据')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.userList.sort((a, b) => {
|
|
||||||
return b.auditState - a.auditState
|
|
||||||
})
|
|
||||||
this.userShow = true
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
cancelAll() {
|
// 菜单采用 PJAX replace 进入,列表返回应回手机首页,不能依赖上一条浏览器历史。
|
||||||
this.$refs.checkboxes.forEach(item => item.toggle(false))
|
// 弹框存在时通过统一栈关闭最上层,重复点击也不会连续退过业务页面。
|
||||||
},
|
goBack() {
|
||||||
allIn() {
|
if (this.formLoading) return
|
||||||
this.$refs.checkboxes.forEach(item => item.toggle(true))
|
const key = this.revokeShow ? 'revokeShow' : this.confirmShow ? 'confirmShow' : this.detailShow ? 'detailShow' : this.filterShow ? 'filterShow' : null
|
||||||
},
|
if (key) {
|
||||||
checkAll(i) {
|
window.popupHistory.close(this._popupOwner, key)
|
||||||
this.$refs.checkboxGroup[i].children.forEach(item => item.toggle(true))
|
|
||||||
},
|
|
||||||
toggleAll(i) {
|
|
||||||
this.$refs.checkboxGroup[i].children.forEach(item => item.toggle())
|
|
||||||
},
|
|
||||||
toggle(i) {
|
|
||||||
this.$refs.checkboxes[i].toggle();
|
|
||||||
},
|
|
||||||
async auditDo() {
|
|
||||||
let idList = this.result
|
|
||||||
if (this.type === 'all') {
|
|
||||||
idList = this.userList.map(o => o.id)
|
|
||||||
}
|
|
||||||
const resp = await $.post('/mobile/activity/site/audit/audit', {
|
|
||||||
ids: JSON.stringify(idList),
|
|
||||||
auditOpinion: this.reason,
|
|
||||||
isPass: this.auditType === 'pass'
|
|
||||||
})
|
|
||||||
if (resp.code === 0) {
|
|
||||||
vant.Toast(resp.msg)
|
|
||||||
this.doSearch()
|
|
||||||
} else {
|
} else {
|
||||||
vant.Toast('操作失败')
|
this.clearPopupHistory(() => pjaxReplace('/mobile/index'))
|
||||||
}
|
}
|
||||||
this.userShow = false
|
|
||||||
},
|
},
|
||||||
audit(type, auditType) {
|
typeName(type) { return {1:'个人预约',2:'单位预约',3:'协会预约'}[type] || '—' },
|
||||||
this.type = type
|
filterOpened() { this.$set(this, 'filterShow', true) },
|
||||||
this.auditType = auditType
|
filterClosed() { this.$set(this, 'filterShow', false) },
|
||||||
if (type === 'many' && this.result.length === 0) {
|
canReview(row) {
|
||||||
vant.Toast('请先选择人员')
|
const stage = this.reviewApi.substring(this.reviewApi.lastIndexOf('/') + 1)
|
||||||
return
|
return Number(row.reserve_state) === {union:4000,club:4010,school:4020}[stage]
|
||||||
}
|
|
||||||
this.str = type === 'many' ? '您选择了' + this.result.length + '个人,请确认您的选择' : '您确定要一键全部审核吗?'
|
|
||||||
this.reason = auditType === 'pass' ? '同意' : '拒绝'
|
|
||||||
this.auditShow = true
|
|
||||||
},
|
},
|
||||||
async openView(id, modulename) {
|
search() {
|
||||||
this.queryType = 'notAudit'
|
// 筛选变更使旧响应失效,避免未审核和已审核数据混入同一列表。
|
||||||
this.userList = []
|
this.$set(this, 'requestVersion', this.requestVersion + 1)
|
||||||
const resp = await $.post('/mobile/activity/site/audit/getLeaveUser', {
|
this.$set(this, 'requesting', false)
|
||||||
siteId: id,
|
this.$set(this, 'rows', [])
|
||||||
moduleName: modulename,
|
this.$set(this, 'page', 1)
|
||||||
auditType: 'canAudit',
|
this.$set(this, 'finished', false)
|
||||||
})
|
this.$set(this, 'listError', false)
|
||||||
if (resp.code === 0) {
|
this.loadMore()
|
||||||
this.userList = resp.data
|
|
||||||
this.userClickList = []
|
|
||||||
if (this.userList.length === 0) {
|
|
||||||
vant.Toast('暂无预约数据')
|
|
||||||
} else {
|
|
||||||
this.userShow = true
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
vant.Toast('系统错误,请联系管理员')
|
|
||||||
},
|
},
|
||||||
popClose() {
|
// isAudit 为审核状态,pageNumber/pageSize 为分页;响应 data 含 list 和 totalCount。
|
||||||
this.$nextTick(function () {
|
loadMore() {
|
||||||
if (this.$refs.checkboxes !== undefined) {
|
if (this.requesting || this.finished) return
|
||||||
this.$refs.checkboxes.forEach(item => item.toggle(false));
|
const version = this.requestVersion
|
||||||
}
|
this.$set(this, 'requesting', true)
|
||||||
})
|
this.$set(this, 'loading', true)
|
||||||
},
|
$.post(this.reviewApi + '/pageData', {
|
||||||
doSearch() {
|
isAudit: this.isAudit, searchKeyword: this.keyword, pageNumber: this.page, pageSize: 10
|
||||||
this.pageForm.pageNumber = 1
|
}).then((res) => {
|
||||||
this.tableData = []
|
if (version !== this.requestVersion) return
|
||||||
this.finished = false
|
|
||||||
this.onLoad()
|
|
||||||
},
|
|
||||||
async onLoad() {
|
|
||||||
this.loading = true
|
|
||||||
$.post('/mobile/activity/site/audit/pageData', this.pageForm).then(res => {
|
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
this.tableData = this.tableData.concat(res.data.list)
|
this.$set(this, 'rows', this.rows.concat(res.data.list))
|
||||||
if (this.tableData.length === res.data.totalCount) {
|
this.$set(this, 'page', this.page + 1)
|
||||||
this.finished = true
|
this.$set(this, 'finished', this.rows.length >= res.data.totalCount)
|
||||||
} else {
|
} else {
|
||||||
this.pageForm.pageNumber++
|
this.$set(this, 'listError', true)
|
||||||
|
this.$toast.fail(res.msg)
|
||||||
}
|
}
|
||||||
}
|
}).fail(() => {
|
||||||
this.loading = false
|
if (version === this.requestVersion) this.$set(this, 'listError', true)
|
||||||
})
|
}).always(() => {
|
||||||
},
|
if (version === this.requestVersion) {
|
||||||
},
|
this.$set(this, 'loading', false)
|
||||||
async created() {
|
this.$set(this, 'requesting', false)
|
||||||
this.typeList = await siteTypeUtil.getAllType()
|
|
||||||
this.typeList.unshift({text: '全部场地类型'})
|
|
||||||
if (this.typeList && this.typeList.length > 0) {
|
|
||||||
this.$set(this.pageForm, "typeId", this.typeList[0].value)
|
|
||||||
}
|
|
||||||
this.$set(this.pageForm, "meetingTime", this.meetingTimeList[1].value)
|
|
||||||
this.onLoad()
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
openDetail(row, approval) {
|
||||||
|
this.$set(this, 'auditReady', false)
|
||||||
|
this.$set(this, 'showApprovalForm', approval)
|
||||||
|
this.$set(this, 'formData', {
|
||||||
|
id: row.id, username: "${@shiro.getPrincipalProperty('username')}",
|
||||||
|
auditTime: moment().format('YYYY-MM-DD HH:mm:ss'), auditOpinion: ''
|
||||||
|
})
|
||||||
|
this.$set(this, 'detailShow', true)
|
||||||
|
this.$nextTick(() => this.$refs.infoRef.onOpen(row.id))
|
||||||
|
},
|
||||||
|
onDetailLoaded(detail) {
|
||||||
|
this.$set(this, 'auditReady', detail.id === this.formData.id && this.canReview(detail))
|
||||||
|
},
|
||||||
|
openConfirm(isPass) {
|
||||||
|
if (this.formLoading || !this.auditReady) return
|
||||||
|
if (!this.formData.auditOpinion.trim()) { this.$toast.fail('请填写审核意见'); return }
|
||||||
|
this.$set(this, 'pendingPass', isPass)
|
||||||
|
this.$set(this, 'confirmShow', true)
|
||||||
|
},
|
||||||
|
// 确认时保留 Dialog,请求收尾通过 done(false) 复位其内部 loading;取消只退一层历史。
|
||||||
|
beforeConfirmClose(action, done) {
|
||||||
|
if (this.formLoading) { done(false); return }
|
||||||
|
if (action === 'confirm') this.review(done)
|
||||||
|
else { done(false); this.goBack() }
|
||||||
|
},
|
||||||
|
// ids 为预约 ID 数组,isPass 为通过/拒绝;响应应为统一 code/msg,空响应不能视为成功。
|
||||||
|
review(dialogDone = () => {}) {
|
||||||
|
if (this.formLoading || !this.auditReady) { dialogDone(false); return }
|
||||||
|
const opinion = (this.formData.auditOpinion || '').trim()
|
||||||
|
if (!opinion) { dialogDone(false); this.$toast.fail('请填写审核意见'); return }
|
||||||
|
this.$set(this, 'formLoading', true)
|
||||||
|
$.post(this.reviewApi + '/doReview', {
|
||||||
|
ids: JSON.stringify([this.formData.id]), isPass: this.pendingPass,
|
||||||
|
auditOpinion: opinion
|
||||||
|
}).then((res) => {
|
||||||
|
// jQuery 1.x 的 then 回调异常可能中断后续链,先检查空或非标准响应再读取字段。
|
||||||
|
if (!res || typeof res !== 'object' || typeof res.code !== 'number') {
|
||||||
|
this.$toast.fail('审核响应异常,请刷新核对申请状态')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (res.code === 0) {
|
||||||
|
this.$set(this, 'auditReady', false)
|
||||||
|
this.clearPopupHistory(() => { this.$toast.success('审核成功'); this.search() })
|
||||||
|
} else { this.$toast.fail(res.msg || '审核失败,请刷新核对申请状态') }
|
||||||
|
}).fail(() => { this.$toast.fail('提交失败,请刷新核对申请状态') }).always(() => {
|
||||||
|
// 同时复位表单按钮及 Vant Dialog 内部按钮,失败时保留意见供核对。
|
||||||
|
this.$set(this, 'formLoading', false)
|
||||||
|
dialogDone(false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() { this.search() },
|
||||||
|
mounted() {
|
||||||
|
// PJAX 替换 DOM 不会自动销毁 Vue,离开时注销弹框历史同步及本页事件。
|
||||||
|
this._siteAuditDispose = () => this.$destroy()
|
||||||
|
$(document).one('pjax:beforeReplace.siteAudit', this._siteAuditDispose)
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
$(document).off('pjax:beforeReplace.siteAudit', this._siteAuditDispose)
|
||||||
|
this.$set(this, 'requestVersion', this.requestVersion + 1)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (window.popupHistoryMixin) {
|
||||||
|
startPage()
|
||||||
|
} else {
|
||||||
|
// 共用一次加载请求;失败后允许重新进入重试,不能在缺少 mixin 时继续挂载。
|
||||||
|
if (!window.siteAuditHistoryLoading) {
|
||||||
|
window.siteAuditHistoryLoading = $.getScript('/assets/mobile/js/popupHistory.js')
|
||||||
|
.fail(() => { window.siteAuditHistoryLoading = null })
|
||||||
|
}
|
||||||
|
window.siteAuditHistoryLoading.then(() => startPage()).fail(() => {
|
||||||
|
if (document.getElementById('app') === pageRoot) vant.Toast.fail('页面组件加载失败,请重新进入')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})()
|
||||||
</script>
|
</script>
|
||||||
|
<!--# } #-->
|
||||||
<!--#
|
|
||||||
}
|
|
||||||
#-->
|
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
const SITE_AUDIT_INFO = {
|
||||||
|
template: /*language=HTML*/ `
|
||||||
|
<div>
|
||||||
|
<div v-if="detailLoading" class="site-audit-loading"><van-loading size="24px">加载中...</van-loading></div>
|
||||||
|
<van-tabs v-else v-model="activeName">
|
||||||
|
<van-tab title="申请信息" name="basic">
|
||||||
|
<van-cell-group>
|
||||||
|
<van-cell title="申请信息" class="site-audit-section" :value="expanded ? '点击收起' : '点击展开'"
|
||||||
|
:icon="expanded ? 'arrow-up' : 'arrow-down'" @click="toggleExpanded"></van-cell>
|
||||||
|
<template v-if="expanded">
|
||||||
|
<van-cell title="申请人" :value="viewData.reserve_person"></van-cell>
|
||||||
|
<van-cell title="工号" :value="viewData.loginname"></van-cell>
|
||||||
|
<van-cell title="所属单位" :value="viewData.reserve_person_unit"></van-cell>
|
||||||
|
<van-cell title="联系电话" :value="viewData.reserve_person_phone"></van-cell>
|
||||||
|
<van-cell title="场地名称" :value="viewData.site_name"></van-cell>
|
||||||
|
<van-cell title="预约类型" :value="typeName(viewData.reserve_type)"></van-cell>
|
||||||
|
<van-cell title="所属协会" :value="viewData.club_name || '—'"></van-cell>
|
||||||
|
<!-- value 插槽只给状态内容着色,与审核列表保持一致。 -->
|
||||||
|
<van-cell title="当前状态">
|
||||||
|
<template #default><span :style="{color: viewData.state_color || null}">{{viewData.state_name}}</span></template>
|
||||||
|
</van-cell>
|
||||||
|
<van-cell title="预约时段" :label="viewData.concat_day"></van-cell>
|
||||||
|
<van-cell title="预约事由" :label="viewData.reserve_cause" class="site-audit-text"></van-cell>
|
||||||
|
</template>
|
||||||
|
</van-cell-group>
|
||||||
|
</van-tab>
|
||||||
|
<van-tab v-for="(item,index) in viewData.auditListTable" :key="item.auditId || index"
|
||||||
|
:title="item.auditStateName + '信息'" :name="'history-' + index">
|
||||||
|
<van-cell-group>
|
||||||
|
<van-cell title="审核人员" :value="item.auditUserName"></van-cell>
|
||||||
|
<van-cell title="审核时间" :value="item.auditTime"></van-cell>
|
||||||
|
<van-cell title="审核结果" :value="item.auditListName"></van-cell>
|
||||||
|
<van-cell title="审核意见" :label="item.auditOption" class="site-audit-text"></van-cell>
|
||||||
|
</van-cell-group>
|
||||||
|
</van-tab>
|
||||||
|
<slot></slot>
|
||||||
|
</van-tabs>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
props: {reviewApi: {type: String, required: true}},
|
||||||
|
data() { return {viewData: {}, activeName: 'basic', expanded: true, detailLoading: false, requestId: 0} },
|
||||||
|
methods: {
|
||||||
|
typeName(type) { return {1:'个人预约',2:'单位预约',3:'协会预约'}[type] || '—' },
|
||||||
|
toggleExpanded() { this.$set(this, 'expanded', !this.expanded) },
|
||||||
|
// id 为预约记录主键;返回预约字段及 auditListTable 历史,loaded 通知父页核验可审核状态。
|
||||||
|
onOpen(id) {
|
||||||
|
const requestId = this.requestId + 1
|
||||||
|
this.$set(this, 'requestId', requestId)
|
||||||
|
this.$set(this, 'viewData', {})
|
||||||
|
this.$set(this, 'activeName', 'basic')
|
||||||
|
this.$set(this, 'expanded', true)
|
||||||
|
this.$set(this, 'detailLoading', true)
|
||||||
|
return $.post(this.reviewApi + '/detail', {id}).then((res) => {
|
||||||
|
// 忽略快速切换申请留下的旧响应,避免审核表单与详情不一致。
|
||||||
|
if (requestId !== this.requestId) return
|
||||||
|
if (res.code === 0) {
|
||||||
|
this.$set(this, 'viewData', res.data)
|
||||||
|
this.$emit('loaded', res.data)
|
||||||
|
} else { this.$toast.fail(res.msg) }
|
||||||
|
}).fail(() => {
|
||||||
|
if (requestId === this.requestId) this.$toast.fail('详情加载失败,请返回重试')
|
||||||
|
}).always(() => {
|
||||||
|
if (requestId === this.requestId) this.$set(this, 'detailLoading', false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -158,19 +158,8 @@ layout("/mobile/platform.html"){
|
|||||||
top: 10px;
|
top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pop {
|
|
||||||
max-height: 70%;
|
|
||||||
width: 90%;
|
|
||||||
padding: 0px 10px 10px 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pop h2 {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pop h4 {
|
|
||||||
line-height: 26px;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div id="app" v-cloak>
|
<div id="app" v-cloak>
|
||||||
@@ -179,7 +168,7 @@ layout("/mobile/platform.html"){
|
|||||||
|
|
||||||
<!--top栏-->
|
<!--top栏-->
|
||||||
<van-nav-bar
|
<van-nav-bar
|
||||||
@click-left="pjaxReplace('/mobile/index')"
|
@click-left="goBack"
|
||||||
fixed
|
fixed
|
||||||
left-arrow
|
left-arrow
|
||||||
placeholder
|
placeholder
|
||||||
@@ -198,7 +187,8 @@ layout("/mobile/platform.html"){
|
|||||||
>
|
>
|
||||||
</van-search>
|
</van-search>
|
||||||
<van-dropdown-menu>
|
<van-dropdown-menu>
|
||||||
<van-dropdown-item :options="typeList" @change="doSearch"
|
<van-dropdown-item :options="typeList" @change="doSearch" ref="typeFilter"
|
||||||
|
@open="setPopupState('typeFilterShow',true)" @close="setPopupState('typeFilterShow',false)"
|
||||||
v-model="pageForm.typeId"></van-dropdown-item>
|
v-model="pageForm.typeId"></van-dropdown-item>
|
||||||
</van-dropdown-menu>
|
</van-dropdown-menu>
|
||||||
</div>
|
</div>
|
||||||
@@ -261,45 +251,13 @@ layout("/mobile/platform.html"){
|
|||||||
></van-empty>
|
></van-empty>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<van-popup class="pop" round v-model:show="show">
|
|
||||||
<div style="text-align: center;padding: 10px 0; font-size: 20px">爱心母婴室管理规定</div>
|
|
||||||
<div>一、基本原则</div>
|
|
||||||
<div style="padding-left: 30px;">
|
|
||||||
<p>1.使用对象:有哺乳需求的本校在职女教职工。</p>
|
|
||||||
<p>2.开放时间:工作日8:00--17:30。</p>
|
|
||||||
<p>3.使用制度:实行预约登记制。凡有哺乳需求的女教职工提前向校工会提出使用申请,经审核通过并开通权限后即可使用。</p>
|
|
||||||
<p>4.日常管理:由校工会负责。</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>二、管理人员</div>
|
|
||||||
<div style="padding-left: 30px;">
|
|
||||||
<p>1.负责爱心母婴室使用登记管理。</p>
|
|
||||||
<p>2.定期保养设备,确保正常使用。</p>
|
|
||||||
<p>3.保持室内整洁、卫生安全,做好保洁消毒记录。</p>
|
|
||||||
<p>4.定期收集意见和建议,不断改进服务。</p>
|
|
||||||
</div>
|
|
||||||
<div>三、使用人员</div>
|
|
||||||
<div style="padding-left: 30px;">
|
|
||||||
<p>1.遵守学校安全管理制度和爱心母婴室管理规定。</p>
|
|
||||||
<p>2.严禁吸烟,安全使用电器,爱护公物,损坏赔偿。</p>
|
|
||||||
<p>3.保持室内卫生清洁,勿大声喧哗,不影响楼内工作秩序。</p>
|
|
||||||
<p>4.妥善保管自带物品,冰存的母乳须贴上姓名标签并及时取走。</p>
|
|
||||||
<p>5.不得擅自携他人入内,不做和哺乳无关事宜,使用完毕及时离开。</p>
|
|
||||||
<p>6.欢迎在《爱心母婴室使用意见簿》上留下您宝贵的意见和建议。</p>
|
|
||||||
</div>
|
|
||||||
<br/>
|
|
||||||
<p>联系人:曾钰媛梦 84894774、15850692181</p>
|
|
||||||
|
|
||||||
<van-button @click="toReserve" color="#246fb4" style="width: 100%" type="primary">已知晓并遵守
|
|
||||||
</van-button>
|
|
||||||
</van-popup>
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<van-tabbar v-model="tarBarActive">
|
<van-tabbar v-model="tarBarActive">
|
||||||
<van-tabbar-item @click="pjaxReplace('/mobile/activity/site/info')" icon="home-o"
|
<van-tabbar-item @click="navigateTo('/mobile/activity/site/info')" icon="home-o"
|
||||||
replace>场地预约
|
replace>场地预约
|
||||||
</van-tabbar-item>
|
</van-tabbar-item>
|
||||||
<van-tabbar-item @click="pjaxReplace('/mobile/activity/site/info/my')" icon="manager-o"
|
<van-tabbar-item @click="navigateTo('/mobile/activity/site/info/my')" icon="manager-o"
|
||||||
replace>我的预约
|
replace>我的预约
|
||||||
</van-tabbar-item>
|
</van-tabbar-item>
|
||||||
</van-tabbar>
|
</van-tabbar>
|
||||||
@@ -308,37 +266,70 @@ layout("/mobile/platform.html"){
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
(() => {
|
||||||
|
// PJAX 先执行内联脚本,等待历史组件加载;离开后的旧加载回调不能挂载到新页面。
|
||||||
|
const pageRoot = document.getElementById('app')
|
||||||
|
const startPage = () => {
|
||||||
|
if (document.getElementById('app') !== pageRoot || !pageRoot.isConnected) return
|
||||||
<!--# include("/platform/activity/includeJs/activityUtil.js"){} #-->
|
<!--# include("/platform/activity/includeJs/activityUtil.js"){} #-->
|
||||||
const siteTypeUtil = new typeUtil()
|
const siteTypeUtil = new typeUtil()
|
||||||
const vue = new Vue({
|
new Vue({
|
||||||
el: '#app',
|
el: '#app',
|
||||||
mixins: [mobileMixins],
|
mixins: [mobileMixins, window.popupHistoryMixin],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
historyPopupKeys: ['typeFilterShow'],
|
||||||
|
typeFilterShow:false, formLoading:false,
|
||||||
tarBarActive: 0,
|
tarBarActive: 0,
|
||||||
list: ['a', 'b'],
|
list: ['a', 'b'],
|
||||||
typeList: [],
|
typeList: [],
|
||||||
show: false,
|
|
||||||
siteId: '',
|
siteId: '',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
// 筛选下拉层没有 v-model 开关,通过状态同步交给全局历史栈管理。
|
||||||
|
typeFilterShow(value) { if (!value && this.$refs.typeFilter) this.$refs.typeFilter.toggle(false) },
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this._siteListDispose = () => this.$destroy()
|
||||||
|
$(document).one('pjax:beforeReplace.siteList', this._siteListDispose)
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
// PJAX 替换 DOM 不会自动销毁实例,显式清理本页监听并触发历史 mixin 注销。
|
||||||
|
$(document).off('pjax:beforeReplace.siteList', this._siteListDispose)
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
setPopupState(key, value) { this.$set(this, key, value) },
|
||||||
|
// 列表由 PJAX replace 进入,导航返回明确回首页;有弹层时只关最上层。
|
||||||
|
goBack() {
|
||||||
|
if (this.formLoading) return
|
||||||
|
const key = this.typeFilterShow ? 'typeFilterShow' : null
|
||||||
|
if (key) window.popupHistory.close(this._popupOwner, key)
|
||||||
|
else this.navigateTo('/mobile/index')
|
||||||
|
},
|
||||||
|
// 切换栏目先清理弹框历史,避免返回时重新出现上一个页面的弹层。
|
||||||
|
navigateTo(url) {
|
||||||
|
if (this.formLoading) return
|
||||||
|
this.clearPopupHistory(() => {
|
||||||
|
if (window.location.pathname !== url) pjaxReplace(url)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
openView(o) {
|
openView(o) {
|
||||||
this.siteId = o.id
|
this.siteId = o.id
|
||||||
if (o.sexlimit === 1 || o.sexlimit === 2) {
|
if (o.sexlimit === 1 || o.sexlimit === 2) {
|
||||||
const sex = o.sexlimit === 1 ? '男' : '女'
|
const sex = o.sexlimit === 1 ? '男' : '女'
|
||||||
if ("${@shiro.getPrincipalProperty('sex')}" === sex) {
|
if ("${@shiro.getPrincipalProperty('sex')}" === sex) {
|
||||||
location.href = '/mobile/activity/site/info/reserve?id=' + o.id
|
this.toReserve()
|
||||||
} else {
|
} else {
|
||||||
vant.Toast('抱歉,该场地仅限' + sex + '性会员预约')
|
vant.Toast('抱歉,该场地仅限' + sex + '性会员预约')
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
location.href = '/mobile/activity/site/info/reserve?id=' + o.id
|
this.toReserve()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
toReserve() {
|
toReserve() {
|
||||||
location.href = '/mobile/activity/site/info/reserve?id=' + this.siteId
|
this.clearPopupHistory(()=>{location.href = '/mobile/activity/site/info/reserve?id=' + this.siteId})
|
||||||
this.show = false
|
|
||||||
},
|
},
|
||||||
doSearch() {
|
doSearch() {
|
||||||
this.pageForm.pageNumber = 1
|
this.pageForm.pageNumber = 1
|
||||||
@@ -346,30 +337,26 @@ layout("/mobile/platform.html"){
|
|||||||
this.finished = false
|
this.finished = false
|
||||||
this.onLoad()
|
this.onLoad()
|
||||||
},
|
},
|
||||||
async onLoad() {
|
onLoad() {
|
||||||
this.loading = true
|
this.loading=true;return $.post('/mobile/activity/site/info/pageData',this.pageForm).then((res)=>{if(res.code===0){this.tableData=this.tableData.concat(res.data.list);this.finished=this.tableData.length>=res.data.totalCount;if(!this.finished)this.pageForm.pageNumber++}else{vant.Toast(res.msg)}}).always(()=>{this.loading=false});
|
||||||
$.post('/mobile/activity/site/info/pageData', this.pageForm).then(res => {
|
|
||||||
if (res.code === 0) {
|
|
||||||
this.tableData = this.tableData.concat(res.data.list)
|
|
||||||
if (this.tableData.length === res.data.totalCount) {
|
|
||||||
this.finished = true
|
|
||||||
} else {
|
|
||||||
this.pageForm.pageNumber++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.loading = false
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
async created() {
|
created() {
|
||||||
this.typeList = await siteTypeUtil.getAllType()
|
siteTypeUtil.getAllType().then((rows)=>{this.typeList=[{text:'全部场地类型'}].concat(rows);this.$set(this.pageForm,'typeId',this.typeList[0].value);this.onLoad()});
|
||||||
this.typeList.unshift({text: '全部场地类型'})
|
|
||||||
if (this.typeList && this.typeList.length > 0) {
|
|
||||||
this.$set(this.pageForm, "typeId", this.typeList[0].value)
|
|
||||||
}
|
|
||||||
this.onLoad()
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
if (window.popupHistoryMixin) startPage()
|
||||||
|
else {
|
||||||
|
if (!window.siteListHistoryLoading) {
|
||||||
|
window.siteListHistoryLoading = $.getScript('/assets/mobile/js/popupHistory.js')
|
||||||
|
.fail(() => { window.siteListHistoryLoading = null })
|
||||||
|
}
|
||||||
|
window.siteListHistoryLoading.then(() => startPage()).fail(() => {
|
||||||
|
if (document.getElementById('app')===pageRoot) vant.Toast.fail('页面组件加载失败,请重新进入')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!--#
|
<!--#
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ layout("/mobile/platform.html"){
|
|||||||
|
|
||||||
<!--top栏-->
|
<!--top栏-->
|
||||||
<van-nav-bar
|
<van-nav-bar
|
||||||
@click-left="pjaxReplace('/mobile/index')"
|
@click-left="goBack"
|
||||||
fixed
|
fixed
|
||||||
left-arrow
|
left-arrow
|
||||||
placeholder
|
placeholder
|
||||||
@@ -188,7 +188,8 @@ layout("/mobile/platform.html"){
|
|||||||
>
|
>
|
||||||
</van-search>
|
</van-search>
|
||||||
<van-dropdown-menu>
|
<van-dropdown-menu>
|
||||||
<van-dropdown-item :title="pageForm.time" @open="" ref="item">
|
<van-dropdown-item :title="pageForm.time" ref="item"
|
||||||
|
@open="setPopupState('monthFilterShow',true)" @close="setPopupState('monthFilterShow',false)">
|
||||||
<van-cell center title="查询全部">
|
<van-cell center title="查询全部">
|
||||||
<template #right-icon>
|
<template #right-icon>
|
||||||
<van-switch active-color="#246fb4" size="24"
|
<van-switch active-color="#246fb4" size="24"
|
||||||
@@ -208,7 +209,8 @@ layout("/mobile/platform.html"){
|
|||||||
</van-button>
|
</van-button>
|
||||||
</div>
|
</div>
|
||||||
</van-dropdown-item>
|
</van-dropdown-item>
|
||||||
<van-dropdown-item :options="typeList" @change="doSearch"
|
<van-dropdown-item :options="typeList" @change="doSearch" ref="typeFilter"
|
||||||
|
@open="setPopupState('typeFilterShow',true)" @close="setPopupState('typeFilterShow',false)"
|
||||||
v-model="pageForm.typeId"></van-dropdown-item>
|
v-model="pageForm.typeId"></van-dropdown-item>
|
||||||
</van-dropdown-menu>
|
</van-dropdown-menu>
|
||||||
</div>
|
</div>
|
||||||
@@ -295,7 +297,8 @@ layout("/mobile/platform.html"){
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<van-dialog @confirm="backDo" show-cancel-button title="反馈意见" v-model="show">
|
<van-dialog v-model="cancelShow" title="撤销预约" show-cancel-button :before-close="beforeCancelClose">确定撤销此预约?已审核的申请不能撤销。</van-dialog>
|
||||||
|
<van-dialog :before-close="beforeFeedbackClose" show-cancel-button title="反馈意见" v-model="show">
|
||||||
<van-field
|
<van-field
|
||||||
autosize
|
autosize
|
||||||
label="反馈意见:"
|
label="反馈意见:"
|
||||||
@@ -310,10 +313,10 @@ layout("/mobile/platform.html"){
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
<van-tabbar v-model="tarBarActive">
|
<van-tabbar v-model="tarBarActive">
|
||||||
<van-tabbar-item @click="pjaxReplace('/mobile/activity/site/info')" icon="home-o"
|
<van-tabbar-item @click="navigateTo('/mobile/activity/site/info')" icon="home-o"
|
||||||
replace>场地预约
|
replace>场地预约
|
||||||
</van-tabbar-item>
|
</van-tabbar-item>
|
||||||
<van-tabbar-item @click="pjaxReplace('/mobile/activity/site/info/my')" icon="manager-o"
|
<van-tabbar-item @click="navigateTo('/mobile/activity/site/info/my')" icon="manager-o"
|
||||||
replace>我的预约
|
replace>我的预约
|
||||||
</van-tabbar-item>
|
</van-tabbar-item>
|
||||||
</van-tabbar>
|
</van-tabbar>
|
||||||
@@ -322,13 +325,21 @@ layout("/mobile/platform.html"){
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
(() => {
|
||||||
|
// PJAX 先执行内联脚本,等待历史组件加载;离开后的旧加载回调不能挂载到新页面。
|
||||||
|
const pageRoot = document.getElementById('app')
|
||||||
|
const startPage = () => {
|
||||||
|
if (document.getElementById('app') !== pageRoot || !pageRoot.isConnected) return
|
||||||
<!--# include("/platform/activity/includeJs/activityUtil.js"){} #-->
|
<!--# include("/platform/activity/includeJs/activityUtil.js"){} #-->
|
||||||
const siteTypeUtil = new typeUtil()
|
const siteTypeUtil = new typeUtil()
|
||||||
const vue = new Vue({
|
new Vue({
|
||||||
el: '#app',
|
el: '#app',
|
||||||
mixins: [mobileMixins],
|
mixins: [mobileMixins, window.popupHistoryMixin],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
historyPopupKeys: ['show','cancelShow','typeFilterShow','monthFilterShow'],
|
||||||
|
typeFilterShow:false, monthFilterShow:false,
|
||||||
|
cancelShow:false,cancelId:'',formLoading:false,
|
||||||
tarBarActive: 1,
|
tarBarActive: 1,
|
||||||
time: new Date(),
|
time: new Date(),
|
||||||
option: '',
|
option: '',
|
||||||
@@ -339,7 +350,50 @@ layout("/mobile/platform.html"){
|
|||||||
info: {},
|
info: {},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
// 筛选下拉层没有 v-model 开关,通过状态同步交给全局历史栈管理。
|
||||||
|
typeFilterShow(value) { if (!value && this.$refs.typeFilter) this.$refs.typeFilter.toggle(false) },
|
||||||
|
monthFilterShow(value) { if (!value && this.$refs.item) this.$refs.item.toggle(false) },
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this._siteListDispose = () => this.$destroy()
|
||||||
|
$(document).one('pjax:beforeReplace.siteList', this._siteListDispose)
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
// PJAX 替换 DOM 不会自动销毁实例,显式清理本页监听并触发历史 mixin 注销。
|
||||||
|
$(document).off('pjax:beforeReplace.siteList', this._siteListDispose)
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
setPopupState(key, value) { this.$set(this, key, value) },
|
||||||
|
// 列表由 PJAX replace 进入,导航返回明确回首页;有弹层时只关最上层。
|
||||||
|
goBack() {
|
||||||
|
if (this.formLoading) return
|
||||||
|
const key = this.cancelShow ? 'cancelShow' : this.show ? 'show' : this.monthFilterShow ? 'monthFilterShow' : this.typeFilterShow ? 'typeFilterShow' : null
|
||||||
|
if (key) window.popupHistory.close(this._popupOwner, key)
|
||||||
|
else this.navigateTo('/mobile/index')
|
||||||
|
},
|
||||||
|
// 切换栏目先清理弹框历史,避免返回时重新出现上一个页面的弹层。
|
||||||
|
navigateTo(url) {
|
||||||
|
if (this.formLoading) return
|
||||||
|
this.clearPopupHistory(() => {
|
||||||
|
if (window.location.pathname !== url) pjaxReplace(url)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// Dialog 保持打开直至成功,取消仅回退对应弹框历史;校验失败保留已输入内容。
|
||||||
|
beforeCancelClose(action, done) {
|
||||||
|
done(false)
|
||||||
|
if (this.formLoading) return
|
||||||
|
if (action==='confirm') this.cancelBooking()
|
||||||
|
else window.popupHistory.close(this._popupOwner, 'cancelShow')
|
||||||
|
},
|
||||||
|
beforeFeedbackClose(action, done) {
|
||||||
|
done(false)
|
||||||
|
if (this.formLoading) return
|
||||||
|
if (action==='confirm') this.backDo()
|
||||||
|
else window.popupHistory.close(this._popupOwner, 'show')
|
||||||
|
},
|
||||||
|
cancelBooking(){if(this.formLoading)return;this.formLoading=true;$.post('/mobile/activity/site/info/rollback',{id:this.cancelId}).then((res)=>{vant.Toast(res.msg);if(res.code===0)this.clearPopupHistory(()=>this.doSearch())}).always(()=>{this.formLoading=false})},
|
||||||
onConfirm() {
|
onConfirm() {
|
||||||
if (this.pageForm.timeSwitch === true) {
|
if (this.pageForm.timeSwitch === true) {
|
||||||
this.timeList = [{text: '全部', value: '全部'}]
|
this.timeList = [{text: '全部', value: '全部'}]
|
||||||
@@ -350,59 +404,28 @@ layout("/mobile/platform.html"){
|
|||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
this.$set(this.pageForm, "time", this.timeList[0].value)
|
this.$set(this.pageForm, "time", this.timeList[0].value)
|
||||||
this.$refs.item.toggle()
|
window.popupHistory.close(this._popupOwner, 'monthFilterShow')
|
||||||
this.doSearch()
|
this.doSearch()
|
||||||
},
|
},
|
||||||
timeFormatter(type, val) {
|
timeFormatter(type, val) {
|
||||||
if (type === 'year') {
|
if (type === 'year') {
|
||||||
return val + `年`;
|
return val + '年';
|
||||||
}
|
}
|
||||||
if (type === 'month') {
|
if (type === 'month') {
|
||||||
return val + `月`;
|
return val + '月';
|
||||||
}
|
}
|
||||||
return val;
|
return val;
|
||||||
},
|
},
|
||||||
async rollback(o) {
|
rollback(o) {
|
||||||
const self = this
|
this.cancelId=o.id;this.cancelShow=true;
|
||||||
const res = await $.post("/platform/activity/site/reserve/isCanRollBack", {id: o.id});
|
|
||||||
if (res === true) {
|
|
||||||
vant.Toast('此预约状态下不能进行撤回操作!')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
vant.Dialog.confirm({
|
|
||||||
title: '温馨提示',
|
|
||||||
message: '您是否要撤销此预约?',
|
|
||||||
}).then(async () => {
|
|
||||||
const resp = await $.post('/mobile/activity/site/info/rollback', {
|
|
||||||
id: o.id
|
|
||||||
})
|
|
||||||
vant.Toast(resp.msg)
|
|
||||||
if (resp.code === 0) {
|
|
||||||
self.doSearch()
|
|
||||||
}
|
|
||||||
}).catch(() => {
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
backOption(o) {
|
backOption(o) {
|
||||||
this.info = o
|
this.info = o
|
||||||
this.option = ''
|
this.option = ''
|
||||||
this.show = true
|
this.show = true
|
||||||
},
|
},
|
||||||
async backDo() {
|
backDo() {
|
||||||
if (this.option === '') {
|
if(this.formLoading)return;if(!this.option.trim()){vant.Toast('请填写反馈意见');return}this.formLoading=true;$.post('/mobile/activity/site/info/backOption',{id:this.info.id,option:this.option}).then((res)=>{vant.Toast(res.msg);if(res.code===0)this.clearPopupHistory(()=>this.doSearch())}).always(()=>{this.formLoading=false});
|
||||||
vant.Toast('请填写反馈意见')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const res = await $.post('/mobile/activity/site/info/backOption', {
|
|
||||||
id: this.info.id,
|
|
||||||
option: this.option
|
|
||||||
})
|
|
||||||
if (res.code === 0) {
|
|
||||||
this.doSearch()
|
|
||||||
vant.Toast(res.msg)
|
|
||||||
} else {
|
|
||||||
vant.Toast('操作失败')
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
getTime(o, day) {
|
getTime(o, day) {
|
||||||
const week = new Date(day).getDay()
|
const week = new Date(day).getDay()
|
||||||
@@ -415,34 +438,26 @@ layout("/mobile/platform.html"){
|
|||||||
this.finished = false
|
this.finished = false
|
||||||
this.onLoad()
|
this.onLoad()
|
||||||
},
|
},
|
||||||
async onLoad() {
|
onLoad() {
|
||||||
this.loading = true
|
this.loading=true;return $.post('/mobile/activity/site/info/myReserve',this.pageForm).then((res)=>{if(res.code===0){this.tableData=this.tableData.concat(res.data.list);this.finished=this.tableData.length>=res.data.totalCount;if(!this.finished)this.pageForm.pageNumber++}else{vant.Toast(res.msg)}}).always(()=>{this.loading=false});
|
||||||
$.post('/mobile/activity/site/info/myReserve', this.pageForm).then(res => {
|
|
||||||
if (res.code === 0) {
|
|
||||||
this.tableData = this.tableData.concat(res.data.list)
|
|
||||||
if (this.tableData.length === res.data.totalCount) {
|
|
||||||
this.finished = true
|
|
||||||
} else {
|
|
||||||
this.pageForm.pageNumber++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.loading = false
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
async created() {
|
created() {
|
||||||
this.typeList = await siteTypeUtil.getAllType()
|
siteTypeUtil.getAllType().then((rows)=>{this.typeList=[{text:'全部场地类型'}].concat(rows);this.$set(this.pageForm,'typeId',this.typeList[0].value);this.timeList=[{text:'全部',value:'全部'}];this.$set(this.pageForm,'time','全部');this.$set(this.pageForm,'timeSwitch',true);this.onLoad()});
|
||||||
this.typeList.unshift({text: '全部场地类型'})
|
|
||||||
if (this.typeList && this.typeList.length > 0) {
|
|
||||||
this.$set(this.pageForm, "typeId", this.typeList[0].value)
|
|
||||||
}
|
|
||||||
//this.timeList.unshift({text: moment().format('YYYY-MM'), value: moment().format('YYYY-MM')})
|
|
||||||
this.timeList = [{text: '全部', value: '全部'}]
|
|
||||||
this.$set(this.pageForm, "time", this.timeList[0].value)
|
|
||||||
this.$set(this.pageForm, "timeSwitch", true)
|
|
||||||
this.onLoad()
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
if (window.popupHistoryMixin) startPage()
|
||||||
|
else {
|
||||||
|
if (!window.siteListHistoryLoading) {
|
||||||
|
window.siteListHistoryLoading = $.getScript('/assets/mobile/js/popupHistory.js')
|
||||||
|
.fail(() => { window.siteListHistoryLoading = null })
|
||||||
|
}
|
||||||
|
window.siteListHistoryLoading.then(() => startPage()).fail(() => {
|
||||||
|
if (document.getElementById('app')===pageRoot) vant.Toast.fail('页面组件加载失败,请重新进入')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!--#
|
<!--#
|
||||||
|
|||||||
@@ -141,6 +141,11 @@ layout("/mobile/platform.html"){
|
|||||||
.van-action-sheet {
|
.van-action-sheet {
|
||||||
max-height: 90%;
|
max-height: 90%;
|
||||||
}
|
}
|
||||||
|
.reserve-choice.van-cell, .site-reserve-options .van-cell { display:flex; }
|
||||||
|
.reserve-choice .van-cell__value { flex:1; overflow-wrap:anywhere; }
|
||||||
|
.site-reserve-options { max-height:70vh; overflow-y:auto; }
|
||||||
|
.site-reserve-options .van-cell__title { flex:1; white-space:normal; }
|
||||||
|
.site-reserve-options .selected-option { color:#246fb4; }
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div id="app" v-cloak>
|
<div id="app" v-cloak>
|
||||||
@@ -151,7 +156,7 @@ layout("/mobile/platform.html"){
|
|||||||
left-arrow
|
left-arrow
|
||||||
placeholder
|
placeholder
|
||||||
fixed
|
fixed
|
||||||
@click-left="pjaxReplace('/mobile/index')"
|
@click-left="goBack"
|
||||||
safe-area-inset-top
|
safe-area-inset-top
|
||||||
></van-nav-bar>
|
></van-nav-bar>
|
||||||
|
|
||||||
@@ -226,12 +231,11 @@ layout("/mobile/platform.html"){
|
|||||||
</div>
|
</div>
|
||||||
</van-cell>
|
</van-cell>
|
||||||
<van-cell title="预约地点">{{site.name}}</van-cell>
|
<van-cell title="预约地点">{{site.name}}</van-cell>
|
||||||
<van-cell title="预约类型" class="reserve_type">
|
<van-cell title="预约类型" :value="reserveTypeName" is-link class="reserve-choice" @click="openOptions('type')"></van-cell>
|
||||||
<van-radio-group v-model="reserve_type" direction="horizontal">
|
<van-cell title="所属协会" v-if="myClubs.length || reserve_type===3" class="reserve-choice"
|
||||||
<van-radio :name="1">个人预约</van-radio>
|
:value="reserve_type===3 ? selectedClubName : myClubs.map(club=>club.name).join('、')"
|
||||||
<van-radio :name="2">单位预约</van-radio>
|
:is-link="reserve_type===3" @click="openOptions('club')"></van-cell>
|
||||||
</van-radio-group>
|
<div v-if="reserve_type===3 && !myClubs.length" style="padding:12px;color:#ee0a24">您暂无已通过入会审核的有效协会</div>
|
||||||
</van-cell>
|
|
||||||
|
|
||||||
<!--<van-field
|
<!--<van-field
|
||||||
v-model="joinUser"
|
v-model="joinUser"
|
||||||
@@ -255,18 +259,33 @@ layout("/mobile/platform.html"){
|
|||||||
></van-field>
|
></van-field>
|
||||||
|
|
||||||
<div style="margin: 20px 0px; display: flex; justify-content: space-between;padding: 0px 22px;">
|
<div style="margin: 20px 0px; display: flex; justify-content: space-between;padding: 0px 22px;">
|
||||||
<van-button @click="message = '';show = false" color="#246fb4" style="width: 47%" size="small" plain
|
<van-button @click="closeConfirmation" :disabled="formLoading" 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="formLoading" color="#246fb4" style="width: 47%" size="small" type="info">确认</van-button>
|
||||||
</div>
|
</div>
|
||||||
</van-action-sheet>
|
</van-action-sheet>
|
||||||
|
<!-- 选项弹层后于确认表单打开,由 Vant 分配更高层级;选择或返回只关闭本层。 -->
|
||||||
|
<van-popup v-model="optionsShow" position="top" class="site-reserve-options" safe-area-inset-top>
|
||||||
|
<van-nav-bar :title="optionKind==='type' ? '选择预约类型' : '选择所属协会'"
|
||||||
|
left-text="返回" left-arrow @click-left="closeOptions"></van-nav-bar>
|
||||||
|
<van-cell v-for="option in selectionOptions" :key="option.value" :title="option.text" clickable
|
||||||
|
:class="{'selected-option':option.value===selectedOption}" @click="selectOption(option)">
|
||||||
|
<template #right-icon><van-icon v-if="option.value===selectedOption" name="success" color="#246fb4"></van-icon></template>
|
||||||
|
</van-cell>
|
||||||
|
<van-empty v-if="!selectionOptions.length" description="暂无可选协会"></van-empty>
|
||||||
|
</van-popup>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
(() => {
|
||||||
|
// PJAX 内联脚本先执行,必须等待弹框历史依赖加载后再创建页面。
|
||||||
|
const pageRoot = document.getElementById('app')
|
||||||
|
const startPage = () => {
|
||||||
|
if (document.getElementById('app') !== pageRoot || !pageRoot.isConnected) return
|
||||||
moment.locale('zh_cn');
|
moment.locale('zh_cn');
|
||||||
|
|
||||||
function getQueryString(name) {
|
const getQueryString = (name) => {
|
||||||
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
|
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
|
||||||
var r = window.location.search.substr(1).match(reg);
|
var r = window.location.search.substr(1).match(reg);
|
||||||
if (r != null) return decodeURI(r[2]);
|
if (r != null) return decodeURI(r[2]);
|
||||||
@@ -275,11 +294,15 @@ layout("/mobile/platform.html"){
|
|||||||
|
|
||||||
<!--# include("/platform/activity/includeJs/activityUtil.js"){} #-->
|
<!--# include("/platform/activity/includeJs/activityUtil.js"){} #-->
|
||||||
const siteTypeUtil = new typeUtil()
|
const siteTypeUtil = new typeUtil()
|
||||||
const vue = new Vue({
|
new Vue({
|
||||||
el: '#app',
|
el: '#app',
|
||||||
mixins: [mobileMixins],
|
mixins: [mobileMixins, window.popupHistoryMixin],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
historyPopupKeys: ['show','calendarVisible','optionsShow'],
|
||||||
|
optionsShow: false, optionKind: 'type',
|
||||||
|
reserveTypeOptions: [{text:'个人预约',value:1},{text:'单位预约',value:2},{text:'协会预约',value:3}],
|
||||||
|
myClubs: [], clubId: '', formLoading: false,
|
||||||
reserve_type: 1,
|
reserve_type: 1,
|
||||||
person: "${@shiro.getPrincipalProperty('username')}",
|
person: "${@shiro.getPrincipalProperty('username')}",
|
||||||
unit: "${@shiro.getPrincipalProperty('unit').getName()}",
|
unit: "${@shiro.getPrincipalProperty('unit').getName()}",
|
||||||
@@ -297,6 +320,15 @@ layout("/mobile/platform.html"){
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
reserveTypeName() { return this.reserveTypeOptions.find(option => option.value===this.reserve_type).text },
|
||||||
|
selectedClubName() {
|
||||||
|
const club = this.myClubs.find(item => item.id===this.clubId)
|
||||||
|
return club ? club.name : '请选择所属协会'
|
||||||
|
},
|
||||||
|
selectionOptions() {
|
||||||
|
return this.optionKind==='type' ? this.reserveTypeOptions : this.myClubs.map(club => ({text:club.name,value:club.id}))
|
||||||
|
},
|
||||||
|
selectedOption() { return this.optionKind==='type' ? this.reserve_type : this.clubId },
|
||||||
getWeekTextByDate() {
|
getWeekTextByDate() {
|
||||||
return (date) => {
|
return (date) => {
|
||||||
const weekNum = moment(date).day()
|
const weekNum = moment(date).day()
|
||||||
@@ -331,7 +363,45 @@ layout("/mobile/platform.html"){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
mounted() {
|
||||||
|
// PJAX 替换页面时显式销毁,触发统一历史 mixin 的监听注销。
|
||||||
|
this._reserveDispose = () => this.$destroy()
|
||||||
|
$(document).one('pjax:beforeReplace.siteReserve', this._reserveDispose)
|
||||||
|
$.post('/platform/activity/site/reserve/myClubs').then((res)=>{
|
||||||
|
if(res.code===0){this.$set(this,'myClubs',res.data);this.$set(this,'clubId',res.data.length===1 ? res.data[0].id : '')}
|
||||||
|
else{vant.Toast(res.msg)}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
$(document).off('pjax:beforeReplace.siteReserve', this._reserveDispose)
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
// type 显示预约类型,club 仅在协会预约时可选;不修改所属协会的成员校验规则。
|
||||||
|
openOptions(kind) {
|
||||||
|
if (this.formLoading || this.optionsShow || kind==='club' && this.reserve_type!==3) return
|
||||||
|
this.$set(this, 'optionKind', kind)
|
||||||
|
this.$set(this, 'optionsShow', true)
|
||||||
|
},
|
||||||
|
// value 保留原接口要求的类型数字/协会 ID,选择后回填并回退选项层历史。
|
||||||
|
selectOption(option) {
|
||||||
|
if (!this.selectionOptions.some(item => item.value===option.value)) return
|
||||||
|
if (this.optionKind==='type') this.$set(this, 'reserve_type', option.value)
|
||||||
|
else this.$set(this, 'clubId', option.value)
|
||||||
|
this.closeOptions()
|
||||||
|
},
|
||||||
|
closeOptions() { window.popupHistory.close(this._popupOwner, 'optionsShow') },
|
||||||
|
closeConfirmation() {
|
||||||
|
if (this.formLoading) return
|
||||||
|
this.$set(this, 'message', '')
|
||||||
|
window.popupHistory.close(this._popupOwner, 'show')
|
||||||
|
},
|
||||||
|
// 有弹层先关闭最上层;直接链接进入也能明确返回场地列表,不依赖上一条历史。
|
||||||
|
goBack() {
|
||||||
|
if (this.formLoading) return
|
||||||
|
const key = this.optionsShow ? 'optionsShow' : this.show ? 'show' : this.calendarVisible ? 'calendarVisible' : null
|
||||||
|
if (key) window.popupHistory.close(this._popupOwner, key)
|
||||||
|
else this.clearPopupHistory(() => pjaxReplace('/mobile/activity/site/info'))
|
||||||
|
},
|
||||||
close(f) {
|
close(f) {
|
||||||
if (this.times.length === 1) {
|
if (this.times.length === 1) {
|
||||||
vant.Toast('必须保留一个时间段')
|
vant.Toast('必须保留一个时间段')
|
||||||
@@ -371,26 +441,13 @@ layout("/mobile/platform.html"){
|
|||||||
}
|
}
|
||||||
this.show = true
|
this.show = true
|
||||||
},
|
},
|
||||||
async reserveDo() {
|
reserveDo() {
|
||||||
const resp = await $.post('/mobile/activity/site/info/reserveDo', {
|
if(this.reserve_type===3 && !this.clubId){vant.Toast('请选择所属协会');return}
|
||||||
siteId: this.site_id,
|
this.formLoading=true;
|
||||||
times: JSON.stringify(this.times),
|
$.post('/mobile/activity/site/info/reserveDo',{siteId:this.site_id,times:JSON.stringify(this.times),message:this.message,reserve_type:this.reserve_type,clubId:this.clubId}).then((res)=>{
|
||||||
message: this.message,
|
if(res.code===0){this.clearPopupHistory(()=>{vant.Toast('预约成功');this.times=[];this.message='';this.getReserve();this.onLoad()})}
|
||||||
joinUser: this.joinUser,
|
else{vant.Toast(res.msg)}
|
||||||
reserve_type: this.reserve_type
|
}).always(()=>{this.formLoading=false})
|
||||||
})
|
|
||||||
if (resp.code === 0) {
|
|
||||||
vant.Toast('预约成功')
|
|
||||||
if (resp.code === 0) {
|
|
||||||
this.show = false
|
|
||||||
this.times = []
|
|
||||||
this.message = null
|
|
||||||
await this.getReserve()
|
|
||||||
await this.onLoad()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
vant.Toast(resp.msg)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
chooseTime(item) {
|
chooseTime(item) {
|
||||||
const cloneItem = clone(item)
|
const cloneItem = clone(item)
|
||||||
@@ -405,22 +462,12 @@ layout("/mobile/platform.html"){
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async onLoad() {
|
onLoad() {
|
||||||
this.loading = true
|
this.loading=true;return $.post('/mobile/activity/site/info/getDetail',{id:this.site_id}).then((res)=>{if(res.code===0){this.site=res.data}else{vant.Toast(res.msg)}}).always(()=>{this.loading=false});
|
||||||
const resp = await $.post('/mobile/activity/site/info/getDetail', {id: this.site_id})
|
|
||||||
if (resp.code === 0) {
|
|
||||||
this.site = resp.data
|
|
||||||
}
|
|
||||||
this.loading = false
|
|
||||||
},
|
},
|
||||||
async getReserve() {
|
getReserve() {
|
||||||
const resp = await $.post('/mobile/activity/site/info/getReserve', {
|
return $.post('/mobile/activity/site/info/getReserve',{siteId:this.site_id,day:moment(this.selectDate).format('YYYY-MM-DD')}).then((res)=>{if(res.code===0){this.timeData=res.data}else{vant.Toast(res.msg)}});
|
||||||
siteId: this.site_id,
|
|
||||||
day: moment(this.selectDate).format('YYYY-MM-DD'),
|
|
||||||
})
|
|
||||||
if (resp.code === 0) {
|
|
||||||
this.timeData = resp.data
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
//获取最新的一个工作日设为当前时间
|
//获取最新的一个工作日设为当前时间
|
||||||
getLatestWorkDay() {
|
getLatestWorkDay() {
|
||||||
@@ -439,14 +486,22 @@ layout("/mobile/platform.html"){
|
|||||||
return selectDate
|
return selectDate
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async created() {
|
created() {
|
||||||
this.site_id = getQueryString('id')
|
this.site_id=getQueryString('id');this.selectDate=this.getLatestWorkDay();this.onLoad().then(()=>{this.setWeekList(this.selectDate);return this.getReserve()});
|
||||||
this.selectDate = this.getLatestWorkDay()
|
|
||||||
await this.onLoad()
|
|
||||||
this.setWeekList(this.selectDate)
|
|
||||||
await this.getReserve()
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
if (window.popupHistoryMixin) startPage()
|
||||||
|
else {
|
||||||
|
if (!window.siteReserveHistoryLoading) {
|
||||||
|
window.siteReserveHistoryLoading = $.getScript('/assets/mobile/js/popupHistory.js')
|
||||||
|
.fail(() => { window.siteReserveHistoryLoading = null })
|
||||||
|
}
|
||||||
|
window.siteReserveHistoryLoading.then(() => startPage()).fail(() => {
|
||||||
|
if (document.getElementById('app') === pageRoot) vant.Toast.fail('页面组件加载失败,请重新进入')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!--#
|
<!--#
|
||||||
|
|||||||
@@ -387,8 +387,8 @@ layout("/mobile/platform.html"){
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!--工作台-->
|
<!--工作台:底部预留导航栏及手机安全区空间,确保最后一排菜单可完整滚动展示。-->
|
||||||
<div style="margin-bottom: 60px" v-if="active === 2">
|
<div style="padding-bottom: 80px; padding-bottom: calc(80px + constant(safe-area-inset-bottom)); padding-bottom: calc(80px + env(safe-area-inset-bottom))" v-if="active === 2">
|
||||||
<van-row class="module" v-for="m in moduleMenus">
|
<van-row class="module" v-for="m in moduleMenus">
|
||||||
<div class="van-sidebar-item van-sidebar-item--select">
|
<div class="van-sidebar-item van-sidebar-item--select">
|
||||||
{{m.moduleName}}
|
{{m.moduleName}}
|
||||||
|
|||||||
@@ -1,31 +1,9 @@
|
|||||||
|
|
||||||
class typeUtil {
|
class typeUtil {
|
||||||
|
// 返回类型数组,text/value 为现有选择组件需要的别名;默认保留停用类型用于历史筛选。
|
||||||
constructor() {
|
getAllType(enabledOnly = false) {
|
||||||
|
return $.post('/platform/activity/site/type/findAll', {enabledOnly}).then((resp) => {
|
||||||
|
if (resp.code !== 0) return [];
|
||||||
|
return resp.data.map((row) => Object.assign({}, row, {text:row.meetingTypeName,value:row.id}));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取全部会议类型
|
|
||||||
* @returns {Promise<*[]|*>}
|
|
||||||
*/
|
|
||||||
async getAllType() {
|
|
||||||
const resp = await $.get('/platform/activity/site/type/findAll')
|
|
||||||
if (resp.code === 0) {
|
|
||||||
resp.data.forEach(v => {
|
|
||||||
v['text'] = v['meetingTypeName']
|
|
||||||
v['value'] = v['id']
|
|
||||||
})
|
|
||||||
return resp.data
|
|
||||||
}
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
async findMaxStateId() {
|
|
||||||
const resp = await $.get('/platform/activity/site/type/findMaxStateId')
|
|
||||||
if (resp.code === 0) {
|
|
||||||
return resp.data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<!DOCTYPE html><html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>场地预约审核</title></head><body>
|
||||||
|
<h3>请选择审核入口</h3>
|
||||||
|
<!--# if(@shiro.hasPermission('activity.site.review.union')){ #--><p><a href="/platform/activity/site/review/union">分工会审核</a> · <a href="/mobile/activity/site/audit/union">手机端</a></p><!--# } #-->
|
||||||
|
<!--# if(@shiro.hasPermission('activity.site.review.club')){ #--><p><a href="/platform/activity/site/review/club">协会审核</a> · <a href="/mobile/activity/site/audit/club">手机端</a></p><!--# } #-->
|
||||||
|
<!--# if(@shiro.hasPermission('activity.site.review.school')){ #--><p><a href="/platform/activity/site/review/school">校工会审核</a> · <a href="/mobile/activity/site/audit/school">手机端</a></p><!--# } #-->
|
||||||
|
</body></html>
|
||||||
@@ -166,7 +166,7 @@ layout("/layouts/platform.html"){
|
|||||||
<el-select v-model="formData.typeId" style="width: 100%" filterable
|
<el-select v-model="formData.typeId" style="width: 100%" filterable
|
||||||
placeholder="请选择场地类型">
|
placeholder="请选择场地类型">
|
||||||
<el-option v-for="item in activityTypeList" :label="item.meetingTypeName"
|
<el-option v-for="item in activityTypeList" :label="item.meetingTypeName"
|
||||||
:value="item.id"></el-option>
|
:value="item.id" :disabled="!item.enabled"></el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
@@ -278,6 +278,7 @@ layout("/layouts/platform.html"){
|
|||||||
tabKey: '',
|
tabKey: '',
|
||||||
tableLoading: false,
|
tableLoading: false,
|
||||||
subDis: false,
|
subDis: false,
|
||||||
|
formLoading: false,
|
||||||
tableData: [],
|
tableData: [],
|
||||||
formData: {
|
formData: {
|
||||||
open_hours: [{}],
|
open_hours: [{}],
|
||||||
@@ -328,41 +329,19 @@ layout("/layouts/platform.html"){
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
async operation() {
|
operation() {
|
||||||
let method = this.formData.id ? "/doEdit" : "/doAdd"
|
this.$refs.addForm.validate((valid)=>{if(!valid)return;this.formLoading=true;this.subDis=true;
|
||||||
this.subDis = true
|
$.post(loc()+(this.formData.id ? '/doEdit' : '/doAdd'),{data:JSON.stringify(this.formData)}).then((res)=>{if(res.code===0){this.pageData();this.$refs.guava.index()}else{this.$message.error(res.msg)}}).always(()=>{this.formLoading=false;this.subDis=false})
|
||||||
this.$refs["addForm"].validate(async (valid) => {
|
|
||||||
if (valid) {
|
|
||||||
const loading = this.$loading({
|
|
||||||
lock: true,
|
|
||||||
text: '正在提交...',
|
|
||||||
spinner: 'el-icon-loading',
|
|
||||||
background: 'rgba(0, 0, 0, 0.7)'
|
|
||||||
});
|
|
||||||
|
|
||||||
const {code, data, msg} = await $.post(location.href + method, {data: JSON.stringify(this.formData)})
|
|
||||||
if (code === 0) {
|
|
||||||
this.pageData()
|
|
||||||
this.$message.success(msg);
|
|
||||||
this.$refs.guava.index()
|
|
||||||
} else {
|
|
||||||
this.$message.error(msg);
|
|
||||||
}
|
|
||||||
loading.close()
|
|
||||||
this.subDis = false
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
async findOneSite(id) {
|
findOneSite(id) {
|
||||||
const {data} = await $.get("/platform/activity/site/mange/findOneSite", {id: id})
|
return $.post('/platform/activity/site/mange/findOneSite',{id}).then((res)=>{if(res.code!==0){this.$message.error(res.msg);return null}return res.data});
|
||||||
return data
|
|
||||||
},
|
},
|
||||||
openView(row) {
|
openView(row) {
|
||||||
this.$refs.guava.view()
|
this.$refs.guava.view()
|
||||||
},
|
},
|
||||||
async openEdit(id) {
|
openEdit(id) {
|
||||||
this.formData = await this.findOneSite(id)
|
this.findOneSite(id).then((row)=>{if(row){this.formData=row;this.$refs.guava.edit()}});
|
||||||
this.$refs.guava.edit()
|
|
||||||
},
|
},
|
||||||
openAdd() {
|
openAdd() {
|
||||||
this.formData = {
|
this.formData = {
|
||||||
@@ -417,9 +396,8 @@ layout("/layouts/platform.html"){
|
|||||||
}, "json");
|
}, "json");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async created() {
|
created() {
|
||||||
this.pageData();
|
this.pageData();activityUtil.getAllType().then((rows)=>{this.activityTypeList=rows});
|
||||||
this.activityTypeList = await activityUtil.getAllType()
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -97,21 +97,9 @@ layout("/layouts/platform.html"){
|
|||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.textDia h2 {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.textDia h4 {
|
|
||||||
line-height: 26px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.textDia .el-dialog__header {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.textDia .el-dialog__body {
|
|
||||||
padding: 10px 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<div id="app" v-cloak>
|
<div id="app" v-cloak>
|
||||||
@@ -345,7 +333,7 @@ layout("/layouts/platform.html"){
|
|||||||
<div
|
<div
|
||||||
v-for="item in reserveViewData">
|
v-for="item in reserveViewData">
|
||||||
<div v-if="data.day.toString()==item.reserve_day"
|
<div v-if="data.day.toString()==item.reserve_day"
|
||||||
:style="{backgroundColor:([0].includes(item.stateaudittype)?'#FAECD9':item.reserve_state==30?'#E0F4D9':'#FEE2E2')}">
|
:style="{backgroundColor:([0].includes(item.stateaudittype)?'#FAECD9':item.reserve_state==4030?'#E0F4D9':'#FEE2E2')}">
|
||||||
<el-row :gutter="20" style="line-height: 30px;margin-bottom: 2px;">
|
<el-row :gutter="20" style="line-height: 30px;margin-bottom: 2px;">
|
||||||
<el-col :span="4">
|
<el-col :span="4">
|
||||||
<el-button v-if="[0].includes(item.stateaudittype)" type="warning"
|
<el-button v-if="[0].includes(item.stateaudittype)" type="warning"
|
||||||
@@ -474,8 +462,16 @@ layout("/layouts/platform.html"){
|
|||||||
<el-radio-group v-model="formData.reserve_type" size="medium">
|
<el-radio-group v-model="formData.reserve_type" size="medium">
|
||||||
<el-radio-button :label="1">个人预约</el-radio-button>
|
<el-radio-button :label="1">个人预约</el-radio-button>
|
||||||
<el-radio-button :label="2">单位预约</el-radio-button>
|
<el-radio-button :label="2">单位预约</el-radio-button>
|
||||||
|
<el-radio-button :label="3">协会预约</el-radio-button>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<!-- 所属协会仅用于协会预约,个人和单位预约不展示该字段。 -->
|
||||||
|
<el-form-item label="所属协会" v-if="formData.reserve_type===3">
|
||||||
|
<el-select v-model="formData.clubId" placeholder="请选择所属协会" style="width:100%">
|
||||||
|
<el-option v-for="club in myClubs" :key="club.id" :label="club.name" :value="club.id"></el-option>
|
||||||
|
</el-select>
|
||||||
|
<div v-if="!myClubs.length">您暂无已通过入会审核的有效协会</div>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item label="预约事由" prop="reserve_cause">
|
<el-form-item label="预约事由" prop="reserve_cause">
|
||||||
<el-input maxlength="1000" placeholder="请输入预约事由" autosize v-model="formData.reserve_cause"
|
<el-input maxlength="1000" placeholder="请输入预约事由" autosize v-model="formData.reserve_cause"
|
||||||
@@ -489,38 +485,6 @@ layout("/layouts/platform.html"){
|
|||||||
</span>
|
</span>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
<el-dialog :visible.sync="drawer" class="textDia" top="3%">
|
|
||||||
<h2>爱心母婴室管理规定</h2>
|
|
||||||
<h4 style="font-weight: bold">一、基本原则</h4>
|
|
||||||
<div style="padding-left: 30px;">
|
|
||||||
<h4>1.使用对象:有哺乳需求的本校在职女教职工。</h4>
|
|
||||||
<h4>2.开放时间:工作日8:00--17:30。</h4>
|
|
||||||
<h4>3.使用制度:实行预约登记制。凡有哺乳需求的女教职工提前向校工会提出使用申请,经审核通过并开通权限后即可使用。</h4>
|
|
||||||
<h4>4.日常管理:由校工会负责。</h4>
|
|
||||||
</div>
|
|
||||||
<h4 style="font-weight: bold">二、管理人员</h4>
|
|
||||||
<div style="padding-left: 30px;">
|
|
||||||
<h4>1.负责爱心母婴室使用登记管理。</h4>
|
|
||||||
<h4>2.定期保养设备,确保正常使用。</h4>
|
|
||||||
<h4>3.保持室内整洁、卫生安全,做好保洁消毒记录。</h4>
|
|
||||||
<h4>4.定期收集意见和建议,不断改进服务。</h4>
|
|
||||||
</div>
|
|
||||||
<h4 style="font-weight: bold">三、使用人员</h4>
|
|
||||||
<div style="padding-left: 30px;">
|
|
||||||
<h4>1.遵守学校安全管理制度和爱心母婴室管理规定。</h4>
|
|
||||||
<h4>2.严禁吸烟,安全使用电器,爱护公物,损坏赔偿。</h4>
|
|
||||||
<h4>3.保持室内卫生清洁,勿大声喧哗,不影响楼内工作秩序。</h4>
|
|
||||||
<h4>4.妥善保管自带物品,冰存的母乳须贴上姓名标签并及时取走。</h4>
|
|
||||||
<h4>5.不得擅自携他人入内,不做和哺乳无关事宜,使用完毕及时离开。</h4>
|
|
||||||
<h4>6.欢迎在《爱心母婴室使用意见簿》上留下您宝贵的意见和建议。</h4>
|
|
||||||
</div>
|
|
||||||
<br/>
|
|
||||||
<h4>联系人:校工会</h4>
|
|
||||||
|
|
||||||
<span slot="footer" class="dialog-footer">
|
|
||||||
<el-button type="primary" @click="openReserve(textData)">已知晓并遵守</el-button>
|
|
||||||
</span>
|
|
||||||
</el-dialog>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -535,10 +499,11 @@ layout("/layouts/platform.html"){
|
|||||||
return {
|
return {
|
||||||
activityTypeList: [],
|
activityTypeList: [],
|
||||||
textData: {},
|
textData: {},
|
||||||
drawer: false,
|
|
||||||
step: 0.5,
|
step: 0.5,
|
||||||
max: 24,
|
max: 24,
|
||||||
min: 0,
|
min: 0,
|
||||||
|
myClubs: [],
|
||||||
|
formLoading: false,
|
||||||
slider_width: 0,
|
slider_width: 0,
|
||||||
single_width: 0,
|
single_width: 0,
|
||||||
marks: {},
|
marks: {},
|
||||||
@@ -621,27 +586,10 @@ layout("/layouts/platform.html"){
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
backOption(row) {
|
backOption(row) {
|
||||||
this.$prompt('请填写反馈意见', '提示', {
|
this.$prompt('请填写反馈意见','提示',{inputValidator:(value)=>value && value.trim() ? true : '请填写反馈意见'}).then(({value})=>{
|
||||||
confirmButtonText: '确定',
|
this.formLoading=true;
|
||||||
cancelButtonText: '取消',
|
return $.post('/mobile/activity/site/info/backOption',{id:row.id,option:value}).then((res)=>{if(res.code===0){this.doSearch()}else{this.$message.error(res.msg)}}).always(()=>{this.formLoading=false})
|
||||||
inputValidator: (value) => {
|
}).catch(()=>{});
|
||||||
if (value === null) {
|
|
||||||
return '反馈意见不能为空'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).then(async ({value}) => {
|
|
||||||
const res = await $.post('/mobile/activity/site/info/backOption', {
|
|
||||||
id: row.id,
|
|
||||||
option: value
|
|
||||||
})
|
|
||||||
if (res.code === 0) {
|
|
||||||
await this.doSearch()
|
|
||||||
this.$notify.success({title: '提示', message: '操作成功!'});
|
|
||||||
} else {
|
|
||||||
this.$notify.warning({title: '警告', message: '操作失败!'});
|
|
||||||
}
|
|
||||||
}).catch(() => {
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
addMinute(val, minute) {
|
addMinute(val, minute) {
|
||||||
if (val) {
|
if (val) {
|
||||||
@@ -682,35 +630,16 @@ layout("/layouts/platform.html"){
|
|||||||
formatTooltip(val) {
|
formatTooltip(val) {
|
||||||
return moment(val * HOUR1 - HOUR8).format('HH:mm')
|
return moment(val * HOUR1 - HOUR8).format('HH:mm')
|
||||||
},
|
},
|
||||||
async selectEnd() {
|
selectEnd() {
|
||||||
const {reserve_day, start_time, end_time, site_id} = this.formData
|
const {reserve_day,start_time,end_time,site_id}=this.formData;
|
||||||
const {open_hours} = this.infoViewData
|
if(reserve_day+' '+start_time<moment().format('YYYY-MM-DD HH:mm')){this.$message.warning('预约时间已过');return}
|
||||||
let mySelectStartTime = reserve_day + " " + start_time;
|
this.checkReserve(reserve_day,start_time,end_time,site_id).then((count)=>{
|
||||||
|
if(count>0 || !this.infoViewData.open_hours.some(v=>start_time>=v.start_time && end_time<=v.end_time)){
|
||||||
//判断预约时间是否在小于当前时间 若小于提示用户选择错误
|
this.$message.warning('该时段不可预约,请重新选择');this.$set(this.formData,'start_time','');this.$set(this.formData,'end_time','')
|
||||||
if (mySelectStartTime < moment().format("YYYY-MM-DD HH:mm:ss")) {
|
|
||||||
this.$notify.warning({title: '警告', message: '您选中的预约时间已超过当前时间,请重新选择!'});
|
|
||||||
this.$set(this.formData, 'start_time', '')
|
|
||||||
this.$set(this.formData, 'end_time', '')
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//判断预约时间是否已被预约 若已预约提示用户选择错误
|
|
||||||
if (await this.checkReserve(reserve_day, start_time, end_time, site_id) > 0) {
|
|
||||||
this.$notify.warning({title: '警告', message: '该时段已被预约,请重新选择!'});
|
|
||||||
this.$set(this.formData, 'start_time', '')
|
|
||||||
this.$set(this.formData, 'end_time', '')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//判断预约时间是否开放 若未开放提示用户选择错误
|
|
||||||
if (!open_hours.some(v => start_time >= v.start_time && end_time <= v.end_time)) {
|
|
||||||
this.$notify.warning({title: '警告', message: '您选中预约时间暂未开放,请重新选择!'});
|
|
||||||
this.$set(this.formData, 'start_time', '')
|
|
||||||
this.$set(this.formData, 'end_time', '')
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
async siteChange(id) {
|
siteChange(id) {
|
||||||
this.pageForm.siteId = id
|
this.pageForm.siteId = id
|
||||||
this.pageData()
|
this.pageData()
|
||||||
},
|
},
|
||||||
@@ -766,7 +695,7 @@ layout("/layouts/platform.html"){
|
|||||||
this.dayArray.sort()
|
this.dayArray.sort()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async openAdd() {
|
openAdd() {
|
||||||
|
|
||||||
if (this.dayArray && this.dayArray.length === 0) {
|
if (this.dayArray && this.dayArray.length === 0) {
|
||||||
this.$notify.warning({title: '警告', message: '请选择预约日期!'});
|
this.$notify.warning({title: '警告', message: '请选择预约日期!'});
|
||||||
@@ -777,6 +706,11 @@ layout("/layouts/platform.html"){
|
|||||||
this.$set(this.formData, "reserve_person_unit", '${@shiro.getPrincipalProperty("unit").getName()}')
|
this.$set(this.formData, "reserve_person_unit", '${@shiro.getPrincipalProperty("unit").getName()}')
|
||||||
this.$set(this.formData, "reserve_person_phone", '${@shiro.getPrincipalProperty("mobile")}')
|
this.$set(this.formData, "reserve_person_phone", '${@shiro.getPrincipalProperty("mobile")}')
|
||||||
this.$set(this.formData, 'reserve_type', 1)
|
this.$set(this.formData, 'reserve_type', 1)
|
||||||
|
// 加载协会预约的可选协会;只有一个时默认选中,多协会由申请人选择。
|
||||||
|
$.post('/platform/activity/site/reserve/myClubs').then((res)=>{
|
||||||
|
if(res.code===0){this.myClubs=res.data;this.$set(this.formData,'clubId',res.data.length===1 ? res.data[0].id : null)}
|
||||||
|
else{this.$message.error(res.msg)}
|
||||||
|
})
|
||||||
|
|
||||||
if (this.$refs['addForm']) {
|
if (this.$refs['addForm']) {
|
||||||
this.$refs['addForm'].resetFields()
|
this.$refs['addForm'].resetFields()
|
||||||
@@ -793,67 +727,26 @@ layout("/layouts/platform.html"){
|
|||||||
})
|
})
|
||||||
|
|
||||||
},
|
},
|
||||||
async doDelete(id, op) {
|
doDelete(id, op) {
|
||||||
if (op === 2) {
|
this.$confirm('确定撤销此预约?已审核的申请不能撤销。','提示').then(()=>{
|
||||||
const res = await $.post(location.href + "/isCanRollBack", {id: id});
|
this.formLoading=true;
|
||||||
if (res === true) {
|
return $.post(loc()+'/doDelete',{id,op}).then((res)=>{if(res.code===0){this.pageData()}else{this.$message.error(res.msg)}}).always(()=>{this.formLoading=false})
|
||||||
this.$notify.warning({title: '警告', message: '此预约状态下不能进行撤回操作!'});
|
}).catch(()=>{});
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let text = op === 1 ? '删除' : '撤销'
|
|
||||||
this.$confirm("您确定要" + text + "此预约吗?", '提示', {
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
cancelButtonText: '取消',
|
|
||||||
type: 'warning',
|
|
||||||
callback: (a, b) => {
|
|
||||||
if ("confirm" === a) {
|
|
||||||
$.post(location.href + "/doDelete", {id, op}, (data) => {
|
|
||||||
if (data.code === 0) {
|
|
||||||
this.$message.success(data.msg)
|
|
||||||
this.pageData();
|
|
||||||
} else {
|
|
||||||
this.$message.warning(data.msg)
|
|
||||||
}
|
|
||||||
}, "json");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
transNum(hm) {
|
transNum(hm) {
|
||||||
return (moment("1970-01-01 " + hm).valueOf() + HOUR8) / HOUR1
|
return (moment("1970-01-01 " + hm).valueOf() + HOUR8) / HOUR1
|
||||||
},
|
},
|
||||||
async doAdd() {
|
doAdd() {
|
||||||
if (!this.formData.time) {
|
if (!this.formData.time) { this.$message.warning('请选择预约时间'); return; }
|
||||||
this.$notify.warning({title: '警告', message: '未选择预约时间!'});
|
if (this.formData.reserve_type===3 && !this.formData.clubId) { this.$message.warning('请选择所属协会'); return; }
|
||||||
return
|
this.$refs.addForm.validate((valid)=>{
|
||||||
}
|
if(!valid) return;
|
||||||
this.$refs["addForm"].validate(async (valid) => {
|
this.formLoading=true; this.subDis=true;
|
||||||
if (valid) {
|
$.post(loc()+'/doAdd',{data:JSON.stringify(this.formData),days:JSON.stringify(this.dayArray)}).then((res)=>{
|
||||||
this.subDis = true
|
if(res.code===0){this.addDialogVisible=false;this.$message.success('预约成功');this.pageData();this.$refs.guava.index()}
|
||||||
const loading = this.$loading({
|
else{this.$message.error(res.msg)}
|
||||||
lock: true,
|
}).always(()=>{this.formLoading=false;this.subDis=false})
|
||||||
text: '正在提交...',
|
|
||||||
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.$notify.warning({title: '警告', message: msg});
|
|
||||||
}
|
|
||||||
loading.close()
|
|
||||||
this.subDis = false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
openView(row) {
|
openView(row) {
|
||||||
this.$refs.guava.view()
|
this.$refs.guava.view()
|
||||||
@@ -863,21 +756,15 @@ layout("/layouts/platform.html"){
|
|||||||
this.$refs.reserve.backOptionShow = (role || backOptionShow)
|
this.$refs.reserve.backOptionShow = (role || backOptionShow)
|
||||||
this.$refs.reserve.openView(row.id)
|
this.$refs.reserve.openView(row.id)
|
||||||
},
|
},
|
||||||
async openReserve(data) {
|
openReserve(data) {
|
||||||
this.drawer = false
|
data.open_hours=site.reverseRankingDate(data.open_hours);
|
||||||
data.open_hours = site.reverseRankingDate(data.open_hours)
|
this.findReserveInfo(data.id).then((rows)=>{
|
||||||
this.reserveViewData = await this.findReserveInfo(data.id)
|
this.reserveViewData=rows;this.reserveViewData.forEach(item=>{item.index=0});
|
||||||
this.reserveViewData.forEach(item => {
|
this.$set(this.formData,'site_id',data.id);this.infoViewData=data;this.$refs.guava.edit()
|
||||||
item.index = 0
|
});
|
||||||
})
|
|
||||||
this.formData.site_id = data.id
|
|
||||||
this.infoViewData = data
|
|
||||||
this.$refs.guava.edit()
|
|
||||||
},
|
},
|
||||||
async doSearch() {
|
doSearch() {
|
||||||
this.pageForm.pageNumber = 1
|
this.pageForm.pageNumber=1;this.pageData();return this.findAllOpenSite().then((rows)=>{this.siteList=rows});
|
||||||
this.pageData()
|
|
||||||
this.siteList = await this.findAllOpenSite()
|
|
||||||
},
|
},
|
||||||
pageOrder(column) {
|
pageOrder(column) {
|
||||||
this.pageForm.pageOrderName = column.prop;
|
this.pageForm.pageOrderName = column.prop;
|
||||||
@@ -907,48 +794,27 @@ layout("/layouts/platform.html"){
|
|||||||
}
|
}
|
||||||
}, "json");
|
}, "json");
|
||||||
},
|
},
|
||||||
async findReserveInfo(siteId) {
|
findReserveInfo(siteId) {
|
||||||
const {data} = await $.get("/platform/activity/site/reserve/findReserveInfo", {siteId})
|
return $.post('/platform/activity/site/reserve/findReserveInfo',{siteId}).then((res)=>{if(res.code!==0){this.$message.error(res.msg);return []}return res.data});
|
||||||
return data;
|
|
||||||
},
|
},
|
||||||
async findReserveInfoByClickDay(siteId, day) {
|
findReserveInfoByClickDay(siteId, day) {
|
||||||
const {data} = await $.get("/platform/activity/site/reserve/findReserveInfo", {
|
return $.post('/platform/activity/site/reserve/findReserveInfo',{siteId,day}).then((res)=>{if(res.code!==0){this.$message.error(res.msg);return []}return res.data});
|
||||||
siteId: siteId,
|
|
||||||
day: day
|
|
||||||
})
|
|
||||||
return data;
|
|
||||||
},
|
},
|
||||||
async findOneSite(id) {
|
findOneSite(id) {
|
||||||
const {data} = await $.get("/platform/activity/site/mange/findOneSite", {id: id})
|
return $.post('/platform/activity/site/mange/findOneSite',{id}).then((res)=>{if(res.code!==0){this.$message.error(res.msg);return []}return res.data});
|
||||||
return data
|
|
||||||
},
|
},
|
||||||
async checkReserve(reserve_day, start_time, end_time, site_id) {
|
checkReserve(reserve_day, start_time, end_time, site_id) {
|
||||||
const {data} = await $.get("/platform/activity/site/reserve/checkReserve", {
|
return $.post('/platform/activity/site/reserve/checkReserve',{reserve_day,start_time,end_time,site_id}).then((res)=>{if(res.code!==0){this.$message.error(res.msg);return 0}return res.data});
|
||||||
reserve_day,
|
|
||||||
start_time,
|
|
||||||
end_time,
|
|
||||||
site_id
|
|
||||||
})
|
|
||||||
return data
|
|
||||||
},
|
},
|
||||||
async findAllOpenSite(siteId) {
|
findAllOpenSite(siteId) {
|
||||||
const {data} = await $.get("/platform/activity/site/reserve/findAllOpenSite", {
|
return $.post('/platform/activity/site/reserve/findAllOpenSite',{time:this.pageForm.time,siteId,siteType:this.pageForm.siteType}).then((res)=>{
|
||||||
time: this.pageForm.time,
|
if(res.code!==0){this.$message.error(res.msg);return []}
|
||||||
siteId: siteId,
|
return res.data.map((row)=>{row.open_hours=site.reverseRankingDate(typeof row.open_hours==='string' ? JSON.parse(row.open_hours) : row.open_hours);return row})
|
||||||
siteType: this.pageForm.siteType,
|
});
|
||||||
})
|
|
||||||
for (let i = 0; i < data.length; i++) {
|
|
||||||
data[i].open_hours = JSON.parse(data[i].open_hours)
|
|
||||||
data[i].open_hours = site.reverseRankingDate(data[i].open_hours)
|
|
||||||
}
|
|
||||||
return data
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
async created() {
|
created() {
|
||||||
this.activityTypeList = await activityUtil.getAllType()
|
activityUtil.getAllType().then((rows)=>{this.activityTypeList=rows});this.findAllOpenSite().then((rows)=>{this.siteList=rows});this.pageData();this.getMarks();
|
||||||
this.siteList = await this.findAllOpenSite()
|
|
||||||
this.pageData();
|
|
||||||
this.getMarks();
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,357 +1,205 @@
|
|||||||
<!--#
|
<!--# layout("/layouts/platform.html"){ #-->
|
||||||
layout("/layouts/platform.html"){
|
|
||||||
#-->
|
|
||||||
<div id="app" v-cloak>
|
<div id="app" v-cloak>
|
||||||
<guava ref="guava">
|
<guava ref="guava">
|
||||||
<template>
|
<template>
|
||||||
<el-card shadow="never">
|
<el-card shadow="never">
|
||||||
<div class="btn-group tool-button">
|
<div class="btn-group tool-button">
|
||||||
<el-date-picker
|
<el-date-picker v-model="pageForm.month" clearable value-format="yyyy-MM"
|
||||||
v-model="pageForm.month" clearable value-format="yyyy-MM"
|
type="month" placeholder="选择月"></el-date-picker>
|
||||||
type="month"
|
|
||||||
placeholder="选择月">
|
|
||||||
</el-date-picker>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="btn-group tool-button">
|
<div class="btn-group tool-button">
|
||||||
<el-input placeholder="请输入内容" clearable v-model="pageForm.searchKeyword"
|
<el-input placeholder="请输入内容" clearable v-model="pageForm.searchKeyword" @keyup.enter.native="doSearch">
|
||||||
@keyup.enter.native="doSearch">
|
<el-select v-model="pageForm.searchName" slot="prepend" placeholder="查询类型" style="width:110px">
|
||||||
<el-select v-model="pageForm.searchName" slot="prepend" placeholder="查询类型"
|
|
||||||
style="width: 110px;">
|
|
||||||
<el-option label="场地名称" value="asi.name"></el-option>
|
<el-option label="场地名称" value="asi.name"></el-option>
|
||||||
<el-option label="预约人" value="asr.reserve_person"></el-option>
|
<el-option label="预约人" value="asr.reserve_person"></el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-input>
|
</el-input>
|
||||||
</div>
|
</div>
|
||||||
<div class="btn-group tool-button">
|
<div class="btn-group tool-button">
|
||||||
<el-select v-model="pageForm.siteType" placeholder="场地类型" clearable
|
<el-select v-model="pageForm.siteType" placeholder="场地类型" clearable style="width:200px">
|
||||||
style="width: 200px;">
|
<el-option v-for="item in activityTypeList" :key="item.value" :label="item.text" :value="item.value"></el-option>
|
||||||
<el-option v-for="item in activityTypeList" :label="item.text" :value="item.value"></el-option>
|
|
||||||
</el-select>
|
</el-select>
|
||||||
</div>
|
</div>
|
||||||
<div class="btn-group tool-button">
|
<div class="btn-group tool-button">
|
||||||
<el-button type="primary" icon="el-icon-search" @click="doSearch"></el-button>
|
<el-button @click="doSearch" icon="el-icon-search" type="primary" aria-label="查询"></el-button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="pull-right offscreen-right">
|
||||||
<div class="pull-right offscreen-right mt5">
|
<el-radio-group @change="doSearch" v-model="pageForm.isAudit">
|
||||||
<el-radio-group v-model="pageForm.isAudit" @change="doSearch">
|
|
||||||
<el-radio-button :label="null">全部</el-radio-button>
|
|
||||||
<el-radio-button :label="true">已审核</el-radio-button>
|
<el-radio-button :label="true">已审核</el-radio-button>
|
||||||
<el-radio-button :label="false">未审核</el-radio-button>
|
<el-radio-button :label="false">未审核</el-radio-button>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
<el-card class="mt20" shadow="never">
|
||||||
<el-card shadow="never" class="mt20">
|
<el-table :data="tableData" v-loading="tableLoading" style="width:100%">
|
||||||
<div style="text-align: right" v-if="pageForm.isAudit === false">
|
<el-table-column align="center" header-align="center" label="序号" type="index" :index="indexMethod" width="60"></el-table-column>
|
||||||
<el-button @click="doReview('many', false)" type="danger" size="mini">一键驳回</el-button>
|
<el-table-column align="center" header-align="center" label="申请人" prop="reserve_person" min-width="90"></el-table-column>
|
||||||
<el-button @click="doReview('many', true)" type="primary" size="mini">一键通过</el-button>
|
<el-table-column align="center" header-align="center" label="工号" prop="loginname" min-width="115"></el-table-column>
|
||||||
</div>
|
<el-table-column align="center" header-align="center" label="场地名称" prop="site_name" min-width="140"></el-table-column>
|
||||||
<el-table :data="tableData" row-key="id" @sort-change="pageOrder"
|
<el-table-column align="center" header-align="center" label="预约类型" min-width="100">
|
||||||
@selection-change="handleSelectionChange"
|
<template slot-scope="{row}">{{typeName(row.reserve_type)}}</template>
|
||||||
v-loading="tableLoading">
|
|
||||||
<el-table-column v-if="pageForm.isAudit === false" type="selection" width="55"
|
|
||||||
:reserve-selection="true"></el-table-column>
|
|
||||||
<el-table-column align="center" header-align="center" label="序号" type="index">
|
|
||||||
<template scope="scope">
|
|
||||||
<span>{{scope.$index+(pageForm.pageNumber - 1) * pageForm.pageSize + 1}} </span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column align="center" sortable show-overflow-tooltip header-align="center" label="场地名称"
|
<el-table-column align="center" header-align="center" label="所属工会" prop="union_name" min-width="140" show-overflow-tooltip></el-table-column>
|
||||||
prop="site_name"></el-table-column>
|
<el-table-column align="center" header-align="center" label="所属单位" prop="reserve_person_unit" min-width="140" show-overflow-tooltip></el-table-column>
|
||||||
<el-table-column align="center" sortable header-align="center" label="预约人"
|
<el-table-column align="center" header-align="center" label="所属协会" min-width="130">
|
||||||
prop="reserve_person"></el-table-column>
|
<template slot-scope="{row}">{{row.club_name || '—'}}</template>
|
||||||
<el-table-column align="center" sortable show-overflow-tooltip header-align="center" label="所属单位"
|
|
||||||
prop="reserve_person_unit"></el-table-column>
|
|
||||||
<el-table-column align="center" header-align="center" label="联系方式"
|
|
||||||
prop="reserve_person_phone"></el-table-column>
|
|
||||||
<el-table-column align="center" sortable show-overflow-tooltip header-align="center"
|
|
||||||
label="预约天数"
|
|
||||||
prop="days"></el-table-column>
|
|
||||||
<el-table-column align="center" sortable show-overflow-tooltip header-align="center"
|
|
||||||
label="预约日期"
|
|
||||||
prop="concat_day"></el-table-column>
|
|
||||||
<el-table-column align="center" sortable show-overflow-tooltip header-align="center"
|
|
||||||
label="开始时间"
|
|
||||||
prop="start_time">
|
|
||||||
<template slot-scope="{row}">
|
|
||||||
<span>{{row.start_time}}</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column align="center" sortable show-overflow-tooltip header-align="center"
|
<el-table-column align="center" header-align="center" label="预约时段" prop="concat_day" min-width="230"></el-table-column>
|
||||||
label="结束时间"
|
<!-- 状态颜色取自后端 state 配置,未配置时使用页面默认字体颜色。 -->
|
||||||
prop="end_time">
|
<el-table-column align="center" header-align="center" label="申请状态" prop="state_name" min-width="120">
|
||||||
<template slot-scope="{row}">
|
<template slot-scope="{row}"><span :style="{color: row.state_color || null}">{{row.state_name}}</span></template>
|
||||||
<span>{{row.end_time}}</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column align="center" show-overflow-tooltip header-align="center" label="预约事由"
|
<el-table-column align="center" header-align="center" label="操作" fixed="right" width="120">
|
||||||
prop="reserve_cause"></el-table-column>
|
|
||||||
<el-table-column align="center" show-overflow-tooltip="true" header-align="center"
|
|
||||||
label="预约状态" prop="state_name">
|
|
||||||
<template slot-scope="{row}">
|
<template slot-scope="{row}">
|
||||||
<!--<span :style="'color:'+row.state_color">{{row.state_name}}</span>-->
|
<el-dropdown @command="dropdownCommand">
|
||||||
<span v-if="[0].includes(row.stateaudittype)"
|
<el-button plain size="mini" aria-label="操作">
|
||||||
style="color: #e6a23c">{{row.state_name}}</span>
|
<i class="ti-settings"></i><span class="ti-angle-down"></span>
|
||||||
<span v-if="row.stateaudittype==3" style="color: #67c23a">{{row.state_name}}</span>
|
|
||||||
<span v-if="row.stateaudittype==1" style="color: #f56c6c">{{row.state_name}}</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="操作" width="250px">
|
|
||||||
<template slot-scope="{row}">
|
|
||||||
<el-button @click="userid = row.reserve_person_id; openView(row.id)" size="mini">查看
|
|
||||||
</el-button>
|
</el-button>
|
||||||
|
<el-dropdown-menu slot="dropdown">
|
||||||
<el-button v-if="pageForm.isAudit === false" type="primary" @click="openReview2(row)"
|
<el-dropdown-item :command="{action:openView,value:row}">查看</el-dropdown-item>
|
||||||
size="mini">审核
|
<el-dropdown-item v-if="canReview(row)" :command="{action:openReview,value:row}">审核</el-dropdown-item>
|
||||||
</el-button>
|
<el-dropdown-item v-if="pageForm.isAudit===true" :disabled="!row.canRevoke || formLoading"
|
||||||
|
:title="row.revokeReason || ''" :command="{action:openRevoke,value:row}">撤回</el-dropdown-item>
|
||||||
<!--# if(@shiro.hasRole('sysadmin')||@shiro.hasRole('xgh02')){ #-->
|
</el-dropdown-menu>
|
||||||
<!--<el-button v-if="pageForm.isAudit === false" type="primary" @click="openReview(row)"
|
</el-dropdown>
|
||||||
size="mini">
|
|
||||||
批示
|
|
||||||
</el-button>-->
|
|
||||||
<!--# } #-->
|
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
|
||||||
</el-table>
|
</el-table>
|
||||||
<el-row class="el-pagination-container">
|
<!--# include("/layouts/pagination.html"){} #-->
|
||||||
<el-pagination
|
|
||||||
@size-change="pageSizeChange"
|
|
||||||
@current-change="pageNumberChange"
|
|
||||||
:current-page="pageForm.pageNumber"
|
|
||||||
:page-sizes="[10, 20, 30, 50]"
|
|
||||||
:page-size="pageForm.pageSize"
|
|
||||||
layout="total, sizes, prev, pager, next, jumper"
|
|
||||||
:total="pageForm.totalCount">
|
|
||||||
</el-pagination>
|
|
||||||
</el-row>
|
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
<template #edit>
|
||||||
<template #edit style="padding: 0 10%">
|
<info ref="auditInfoRef" :review-api="reviewApi" @loaded="onAuditLoaded">
|
||||||
<reserve-info v-if="pageForm.isAudit === false" ref="reserve3" btn>
|
<el-tab-pane :label="reviewTitle" name="review">
|
||||||
<template #btn>
|
<el-form :model="formData" :rules="formRules" label-width="80px" ref="form">
|
||||||
<el-row style="margin: 40px 0;text-align: right">
|
<el-row :gutter="20">
|
||||||
<el-button type="danger" @click="doReview('one', false)">驳回</el-button>
|
|
||||||
<el-button type="primary" @click="doReview('one', true)">通过</el-button>
|
|
||||||
</el-row>
|
|
||||||
</template>
|
|
||||||
</reserve-info>
|
|
||||||
|
|
||||||
<!--<reserve-info v-if="pageForm.isAudit === false" ref="reserve2" label="主席批示" handle>
|
|
||||||
<template #handle>
|
|
||||||
<el-form :model="formData" ref="addForm" :rules="formRules" size="small" label-width="100px">
|
|
||||||
<el-form-item label="批示信息 " label-width="135px" class="view-header">
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item prop="xld_id" label="批示人">
|
<el-form-item label="审核人员"><el-input disabled v-model="formData.username"></el-input></el-form-item>
|
||||||
<el-input value="${@shiro.getPrincipalProperty('username')}"
|
|
||||||
disabled></el-input>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item prop="time" label="批示时间">
|
<el-form-item label="当前时间"><el-input disabled v-model="formData.auditTime"></el-input></el-form-item>
|
||||||
<el-input v-model="formData.time" disabled></el-input>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
<el-form-item label="审核意见" prop="auditOpinion">
|
||||||
<el-form-item prop="opinion" label="批示意见">
|
<el-input maxlength="500" show-word-limit placeholder="请填写您的审核意见" :rows="4"
|
||||||
<el-input type="textarea" v-model="formData.opinion" rows="6" maxlength="500"
|
type="textarea" v-model="formData.auditOpinion"></el-input>
|
||||||
placeholder="请填写您的批示意见"></el-input>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
<el-row type="flex" justify="end">
|
||||||
<el-row style="margin: 40px 0;text-align: right">
|
<el-button type="danger" :loading="formLoading" :disabled="!auditReady" @click="doReview(false)">拒绝</el-button>
|
||||||
<el-button type="primary" @click="doInstructions">确 定</el-button>
|
<el-button type="primary" :loading="formLoading" :disabled="!auditReady" @click="doReview(true)">通过</el-button>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
</el-tab-pane>
|
||||||
|
</info>
|
||||||
</template>
|
</template>
|
||||||
</reserve-info>-->
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #view>
|
<template #view>
|
||||||
<reserve-info ref="reserve"></reserve-info>
|
<info ref="viewInfoRef" :review-api="reviewApi"></info>
|
||||||
</template>
|
</template>
|
||||||
</guava>
|
</guava>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
<!--# include("/platform/activity/includeJs/activityUtil.js"){} #-->
|
<!--# include('./common/info.js'){} #-->
|
||||||
const activityUtil = new typeUtil()
|
<!--# include('/platform/activity/includeJs/activityUtil.js'){} #-->
|
||||||
const vue = new Vue({
|
const vue = new Vue({
|
||||||
el: '#app',
|
el: '#app',
|
||||||
|
mixins: [initTableMixins],
|
||||||
|
components: {info},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
selectTable: [],
|
reviewApi: '${reviewApi}',
|
||||||
|
reviewTitle: '${reviewTitle}',
|
||||||
|
auditReady: false,
|
||||||
activityTypeList: [],
|
activityTypeList: [],
|
||||||
reserveViewData: {},
|
pageForm: {isAudit: false, month: '', searchName: 'asi.name', siteType: ''},
|
||||||
tableLoading: false,
|
|
||||||
tableData: [],
|
|
||||||
formData: {},
|
|
||||||
pageForm: {
|
|
||||||
searchName: "asi.name",
|
|
||||||
searchKeyword: "",
|
|
||||||
pageNumber: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
totalCount: 0,
|
|
||||||
pageOrderName: "",
|
|
||||||
pageOrderBy: "",
|
|
||||||
isAudit: false,
|
|
||||||
},
|
|
||||||
formRules: {
|
formRules: {
|
||||||
opinion: [{required: true, message: '请填写批示意见', trigger: ['blur', 'change']}],
|
auditOpinion: [{required: true, whitespace: true, message: '请填写审核意见', trigger: ['blur', 'change']}]
|
||||||
},
|
}
|
||||||
userid: '',
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
components: {
|
|
||||||
'guava': httpVueLoader('/components/plugins/Guava.vue'),
|
|
||||||
'reserve-info': httpVueLoader('/components/activity/site/ReserveInfo.vue'),
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
openView(id) {
|
// 已审核列表提供撤回;后端复查最新节点,确认前不修改申请状态。
|
||||||
this.$refs.guava.view()
|
openRevoke(row) {
|
||||||
const role = "${@shiro.hasRole('sysadmin')||@shiro.hasRole('xghng')||@shiro.hasRole('A06')}"
|
if (this.formLoading || !row.canRevoke) return
|
||||||
this.$refs.reserve.role = role
|
this.$confirm('确定撤回本次审核吗?撤回后申请恢复到当前节点待审核。', '撤回审核', {
|
||||||
const backOptionShow = "${@shiro.getPrincipalProperty('id')}" === this.userid
|
confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning'
|
||||||
this.$refs.reserve.backOptionShow = (role || backOptionShow)
|
}).then(() => {
|
||||||
this.$refs.reserve.openView(id)
|
if (this.formLoading) return
|
||||||
|
this.$set(this, 'formLoading', true)
|
||||||
|
$.post(this.reviewApi + '/doRevoke', {id:row.id}).then((res) => {
|
||||||
|
if (res && res.code===0) {
|
||||||
|
this.$message.success('已撤回审核')
|
||||||
|
this.pageData()
|
||||||
|
} else { this.$message.error(res && res.msg || '响应异常,请刷新核对申请状态') }
|
||||||
|
}, () => { this.$message.error('请求失败,请刷新核对申请状态') }).always(() => {
|
||||||
|
this.$set(this, 'formLoading', false)
|
||||||
|
})
|
||||||
|
}).catch(() => {})
|
||||||
},
|
},
|
||||||
handleSelectionChange(val) {
|
typeName(type) { return {1:'个人预约',2:'单位预约',3:'协会预约'}[type] || '—' },
|
||||||
this.selectTable = val;
|
// 以申请实际节点决定审核入口,已流转的申请只允许查看。
|
||||||
|
canReview(row) {
|
||||||
|
const stage = this.reviewApi.substring(this.reviewApi.lastIndexOf('/') + 1)
|
||||||
|
return Number(row.reserve_state) === {union:4000,club:4010,school:4020}[stage]
|
||||||
|
},
|
||||||
|
openView(row) {
|
||||||
|
this.$refs.guava.view()
|
||||||
|
this.$refs.viewInfoRef.onOpen(row.id)
|
||||||
},
|
},
|
||||||
openReview(row) {
|
openReview(row) {
|
||||||
this.reserveViewData = row
|
this.$set(this, 'auditReady', false)
|
||||||
this.formData = {
|
this.$set(this, 'formData', {
|
||||||
id: row.id,
|
id: row.id,
|
||||||
state: 30,
|
username: "${@shiro.getPrincipalProperty('username')}",
|
||||||
time: moment().format('YYYY-MM-DD')
|
auditTime: moment().format('YYYY-MM-DD HH:mm:ss'),
|
||||||
}
|
auditOpinion: ''
|
||||||
|
|
||||||
if (this.$refs['addForm']) {
|
|
||||||
this.$refs['addForm'].resetFields()
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.$refs.guava.edit()
|
|
||||||
this.$refs.reserve2.openView(row.id)
|
|
||||||
})
|
})
|
||||||
},
|
|
||||||
openReview2(row) {
|
|
||||||
this.reserveViewData = row
|
|
||||||
this.formData = {
|
|
||||||
id: row.id,
|
|
||||||
}
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.$refs.guava.edit()
|
this.$refs.guava.edit()
|
||||||
const role = "${@shiro.hasRole('sysadmin')||@shiro.hasRole('xghng')||@shiro.hasRole('A06')}"
|
this.$nextTick(() => this.$refs.form.clearValidate())
|
||||||
this.$refs.reserve3.role = role
|
this.$refs.auditInfoRef.onOpen(row.id, 'review')
|
||||||
const backOptionShow = "${@shiro.getPrincipalProperty('id')}" === row.reserve_person_id
|
|
||||||
this.$refs.reserve3.backOptionShow = (role || backOptionShow)
|
|
||||||
this.$refs.reserve3.openView(row.id)
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
doReview(type, state) {
|
// 详情加载成功且仍属于当前节点时才开放提交,避免使用列表中的过期状态。
|
||||||
let that = this
|
onAuditLoaded(detail) {
|
||||||
if (type === 'many' && this.selectTable.length === 0) {
|
this.$set(this, 'auditReady', detail.id === this.formData.id && this.canReview(detail))
|
||||||
this.$notify.warning({title: '警告', message: '一键审核请先在多选框中进行选择!'});
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let typeText = type === 'many' ? '一键' : ''
|
|
||||||
let typeT = type === 'many' ? '这些' : '此'
|
|
||||||
let text = state ? '通过' : '驳回'
|
|
||||||
this.$prompt('您确定要' + typeText + text + typeT + '预约吗?', '温馨提示', {
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
cancelButtonText: '取消',
|
|
||||||
inputPlaceholder: '请在此填写审核意见',
|
|
||||||
inputType: 'textarea',
|
|
||||||
inputValidator: (value) => {
|
|
||||||
if (value === null) {
|
|
||||||
return '审核意见不能为空'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).then(async ({value}) => {
|
|
||||||
let id = ''
|
|
||||||
if (type === 'one') {
|
|
||||||
id = JSON.stringify([that.formData.id])
|
|
||||||
} else {
|
|
||||||
id = JSON.stringify(this.selectTable.map(x => x.id))
|
|
||||||
}
|
|
||||||
$.post(location.href + "/doReview", {
|
|
||||||
id: id,
|
|
||||||
auditOpinion: value,
|
|
||||||
isPass: state
|
|
||||||
}, (res) => {
|
|
||||||
if (res.code === 0) {
|
|
||||||
that.$message.success(res.msg)
|
|
||||||
this.selectTable = []
|
|
||||||
that.pageData();
|
|
||||||
that.$refs.guava.index()
|
|
||||||
}
|
|
||||||
}, "json");
|
|
||||||
}).catch(() => {
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
doInstructions() {
|
// isPass 为通过/拒绝布尔值;仅提交预约 ID 和意见,审核身份与时间由后端保存。
|
||||||
$.post(location.href + "/doReview", this.formData, (res) => {
|
doReview(isPass) {
|
||||||
if (res.code == 0) {
|
if (this.formLoading || !this.auditReady) return
|
||||||
this.$message.success(res.msg)
|
this.$refs.form.validate((valid) => {
|
||||||
this.pageData();
|
if (!valid) return
|
||||||
|
this.$set(this, 'formLoading', true)
|
||||||
|
$.post(this.reviewApi + '/doReview', {
|
||||||
|
ids: JSON.stringify([this.formData.id]),
|
||||||
|
isPass,
|
||||||
|
auditOpinion: this.formData.auditOpinion.trim()
|
||||||
|
}).then((res) => {
|
||||||
|
// jQuery 1.11 的回调异常会阻断 always,保护结果处理并校验空响应。
|
||||||
|
try {
|
||||||
|
if (res && res.code === 0) {
|
||||||
|
this.$message.success('审核完成')
|
||||||
|
this.$set(this, 'auditReady', false)
|
||||||
this.$refs.guava.index()
|
this.$refs.guava.index()
|
||||||
}
|
|
||||||
}, "json");
|
|
||||||
},
|
|
||||||
doSearch() {
|
|
||||||
this.pageForm.pageNumber = 1
|
|
||||||
this.pageData()
|
this.pageData()
|
||||||
},
|
|
||||||
pageOrder(column) {
|
|
||||||
this.pageForm.pageOrderName = column.prop;
|
|
||||||
this.pageForm.pageOrderBy = column.order;
|
|
||||||
this.pageData();
|
|
||||||
},
|
|
||||||
pageNumberChange(val) {
|
|
||||||
this.pageForm.pageNumber = val;
|
|
||||||
this.pageData();
|
|
||||||
},
|
|
||||||
pageSizeChange(val) {
|
|
||||||
this.pageForm.pageSize = val;
|
|
||||||
this.pageData();
|
|
||||||
},
|
|
||||||
pageData() {
|
|
||||||
sublime.showLoadingbar();
|
|
||||||
this.tableLoading = true
|
|
||||||
let form = clone(this.pageForm)
|
|
||||||
if (form.isAudit === null) {
|
|
||||||
delete form.isAudit
|
|
||||||
}
|
|
||||||
$.post(location.href + "/pageData", form, (res) => {
|
|
||||||
const {code, data, msg} = res
|
|
||||||
sublime.closeLoadingbar();
|
|
||||||
this.tableLoading = false
|
|
||||||
if (code == 0) {
|
|
||||||
this.tableData = data.list;
|
|
||||||
this.pageForm.totalCount = data.totalCount;
|
|
||||||
} else {
|
} else {
|
||||||
this.$message.error(msg);
|
this.$message.error(res && res.msg || '接口返回异常,请刷新列表核对审核状态')
|
||||||
}
|
}
|
||||||
}, "json");
|
} catch (error) {
|
||||||
|
this.$message.error('结果处理失败,请刷新列表核对审核状态')
|
||||||
|
}
|
||||||
|
}, () => {
|
||||||
|
this.$message.error('请求失败,请刷新列表核对审核状态')
|
||||||
|
}).always(() => {
|
||||||
|
this.$set(this, 'formLoading', false)
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async created() {
|
created() {
|
||||||
this.activityTypeList = await activityUtil.getAllType()
|
this.pageData()
|
||||||
this.pageData();
|
// 历史审核可按停用的场地类型查询,复用记录页面的类型列表工具。
|
||||||
},
|
new typeUtil().getAllType().then((types) => {
|
||||||
|
this.$set(this, 'activityTypeList', types)
|
||||||
|
}).fail(() => { this.$message.error('场地类型加载失败,请刷新重试') })
|
||||||
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<!--#
|
<!--# } #-->
|
||||||
}
|
|
||||||
#-->
|
|
||||||
|
|||||||
@@ -1,68 +1,26 @@
|
|||||||
<!--#
|
<!--# layout("/layouts/platform.html"){ #-->
|
||||||
layout("/layouts/platform.html"){
|
|
||||||
#-->
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.audit-userids-select .el-select__tags {
|
|
||||||
flex-wrap: unset;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div id="app" v-cloak>
|
<div id="app" v-cloak>
|
||||||
<guava ref="guava">
|
|
||||||
<el-card shadow="never">
|
<el-card shadow="never">
|
||||||
<table-tool :app="this" label="类型列表">
|
<table-tool :app="this" label="类型列表"><template #func><el-button type="primary" size="small" @click="openAdd">新增类型</el-button></template></table-tool>
|
||||||
<template #func>
|
<el-table :data="tableData" v-loading="tableLoading" :size="tableSize">
|
||||||
<el-button @click="openAdd" size="small" type="primary">新增类型</el-button>
|
<el-table-column type="index" :index="indexMethod" label="序号" width="80"></el-table-column>
|
||||||
</template>
|
<el-table-column prop="code" label="类型编码" align="center"></el-table-column>
|
||||||
</table-tool>
|
<el-table-column prop="meetingTypeName" label="类型名称" align="center"></el-table-column>
|
||||||
<el-table :data="tableData" :size="tableSize"
|
<el-table-column label="是否启用" align="center"><template slot-scope="{row}"><el-switch :value="row.enabled" :disabled="formLoading" @change="changeEnabled(row,$event)"></el-switch></template></el-table-column>
|
||||||
@sort-change="pageOrder" class="vi-table"
|
<el-table-column label="操作" align="center"><template slot-scope="{row}"><el-button size="mini" type="primary" @click="openEdit(row)">编辑</el-button><el-button size="mini" type="danger" @click="remove(row)">删除</el-button></template></el-table-column>
|
||||||
row-key="id" style="width: 100%">
|
|
||||||
|
|
||||||
<el-table-column align="center" header-align="center" label="序号" type="index"
|
|
||||||
width="80px"></el-table-column>
|
|
||||||
|
|
||||||
<el-table-column
|
|
||||||
:label="column.label"
|
|
||||||
:prop="column.prop"
|
|
||||||
:sortable="column.sortable"
|
|
||||||
:width="column.width"
|
|
||||||
align="center"
|
|
||||||
header-align="center"
|
|
||||||
min-width="50"
|
|
||||||
show-overflow-tooltip
|
|
||||||
v-for="column in tableColumns"
|
|
||||||
>
|
|
||||||
</el-table-column>
|
|
||||||
|
|
||||||
<el-table-column label="操作">
|
|
||||||
<template scope="{row}">
|
|
||||||
<el-button @click="openEdit(row)" size="mini" type="primary">编辑</el-button>
|
|
||||||
<el-button @click="doDelete(row.id)" size="mini" type="danger">删除</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
|
|
||||||
</el-table>
|
</el-table>
|
||||||
|
<!--# include("/layouts/pagination.html"){} #-->
|
||||||
</el-card>
|
</el-card>
|
||||||
|
<el-dialog :title="formData.id ? '编辑类型' : '新增类型'" :visible.sync="visible" width="520px" :close-on-click-modal="false">
|
||||||
<template #edit>
|
<el-form ref="form" :model="formData" :rules="formRules" label-width="100px">
|
||||||
<vi-title title="基础信息"></vi-title>
|
<el-form-item label="类型编码" prop="code"><el-input v-model="formData.code" maxlength="50"></el-input></el-form-item>
|
||||||
<el-form :model="formData" :rules="formRules" label-width="80px" ref="form">
|
<el-form-item label="类型名称" prop="meetingTypeName"><el-input v-model="formData.meetingTypeName" maxlength="50"></el-input></el-form-item>
|
||||||
<el-form-item label="名称" prop="meetingTypeName">
|
<el-form-item label="是否启用"><el-switch v-model="formData.enabled"></el-switch></el-form-item>
|
||||||
<el-input v-model="formData.meetingTypeName"></el-input>
|
</el-form>
|
||||||
</el-form-item>
|
<template slot="footer"><el-button @click="visible=false" :disabled="formLoading">取消</el-button><el-button type="primary" :loading="formLoading" @click="save">确定</el-button></template>
|
||||||
|
</el-dialog>
|
||||||
<el-form-item label="模块名称" prop="moduleName">
|
</div>
|
||||||
<el-input v-model="formData.moduleName"></el-input>
|
<!-- 原类型审核节点配置保留供追溯,新预约不再从类型读取节点。
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item label="序号" prop="sortNum">
|
|
||||||
<el-input-number :max="tableData.length + 1" :min="1" :precision="0"
|
|
||||||
v-model="formData.sortNum"></el-input-number>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<vi-title title="审核节点信息(温馨提醒:如审核节点没有限制,审核节点类型不选或选择不限制)"></vi-title>
|
<vi-title title="审核节点信息(温馨提醒:如审核节点没有限制,审核节点类型不选或选择不限制)"></vi-title>
|
||||||
<el-table :data="formData.auditStateList">
|
<el-table :data="formData.auditStateList">
|
||||||
<el-table-column label="节点ID" prop="stateId" width="100px">
|
<el-table-column label="节点ID" prop="stateId" width="100px">
|
||||||
@@ -151,263 +109,82 @@ layout("/layouts/platform.html"){
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
</el-form>
|
-->
|
||||||
<el-row class="mt10" justify="end" type="flex">
|
|
||||||
<el-button @click="$refs.guava.index()">取消</el-button>
|
|
||||||
<el-button @click="doConfirm" type="primary">确定</el-button>
|
|
||||||
</el-row>
|
|
||||||
</template>
|
|
||||||
</guava>
|
|
||||||
|
|
||||||
<condition :fields="fields"
|
|
||||||
:show_filter_result="true"
|
|
||||||
@confirm="cndConfirm"
|
|
||||||
ref="cnd"
|
|
||||||
v-model="cnd"></condition>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
<!--# include("/platform/activity/includeJs/activityUtil.js"){} #-->
|
const vue = new Vue({
|
||||||
const activityUtil = new typeUtil()
|
el: '#app', mixins: [initTableMixins],
|
||||||
const vue = new Vue({
|
data() { return {visible: false,formRules: {code: [{required:true,message:'请输入类型编码',trigger:'blur'}],meetingTypeName:[{required:true,message:'请输入类型名称',trigger:'blur'}]}} },
|
||||||
el: '#app',
|
|
||||||
mixins: [initTableMixins],
|
|
||||||
components: {
|
|
||||||
'condition': httpVueLoader('/components/plugins/ConditionStructure.vue?v=1.0.0'),
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
tableColumns: [
|
|
||||||
{prop: 'meetingTypeName', label: '类型名称'},
|
|
||||||
{prop: 'moduleName', label: '模块名称'},
|
|
||||||
{prop: 'sortNum', label: '排序编号'}],
|
|
||||||
stateAuditTypeList: [],
|
|
||||||
formData: {
|
|
||||||
auditStateList: []
|
|
||||||
},
|
|
||||||
formRules: {
|
|
||||||
meetingTypeName: [{required: true, message: '请输入类型名称', trigger: ['blur', 'change']}],
|
|
||||||
moduleName: [{required: true, message: '请输入模块名称,英文命名', trigger: ['blur', 'change']}],
|
|
||||||
sortNum: [{required: true, message: '请输入排序编号', trigger: ['blur', 'change']}]
|
|
||||||
},
|
|
||||||
userList: [],
|
|
||||||
maxStateId: 0,
|
|
||||||
userSelectDialogVisible: false,
|
|
||||||
cnd: {},
|
|
||||||
fields: [],
|
|
||||||
nodeIndex: null,
|
|
||||||
activityAuditTypeEnum:[],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
async 'formData.auditStateList'(val) {
|
|
||||||
const {id} = this.formData
|
|
||||||
for (let i = 0; i < val.length; i++) {
|
|
||||||
let item = val[i]
|
|
||||||
if (id) {
|
|
||||||
if (item.isAddRow) {
|
|
||||||
//当前行是新增的第几行
|
|
||||||
const addIndex = i + 1 - val.filter(v => !v.isAddRow).length
|
|
||||||
|
|
||||||
const stateId = val[0].stateId + i * 10
|
|
||||||
const licitStateCode = await this.checkStateId(stateId, addIndex)
|
|
||||||
item['stateId'] = licitStateCode
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
item['stateId'] = this.maxStateId + (i + 1) * 10
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
// this.$set(this.formData,'auditStateList',val)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
dialogTitle() {
|
|
||||||
if (this.formData.id) {
|
|
||||||
return '编辑'
|
|
||||||
}
|
|
||||||
return '新增'
|
|
||||||
},
|
|
||||||
stateIdList() {
|
|
||||||
return this.formData.auditStateList.map(v => v.stateId)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
async checkStateId(stateId, stateIndex) {
|
openAdd() { this.formData={code:'',meetingTypeName:'',enabled:true}; this.visible=true; this.$nextTick(()=>this.$refs.form.clearValidate()) },
|
||||||
const resp = await $.get(loc() + '/checkStateId', {stateId, stateIndex})
|
openEdit(row) { this.formData=Object.assign({},row); this.visible=true; this.$nextTick(()=>this.$refs.form.clearValidate()) },
|
||||||
if (resp.code === 0) {
|
// 提交类型 JSON;响应 code=0 表示成功,msg 为失败原因,空响应不得当作成功。
|
||||||
return resp.data
|
save() {
|
||||||
} else {
|
if (this.formLoading) return
|
||||||
this.notifyError(resp.msg)
|
this.$refs.form.validate((valid) => {
|
||||||
}
|
if (!valid) return
|
||||||
},
|
this.$set(this, 'formLoading', true)
|
||||||
|
$.post(loc() + '/doHandle', {data: JSON.stringify(this.formData)}).then((res) => {
|
||||||
addState() {
|
// jQuery 1.11 的 then 不会捕获回调异常,需保护回调以保证 always 执行。
|
||||||
this.formData.auditStateList.push({
|
|
||||||
stateId: this.maxStateId,
|
|
||||||
stateName: null,
|
|
||||||
selectUserId: [],
|
|
||||||
auditStateUserList: [],
|
|
||||||
afterPassStateId: null,
|
|
||||||
afterRejectStateId: null,
|
|
||||||
isAddRow: true
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
async findAll() {
|
|
||||||
const resp_data = await $.get(loc() + '/findAll')
|
|
||||||
if (resp_data.code === 0) {
|
|
||||||
resp_data.data.forEach(v => {
|
|
||||||
v.auditStateList.forEach(x => {
|
|
||||||
if (x.matchCnd) {
|
|
||||||
x.matchCnd = JSON.parse(x.matchCnd)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
this.tableData = resp_data.data
|
|
||||||
}
|
|
||||||
},
|
|
||||||
openAdd() {
|
|
||||||
this.formData = {
|
|
||||||
meetingName: null,
|
|
||||||
sortNum: this.tableData.length + 1,
|
|
||||||
auditStateList: [{}]
|
|
||||||
}
|
|
||||||
this.$refs.guava.edit()
|
|
||||||
},
|
|
||||||
async doConfirm() {
|
|
||||||
try {
|
try {
|
||||||
const valid = await this.$refs['form'].validate()
|
if (res && res.code === 0) {
|
||||||
if (valid) {
|
this.$set(this, 'visible', false)
|
||||||
this.formData.auditStateList.forEach(v => {
|
this.pageData()
|
||||||
v['auditStateUserList'] = v.selectUserId.map(x => {
|
|
||||||
return {userId: x}
|
|
||||||
})
|
|
||||||
v['matchCnd'] = JSON.stringify(v['matchCnd'])
|
|
||||||
})
|
|
||||||
const resp = await $.post(loc() + '/doHandle', {data: JSON.stringify(this.formData)})
|
|
||||||
if (resp.code === 0) {
|
|
||||||
this.$refs.guava.index()
|
|
||||||
this.$message.success(resp.msg)
|
|
||||||
this.findAll()
|
|
||||||
} else {
|
} else {
|
||||||
this.notifyError(resp.msg)
|
this.$message.error(res && res.msg || '接口返回异常,请刷新列表核对类型状态')
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.$message.error('结果处理失败,请刷新列表核对类型状态')
|
||||||
}
|
}
|
||||||
} catch (e) {
|
}, () => {
|
||||||
}
|
this.$message.error('请求失败,请刷新列表核对类型状态')
|
||||||
},
|
}).always(() => {
|
||||||
async openEdit(row) {
|
this.$set(this, 'formLoading', false)
|
||||||
const d = clone(row)
|
})
|
||||||
let u = []
|
|
||||||
const {data} = await $.post(loc() + '/findOne', {module: row.moduleName})
|
|
||||||
data.forEach(v => {
|
|
||||||
v['selectUserId'] = v.auditStateUserList.map(x => x.userId)
|
|
||||||
u = u.concat(v['selectUserId'])
|
|
||||||
})
|
})
|
||||||
d.auditStateList = data
|
|
||||||
this.userList = await queryUserByIds(u)
|
|
||||||
|
|
||||||
this.formData = d
|
|
||||||
this.$refs.guava.edit()
|
|
||||||
},
|
},
|
||||||
async doDelete(id) {
|
// id 指定类型,enabled 为目标启用状态;仅在返回 code=0 后更新开关。
|
||||||
|
changeEnabled(row, value) {
|
||||||
|
if (this.formLoading) return
|
||||||
|
this.$set(this, 'formLoading', true)
|
||||||
|
$.post(loc() + '/setEnabled', {id: row.id, enabled: value}).then((res) => {
|
||||||
try {
|
try {
|
||||||
const confirm = await this.$confirm('您确定要删除吗', '提示', {
|
if (res && res.code === 0) {
|
||||||
confirmButtonText: '确定',
|
this.$set(row, 'enabled', value)
|
||||||
cancelButtonText: '取消',
|
|
||||||
type: 'warning'
|
|
||||||
})
|
|
||||||
if (confirm === 'confirm') {
|
|
||||||
const resp = await $.post(loc() + '/delete/' + id)
|
|
||||||
if (resp.code === 0) {
|
|
||||||
this.$message.success(resp.msg)
|
|
||||||
this.findAll()
|
|
||||||
} else {
|
} else {
|
||||||
this.$message.warning(resp.msg)
|
this.$message.error(res && res.msg || '接口返回异常,请刷新列表核对启用状态')
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.$message.error('结果处理失败,请刷新列表核对启用状态')
|
||||||
}
|
}
|
||||||
} catch (e) {
|
}, () => {
|
||||||
console.log(e)
|
this.$message.error('请求失败,请刷新列表核对启用状态')
|
||||||
}
|
}).always(() => {
|
||||||
|
this.$set(this, 'formLoading', false)
|
||||||
|
})
|
||||||
},
|
},
|
||||||
async userRemoteMethod(query) {
|
// 删除指定类型,服务端校验引用关系;取消确认不发请求,失败后恢复按钮状态。
|
||||||
if (query) {
|
remove(row) {
|
||||||
this.userList = await searchUser(query)
|
if (this.formLoading) return
|
||||||
}
|
this.$confirm('确定删除该类型?已使用的类型不能删除。', '提示').then(() => {
|
||||||
},
|
this.$set(this, 'formLoading', true)
|
||||||
openUserSelect(node_index) {
|
return $.post(loc() + '/delete/' + row.id).then((res) => {
|
||||||
this.nodeIndex = node_index
|
try {
|
||||||
const node = this.formData.auditStateList[node_index]
|
if (res && res.code === 0) {
|
||||||
if (node.matchCnd) {
|
this.pageData()
|
||||||
this.cnd = node.matchCnd
|
|
||||||
} else {
|
} else {
|
||||||
this.cnd = {method: "AND", conditions: [{}]}
|
this.$message.error(res && res.msg || '接口返回异常,请刷新列表核对删除结果')
|
||||||
}
|
}
|
||||||
this.$refs.cnd.open(false)
|
} catch (error) {
|
||||||
},
|
this.$message.error('结果处理失败,请刷新列表核对删除结果')
|
||||||
async initCndFields() {
|
|
||||||
this.cnd = {
|
|
||||||
method: "AND",
|
|
||||||
conditions: [{}],
|
|
||||||
}
|
}
|
||||||
/*const meet_data = await getOpenMeeting()
|
}, () => {
|
||||||
const meet_options = meet_data.map(v => {
|
this.$message.error('请求失败,请刷新列表核对删除结果')
|
||||||
return {"label": v.jdhallname, "value": v.id}
|
}).always(() => {
|
||||||
|
this.$set(this, 'formLoading', false)
|
||||||
})
|
})
|
||||||
this.fields.push(
|
}).catch(() => {})
|
||||||
{label: '教代会', value: 'jdhid', type: "select", options: meet_options}
|
|
||||||
)*/
|
|
||||||
const {data: role_data} = await $.get('/platform/sys/role/getAllRoles')
|
|
||||||
const role_options = role_data.map(v => {
|
|
||||||
return {"label": v.name, "value": v.id}
|
|
||||||
})
|
|
||||||
this.fields.push(
|
|
||||||
{label: '角色', value: 'roleId', type: "select", options: role_options}
|
|
||||||
)
|
|
||||||
this.fields.push(
|
|
||||||
{label: '姓名', value: 'username', options: null}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
async cndConfirm(val) {
|
|
||||||
if (val) {
|
|
||||||
const matchCnd = clone(val)
|
|
||||||
if (matchCnd.conditions && matchCnd.conditions.length > 0) {
|
|
||||||
this.formData.auditStateList[this.nodeIndex].matchCnd = matchCnd
|
|
||||||
const resp = await $.post('/platform/userFilter/findUserByCnd', {cnd: JSON.stringify(matchCnd)})
|
|
||||||
if (resp.code === 0) {
|
|
||||||
if (resp.data) {
|
|
||||||
const d = this.formData.auditStateList[this.nodeIndex]
|
|
||||||
d.matchCnd = matchCnd
|
|
||||||
d.selectUserId = [...new Set(d.selectUserId.concat(resp.data.map(v => v.id)))]
|
|
||||||
|
|
||||||
this.$set(this.formData.auditStateList, this.nodeIndex, d)
|
|
||||||
let u = []
|
|
||||||
this.formData.auditStateList.forEach(v => {
|
|
||||||
u = u.concat(v.selectUserId.map(x => x))
|
|
||||||
})
|
|
||||||
this.userList = await queryUserByIds([...new Set(u)])
|
|
||||||
}
|
}
|
||||||
}
|
}, created() { this.pageData() }
|
||||||
}
|
})
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async created() {
|
|
||||||
this.findAll()
|
|
||||||
this.initCndFields()
|
|
||||||
this.maxStateId = await activityUtil.findMaxStateId()
|
|
||||||
this.stateAuditTypeList = await getEnumOptions('AuditTypeEnum')
|
|
||||||
this.activityAuditTypeEnum = await getEnumOptions('ActivityAuditTypeEnum')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
<!--# } #-->
|
||||||
|
|
||||||
<!--#
|
|
||||||
}
|
|
||||||
#-->
|
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
const info = {
|
||||||
|
template: /*language=HTML*/ `
|
||||||
|
<div v-loading="detailLoading">
|
||||||
|
<el-tabs tab-position="top" v-model="activeName">
|
||||||
|
<el-tab-pane label="申请信息" name="info">
|
||||||
|
<el-descriptions :column="3" border>
|
||||||
|
<el-descriptions-item label="申请人">{{viewData.reserve_person}}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="工号">{{viewData.loginname}}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="联系方式">{{viewData.reserve_person_phone}}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="所属单位">{{viewData.reserve_person_unit}}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="预约类型">{{typeName(viewData.reserve_type)}}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="所属协会">{{viewData.club_name || '—'}}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="场地名称" :span="2">{{viewData.site_name}}</el-descriptions-item>
|
||||||
|
<!-- 详情与审核列表使用同一状态配置颜色。 -->
|
||||||
|
<el-descriptions-item label="申请状态"><span :style="{color: viewData.state_color || null}">{{viewData.state_name}}</span></el-descriptions-item>
|
||||||
|
<el-descriptions-item label="预约时段" :span="3">{{viewData.concat_day}}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="预约事由" :span="3">
|
||||||
|
<div class="text-left" style="white-space:pre-wrap">{{viewData.reserve_cause}}</div>
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane v-for="(item,index) in viewData.auditListTable" :key="item.auditId || index"
|
||||||
|
:label="item.auditStateName + '信息'" :name="'history-' + index">
|
||||||
|
<el-descriptions :column="2" border>
|
||||||
|
<el-descriptions-item label="审核人">{{item.auditUserName}}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="审核时间">{{item.auditTime}}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="审核结果" :span="2">{{item.auditListName}}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="审核意见" :span="2">
|
||||||
|
<div class="text-left" style="white-space:pre-wrap">{{item.auditOption}}</div>
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</el-tab-pane>
|
||||||
|
<slot></slot>
|
||||||
|
</el-tabs>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
props: {reviewApi: {type: String, required: true}},
|
||||||
|
data() {
|
||||||
|
return {viewData: {}, activeName: 'info', detailLoading: false, requestId: 0}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
typeName(type) { return {1:'个人预约',2:'单位预约',3:'协会预约'}[type] || '—' },
|
||||||
|
// id 为预约记录主键,activeName 为申请信息或审核标签;结果含预约字段及 auditListTable 审核历史。
|
||||||
|
onOpen(id, activeName = 'info') {
|
||||||
|
const requestId = this.requestId + 1
|
||||||
|
this.$set(this, 'requestId', requestId)
|
||||||
|
this.$set(this, 'viewData', {})
|
||||||
|
this.$set(this, 'activeName', activeName)
|
||||||
|
this.$set(this, 'detailLoading', true)
|
||||||
|
return $.post(this.reviewApi + '/detail', {id}).then((res) => {
|
||||||
|
// 快速切换申请时忽略旧请求,防止旧详情覆盖当前审核对象。
|
||||||
|
if (requestId !== this.requestId) return
|
||||||
|
if (res.code === 0) {
|
||||||
|
this.$set(this, 'viewData', res.data)
|
||||||
|
this.$emit('loaded', res.data)
|
||||||
|
} else {
|
||||||
|
this.$message.error(res.msg)
|
||||||
|
}
|
||||||
|
}).always(() => {
|
||||||
|
if (requestId === this.requestId) this.$set(this, 'detailLoading', false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user