场地预约整改:分工会/协会审核-校工会审核
This commit is contained in:
+34
-148
@@ -1,164 +1,50 @@
|
||||
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.services.ActivityCommonService;
|
||||
import io.v.nutz.zhgh.activity.services.ActivityTypeService;
|
||||
import io.v.nutz.base.annontation.ViReturn;
|
||||
import io.v.nutz.base.model.AuditState;
|
||||
import io.v.nutz.base.model.AuditStateUser;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.apache.shiro.authz.annotation.*;
|
||||
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.dao.util.cri.SqlExpressionGroup;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.ioc.loader.annotation.*;
|
||||
import org.nutz.ioc.aop.Aop;
|
||||
import org.nutz.aop.interceptor.ioc.TransAop;
|
||||
import org.nutz.json.Json;
|
||||
import org.nutz.mvc.annotation.At;
|
||||
import org.nutz.mvc.annotation.Ok;
|
||||
import org.nutz.mvc.annotation.Param;
|
||||
import org.nutz.mvc.annotation.*;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author JyuHsin
|
||||
* @Date 2022/5/11
|
||||
* @Description
|
||||
*/
|
||||
@IocBean
|
||||
@At("/platform/activity/site/type")
|
||||
@Ok("json:full")
|
||||
public class ActivityTypeController {
|
||||
|
||||
@Inject
|
||||
private Dao dao;
|
||||
|
||||
@Inject
|
||||
private ActivityTypeService activityTypeService;
|
||||
|
||||
@Inject
|
||||
private ActivityCommonService activityCommonService;
|
||||
|
||||
@At("")
|
||||
@Ok("beetl:/platform/activity/site/SiteType.html")
|
||||
@RequiresPermissions("activity.site.type")
|
||||
public void index(HttpServletRequest request) {
|
||||
|
||||
@Inject private ActivityTypeService activityTypeService;
|
||||
@At("") @Ok("beetl:/platform/activity/site/SiteType.html") @RequiresPermissions("activity.site.type")
|
||||
public void index() { }
|
||||
/** enabledOnly=true 仅返回可预约类型;默认返回全部供历史筛选,结果为类型数组。 */
|
||||
@At @ViReturn @RequiresAuthentication
|
||||
public Object findAll(Boolean enabledOnly) {
|
||||
Cnd c=Cnd.NEW(); c.asc("sortNum");
|
||||
if(Boolean.TRUE.equals(enabledOnly)) c.and("enabled","=",true);
|
||||
return activityTypeService.query(c);
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
@At("/delete/?")
|
||||
@Ok("json:full")
|
||||
@ViReturn
|
||||
@RequiresPermissions("activity.site.type")
|
||||
public Object delete(Integer id) {
|
||||
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()));
|
||||
/** id 为类型主键,enabled 为是否启用;返回 Object,由 @ViReturn 包装 code(0 成功)和 msg(结果说明)。 */
|
||||
@At @ViReturn @RequiresPermissions("activity.site.type") @Aop(TransAop.READ_COMMITTED)
|
||||
public Object setEnabled(Integer id,Boolean enabled) {
|
||||
ActivityType t=activityTypeService.fetch(id);
|
||||
if(t==null) throw new IllegalArgumentException("类型不存在");
|
||||
t.setEnabled(enabled); activityTypeService.edit(t);
|
||||
return null;
|
||||
}
|
||||
|
||||
@At("/findUserList")
|
||||
@Ok("json:full")
|
||||
@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);
|
||||
}
|
||||
}
|
||||
/** id 为待删除类型主键,服务层检查场地引用;返回 Object,由 @ViReturn 包装 code(0 成功)和 msg(结果说明)。 */
|
||||
@At("/delete/?") @ViReturn @RequiresPermissions("activity.site.type") @Aop(TransAop.READ_COMMITTED)
|
||||
public Object delete(Integer id) { activityTypeService.delete(activityTypeService.fetch(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/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
|
||||
activity_site_info asi ON asi.id = asr.site_id
|
||||
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
|
||||
sys_user u on u.id = asr.reserve_person_id
|
||||
$condition
|
||||
|
||||
@@ -32,6 +32,9 @@ import org.nutz.lang.Strings;
|
||||
import org.nutz.lang.random.R;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
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.Param;
|
||||
|
||||
@@ -50,6 +53,8 @@ import java.util.stream.Collectors;
|
||||
@At("/platform/activity/site/reserve")
|
||||
public class SiteReserveController {
|
||||
|
||||
@Inject private SiteBookingService siteBookingService;
|
||||
|
||||
@Inject
|
||||
private SiteInfoService siteInfoService;
|
||||
|
||||
@@ -101,7 +106,7 @@ public class SiteReserveController {
|
||||
LEFT JOIN
|
||||
activity_site_info asi ON asi.id = asr.site_id
|
||||
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
|
||||
""");
|
||||
|
||||
@@ -115,6 +120,7 @@ public class SiteReserveController {
|
||||
|
||||
if (Strings.isNotBlank(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)) {
|
||||
@@ -140,124 +146,30 @@ public class SiteReserveController {
|
||||
@At
|
||||
@ViReturn
|
||||
@RequiresPermissions("activity.site.reserve")
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public Object doDelete(String id, Integer op) {
|
||||
ActivitySiteReserve fetch = siteReserveService.fetch(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()));
|
||||
}
|
||||
siteBookingService.cancel(id);
|
||||
return null;
|
||||
}
|
||||
|
||||
@At
|
||||
@RequiresPermissions("activity.site.reserve")
|
||||
public Object isCanRollBack(String id) {
|
||||
//根据活动id查询第一个审核节点
|
||||
ActivitySiteReserve fetch = siteReserveService.fetch(id);
|
||||
String moduleName = activityCommonService.findModuleNameByActivityId(fetch.getSite_id());
|
||||
Integer stateCode = activityCommonService.findStartStateCodeByModuleName(moduleName);
|
||||
|
||||
return fetch.getReserve_state() > stateCode;
|
||||
ActivitySiteReserve b=siteReserveService.fetch(id);
|
||||
if(b==null) return true;
|
||||
return b.getReserve_state()!=siteBookingService.firstState(b.getReserve_type()) || b.getAuditList()!=null && !b.getAuditList().isEmpty();
|
||||
}
|
||||
|
||||
@At
|
||||
@ViReturn
|
||||
@RequiresPermissions("activity.site.reserve")
|
||||
/** data 为场地、类型、协会及事由;days 为日期数组,起止时间取 data;返回新申请 sqid。 */
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public Object doAdd(@Param("data") ActivitySiteReserve activitySiteReserve, @Param("days") String[] days) {
|
||||
|
||||
if(activitySiteReserve.getReserve_type() == 1 && days.length > 1) {
|
||||
return Result.error("个人预约只支持预约明天的时间");
|
||||
}
|
||||
|
||||
//主要来查询个人预约时的人数上限
|
||||
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;
|
||||
List<NutMap> slots = new ArrayList<>();
|
||||
if (activitySiteReserve == null || days == null) throw new IllegalArgumentException("请选择预约日期");
|
||||
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);
|
||||
}
|
||||
|
||||
@At
|
||||
@@ -267,7 +179,7 @@ public class SiteReserveController {
|
||||
@Param(value = "siteType",required = false) String siteType) {
|
||||
String sb = "SELECT " +
|
||||
"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)) {
|
||||
sb += " AND left(reserve_day, 7) = '" + time + "'";
|
||||
}
|
||||
@@ -276,6 +188,7 @@ public class SiteReserveController {
|
||||
Sql sql = Sqls.create(sb);
|
||||
Cnd cnd = Cnd.NEW();
|
||||
cnd.and("state", "=", true);
|
||||
cnd.and("typeId", "in", Sqls.create("select id from activity_type where enabled=1"));
|
||||
if (StrUtil.isNotBlank(siteType)) {
|
||||
cnd.and("typeId", "=", siteType);
|
||||
}
|
||||
@@ -302,28 +215,14 @@ public class SiteReserveController {
|
||||
@RequiresPermissions("activity.site.reserve")
|
||||
public Object findReserveInfo(@Param(value = "siteId",required = false) String siteId,
|
||||
@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");
|
||||
|
||||
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());
|
||||
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");
|
||||
|
||||
Cnd cnd = Cnd.NEW();
|
||||
cnd.and("site_id", "=", siteId);
|
||||
cnd.and("reserve_state", "not in", java.util.Arrays.asList(4040,4050));
|
||||
if (StrUtil.isNotBlank(day)) {
|
||||
cnd.and("reserve_day", "=", day);
|
||||
cnd.and("reserve_state", "not in ", stateList);
|
||||
|
||||
}
|
||||
sql.setCondition(cnd);
|
||||
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) {
|
||||
Cnd cnd = Cnd.NEW();
|
||||
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);
|
||||
SqlExpressionGroup group = new SqlExpressionGroup();
|
||||
group.or("start_time", "<=", start_time).and("end_time", ">=", start_time);
|
||||
@@ -348,84 +247,10 @@ public class SiteReserveController {
|
||||
@ViReturn
|
||||
@RequiresPermissions("activity.site.reserve")
|
||||
public Object findOne(String id) {
|
||||
ActivitySiteReserve fetch = siteReserveService.fetch(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;
|
||||
return siteBookingService.detail(id);
|
||||
}
|
||||
|
||||
/** 返回当前用户有效协会数组(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;
|
||||
|
||||
|
||||
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.lang.Lang;
|
||||
import org.nutz.lang.Strings;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
import org.nutz.mvc.annotation.At;
|
||||
import org.nutz.mvc.annotation.Ok;
|
||||
import org.nutz.mvc.annotation.Param;
|
||||
|
||||
import 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")
|
||||
import org.nutz.mvc.annotation.*;
|
||||
import org.apache.shiro.authz.annotation.RequiresAuthentication;
|
||||
/** 旧审核入口仅展示三个新入口,不再提供旧审核提交接口。 */
|
||||
@IocBean @At("/platform/activity/site/review") @RequiresAuthentication
|
||||
public class SiteReviewController {
|
||||
|
||||
@Inject
|
||||
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;
|
||||
}
|
||||
@At("") @Ok("beetl:/platform/activity/site/ReviewEntries.html")
|
||||
public void index() { }
|
||||
}
|
||||
|
||||
@@ -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("活动场地预约信息")
|
||||
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
|
||||
@Name
|
||||
@Comment("ID")
|
||||
@@ -127,7 +137,7 @@ public class ActivitySiteReserve extends BaseModel {
|
||||
private String joinUser;
|
||||
|
||||
@Column
|
||||
@Comment("预约类型(1.个人预约,2.单位预约)")
|
||||
@Comment("预约类型(1.个人预约,2.单位预约,3.协会预约)")
|
||||
@ColDefine(type = ColType.INT, width = 10)
|
||||
private Integer reserve_type;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,16 @@ import java.util.List;
|
||||
@Comment("活动场地类型")
|
||||
public class ActivityType {
|
||||
|
||||
@Column
|
||||
@Comment("类型编码,字符串保留前导零")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 50)
|
||||
private String code;
|
||||
|
||||
@Column
|
||||
@Comment("是否启用;停用后不可新增预约")
|
||||
@Default("1")
|
||||
private Boolean enabled;
|
||||
|
||||
@Id
|
||||
@Comment("ID")
|
||||
@ColDefine(type = ColType.INT)
|
||||
|
||||
@@ -1,103 +1,47 @@
|
||||
package io.v.nutz.zhgh.activity.services.impl;
|
||||
|
||||
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.base.model.AuditState;
|
||||
import io.v.nutz.base.model.AuditStateUser;
|
||||
import io.v.nutz.base.service.impl.ViServiceImpl;
|
||||
import org.nutz.aop.interceptor.ioc.TransAop;
|
||||
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.dao.*;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.lang.Lang;
|
||||
|
||||
import java.util.List;
|
||||
import org.nutz.ioc.aop.Aop;
|
||||
import org.nutz.aop.interceptor.ioc.TransAop;
|
||||
import org.nutz.lang.Strings;
|
||||
|
||||
/** 类型仅管理分类资料;保留旧模块字段,不再增删审核节点。 */
|
||||
@IocBean(args = {"refer:dao"})
|
||||
public class ActivityTypeServiceImpl extends ViServiceImpl<ActivityType> implements ActivityTypeService {
|
||||
|
||||
@Inject
|
||||
private ActivityCommonService activityCommonService;
|
||||
|
||||
public ActivityTypeServiceImpl(Dao dao) {
|
||||
super(dao);
|
||||
public ActivityTypeServiceImpl(Dao dao) { super(dao); }
|
||||
/** 旧接口兼容,不用于新预约流程。 */
|
||||
public int findMaxStateId() { return dao().func("audit_state", "max", "stateId"); }
|
||||
/** 校验编码/名称/启用状态,编码须唯一且保留前导零。 */
|
||||
private void validate(ActivityType t) {
|
||||
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)
|
||||
public void add(ActivityType activityType) {
|
||||
dao().insert(activityType);
|
||||
insertAuditInfo(activityType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(ActivityType t) { validate(t); t.setSortNum(count()+1); t.setModuleName(t.getCode()); dao().insert(t); }
|
||||
/** 更新分类资料,保留历史 moduleName 和关联节点。 */
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public void edit(ActivityType activityType) {
|
||||
dao().update(activityType);
|
||||
|
||||
//查询该类型原来关联的stateId
|
||||
String[] stateIdStr = findStateIdArray(activityType.getModuleName());
|
||||
|
||||
//删除原来审核状态下的审核人员
|
||||
dao().clear(AuditStateUser.class, Cnd.where("stateId", "in", stateIdStr));
|
||||
//删除原来的审核状态
|
||||
dao().clear(AuditState.class, Cnd.where("stateId", "in", stateIdStr));
|
||||
|
||||
//添加新的审核流程
|
||||
insertAuditInfo(activityType);
|
||||
public void edit(ActivityType t) {
|
||||
validate(t);
|
||||
ActivityType old=fetch(t.getId());
|
||||
if(old==null) throw new IllegalArgumentException("类型不存在");
|
||||
old.setCode(t.getCode()).setMeetingTypeName(t.getMeetingTypeName()).setEnabled(t.getEnabled()); dao().update(old);
|
||||
}
|
||||
|
||||
@Override
|
||||
/** 被场地使用的类型不可删除,防止预约历史失去分类。 */
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public void delete(ActivityType activityType) {
|
||||
//查询该类型原来关联的stateId
|
||||
String[] stateIdStr = findStateIdArray(activityType.getModuleName());
|
||||
|
||||
//删除原来审核状态下的审核人员
|
||||
dao().clear(AuditStateUser.class, Cnd.where("stateId", "in", stateIdStr));
|
||||
//删除原来的审核状态
|
||||
dao().clear(AuditState.class, Cnd.where("stateId", "in", stateIdStr));
|
||||
|
||||
dao().delete(activityType);
|
||||
public void delete(ActivityType t) {
|
||||
if(t==null) throw new IllegalArgumentException("类型不存在");
|
||||
if(dao().count(ActivitySiteInfo.class,Cnd.where("typeId","=",t.getId()))>0) throw new IllegalArgumentException("该类型已被场地使用,请停用而非删除");
|
||||
dao().delete(t);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
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.json.Json;
|
||||
import org.nutz.lang.Lang;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
import org.nutz.mvc.annotation.At;
|
||||
import org.nutz.mvc.annotation.Ok;
|
||||
import org.nutz.mvc.annotation.Param;
|
||||
|
||||
import 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")
|
||||
import org.nutz.mvc.annotation.*;
|
||||
import org.apache.shiro.authz.annotation.RequiresAuthentication;
|
||||
/** 旧审核入口仅展示三个新入口,不再提供旧审核提交接口。 */
|
||||
@IocBean @At("/mobile/activity/site/audit") @RequiresAuthentication
|
||||
public class SiteAuditMobileController {
|
||||
|
||||
@Inject
|
||||
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);
|
||||
}
|
||||
@At("") @Ok("beetl:/platform/activity/site/ReviewEntries.html")
|
||||
public void index() { }
|
||||
}
|
||||
|
||||
@@ -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.util.NutMap;
|
||||
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.Param;
|
||||
|
||||
@@ -45,9 +48,12 @@ import java.util.List;
|
||||
*/
|
||||
@IocBean
|
||||
@Ok("json:full")
|
||||
@org.apache.shiro.authz.annotation.RequiresPermissions("activity.site.reserve")
|
||||
@At("/mobile/activity/site/info")
|
||||
public class SiteInfoMobileController {
|
||||
|
||||
@Inject private SiteBookingService siteBookingService;
|
||||
|
||||
@Inject
|
||||
private SiteInfoService siteInfoService;
|
||||
|
||||
@@ -88,20 +94,20 @@ public class SiteInfoMobileController {
|
||||
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
info.*,
|
||||
type.meetingTypeName,
|
||||
type.moduleName,
|
||||
(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
|
||||
info.*,
|
||||
type.meetingTypeName,
|
||||
type.moduleName,
|
||||
(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 = 4030) count
|
||||
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
|
||||
activity_site_info info
|
||||
LEFT JOIN activity_type type ON type.id = info.typeId
|
||||
$condition
|
||||
""");
|
||||
Cnd cnd = Cnd.NEW();
|
||||
|
||||
cnd.and("info.state", "=", true);
|
||||
cnd.and("type.enabled","=",true);
|
||||
if (StringUtils.isNotBlank(pageForm.getSearchKeyword())) {
|
||||
SqlExpressionGroup seg = new SqlExpressionGroup();
|
||||
seg.orLike("info.name", pageForm.getSearchKeyword());
|
||||
@@ -147,7 +153,7 @@ public class SiteInfoMobileController {
|
||||
from
|
||||
activity_site_reserve ar
|
||||
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
|
||||
""");
|
||||
|
||||
@@ -161,7 +167,7 @@ public class SiteInfoMobileController {
|
||||
if (StrUtil.isNotBlank(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("reserve_person_id", "=", ShiroUtil.getPrincipalProperty("id"));
|
||||
@@ -183,26 +189,18 @@ public class SiteInfoMobileController {
|
||||
@At("/rollback")
|
||||
@Ok("json:full")
|
||||
@ViReturn
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public Object rollback(String id) {
|
||||
//根据活动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()));
|
||||
siteBookingService.cancel(id);
|
||||
return null;
|
||||
}
|
||||
|
||||
@At("/backOption")
|
||||
@Ok("json:full")
|
||||
@ViReturn
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public Object backOption(String id, String option) {
|
||||
ActivitySiteReserve fetch = siteReserveService.fetch(id);
|
||||
siteReserveService.update(Chain.make("back_option", option), Cnd.NEW().and("sqid", "=", fetch.getSqid()));
|
||||
siteBookingService.feedback(id,option);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -213,7 +211,6 @@ public class SiteInfoMobileController {
|
||||
|
||||
//获取场地的开放时间
|
||||
ActivitySiteInfo fetch = siteInfoService.fetch(siteId);
|
||||
String moduleName = activityCommonService.findModuleNameByActivityId(siteId);
|
||||
List<NutMap> hours = fetch.getOpen_hours();
|
||||
Integer limitNum = fetch.getLimitNum();
|
||||
|
||||
@@ -240,10 +237,10 @@ public class SiteInfoMobileController {
|
||||
.and("start_time", "=", startTime)
|
||||
.and("end_time", "=", endTime)
|
||||
.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) {
|
||||
item.put("code", -2);
|
||||
item.put("msg", "已约满");
|
||||
@@ -274,111 +271,11 @@ public class SiteInfoMobileController {
|
||||
@At("/reserveDo")
|
||||
@Ok("json:full")
|
||||
@ViReturn
|
||||
public Object reserveDo(String siteId, String times, String message, Integer reserve_type) {
|
||||
String moduleName = activityCommonService.findModuleNameByActivityId(siteId);
|
||||
Integer stateCode = activityCommonService.findStartStateCodeByModuleName(moduleName);
|
||||
|
||||
if (stateCode == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
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;
|
||||
/** siteId 为场地;times 为 day/start_time/end_time 数组 JSON;message 为事由;reserve_type 为1/2/3,clubId 为协会预约所属协会;返回 sqid。 */
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public Object reserveDo(String siteId, String times, String message, Integer reserve_type, String clubId) {
|
||||
ActivitySiteReserve form=new ActivitySiteReserve();
|
||||
form.setSite_id(siteId);form.setReserve_cause(message);form.setReserve_type(reserve_type);form.setClubId(clubId);
|
||||
return siteBookingService.submit(form,Json.fromJsonAsList(NutMap.class,times));
|
||||
}
|
||||
}
|
||||
|
||||
+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"
|
||||
sublime.showLoadingbar();
|
||||
this.tableLoading = true
|
||||
$.post(address, data ? data : this.pageForm, (data) => {
|
||||
sublime.closeLoadingbar();
|
||||
this.tableLoading = false
|
||||
if (data.code === 0) {
|
||||
this.tableData = data.data.list;
|
||||
this.pageForm.totalCount = data.data.totalCount;
|
||||
// 公共查询统一在 always 收尾;分页及查询入口继续由各页面复用。
|
||||
return $.post(address, data ? data : this.pageForm).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.tableData = res.data.list;
|
||||
this.pageForm.totalCount = res.data.totalCount;
|
||||
} else {
|
||||
this.$message.error(data.msg);
|
||||
this.$message.error(res.msg);
|
||||
}
|
||||
}, "json");
|
||||
}).always(() => {
|
||||
sublime.closeLoadingbar();
|
||||
this.tableLoading = false;
|
||||
});
|
||||
},
|
||||
notifySuccess(msg) {
|
||||
this.$notify({
|
||||
|
||||
@@ -33,6 +33,8 @@
|
||||
|
||||
<el-tab-pane label="预约基本信息" name="2">
|
||||
<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 :span="2" label="场地名称"> {{ viewData.site_name }}</el-descriptions-item>
|
||||
<el-descriptions-item label="性别">{{ viewData.sex }}</el-descriptions-item>
|
||||
@@ -123,22 +125,18 @@ module.exports = {
|
||||
}
|
||||
return this.panes.includes(name)
|
||||
},
|
||||
async getSiteReserveInfo(id) {
|
||||
const {data} = await $.get(base + "/platform/activity/site/reserve/findOne", {id})
|
||||
return data
|
||||
},
|
||||
async openView(id) {
|
||||
this.loading = true
|
||||
this.$forceUpdate()
|
||||
this.viewData = await this.getSiteReserveInfo(id)
|
||||
if (this.viewData) {
|
||||
this.activeName = this.role === true ? "1" : "2"
|
||||
} else {
|
||||
this.viewData = {}
|
||||
this.$message.error("获取场地预约信息失败");
|
||||
}
|
||||
this.$forceUpdate()
|
||||
this.loading = false
|
||||
// id 为预约记录主键;返回包含申请、时段和审核历史的 data。
|
||||
openView(id) {
|
||||
this.loading = true;
|
||||
$.post(base + '/platform/activity/site/reserve/findOne', {id}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.viewData = res.data;
|
||||
this.activeName = this.role === true ? '1' : '2';
|
||||
} else {
|
||||
this.viewData = {};
|
||||
this.$message.error(res.msg);
|
||||
}
|
||||
}).always(() => { this.loading = false });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,551 +1,258 @@
|
||||
<!--#
|
||||
layout("/mobile/platform.html"){
|
||||
#-->
|
||||
|
||||
<!--# layout("/mobile/platform.html"){ #-->
|
||||
<style>
|
||||
|
||||
.van-hairline--top-bottom::after, .van-hairline-unset--top-bottom::after {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.van-index-bar__sidebar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.van-doc-card {
|
||||
margin: 14px;
|
||||
padding: 12px 12px 12px 12px;
|
||||
background-color: #fff;
|
||||
border-radius: 10px;
|
||||
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;
|
||||
}
|
||||
.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; }
|
||||
.site-audit .card-title { font-size:18px; font-weight:bold; color:#1867b0; padding-left:4px; }
|
||||
.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; }
|
||||
.site-audit .van-divider { margin:6px 0; }
|
||||
.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; }
|
||||
.site-audit .audit-actions { display:flex; gap:10px; padding:10px; }
|
||||
.site-audit .site-audit-section { color:#1867b0; font-weight:bold; font-size:16px; }
|
||||
.site-audit .site-audit-loading { padding:24px; text-align:center; }
|
||||
.site-audit .site-audit-text .van-cell__label { white-space:pre-wrap; overflow-wrap:anywhere; }
|
||||
.site-audit .detail-popup { background:#f7f8fa; }
|
||||
</style>
|
||||
|
||||
<div id="app" v-cloak>
|
||||
|
||||
<m-page-loading v-if="mLoading"></m-page-loading>
|
||||
|
||||
<!--top栏-->
|
||||
<van-nav-bar
|
||||
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>
|
||||
<div id="app" class="site-audit" v-cloak>
|
||||
<van-sticky>
|
||||
<van-nav-bar title="${reviewTitle}" left-text="返回" left-arrow @click-left="goBack"></van-nav-bar>
|
||||
<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">
|
||||
<van-dropdown-item ref="auditFilter" v-model="isAudit" :options="auditOptions" @change="search"
|
||||
@open="filterOpened" @close="filterClosed"></van-dropdown-item>
|
||||
</van-dropdown-menu>
|
||||
</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>
|
||||
|
||||
<div style="margin-top: 10px">
|
||||
<van-row>
|
||||
<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>
|
||||
<van-button @click.stop="getAuditList(o)" class="cus_button"
|
||||
type="primary" size="small" color="#1867b0"
|
||||
style="margin-right: 8px; width: auto;">已审{{o.audit}}
|
||||
</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>
|
||||
</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>
|
||||
<van-divider></van-divider>
|
||||
<div class="card-footer">
|
||||
<!-- 仅状态内容绑定配置颜色,保留字段标签原有灰色。 -->
|
||||
<div><span class="field-label">当前状态:</span><span :style="{color: row.state_color || null}">{{row.state_name}}</span></div>
|
||||
<div>
|
||||
<van-button size="small" type="info" @click="openDetail(row,false)">查看</van-button>
|
||||
<van-button v-if="canReview(row)" size="small" type="primary" @click="openDetail(row,true)">审核</van-button>
|
||||
<van-button v-if="isAudit===true" size="small" type="danger" :disabled="!row.canRevoke || formLoading"
|
||||
:loading="formLoading && revokeId===row.id" @click="openRevoke(row)">撤回</van-button>
|
||||
</div>
|
||||
</div>
|
||||
</van-list>
|
||||
<van-empty
|
||||
v-if="mLoading==false&&tableData.length==0"
|
||||
class="custom-image"
|
||||
image="/none.svg"
|
||||
description="暂无数据"
|
||||
></van-empty>
|
||||
</div>
|
||||
|
||||
<!--弹出框-->
|
||||
<van-popup v-model:show="popupShow" class="cus_popup">
|
||||
<pre style="margin: 0; white-space: break-spaces">{{desc}}</pre>
|
||||
</div>
|
||||
</van-list>
|
||||
<van-empty v-if="!loading && !listError && !rows.length" image="/none.svg" description="暂无数据"></van-empty>
|
||||
<van-popup v-model="detailShow" position="right" :style="{height:'100%',width:'100%'}" class="detail-popup" safe-area-inset-bottom>
|
||||
<van-sticky>
|
||||
<van-nav-bar title="申请详情" left-text="返回" left-arrow @click-left="goBack"></van-nav-bar>
|
||||
</van-sticky>
|
||||
<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>
|
||||
</van-popup>
|
||||
|
||||
<van-icon @click="popupShow = false" class="cus_icon" v-if="popupShow" name="close"></van-icon>
|
||||
|
||||
<!--审核人员-->
|
||||
<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 v-model="confirmShow" title="提示" show-cancel-button :before-close="beforeConfirmClose"
|
||||
:message="pendingPass ? '确定通过该申请吗?' : '确定拒绝该申请吗?拒绝后流程结束。'">
|
||||
</van-dialog>
|
||||
<van-dialog v-model="revokeShow" title="撤回审核" show-cancel-button :before-close="beforeRevokeClose"
|
||||
message="确定撤回本次审核吗?撤回后申请恢复到当前节点待审核。"></van-dialog>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
<!--# include("/platform/activity/includeJs/activityUtil.js"){} #-->
|
||||
const siteTypeUtil = new typeUtil()
|
||||
const vue = new Vue({
|
||||
(() => {
|
||||
// PJAX 先执行内联脚本;捕获本次页面,依赖加载完成后才允许创建 Vue。
|
||||
const pageRoot = document.getElementById('app')
|
||||
const startPage = () => {
|
||||
if (document.getElementById('app') !== pageRoot || !pageRoot.isConnected) return
|
||||
<!--# include('./common/info.js'){} #-->
|
||||
new Vue({
|
||||
el: '#app',
|
||||
mixins: [mobileMixins],
|
||||
mixins: [window.popupHistoryMixin],
|
||||
components: {'site-audit-info': SITE_AUDIT_INFO},
|
||||
data() {
|
||||
return {
|
||||
desc: '',
|
||||
list: ['a', 'b'],
|
||||
result: [],
|
||||
reason: '',
|
||||
auditShow: false,
|
||||
popupShow: false,
|
||||
userShow: false,
|
||||
userList: [],
|
||||
userClickList: [],
|
||||
str: '',
|
||||
type: '',
|
||||
auditType: '',
|
||||
queryType: '',
|
||||
typeList: [],
|
||||
meetingTimeList: [{text: '全部时间', value: null}, {text: '即将开始', value: 0}, {text: '已结束', value: 1}],
|
||||
historyPopupKeys: ['filterShow','detailShow','confirmShow','revokeShow'],
|
||||
revokeShow:false, revokeId:'',
|
||||
reviewApi: '${reviewApi}',
|
||||
auditOptions: [{text:'已审核',value:true},{text:'未审核',value:false}],
|
||||
isAudit: false, keyword: '', rows: [], page: 1,
|
||||
loading: false, finished: false, listError: false, requestVersion: 0, requesting: false,
|
||||
filterShow: false, detailShow: false, confirmShow: false,
|
||||
showApprovalForm: false, auditReady: false, pendingPass: false, formLoading: false,
|
||||
formData: {id:'',username:'',auditTime:'',auditOpinion:''}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
// DropdownItem 没有弹层 v-model,通过全局历史状态同步关闭内部下拉层。
|
||||
filterShow(value) { if (!value && this.$refs.auditFilter) this.$refs.auditFilter.toggle(false) }
|
||||
},
|
||||
methods: {
|
||||
getTime(o, day) {
|
||||
const week = new Date(day).getDay()
|
||||
const arr = ['日', '一', '二', '三', '四', '五', '六']
|
||||
return moment(day).format('MM月DD日') + ' 周' + arr[week] + ' ' + o.start_time + '-' + o.end_time
|
||||
openRevoke(row) {
|
||||
if (this.formLoading || !row.canRevoke) return
|
||||
this.$set(this, 'revokeId', row.id)
|
||||
this.$set(this, 'revokeShow', true)
|
||||
},
|
||||
async getAuditList(o) {
|
||||
this.queryType = 'hasAudit'
|
||||
const resp = await $.post('/mobile/activity/site/audit/getLeaveUser', {
|
||||
siteId: o.id,
|
||||
auditType: 'hasAudit',
|
||||
// 确认框也进入历史栈;失败保留弹框,请求结束复位 Vant 内部 loading。
|
||||
beforeRevokeClose(action, done) {
|
||||
if (this.formLoading) { done(false); return }
|
||||
if (action!=='confirm') { done(false); this.goBack(); return }
|
||||
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('暂无数据')
|
||||
},
|
||||
// 菜单采用 PJAX replace 进入,列表返回应回手机首页,不能依赖上一条浏览器历史。
|
||||
// 弹框存在时通过统一栈关闭最上层,重复点击也不会连续退过业务页面。
|
||||
goBack() {
|
||||
if (this.formLoading) return
|
||||
const key = this.revokeShow ? 'revokeShow' : this.confirmShow ? 'confirmShow' : this.detailShow ? 'detailShow' : this.filterShow ? 'filterShow' : null
|
||||
if (key) {
|
||||
window.popupHistory.close(this._popupOwner, key)
|
||||
} else {
|
||||
this.clearPopupHistory(() => pjaxReplace('/mobile/index'))
|
||||
}
|
||||
},
|
||||
typeName(type) { return {1:'个人预约',2:'单位预约',3:'协会预约'}[type] || '—' },
|
||||
filterOpened() { this.$set(this, 'filterShow', true) },
|
||||
filterClosed() { this.$set(this, 'filterShow', false) },
|
||||
canReview(row) {
|
||||
const stage = this.reviewApi.substring(this.reviewApi.lastIndexOf('/') + 1)
|
||||
return Number(row.reserve_state) === {union:4000,club:4010,school:4020}[stage]
|
||||
},
|
||||
search() {
|
||||
// 筛选变更使旧响应失效,避免未审核和已审核数据混入同一列表。
|
||||
this.$set(this, 'requestVersion', this.requestVersion + 1)
|
||||
this.$set(this, 'requesting', false)
|
||||
this.$set(this, 'rows', [])
|
||||
this.$set(this, 'page', 1)
|
||||
this.$set(this, 'finished', false)
|
||||
this.$set(this, 'listError', false)
|
||||
this.loadMore()
|
||||
},
|
||||
// isAudit 为审核状态,pageNumber/pageSize 为分页;响应 data 含 list 和 totalCount。
|
||||
loadMore() {
|
||||
if (this.requesting || this.finished) return
|
||||
const version = this.requestVersion
|
||||
this.$set(this, 'requesting', true)
|
||||
this.$set(this, 'loading', true)
|
||||
$.post(this.reviewApi + '/pageData', {
|
||||
isAudit: this.isAudit, searchKeyword: this.keyword, pageNumber: this.page, pageSize: 10
|
||||
}).then((res) => {
|
||||
if (version !== this.requestVersion) return
|
||||
if (res.code === 0) {
|
||||
this.$set(this, 'rows', this.rows.concat(res.data.list))
|
||||
this.$set(this, 'page', this.page + 1)
|
||||
this.$set(this, 'finished', this.rows.length >= res.data.totalCount)
|
||||
} else {
|
||||
this.$set(this, 'listError', true)
|
||||
this.$toast.fail(res.msg)
|
||||
}
|
||||
}).fail(() => {
|
||||
if (version === this.requestVersion) this.$set(this, 'listError', true)
|
||||
}).always(() => {
|
||||
if (version === this.requestVersion) {
|
||||
this.$set(this, 'loading', false)
|
||||
this.$set(this, 'requesting', false)
|
||||
}
|
||||
})
|
||||
},
|
||||
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
|
||||
}
|
||||
this.userList.sort((a, b) => {
|
||||
return b.auditState - a.auditState
|
||||
})
|
||||
this.userShow = true
|
||||
}
|
||||
},
|
||||
cancelAll() {
|
||||
this.$refs.checkboxes.forEach(item => item.toggle(false))
|
||||
},
|
||||
allIn() {
|
||||
this.$refs.checkboxes.forEach(item => item.toggle(true))
|
||||
},
|
||||
checkAll(i) {
|
||||
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 {
|
||||
vant.Toast('操作失败')
|
||||
}
|
||||
this.userShow = false
|
||||
},
|
||||
audit(type, auditType) {
|
||||
this.type = type
|
||||
this.auditType = auditType
|
||||
if (type === 'many' && this.result.length === 0) {
|
||||
vant.Toast('请先选择人员')
|
||||
return
|
||||
}
|
||||
this.str = type === 'many' ? '您选择了' + this.result.length + '个人,请确认您的选择' : '您确定要一键全部审核吗?'
|
||||
this.reason = auditType === 'pass' ? '同意' : '拒绝'
|
||||
this.auditShow = true
|
||||
},
|
||||
async openView(id, modulename) {
|
||||
this.queryType = 'notAudit'
|
||||
this.userList = []
|
||||
const resp = await $.post('/mobile/activity/site/audit/getLeaveUser', {
|
||||
siteId: id,
|
||||
moduleName: modulename,
|
||||
auditType: 'canAudit',
|
||||
})
|
||||
if (resp.code === 0) {
|
||||
this.userList = resp.data
|
||||
this.userClickList = []
|
||||
if (this.userList.length === 0) {
|
||||
vant.Toast('暂无预约数据')
|
||||
} else {
|
||||
this.userShow = true
|
||||
}
|
||||
return
|
||||
}
|
||||
vant.Toast('系统错误,请联系管理员')
|
||||
},
|
||||
popClose() {
|
||||
this.$nextTick(function () {
|
||||
if (this.$refs.checkboxes !== undefined) {
|
||||
this.$refs.checkboxes.forEach(item => item.toggle(false));
|
||||
}
|
||||
})
|
||||
},
|
||||
doSearch() {
|
||||
this.pageForm.pageNumber = 1
|
||||
this.tableData = []
|
||||
this.finished = false
|
||||
this.onLoad()
|
||||
},
|
||||
async onLoad() {
|
||||
this.loading = true
|
||||
$.post('/mobile/activity/site/audit/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
|
||||
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)
|
||||
})
|
||||
},
|
||||
},
|
||||
async created() {
|
||||
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()
|
||||
},
|
||||
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>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
<!--# } #-->
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
.pop {
|
||||
max-height: 70%;
|
||||
width: 90%;
|
||||
padding: 0px 10px 10px 10px;
|
||||
}
|
||||
|
||||
.pop h2 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.pop h4 {
|
||||
line-height: 26px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" v-cloak>
|
||||
@@ -179,7 +168,7 @@ layout("/mobile/platform.html"){
|
||||
|
||||
<!--top栏-->
|
||||
<van-nav-bar
|
||||
@click-left="pjaxReplace('/mobile/index')"
|
||||
@click-left="goBack"
|
||||
fixed
|
||||
left-arrow
|
||||
placeholder
|
||||
@@ -198,7 +187,8 @@ layout("/mobile/platform.html"){
|
||||
>
|
||||
</van-search>
|
||||
<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>
|
||||
</van-dropdown-menu>
|
||||
</div>
|
||||
@@ -261,45 +251,13 @@ layout("/mobile/platform.html"){
|
||||
></van-empty>
|
||||
</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>
|
||||
<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>场地预约
|
||||
</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>我的预约
|
||||
</van-tabbar-item>
|
||||
</van-tabbar>
|
||||
@@ -308,37 +266,70 @@ layout("/mobile/platform.html"){
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(() => {
|
||||
// PJAX 先执行内联脚本,等待历史组件加载;离开后的旧加载回调不能挂载到新页面。
|
||||
const pageRoot = document.getElementById('app')
|
||||
const startPage = () => {
|
||||
if (document.getElementById('app') !== pageRoot || !pageRoot.isConnected) return
|
||||
<!--# include("/platform/activity/includeJs/activityUtil.js"){} #-->
|
||||
const siteTypeUtil = new typeUtil()
|
||||
const vue = new Vue({
|
||||
new Vue({
|
||||
el: '#app',
|
||||
mixins: [mobileMixins],
|
||||
mixins: [mobileMixins, window.popupHistoryMixin],
|
||||
data() {
|
||||
return {
|
||||
historyPopupKeys: ['typeFilterShow'],
|
||||
typeFilterShow:false, formLoading:false,
|
||||
tarBarActive: 0,
|
||||
list: ['a', 'b'],
|
||||
typeList: [],
|
||||
show: false,
|
||||
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: {
|
||||
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) {
|
||||
this.siteId = o.id
|
||||
if (o.sexlimit === 1 || o.sexlimit === 2) {
|
||||
const sex = o.sexlimit === 1 ? '男' : '女'
|
||||
if ("${@shiro.getPrincipalProperty('sex')}" === sex) {
|
||||
location.href = '/mobile/activity/site/info/reserve?id=' + o.id
|
||||
this.toReserve()
|
||||
} else {
|
||||
vant.Toast('抱歉,该场地仅限' + sex + '性会员预约')
|
||||
}
|
||||
} else {
|
||||
location.href = '/mobile/activity/site/info/reserve?id=' + o.id
|
||||
this.toReserve()
|
||||
}
|
||||
},
|
||||
toReserve() {
|
||||
location.href = '/mobile/activity/site/info/reserve?id=' + this.siteId
|
||||
this.show = false
|
||||
this.clearPopupHistory(()=>{location.href = '/mobile/activity/site/info/reserve?id=' + this.siteId})
|
||||
},
|
||||
doSearch() {
|
||||
this.pageForm.pageNumber = 1
|
||||
@@ -346,30 +337,26 @@ layout("/mobile/platform.html"){
|
||||
this.finished = false
|
||||
this.onLoad()
|
||||
},
|
||||
async onLoad() {
|
||||
this.loading = true
|
||||
$.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
|
||||
})
|
||||
onLoad() {
|
||||
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});
|
||||
},
|
||||
},
|
||||
async created() {
|
||||
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.onLoad()
|
||||
created() {
|
||||
siteTypeUtil.getAllType().then((rows)=>{this.typeList=[{text:'全部场地类型'}].concat(rows);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>
|
||||
|
||||
<!--#
|
||||
|
||||
@@ -169,7 +169,7 @@ layout("/mobile/platform.html"){
|
||||
|
||||
<!--top栏-->
|
||||
<van-nav-bar
|
||||
@click-left="pjaxReplace('/mobile/index')"
|
||||
@click-left="goBack"
|
||||
fixed
|
||||
left-arrow
|
||||
placeholder
|
||||
@@ -188,7 +188,8 @@ layout("/mobile/platform.html"){
|
||||
>
|
||||
</van-search>
|
||||
<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="查询全部">
|
||||
<template #right-icon>
|
||||
<van-switch active-color="#246fb4" size="24"
|
||||
@@ -208,7 +209,8 @@ layout("/mobile/platform.html"){
|
||||
</van-button>
|
||||
</div>
|
||||
</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>
|
||||
</van-dropdown-menu>
|
||||
</div>
|
||||
@@ -295,7 +297,8 @@ layout("/mobile/platform.html"){
|
||||
</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
|
||||
autosize
|
||||
label="反馈意见:"
|
||||
@@ -310,10 +313,10 @@ layout("/mobile/platform.html"){
|
||||
|
||||
<div>
|
||||
<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>场地预约
|
||||
</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>我的预约
|
||||
</van-tabbar-item>
|
||||
</van-tabbar>
|
||||
@@ -322,13 +325,21 @@ layout("/mobile/platform.html"){
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(() => {
|
||||
// PJAX 先执行内联脚本,等待历史组件加载;离开后的旧加载回调不能挂载到新页面。
|
||||
const pageRoot = document.getElementById('app')
|
||||
const startPage = () => {
|
||||
if (document.getElementById('app') !== pageRoot || !pageRoot.isConnected) return
|
||||
<!--# include("/platform/activity/includeJs/activityUtil.js"){} #-->
|
||||
const siteTypeUtil = new typeUtil()
|
||||
const vue = new Vue({
|
||||
new Vue({
|
||||
el: '#app',
|
||||
mixins: [mobileMixins],
|
||||
mixins: [mobileMixins, window.popupHistoryMixin],
|
||||
data() {
|
||||
return {
|
||||
historyPopupKeys: ['show','cancelShow','typeFilterShow','monthFilterShow'],
|
||||
typeFilterShow:false, monthFilterShow:false,
|
||||
cancelShow:false,cancelId:'',formLoading:false,
|
||||
tarBarActive: 1,
|
||||
time: new Date(),
|
||||
option: '',
|
||||
@@ -339,7 +350,50 @@ layout("/mobile/platform.html"){
|
||||
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: {
|
||||
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() {
|
||||
if (this.pageForm.timeSwitch === true) {
|
||||
this.timeList = [{text: '全部', value: '全部'}]
|
||||
@@ -350,59 +404,28 @@ layout("/mobile/platform.html"){
|
||||
}]
|
||||
}
|
||||
this.$set(this.pageForm, "time", this.timeList[0].value)
|
||||
this.$refs.item.toggle()
|
||||
window.popupHistory.close(this._popupOwner, 'monthFilterShow')
|
||||
this.doSearch()
|
||||
},
|
||||
timeFormatter(type, val) {
|
||||
if (type === 'year') {
|
||||
return val + `年`;
|
||||
return val + '年';
|
||||
}
|
||||
if (type === 'month') {
|
||||
return val + `月`;
|
||||
return val + '月';
|
||||
}
|
||||
return val;
|
||||
},
|
||||
async rollback(o) {
|
||||
const self = this
|
||||
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(() => {
|
||||
});
|
||||
rollback(o) {
|
||||
this.cancelId=o.id;this.cancelShow=true;
|
||||
},
|
||||
backOption(o) {
|
||||
this.info = o
|
||||
this.option = ''
|
||||
this.show = true
|
||||
},
|
||||
async backDo() {
|
||||
if (this.option === '') {
|
||||
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('操作失败')
|
||||
}
|
||||
backDo() {
|
||||
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});
|
||||
},
|
||||
getTime(o, day) {
|
||||
const week = new Date(day).getDay()
|
||||
@@ -415,34 +438,26 @@ layout("/mobile/platform.html"){
|
||||
this.finished = false
|
||||
this.onLoad()
|
||||
},
|
||||
async onLoad() {
|
||||
this.loading = true
|
||||
$.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
|
||||
})
|
||||
onLoad() {
|
||||
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});
|
||||
},
|
||||
},
|
||||
async created() {
|
||||
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.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()
|
||||
created() {
|
||||
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()});
|
||||
}
|
||||
})
|
||||
}
|
||||
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>
|
||||
|
||||
<!--#
|
||||
|
||||
@@ -141,6 +141,11 @@ layout("/mobile/platform.html"){
|
||||
.van-action-sheet {
|
||||
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>
|
||||
|
||||
<div id="app" v-cloak>
|
||||
@@ -151,7 +156,7 @@ layout("/mobile/platform.html"){
|
||||
left-arrow
|
||||
placeholder
|
||||
fixed
|
||||
@click-left="pjaxReplace('/mobile/index')"
|
||||
@click-left="goBack"
|
||||
safe-area-inset-top
|
||||
></van-nav-bar>
|
||||
|
||||
@@ -226,12 +231,11 @@ layout("/mobile/platform.html"){
|
||||
</div>
|
||||
</van-cell>
|
||||
<van-cell title="预约地点">{{site.name}}</van-cell>
|
||||
<van-cell title="预约类型" class="reserve_type">
|
||||
<van-radio-group v-model="reserve_type" direction="horizontal">
|
||||
<van-radio :name="1">个人预约</van-radio>
|
||||
<van-radio :name="2">单位预约</van-radio>
|
||||
</van-radio-group>
|
||||
</van-cell>
|
||||
<van-cell title="预约类型" :value="reserveTypeName" is-link class="reserve-choice" @click="openOptions('type')"></van-cell>
|
||||
<van-cell title="所属协会" v-if="myClubs.length || reserve_type===3" class="reserve-choice"
|
||||
:value="reserve_type===3 ? selectedClubName : myClubs.map(club=>club.name).join('、')"
|
||||
:is-link="reserve_type===3" @click="openOptions('club')"></van-cell>
|
||||
<div v-if="reserve_type===3 && !myClubs.length" style="padding:12px;color:#ee0a24">您暂无已通过入会审核的有效协会</div>
|
||||
|
||||
<!--<van-field
|
||||
v-model="joinUser"
|
||||
@@ -255,18 +259,33 @@ layout("/mobile/platform.html"){
|
||||
></van-field>
|
||||
|
||||
<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">关闭
|
||||
</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>
|
||||
</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>
|
||||
|
||||
<script>
|
||||
(() => {
|
||||
// PJAX 内联脚本先执行,必须等待弹框历史依赖加载后再创建页面。
|
||||
const pageRoot = document.getElementById('app')
|
||||
const startPage = () => {
|
||||
if (document.getElementById('app') !== pageRoot || !pageRoot.isConnected) return
|
||||
moment.locale('zh_cn');
|
||||
|
||||
function getQueryString(name) {
|
||||
const getQueryString = (name) => {
|
||||
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
|
||||
var r = window.location.search.substr(1).match(reg);
|
||||
if (r != null) return decodeURI(r[2]);
|
||||
@@ -275,11 +294,15 @@ layout("/mobile/platform.html"){
|
||||
|
||||
<!--# include("/platform/activity/includeJs/activityUtil.js"){} #-->
|
||||
const siteTypeUtil = new typeUtil()
|
||||
const vue = new Vue({
|
||||
new Vue({
|
||||
el: '#app',
|
||||
mixins: [mobileMixins],
|
||||
mixins: [mobileMixins, window.popupHistoryMixin],
|
||||
data() {
|
||||
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,
|
||||
person: "${@shiro.getPrincipalProperty('username')}",
|
||||
unit: "${@shiro.getPrincipalProperty('unit').getName()}",
|
||||
@@ -297,6 +320,15 @@ layout("/mobile/platform.html"){
|
||||
}
|
||||
},
|
||||
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() {
|
||||
return (date) => {
|
||||
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: {
|
||||
// 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) {
|
||||
if (this.times.length === 1) {
|
||||
vant.Toast('必须保留一个时间段')
|
||||
@@ -371,26 +441,13 @@ layout("/mobile/platform.html"){
|
||||
}
|
||||
this.show = true
|
||||
},
|
||||
async reserveDo() {
|
||||
const resp = await $.post('/mobile/activity/site/info/reserveDo', {
|
||||
siteId: this.site_id,
|
||||
times: JSON.stringify(this.times),
|
||||
message: this.message,
|
||||
joinUser: this.joinUser,
|
||||
reserve_type: this.reserve_type
|
||||
})
|
||||
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)
|
||||
}
|
||||
reserveDo() {
|
||||
if(this.reserve_type===3 && !this.clubId){vant.Toast('请选择所属协会');return}
|
||||
this.formLoading=true;
|
||||
$.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)=>{
|
||||
if(res.code===0){this.clearPopupHistory(()=>{vant.Toast('预约成功');this.times=[];this.message='';this.getReserve();this.onLoad()})}
|
||||
else{vant.Toast(res.msg)}
|
||||
}).always(()=>{this.formLoading=false})
|
||||
},
|
||||
chooseTime(item) {
|
||||
const cloneItem = clone(item)
|
||||
@@ -405,22 +462,12 @@ layout("/mobile/platform.html"){
|
||||
}
|
||||
},
|
||||
|
||||
async onLoad() {
|
||||
this.loading = true
|
||||
const resp = await $.post('/mobile/activity/site/info/getDetail', {id: this.site_id})
|
||||
if (resp.code === 0) {
|
||||
this.site = resp.data
|
||||
}
|
||||
this.loading = false
|
||||
onLoad() {
|
||||
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});
|
||||
|
||||
},
|
||||
async getReserve() {
|
||||
const resp = await $.post('/mobile/activity/site/info/getReserve', {
|
||||
siteId: this.site_id,
|
||||
day: moment(this.selectDate).format('YYYY-MM-DD'),
|
||||
})
|
||||
if (resp.code === 0) {
|
||||
this.timeData = resp.data
|
||||
}
|
||||
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)}});
|
||||
},
|
||||
//获取最新的一个工作日设为当前时间
|
||||
getLatestWorkDay() {
|
||||
@@ -439,14 +486,22 @@ layout("/mobile/platform.html"){
|
||||
return selectDate
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.site_id = getQueryString('id')
|
||||
this.selectDate = this.getLatestWorkDay()
|
||||
await this.onLoad()
|
||||
this.setWeekList(this.selectDate)
|
||||
await this.getReserve()
|
||||
created() {
|
||||
this.site_id=getQueryString('id');this.selectDate=this.getLatestWorkDay();this.onLoad().then(()=>{this.setWeekList(this.selectDate);return 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>
|
||||
|
||||
<!--#
|
||||
|
||||
@@ -387,8 +387,8 @@ layout("/mobile/platform.html"){
|
||||
|
||||
</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">
|
||||
<div class="van-sidebar-item van-sidebar-item--select">
|
||||
{{m.moduleName}}
|
||||
|
||||
@@ -1,31 +1,9 @@
|
||||
|
||||
class typeUtil {
|
||||
|
||||
constructor() {
|
||||
|
||||
// 返回类型数组,text/value 为现有选择组件需要的别名;默认保留停用类型用于历史筛选。
|
||||
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
|
||||
placeholder="请选择场地类型">
|
||||
<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-form-item>
|
||||
</el-col>
|
||||
@@ -278,6 +278,7 @@ layout("/layouts/platform.html"){
|
||||
tabKey: '',
|
||||
tableLoading: false,
|
||||
subDis: false,
|
||||
formLoading: false,
|
||||
tableData: [],
|
||||
formData: {
|
||||
open_hours: [{}],
|
||||
@@ -328,41 +329,19 @@ layout("/layouts/platform.html"){
|
||||
}
|
||||
});
|
||||
},
|
||||
async operation() {
|
||||
let method = this.formData.id ? "/doEdit" : "/doAdd"
|
||||
this.subDis = true
|
||||
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
|
||||
}
|
||||
operation() {
|
||||
this.$refs.addForm.validate((valid)=>{if(!valid)return;this.formLoading=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})
|
||||
});
|
||||
},
|
||||
async findOneSite(id) {
|
||||
const {data} = await $.get("/platform/activity/site/mange/findOneSite", {id: id})
|
||||
return data
|
||||
findOneSite(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});
|
||||
},
|
||||
openView(row) {
|
||||
this.$refs.guava.view()
|
||||
},
|
||||
async openEdit(id) {
|
||||
this.formData = await this.findOneSite(id)
|
||||
this.$refs.guava.edit()
|
||||
openEdit(id) {
|
||||
this.findOneSite(id).then((row)=>{if(row){this.formData=row;this.$refs.guava.edit()}});
|
||||
},
|
||||
openAdd() {
|
||||
this.formData = {
|
||||
@@ -417,9 +396,8 @@ layout("/layouts/platform.html"){
|
||||
}, "json");
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.pageData();
|
||||
this.activityTypeList = await activityUtil.getAllType()
|
||||
created() {
|
||||
this.pageData();activityUtil.getAllType().then((rows)=>{this.activityTypeList=rows});
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -97,21 +97,9 @@ layout("/layouts/platform.html"){
|
||||
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>
|
||||
<div id="app" v-cloak>
|
||||
@@ -345,7 +333,7 @@ layout("/layouts/platform.html"){
|
||||
<div
|
||||
v-for="item in reserveViewData">
|
||||
<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-col :span="4">
|
||||
<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-button :label="1">个人预约</el-radio-button>
|
||||
<el-radio-button :label="2">单位预约</el-radio-button>
|
||||
<el-radio-button :label="3">协会预约</el-radio-button>
|
||||
</el-radio-group>
|
||||
</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-input maxlength="1000" placeholder="请输入预约事由" autosize v-model="formData.reserve_cause"
|
||||
@@ -489,38 +485,6 @@ layout("/layouts/platform.html"){
|
||||
</span>
|
||||
</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>
|
||||
|
||||
<script>
|
||||
@@ -535,10 +499,11 @@ layout("/layouts/platform.html"){
|
||||
return {
|
||||
activityTypeList: [],
|
||||
textData: {},
|
||||
drawer: false,
|
||||
step: 0.5,
|
||||
max: 24,
|
||||
min: 0,
|
||||
myClubs: [],
|
||||
formLoading: false,
|
||||
slider_width: 0,
|
||||
single_width: 0,
|
||||
marks: {},
|
||||
@@ -621,27 +586,10 @@ layout("/layouts/platform.html"){
|
||||
}
|
||||
},
|
||||
backOption(row) {
|
||||
this.$prompt('请填写反馈意见', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
inputValidator: (value) => {
|
||||
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(() => {
|
||||
});
|
||||
this.$prompt('请填写反馈意见','提示',{inputValidator:(value)=>value && value.trim() ? true : '请填写反馈意见'}).then(({value})=>{
|
||||
this.formLoading=true;
|
||||
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})
|
||||
}).catch(()=>{});
|
||||
},
|
||||
addMinute(val, minute) {
|
||||
if (val) {
|
||||
@@ -682,35 +630,16 @@ layout("/layouts/platform.html"){
|
||||
formatTooltip(val) {
|
||||
return moment(val * HOUR1 - HOUR8).format('HH:mm')
|
||||
},
|
||||
async selectEnd() {
|
||||
const {reserve_day, start_time, end_time, site_id} = this.formData
|
||||
const {open_hours} = this.infoViewData
|
||||
let mySelectStartTime = reserve_day + " " + start_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', '')
|
||||
}
|
||||
selectEnd() {
|
||||
const {reserve_day,start_time,end_time,site_id}=this.formData;
|
||||
if(reserve_day+' '+start_time<moment().format('YYYY-MM-DD HH:mm')){this.$message.warning('预约时间已过');return}
|
||||
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','')
|
||||
}
|
||||
});
|
||||
},
|
||||
async siteChange(id) {
|
||||
siteChange(id) {
|
||||
this.pageForm.siteId = id
|
||||
this.pageData()
|
||||
},
|
||||
@@ -766,7 +695,7 @@ layout("/layouts/platform.html"){
|
||||
this.dayArray.sort()
|
||||
}
|
||||
},
|
||||
async openAdd() {
|
||||
openAdd() {
|
||||
|
||||
if (this.dayArray && this.dayArray.length === 0) {
|
||||
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_phone", '${@shiro.getPrincipalProperty("mobile")}')
|
||||
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']) {
|
||||
this.$refs['addForm'].resetFields()
|
||||
@@ -793,67 +727,26 @@ layout("/layouts/platform.html"){
|
||||
})
|
||||
|
||||
},
|
||||
async doDelete(id, op) {
|
||||
if (op === 2) {
|
||||
const res = await $.post(location.href + "/isCanRollBack", {id: id});
|
||||
if (res === true) {
|
||||
this.$notify.warning({title: '警告', message: '此预约状态下不能进行撤回操作!'});
|
||||
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");
|
||||
}
|
||||
}
|
||||
});
|
||||
doDelete(id, op) {
|
||||
this.$confirm('确定撤销此预约?已审核的申请不能撤销。','提示').then(()=>{
|
||||
this.formLoading=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})
|
||||
}).catch(()=>{});
|
||||
},
|
||||
transNum(hm) {
|
||||
return (moment("1970-01-01 " + hm).valueOf() + HOUR8) / HOUR1
|
||||
},
|
||||
async doAdd() {
|
||||
if (!this.formData.time) {
|
||||
this.$notify.warning({title: '警告', message: '未选择预约时间!'});
|
||||
return
|
||||
}
|
||||
this.$refs["addForm"].validate(async (valid) => {
|
||||
if (valid) {
|
||||
this.subDis = true
|
||||
const loading = this.$loading({
|
||||
lock: true,
|
||||
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
|
||||
}
|
||||
});
|
||||
doAdd() {
|
||||
if (!this.formData.time) { this.$message.warning('请选择预约时间'); return; }
|
||||
if (this.formData.reserve_type===3 && !this.formData.clubId) { this.$message.warning('请选择所属协会'); return; }
|
||||
this.$refs.addForm.validate((valid)=>{
|
||||
if(!valid) return;
|
||||
this.formLoading=true; this.subDis=true;
|
||||
$.post(loc()+'/doAdd',{data:JSON.stringify(this.formData),days:JSON.stringify(this.dayArray)}).then((res)=>{
|
||||
if(res.code===0){this.addDialogVisible=false;this.$message.success('预约成功');this.pageData();this.$refs.guava.index()}
|
||||
else{this.$message.error(res.msg)}
|
||||
}).always(()=>{this.formLoading=false;this.subDis=false})
|
||||
})
|
||||
},
|
||||
openView(row) {
|
||||
this.$refs.guava.view()
|
||||
@@ -863,21 +756,15 @@ layout("/layouts/platform.html"){
|
||||
this.$refs.reserve.backOptionShow = (role || backOptionShow)
|
||||
this.$refs.reserve.openView(row.id)
|
||||
},
|
||||
async openReserve(data) {
|
||||
this.drawer = false
|
||||
data.open_hours = site.reverseRankingDate(data.open_hours)
|
||||
this.reserveViewData = await this.findReserveInfo(data.id)
|
||||
this.reserveViewData.forEach(item => {
|
||||
item.index = 0
|
||||
})
|
||||
this.formData.site_id = data.id
|
||||
this.infoViewData = data
|
||||
this.$refs.guava.edit()
|
||||
openReserve(data) {
|
||||
data.open_hours=site.reverseRankingDate(data.open_hours);
|
||||
this.findReserveInfo(data.id).then((rows)=>{
|
||||
this.reserveViewData=rows;this.reserveViewData.forEach(item=>{item.index=0});
|
||||
this.$set(this.formData,'site_id',data.id);this.infoViewData=data;this.$refs.guava.edit()
|
||||
});
|
||||
},
|
||||
async doSearch() {
|
||||
this.pageForm.pageNumber = 1
|
||||
this.pageData()
|
||||
this.siteList = await this.findAllOpenSite()
|
||||
doSearch() {
|
||||
this.pageForm.pageNumber=1;this.pageData();return this.findAllOpenSite().then((rows)=>{this.siteList=rows});
|
||||
},
|
||||
pageOrder(column) {
|
||||
this.pageForm.pageOrderName = column.prop;
|
||||
@@ -907,48 +794,27 @@ layout("/layouts/platform.html"){
|
||||
}
|
||||
}, "json");
|
||||
},
|
||||
async findReserveInfo(siteId) {
|
||||
const {data} = await $.get("/platform/activity/site/reserve/findReserveInfo", {siteId})
|
||||
return data;
|
||||
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});
|
||||
},
|
||||
async findReserveInfoByClickDay(siteId, day) {
|
||||
const {data} = await $.get("/platform/activity/site/reserve/findReserveInfo", {
|
||||
siteId: siteId,
|
||||
day: day
|
||||
})
|
||||
return data;
|
||||
findReserveInfoByClickDay(siteId, day) {
|
||||
return $.post('/platform/activity/site/reserve/findReserveInfo',{siteId,day}).then((res)=>{if(res.code!==0){this.$message.error(res.msg);return []}return res.data});
|
||||
},
|
||||
async findOneSite(id) {
|
||||
const {data} = await $.get("/platform/activity/site/mange/findOneSite", {id: id})
|
||||
return data
|
||||
findOneSite(id) {
|
||||
return $.post('/platform/activity/site/mange/findOneSite',{id}).then((res)=>{if(res.code!==0){this.$message.error(res.msg);return []}return res.data});
|
||||
},
|
||||
async checkReserve(reserve_day, start_time, end_time, site_id) {
|
||||
const {data} = await $.get("/platform/activity/site/reserve/checkReserve", {
|
||||
reserve_day,
|
||||
start_time,
|
||||
end_time,
|
||||
site_id
|
||||
})
|
||||
return data
|
||||
checkReserve(reserve_day, start_time, end_time, site_id) {
|
||||
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});
|
||||
},
|
||||
async findAllOpenSite(siteId) {
|
||||
const {data} = await $.get("/platform/activity/site/reserve/findAllOpenSite", {
|
||||
time: this.pageForm.time,
|
||||
siteId: siteId,
|
||||
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
|
||||
findAllOpenSite(siteId) {
|
||||
return $.post('/platform/activity/site/reserve/findAllOpenSite',{time:this.pageForm.time,siteId,siteType:this.pageForm.siteType}).then((res)=>{
|
||||
if(res.code!==0){this.$message.error(res.msg);return []}
|
||||
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})
|
||||
});
|
||||
},
|
||||
},
|
||||
async created() {
|
||||
this.activityTypeList = await activityUtil.getAllType()
|
||||
this.siteList = await this.findAllOpenSite()
|
||||
this.pageData();
|
||||
this.getMarks();
|
||||
created() {
|
||||
activityUtil.getAllType().then((rows)=>{this.activityTypeList=rows});this.findAllOpenSite().then((rows)=>{this.siteList=rows});this.pageData();this.getMarks();
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -1,357 +1,205 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<!--# layout("/layouts/platform.html"){ #-->
|
||||
<div id="app" v-cloak>
|
||||
<guava ref="guava">
|
||||
<template>
|
||||
<el-card shadow="never">
|
||||
<div class="btn-group tool-button">
|
||||
<el-date-picker
|
||||
v-model="pageForm.month" clearable value-format="yyyy-MM"
|
||||
type="month"
|
||||
placeholder="选择月">
|
||||
</el-date-picker>
|
||||
<el-date-picker v-model="pageForm.month" clearable value-format="yyyy-MM"
|
||||
type="month" placeholder="选择月"></el-date-picker>
|
||||
</div>
|
||||
<div class="btn-group tool-button">
|
||||
<el-input placeholder="请输入内容" clearable v-model="pageForm.searchKeyword"
|
||||
@keyup.enter.native="doSearch">
|
||||
<el-select v-model="pageForm.searchName" slot="prepend" placeholder="查询类型"
|
||||
style="width: 110px;">
|
||||
<el-input placeholder="请输入内容" clearable v-model="pageForm.searchKeyword" @keyup.enter.native="doSearch">
|
||||
<el-select v-model="pageForm.searchName" slot="prepend" placeholder="查询类型" style="width:110px">
|
||||
<el-option label="场地名称" value="asi.name"></el-option>
|
||||
<el-option label="预约人" value="asr.reserve_person"></el-option>
|
||||
</el-select>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="btn-group tool-button">
|
||||
<el-select v-model="pageForm.siteType" placeholder="场地类型" clearable
|
||||
style="width: 200px;">
|
||||
<el-option v-for="item in activityTypeList" :label="item.text" :value="item.value"></el-option>
|
||||
<el-select v-model="pageForm.siteType" placeholder="场地类型" clearable style="width:200px">
|
||||
<el-option v-for="item in activityTypeList" :key="item.value" :label="item.text" :value="item.value"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<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 class="pull-right offscreen-right mt5">
|
||||
<el-radio-group v-model="pageForm.isAudit" @change="doSearch">
|
||||
<el-radio-button :label="null">全部</el-radio-button>
|
||||
<div class="pull-right offscreen-right">
|
||||
<el-radio-group @change="doSearch" v-model="pageForm.isAudit">
|
||||
<el-radio-button :label="true">已审核</el-radio-button>
|
||||
<el-radio-button :label="false">未审核</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never" class="mt20">
|
||||
<div style="text-align: right" v-if="pageForm.isAudit === false">
|
||||
<el-button @click="doReview('many', false)" type="danger" size="mini">一键驳回</el-button>
|
||||
<el-button @click="doReview('many', true)" type="primary" size="mini">一键通过</el-button>
|
||||
</div>
|
||||
<el-table :data="tableData" row-key="id" @sort-change="pageOrder"
|
||||
@selection-change="handleSelectionChange"
|
||||
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-card class="mt20" shadow="never">
|
||||
<el-table :data="tableData" v-loading="tableLoading" style="width:100%">
|
||||
<el-table-column align="center" header-align="center" label="序号" type="index" :index="indexMethod" width="60"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="申请人" prop="reserve_person" min-width="90"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="工号" prop="loginname" min-width="115"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="场地名称" prop="site_name" min-width="140"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="预约类型" min-width="100">
|
||||
<template slot-scope="{row}">{{typeName(row.reserve_type)}}</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" sortable show-overflow-tooltip header-align="center" label="场地名称"
|
||||
prop="site_name"></el-table-column>
|
||||
<el-table-column align="center" sortable header-align="center" label="预约人"
|
||||
prop="reserve_person"></el-table-column>
|
||||
<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">
|
||||
<el-table-column align="center" header-align="center" label="所属工会" prop="union_name" min-width="140" show-overflow-tooltip></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" header-align="center" label="所属协会" min-width="130">
|
||||
<template slot-scope="{row}">{{row.club_name || '—'}}</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="预约时段" prop="concat_day" min-width="230"></el-table-column>
|
||||
<!-- 状态颜色取自后端 state 配置,未配置时使用页面默认字体颜色。 -->
|
||||
<el-table-column align="center" header-align="center" label="申请状态" prop="state_name" min-width="120">
|
||||
<template slot-scope="{row}"><span :style="{color: row.state_color || null}">{{row.state_name}}</span></template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="操作" fixed="right" width="120">
|
||||
<template slot-scope="{row}">
|
||||
<span>{{row.start_time}}</span>
|
||||
<el-dropdown @command="dropdownCommand">
|
||||
<el-button plain size="mini" aria-label="操作">
|
||||
<i class="ti-settings"></i><span class="ti-angle-down"></span>
|
||||
</el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item :command="{action:openView,value:row}">查看</el-dropdown-item>
|
||||
<el-dropdown-item v-if="canReview(row)" :command="{action:openReview,value:row}">审核</el-dropdown-item>
|
||||
<el-dropdown-item v-if="pageForm.isAudit===true" :disabled="!row.canRevoke || formLoading"
|
||||
:title="row.revokeReason || ''" :command="{action:openRevoke,value:row}">撤回</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" sortable show-overflow-tooltip header-align="center"
|
||||
label="结束时间"
|
||||
prop="end_time">
|
||||
<template slot-scope="{row}">
|
||||
<span>{{row.end_time}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" show-overflow-tooltip header-align="center" label="预约事由"
|
||||
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}">
|
||||
<!--<span :style="'color:'+row.state_color">{{row.state_name}}</span>-->
|
||||
<span v-if="[0].includes(row.stateaudittype)"
|
||||
style="color: #e6a23c">{{row.state_name}}</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 v-if="pageForm.isAudit === false" type="primary" @click="openReview2(row)"
|
||||
size="mini">审核
|
||||
</el-button>
|
||||
|
||||
<!--# if(@shiro.hasRole('sysadmin')||@shiro.hasRole('xgh02')){ #-->
|
||||
<!--<el-button v-if="pageForm.isAudit === false" type="primary" @click="openReview(row)"
|
||||
size="mini">
|
||||
批示
|
||||
</el-button>-->
|
||||
<!--# } #-->
|
||||
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
</el-table>
|
||||
<el-row class="el-pagination-container">
|
||||
<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>
|
||||
<!--# include("/layouts/pagination.html"){} #-->
|
||||
</el-card>
|
||||
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<template #edit style="padding: 0 10%">
|
||||
<reserve-info v-if="pageForm.isAudit === false" ref="reserve3" btn>
|
||||
<template #btn>
|
||||
<el-row style="margin: 40px 0;text-align: right">
|
||||
<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>
|
||||
<template #edit>
|
||||
<info ref="auditInfoRef" :review-api="reviewApi" @loaded="onAuditLoaded">
|
||||
<el-tab-pane :label="reviewTitle" name="review">
|
||||
<el-form :model="formData" :rules="formRules" label-width="80px" ref="form">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="xld_id" label="批示人">
|
||||
<el-input value="${@shiro.getPrincipalProperty('username')}"
|
||||
disabled></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="审核人员"><el-input disabled v-model="formData.username"></el-input></el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="time" label="批示时间">
|
||||
<el-input v-model="formData.time" disabled></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="当前时间"><el-input disabled v-model="formData.auditTime"></el-input></el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-form-item prop="opinion" label="批示意见">
|
||||
<el-input type="textarea" v-model="formData.opinion" rows="6" maxlength="500"
|
||||
placeholder="请填写您的批示意见"></el-input>
|
||||
<el-form-item label="审核意见" prop="auditOpinion">
|
||||
<el-input maxlength="500" show-word-limit placeholder="请填写您的审核意见" :rows="4"
|
||||
type="textarea" v-model="formData.auditOpinion"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-row style="margin: 40px 0;text-align: right">
|
||||
<el-button type="primary" @click="doInstructions">确 定</el-button>
|
||||
<el-row type="flex" justify="end">
|
||||
<el-button type="danger" :loading="formLoading" :disabled="!auditReady" @click="doReview(false)">拒绝</el-button>
|
||||
<el-button type="primary" :loading="formLoading" :disabled="!auditReady" @click="doReview(true)">通过</el-button>
|
||||
</el-row>
|
||||
</template>
|
||||
</reserve-info>-->
|
||||
</el-tab-pane>
|
||||
</info>
|
||||
</template>
|
||||
|
||||
<template #view>
|
||||
<reserve-info ref="reserve"></reserve-info>
|
||||
<info ref="viewInfoRef" :review-api="reviewApi"></info>
|
||||
</template>
|
||||
</guava>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
<!--# include("/platform/activity/includeJs/activityUtil.js"){} #-->
|
||||
const activityUtil = new typeUtil()
|
||||
<!--# include('./common/info.js'){} #-->
|
||||
<!--# include('/platform/activity/includeJs/activityUtil.js'){} #-->
|
||||
const vue = new Vue({
|
||||
el: '#app',
|
||||
mixins: [initTableMixins],
|
||||
components: {info},
|
||||
data() {
|
||||
return {
|
||||
selectTable: [],
|
||||
reviewApi: '${reviewApi}',
|
||||
reviewTitle: '${reviewTitle}',
|
||||
auditReady: false,
|
||||
activityTypeList: [],
|
||||
reserveViewData: {},
|
||||
tableLoading: false,
|
||||
tableData: [],
|
||||
formData: {},
|
||||
pageForm: {
|
||||
searchName: "asi.name",
|
||||
searchKeyword: "",
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
pageOrderName: "",
|
||||
pageOrderBy: "",
|
||||
isAudit: false,
|
||||
},
|
||||
pageForm: {isAudit: false, month: '', searchName: 'asi.name', siteType: ''},
|
||||
formRules: {
|
||||
opinion: [{required: true, message: '请填写批示意见', trigger: ['blur', 'change']}],
|
||||
},
|
||||
userid: '',
|
||||
auditOpinion: [{required: true, whitespace: true, message: '请填写审核意见', trigger: ['blur', 'change']}]
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
'guava': httpVueLoader('/components/plugins/Guava.vue'),
|
||||
'reserve-info': httpVueLoader('/components/activity/site/ReserveInfo.vue'),
|
||||
},
|
||||
methods: {
|
||||
openView(id) {
|
||||
this.$refs.guava.view()
|
||||
const role = "${@shiro.hasRole('sysadmin')||@shiro.hasRole('xghng')||@shiro.hasRole('A06')}"
|
||||
this.$refs.reserve.role = role
|
||||
const backOptionShow = "${@shiro.getPrincipalProperty('id')}" === this.userid
|
||||
this.$refs.reserve.backOptionShow = (role || backOptionShow)
|
||||
this.$refs.reserve.openView(id)
|
||||
// 已审核列表提供撤回;后端复查最新节点,确认前不修改申请状态。
|
||||
openRevoke(row) {
|
||||
if (this.formLoading || !row.canRevoke) return
|
||||
this.$confirm('确定撤回本次审核吗?撤回后申请恢复到当前节点待审核。', '撤回审核', {
|
||||
confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning'
|
||||
}).then(() => {
|
||||
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) {
|
||||
this.selectTable = val;
|
||||
typeName(type) { return {1:'个人预约',2:'单位预约',3:'协会预约'}[type] || '—' },
|
||||
// 以申请实际节点决定审核入口,已流转的申请只允许查看。
|
||||
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) {
|
||||
this.reserveViewData = row
|
||||
this.formData = {
|
||||
this.$set(this, 'auditReady', false)
|
||||
this.$set(this, 'formData', {
|
||||
id: row.id,
|
||||
state: 30,
|
||||
time: moment().format('YYYY-MM-DD')
|
||||
}
|
||||
|
||||
if (this.$refs['addForm']) {
|
||||
this.$refs['addForm'].resetFields()
|
||||
}
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.$refs.guava.edit()
|
||||
this.$refs.reserve2.openView(row.id)
|
||||
username: "${@shiro.getPrincipalProperty('username')}",
|
||||
auditTime: moment().format('YYYY-MM-DD HH:mm:ss'),
|
||||
auditOpinion: ''
|
||||
})
|
||||
this.$refs.guava.edit()
|
||||
this.$nextTick(() => this.$refs.form.clearValidate())
|
||||
this.$refs.auditInfoRef.onOpen(row.id, 'review')
|
||||
},
|
||||
openReview2(row) {
|
||||
this.reserveViewData = row
|
||||
this.formData = {
|
||||
id: row.id,
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
this.$refs.guava.edit()
|
||||
const role = "${@shiro.hasRole('sysadmin')||@shiro.hasRole('xghng')||@shiro.hasRole('A06')}"
|
||||
this.$refs.reserve3.role = role
|
||||
const backOptionShow = "${@shiro.getPrincipalProperty('id')}" === row.reserve_person_id
|
||||
this.$refs.reserve3.backOptionShow = (role || backOptionShow)
|
||||
this.$refs.reserve3.openView(row.id)
|
||||
// 详情加载成功且仍属于当前节点时才开放提交,避免使用列表中的过期状态。
|
||||
onAuditLoaded(detail) {
|
||||
this.$set(this, 'auditReady', detail.id === this.formData.id && this.canReview(detail))
|
||||
},
|
||||
// isPass 为通过/拒绝布尔值;仅提交预约 ID 和意见,审核身份与时间由后端保存。
|
||||
doReview(isPass) {
|
||||
if (this.formLoading || !this.auditReady) return
|
||||
this.$refs.form.validate((valid) => {
|
||||
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.pageData()
|
||||
} else {
|
||||
this.$message.error(res && res.msg || '接口返回异常,请刷新列表核对审核状态')
|
||||
}
|
||||
} catch (error) {
|
||||
this.$message.error('结果处理失败,请刷新列表核对审核状态')
|
||||
}
|
||||
}, () => {
|
||||
this.$message.error('请求失败,请刷新列表核对审核状态')
|
||||
}).always(() => {
|
||||
this.$set(this, 'formLoading', false)
|
||||
})
|
||||
})
|
||||
},
|
||||
doReview(type, state) {
|
||||
let that = this
|
||||
if (type === 'many' && this.selectTable.length === 0) {
|
||||
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() {
|
||||
$.post(location.href + "/doReview", this.formData, (res) => {
|
||||
if (res.code == 0) {
|
||||
this.$message.success(res.msg)
|
||||
this.pageData();
|
||||
this.$refs.guava.index()
|
||||
}
|
||||
}, "json");
|
||||
},
|
||||
doSearch() {
|
||||
this.pageForm.pageNumber = 1
|
||||
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 {
|
||||
this.$message.error(msg);
|
||||
}
|
||||
}, "json");
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.activityTypeList = await activityUtil.getAllType()
|
||||
this.pageData();
|
||||
},
|
||||
created() {
|
||||
this.pageData()
|
||||
// 历史审核可按停用的场地类型查询,复用记录页面的类型列表工具。
|
||||
new typeUtil().getAllType().then((types) => {
|
||||
this.$set(this, 'activityTypeList', types)
|
||||
}).fail(() => { this.$message.error('场地类型加载失败,请刷新重试') })
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
<!--# } #-->
|
||||
|
||||
@@ -1,68 +1,26 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<style>
|
||||
.audit-userids-select .el-select__tags {
|
||||
flex-wrap: unset;
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!--# layout("/layouts/platform.html"){ #-->
|
||||
<div id="app" v-cloak>
|
||||
<guava ref="guava">
|
||||
<el-card shadow="never">
|
||||
<table-tool :app="this" label="类型列表">
|
||||
<template #func>
|
||||
<el-button @click="openAdd" size="small" type="primary">新增类型</el-button>
|
||||
</template>
|
||||
</table-tool>
|
||||
<el-table :data="tableData" :size="tableSize"
|
||||
@sort-change="pageOrder" class="vi-table"
|
||||
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-card>
|
||||
|
||||
<template #edit>
|
||||
<vi-title title="基础信息"></vi-title>
|
||||
<el-form :model="formData" :rules="formRules" label-width="80px" ref="form">
|
||||
<el-form-item label="名称" prop="meetingTypeName">
|
||||
<el-input v-model="formData.meetingTypeName"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="模块名称" prop="moduleName">
|
||||
<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>
|
||||
|
||||
<el-card shadow="never">
|
||||
<table-tool :app="this" label="类型列表"><template #func><el-button type="primary" size="small" @click="openAdd">新增类型</el-button></template></table-tool>
|
||||
<el-table :data="tableData" v-loading="tableLoading" :size="tableSize">
|
||||
<el-table-column type="index" :index="indexMethod" label="序号" width="80"></el-table-column>
|
||||
<el-table-column prop="code" label="类型编码" align="center"></el-table-column>
|
||||
<el-table-column prop="meetingTypeName" label="类型名称" align="center"></el-table-column>
|
||||
<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>
|
||||
<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>
|
||||
</el-table>
|
||||
<!--# include("/layouts/pagination.html"){} #-->
|
||||
</el-card>
|
||||
<el-dialog :title="formData.id ? '编辑类型' : '新增类型'" :visible.sync="visible" width="520px" :close-on-click-modal="false">
|
||||
<el-form ref="form" :model="formData" :rules="formRules" label-width="100px">
|
||||
<el-form-item label="类型编码" prop="code"><el-input v-model="formData.code" maxlength="50"></el-input></el-form-item>
|
||||
<el-form-item label="类型名称" prop="meetingTypeName"><el-input v-model="formData.meetingTypeName" maxlength="50"></el-input></el-form-item>
|
||||
<el-form-item label="是否启用"><el-switch v-model="formData.enabled"></el-switch></el-form-item>
|
||||
</el-form>
|
||||
<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>
|
||||
</div>
|
||||
<!-- 原类型审核节点配置保留供追溯,新预约不再从类型读取节点。
|
||||
<vi-title title="审核节点信息(温馨提醒:如审核节点没有限制,审核节点类型不选或选择不限制)"></vi-title>
|
||||
<el-table :data="formData.auditStateList">
|
||||
<el-table-column label="节点ID" prop="stateId" width="100px">
|
||||
@@ -151,263 +109,82 @@ layout("/layouts/platform.html"){
|
||||
</template>
|
||||
</el-table-column>
|
||||
</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>
|
||||
<!--# include("/platform/activity/includeJs/activityUtil.js"){} #-->
|
||||
const activityUtil = new typeUtil()
|
||||
const vue = new Vue({
|
||||
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
|
||||
const vue = new Vue({
|
||||
el: '#app', mixins: [initTableMixins],
|
||||
data() { return {visible: false,formRules: {code: [{required:true,message:'请输入类型编码',trigger:'blur'}],meetingTypeName:[{required:true,message:'请输入类型名称',trigger:'blur'}]}} },
|
||||
methods: {
|
||||
openAdd() { this.formData={code:'',meetingTypeName:'',enabled:true}; this.visible=true; this.$nextTick(()=>this.$refs.form.clearValidate()) },
|
||||
openEdit(row) { this.formData=Object.assign({},row); this.visible=true; this.$nextTick(()=>this.$refs.form.clearValidate()) },
|
||||
// 提交类型 JSON;响应 code=0 表示成功,msg 为失败原因,空响应不得当作成功。
|
||||
save() {
|
||||
if (this.formLoading) return
|
||||
this.$refs.form.validate((valid) => {
|
||||
if (!valid) return
|
||||
this.$set(this, 'formLoading', true)
|
||||
$.post(loc() + '/doHandle', {data: JSON.stringify(this.formData)}).then((res) => {
|
||||
// jQuery 1.11 的 then 不会捕获回调异常,需保护回调以保证 always 执行。
|
||||
try {
|
||||
if (res && res.code === 0) {
|
||||
this.$set(this, 'visible', false)
|
||||
this.pageData()
|
||||
} else {
|
||||
this.$message.error(res && res.msg || '接口返回异常,请刷新列表核对类型状态')
|
||||
}
|
||||
} catch (error) {
|
||||
this.$message.error('结果处理失败,请刷新列表核对类型状态')
|
||||
}
|
||||
}, () => {
|
||||
this.$message.error('请求失败,请刷新列表核对类型状态')
|
||||
}).always(() => {
|
||||
this.$set(this, 'formLoading', false)
|
||||
})
|
||||
})
|
||||
},
|
||||
// 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 {
|
||||
if (res && res.code === 0) {
|
||||
this.$set(row, 'enabled', value)
|
||||
} else {
|
||||
item['stateId'] = this.maxStateId + (i + 1) * 10
|
||||
this.$message.error(res && res.msg || '接口返回异常,请刷新列表核对启用状态')
|
||||
}
|
||||
|
||||
|
||||
} catch (error) {
|
||||
this.$message.error('结果处理失败,请刷新列表核对启用状态')
|
||||
}
|
||||
// this.$set(this.formData,'auditStateList',val)
|
||||
}
|
||||
}, () => {
|
||||
this.$message.error('请求失败,请刷新列表核对启用状态')
|
||||
}).always(() => {
|
||||
this.$set(this, 'formLoading', false)
|
||||
})
|
||||
},
|
||||
computed: {
|
||||
dialogTitle() {
|
||||
if (this.formData.id) {
|
||||
return '编辑'
|
||||
}
|
||||
return '新增'
|
||||
},
|
||||
stateIdList() {
|
||||
return this.formData.auditStateList.map(v => v.stateId)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async checkStateId(stateId, stateIndex) {
|
||||
const resp = await $.get(loc() + '/checkStateId', {stateId, stateIndex})
|
||||
if (resp.code === 0) {
|
||||
return resp.data
|
||||
} else {
|
||||
this.notifyError(resp.msg)
|
||||
}
|
||||
},
|
||||
|
||||
addState() {
|
||||
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 {
|
||||
const valid = await this.$refs['form'].validate()
|
||||
if (valid) {
|
||||
this.formData.auditStateList.forEach(v => {
|
||||
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()
|
||||
// 删除指定类型,服务端校验引用关系;取消确认不发请求,失败后恢复按钮状态。
|
||||
remove(row) {
|
||||
if (this.formLoading) return
|
||||
this.$confirm('确定删除该类型?已使用的类型不能删除。', '提示').then(() => {
|
||||
this.$set(this, 'formLoading', true)
|
||||
return $.post(loc() + '/delete/' + row.id).then((res) => {
|
||||
try {
|
||||
if (res && res.code === 0) {
|
||||
this.pageData()
|
||||
} else {
|
||||
this.notifyError(resp.msg)
|
||||
this.$message.error(res && res.msg || '接口返回异常,请刷新列表核对删除结果')
|
||||
}
|
||||
} catch (error) {
|
||||
this.$message.error('结果处理失败,请刷新列表核对删除结果')
|
||||
}
|
||||
} catch (e) {
|
||||
}
|
||||
},
|
||||
async openEdit(row) {
|
||||
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'])
|
||||
}, () => {
|
||||
this.$message.error('请求失败,请刷新列表核对删除结果')
|
||||
}).always(() => {
|
||||
this.$set(this, 'formLoading', false)
|
||||
})
|
||||
d.auditStateList = data
|
||||
this.userList = await queryUserByIds(u)
|
||||
|
||||
this.formData = d
|
||||
this.$refs.guava.edit()
|
||||
},
|
||||
async doDelete(id) {
|
||||
try {
|
||||
const confirm = await this.$confirm('您确定要删除吗', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
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 {
|
||||
this.$message.warning(resp.msg)
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
},
|
||||
async userRemoteMethod(query) {
|
||||
if (query) {
|
||||
this.userList = await searchUser(query)
|
||||
}
|
||||
},
|
||||
openUserSelect(node_index) {
|
||||
this.nodeIndex = node_index
|
||||
const node = this.formData.auditStateList[node_index]
|
||||
if (node.matchCnd) {
|
||||
this.cnd = node.matchCnd
|
||||
} else {
|
||||
this.cnd = {method: "AND", conditions: [{}]}
|
||||
}
|
||||
this.$refs.cnd.open(false)
|
||||
},
|
||||
async initCndFields() {
|
||||
this.cnd = {
|
||||
method: "AND",
|
||||
conditions: [{}],
|
||||
}
|
||||
/*const meet_data = await getOpenMeeting()
|
||||
const meet_options = meet_data.map(v => {
|
||||
return {"label": v.jdhallname, "value": v.id}
|
||||
})
|
||||
this.fields.push(
|
||||
{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)])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.findAll()
|
||||
this.initCndFields()
|
||||
this.maxStateId = await activityUtil.findMaxStateId()
|
||||
this.stateAuditTypeList = await getEnumOptions('AuditTypeEnum')
|
||||
this.activityAuditTypeEnum = await getEnumOptions('ActivityAuditTypeEnum')
|
||||
}).catch(() => {})
|
||||
}
|
||||
})
|
||||
}, created() { this.pageData() }
|
||||
})
|
||||
</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