场地预约整改

This commit is contained in:
=
2026-06-17 09:58:15 +08:00
parent ef7ac57bb8
commit 33859d3be0
27 changed files with 2686 additions and 523 deletions
@@ -5,32 +5,26 @@ import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import cn.hutool.json.JSONObject;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.result.Result;
import com.budwk.app.sys.models.Sys_home_activity;
import com.budwk.app.sys.models.Sys_home_template;
import com.budwk.app.sys.models.Sys_menu;
import com.budwk.app.sys.services.SysHomeNewsService;
import com.budwk.app.sys.services.SysMenuService;
import com.budwk.app.sys.services.SysUserService;
import com.budwk.app.web.commons.auth.utils.AuthUtil;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.activity.basic.service.ActivityBasicScopeService;
import io.swagger.annotations.ApiOperation;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.FieldFilter;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.Daos;
import org.nutz.integration.jedis.RedisService;
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.Strings;
import org.nutz.lang.util.NutMap;
@@ -42,9 +36,7 @@ import org.nutz.mvc.annotation.Param;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
@IocBean
@@ -58,7 +50,7 @@ public class SysHomeController {
@Inject
private SysUserService sysUserService;
@Inject
private RedisService redisService;
private SysHomeNewsService sysHomeNewsService;
@Inject
private ActivityBasicScopeService activityBasicScopeService;
@@ -285,31 +277,11 @@ public class SysHomeController {
// @CacheDefaults(cacheName = RedisConstant.PLATFORM_REDIS_WKCACHE_PREFIX + "web_news", isHash = true)
// @CacheResult(cacheKey = "news", ignoreNull = true, cacheLiveTime = 60 * 60 * 24)
public Result getNews() {
try {
String url = "https://xgh.cug.edu.cn/";
Document document = Jsoup.connect(url).get();
Elements homeElements = document.select(".gh,.jc,.fc,.jg");
List<Map<String, Object>> news = homeElements.stream().map(homeElement -> {
//获取板块名称
String name = homeElement.selectFirst(".title").text();
Elements liElements = homeElement.select("li");
List<NutMap> ele = liElements.stream().map(liElement -> {
NutMap row = NutMap.NEW();
String href = liElement.selectFirst("a").attr("href");
String time = liElement.selectFirst("span").text();
String title = liElement.selectFirst("a").attr("title");
row.put("href", href);
row.put("time", time);
row.put("text", title);
return row;
}).toList();
return Map.of("label", name, "value", ele);
}).collect(Collectors.toList());
redisService.setex("gonghui_news", 60 * 60 * 24, Json.toJson(news));
return Result.success(news);
} catch (IOException e) {
throw new RuntimeException(e);
}
try {
return Result.success(sysHomeNewsService.getUnionNoticeNews());
} catch (Exception e) {
log.error(e);
return Result.success();
}
}
}
@@ -0,0 +1,15 @@
package com.budwk.app.sys.services;
import cn.hutool.json.JSONObject;
import java.io.IOException;
public interface SysHomeNewsService {
/**
* 获取江苏农牧科技职业学院工会网站通知公告,并保持首页新闻组件需要的数据结构。
*
* @return 首页网站新闻数据,当前使用 grassroots 字段兼容前端组件
*/
JSONObject getUnionNoticeNews() throws IOException;
}
@@ -0,0 +1,70 @@
package com.budwk.app.sys.services.impl;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import com.budwk.app.sys.services.SysHomeNewsService;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.nutz.ioc.loader.annotation.IocBean;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@IocBean
public class SysHomeNewsServiceImpl implements SysHomeNewsService {
private static final String UNION_HOME_URL = "https://www.jsahvc.edu.cn/gh/main.htm";
@Override
public JSONObject getUnionNoticeNews() throws IOException {
JSONObject root = new JSONObject();
List<JSONObject> noticeList = new ArrayList<>();
Document doc = Jsoup.connect(UNION_HOME_URL)
// 学校官网会过滤部分非浏览器请求,这里补充常规浏览器请求头提高抓取稳定性。
.userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36")
.referrer("https://www.jsahvc.edu.cn/")
.timeout(10000)
.get();
Elements noticeItems = getNoticeItems(doc);
for (Element item : noticeItems) {
Element titleLink = item.selectFirst(".news_title a, a[title], a");
if (titleLink == null) {
continue;
}
String title = StrUtil.blankToDefault(titleLink.attr("title"), titleLink.text()).trim();
String url = titleLink.absUrl("href").trim();
Element meta = item.selectFirst(".news_meta, .date, .news_date, .time");
JSONObject node = new JSONObject();
node.set("title", title);
node.set("date", meta == null ? "" : meta.text().trim());
node.set("url", url);
noticeList.add(node);
}
root.set("grassroots", noticeList);
return root;
}
/**
* 优先按栏目标题查找通知公告列表,找不到时再使用学校官网历史模板中的栏目位兜底。
*
* @param doc 工会网站首页文档
* @return 通知公告列表节点
*/
private Elements getNoticeItems(Document doc) {
for (Element post : doc.select(".post")) {
Element title = post.selectFirst(".tt .title, .tit .title, h3, h2");
if (title != null && title.text().contains("通知公告")) {
return post.select(".news_list li, .wp_article_list li");
}
}
return doc.select(".post-18 .news_list li, .post-18 .wp_article_list li");
}
}
@@ -284,19 +284,52 @@ public class SiteCugApplyController {
private List<int[]> buildDisabledRanges(NutMap site, String today) {
List<NutMap> disabledList = toNutMapList(site.get("notApplyTimeList"));
List<int[]> result = new ArrayList<>();
int targetWeekDay = toWeekDay(DateUtil.parseDate(today));
for (NutMap item : disabledList) {
if (item == null || !StrUtil.equals(today, item.getString("date"))) {
if (item == null || StrUtil.hasBlank(item.getString("startTime"), item.getString("endTime"))) {
continue;
}
Integer start = parseTimeToMinutes(item.getString("startTime"));
Integer end = parseTimeToMinutes(item.getString("endTime"));
if (start != null && end != null && end > start) {
result.add(new int[]{start, end});
// 禁用时间兼容旧的按日期配置,同时支持新增的按星期配置。
if (isDateDisabledRange(item, today) || isWeekDisabledRange(item, targetWeekDay)) {
addDisabledRange(result, item);
}
}
return result;
}
private boolean isDateDisabledRange(NutMap item, String today) {
String type = item.getString("type");
return (StrUtil.isBlank(type) || StrUtil.equals("date", type))
&& StrUtil.equals(today, item.getString("date"));
}
private boolean isWeekDisabledRange(NutMap item, int targetWeekDay) {
Integer weekDay = parseWeekDay(item.get("weekDay"));
return StrUtil.equals("week", item.getString("type"))
&& weekDay != null
&& weekDay == targetWeekDay;
}
private void addDisabledRange(List<int[]> result, NutMap item) {
Integer start = parseTimeToMinutes(item.getString("startTime"));
Integer end = parseTimeToMinutes(item.getString("endTime"));
if (start != null && end != null && end > start) {
result.add(new int[]{start, end});
}
}
private Integer parseWeekDay(Object value) {
if (value == null) {
return null;
}
try {
int weekDay = Integer.parseInt(String.valueOf(value));
return weekDay >= 1 && weekDay <= 7 ? weekDay : null;
} catch (NumberFormatException e) {
return null;
}
}
private List<int[]> buildReservedRanges(List<SiteCugApply> applyList, String today) {
List<int[]> result = new ArrayList<>();
String dayStart = today + " 00:00:00";
@@ -404,6 +437,11 @@ public class SiteCugApplyController {
return week == 0 || week == 6;
}
private int toWeekDay(DateTime date) {
int week = date.dayOfWeek() - 1;
return week == 0 ? 7 : week;
}
private List<NutMap> buildAvailabilityBlocks(SiteCugInfo siteInfo, String day, List<SiteCugApply> applyList, Set<String> holidaySet) {
if (siteInfo.getReserveTimeType() != null && siteInfo.getReserveTimeType() == 2) {
return buildFullDayAvailabilityBlocks(siteInfo, day, applyList, holidaySet);
@@ -802,6 +840,7 @@ public class SiteCugApplyController {
.setReserveStartTime(apply.getReserveStartTime())
.setReserveEndTime(apply.getReserveEndTime())
.setJoinCount(apply.getJoinCount())
.setCoffeeNeeded(apply.getCoffeeNeeded())
.setApplyCause(apply.getApplyCause())
.setBackOption(apply.getBackOption())
.setYearlyReserveEndDate(apply.getYearlyReserveEndDate());
@@ -0,0 +1,66 @@
package com.budwk.app.zhgh.dayofficework.siteCug.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaMode;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.zhgh.dayofficework.siteCug.service.SiteCugApplyService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
@IocBean
@At("/platform/siteCug/branchUnionAudit")
@Api("场馆分工会审核")
@Ok("json:full")
public class SiteCugBranchUnionAuditController {
private static final String AUDIT_TASK_NAME = "c4493b93-75c0-4980-b604-19df2be2c42c";
@Inject
private SiteCugApplyService applyService;
@At("/")
@Ok("beetl:/platform/zhgh/dayofficework/siteCug/branchUnionAudit/index.html")
@SaCheckPermission("siteCug.branchUnionAudit")
public void index() {
}
@At("/h5")
@Ok("beetl:/platform/zhghh5/dayofficework/siteCug/branchUnionAudit/index.html")
@SaCheckPermission("h5.siteCug.branchUnionAudit")
public void h5Index() {
}
@At
@ApiOperation("分页查询")
@SaCheckPermission(value = {"siteCug.branchUnionAudit", "h5.siteCug.branchUnionAudit"}, mode = SaMode.OR)
public Result pageData(PageForm pageForm,
@Param("siteType") String siteType,
@Param("siteId") String siteId,
@Param("approval") Boolean approval) {
return Result.success(applyService.listAuditPage(pageForm, siteType, siteId, approval, AUDIT_TASK_NAME));
}
@At("/executeTask")
@ApiOperation("执行审核任务")
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission(value = {"siteCug.branchUnionAudit", "h5.siteCug.branchUnionAudit"}, mode = SaMode.OR)
public Result executeTask(@Param("data") String data) {
return applyService.executeAuditTask(data);
}
@At("/revokeTask")
@ApiOperation("撤回审核任务")
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission(value = {"siteCug.branchUnionAudit", "h5.siteCug.branchUnionAudit"}, mode = SaMode.OR)
public Result revokeTask(@Param("taskId") Long taskId, @Param("applyId") String applyId) {
return applyService.revokeAuditTask(taskId, applyId);
}
}
@@ -0,0 +1,66 @@
package com.budwk.app.zhgh.dayofficework.siteCug.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaMode;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.zhgh.dayofficework.siteCug.service.SiteCugApplyService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
@IocBean
@At("/platform/siteCug/deputySchoolUnion")
@Api("场馆校工会副主席审核")
@Ok("json:full")
public class SiteCugDeputySchoolUnionController {
private static final String AUDIT_TASK_NAME = "de7044e4-187b-43c8-ab47-87eb0998302a";
@Inject
private SiteCugApplyService applyService;
@At("/")
@Ok("beetl:/platform/zhgh/dayofficework/siteCug/deputySchoolUnion/index.html")
@SaCheckPermission("siteCug.deputySchoolUnion")
public void index() {
}
@At("/h5")
@Ok("beetl:/platform/zhghh5/dayofficework/siteCug/deputySchoolUnion/index.html")
@SaCheckPermission("h5.siteCug.deputySchoolUnion")
public void h5Index() {
}
@At
@ApiOperation("分页查询")
@SaCheckPermission(value = {"siteCug.deputySchoolUnion", "h5.siteCug.deputySchoolUnion"}, mode = SaMode.OR)
public Result pageData(PageForm pageForm,
@Param("siteType") String siteType,
@Param("siteId") String siteId,
@Param("approval") Boolean approval) {
return Result.success(applyService.listAuditPage(pageForm, siteType, siteId, approval, AUDIT_TASK_NAME));
}
@At("/executeTask")
@ApiOperation("执行审核任务")
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission(value = {"siteCug.deputySchoolUnion", "h5.siteCug.deputySchoolUnion"}, mode = SaMode.OR)
public Result executeTask(@Param("data") String data) {
return applyService.executeAuditTask(data);
}
@At("/revokeTask")
@ApiOperation("撤回审核任务")
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission(value = {"siteCug.deputySchoolUnion", "h5.siteCug.deputySchoolUnion"}, mode = SaMode.OR)
public Result revokeTask(@Param("taskId") Long taskId, @Param("applyId") String applyId) {
return applyService.revokeAuditTask(taskId, applyId);
}
}
@@ -83,15 +83,11 @@ public class SiteCugManageController {
public Object submit(@Param("data") SiteCugInfo info) {
List<NutMap> notApplyTimeList = info.getNotApplyTimeList();
if (notApplyTimeList != null) {
boolean invalid = notApplyTimeList.stream().anyMatch(item ->
item == null
|| StrUtil.isBlank(item.getString("date"))
|| StrUtil.isBlank(item.getString("startTime"))
|| StrUtil.isBlank(item.getString("endTime"))
|| item.getString("startTime").compareTo(item.getString("endTime")) >= 0);
boolean invalid = notApplyTimeList.stream().anyMatch(this::isInvalidDisabledTime);
if (invalid) {
return Result.error("Invalid disabled time settings");
}
notApplyTimeList.forEach(this::normalizeDisabledTime);
}
if (info.getReserveTimeType() == null) {
@@ -196,6 +192,62 @@ public class SiteCugManageController {
}
}
private boolean isInvalidDisabledTime(NutMap item) {
if (item == null
|| StrUtil.isBlank(item.getString("startTime"))
|| StrUtil.isBlank(item.getString("endTime"))
|| item.getString("startTime").compareTo(item.getString("endTime")) >= 0) {
return true;
}
String type = item.getString("type");
if (StrUtil.isBlank(type) || StrUtil.equals(type, "date")) {
return StrUtil.isBlank(item.getString("date"));
}
if (StrUtil.equals(type, "week")) {
return !isWeekDay(item.get("weekDay"));
}
return true;
}
private void normalizeDisabledTime(NutMap item) {
if (item == null) {
return;
}
if (StrUtil.isBlank(item.getString("type"))) {
item.put("type", "date");
}
if (StrUtil.equals(item.getString("type"), "week")) {
int weekDay = Integer.parseInt(String.valueOf(item.get("weekDay")));
item.put("weekDay", weekDay);
item.put("weekName", getWeekName(weekDay));
}
}
private boolean isWeekDay(Object value) {
if (value == null) {
return false;
}
try {
int weekDay = Integer.parseInt(String.valueOf(value));
return weekDay >= 1 && weekDay <= 7;
} catch (Exception e) {
return false;
}
}
private String getWeekName(int weekDay) {
return switch (weekDay) {
case 1 -> "星期一";
case 2 -> "星期二";
case 3 -> "星期三";
case 4 -> "星期四";
case 5 -> "星期五";
case 6 -> "星期六";
case 7 -> "星期日";
default -> "";
};
}
private NutMap normalizeOpenHourTimeUnit(NutMap item) {
if (item == null) {
return null;
@@ -2,55 +2,29 @@ package com.budwk.app.zhgh.dayofficework.siteCug.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaMode;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.utils.PageUtil;
import com.budwk.app.flow.constant.FlowConst;
import com.budwk.app.flow.entity.ProcessTask;
import com.budwk.app.flow.enums.ProcessTaskStateEnum;
import com.budwk.app.flow.service.FlowCommonService;
import com.budwk.app.flow.service.ProcessTaskService;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
import com.budwk.app.zhgh.dayofficework.siteCug.model.SiteCugApply;
import com.budwk.app.zhgh.dayofficework.siteCug.service.SiteCugApplyService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Cnd;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.util.NutMap;
import org.nutz.json.Json;
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;
@IocBean
@At("/platform/siteCug/schoolUnionAudit")
@Api("场馆校工会审核")
@Api("场馆校工会主席审核")
@Ok("json:full")
public class SiteCugSchoolUnionAuditController {
private static final String YEARLY_BATCH_PREFIX = "YEARLY_BATCH:";
private static final String AUDIT_TASK_NAME = "c8c03407-cf26-4e0b-82f1-b2afc9c79996";
@Inject
private SiteCugApplyService applyService;
@Inject
private ProcessTaskService processTaskService;
@Inject
private FlowCommonService flowCommonService;
@At("/")
@Ok("beetl:/platform/zhgh/dayofficework/siteCug/schoolUnionAudit/index.html")
@@ -71,102 +45,7 @@ public class SiteCugSchoolUnionAuditController {
@Param("siteType") String siteType,
@Param("siteId") String siteId,
@Param("approval") Boolean approval) {
Sql sql = Sqls.create("""
SELECT
info.*,
si.name as siteName,
CASE WHEN info.reserveType = 'club' THEN info.clubName ELSE info.applyUnionName END AS reserveTargetName,
ins.id AS instanceId,
ins.businessNo,
ins.state instanceState,
ins.variable instanceVariable,
ins.processDefineId instanceProcessDefineId,
t.id taskId,
t.taskName AS taskKey,
t.displayName taskName,
t.taskType,
t.performType taskPerformType,
t.taskState,
t.finishTime,
t.taskParentId,
t.variable taskVariable,
IFNULL(GROUP_CONCAT(DISTINCT nt.displayName),'结束') curTaskName,
CASE WHEN EXISTS (
SELECT 1 FROM wf_process_task rt
WHERE rt.processInstanceId = ins.id AND rt.taskParentId = t.id
) THEN 1 ELSE 0 END AS canRevoke,
CASE WHEN info.backOption LIKE 'YEARLY_BATCH:%' THEN 1 ELSE 0 END AS yearlyBatch,
CASE WHEN info.backOption LIKE 'YEARLY_BATCH:%' THEN (
SELECT COUNT(1) FROM site_cug_apply batch_info WHERE batch_info.backOption = info.backOption AND batch_info.createdBy = info.createdBy
) ELSE 1 END AS batchAuditCount
FROM
wf_process_task t
LEFT JOIN wf_process_instance ins ON ins.id = t.processInstanceId
LEFT JOIN site_cug_apply info ON info.id = ins.businessNo
LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = t.id
LEFT JOIN site_cug_info si ON info.siteId = si.id
LEFT JOIN wf_process_task nt ON nt.processInstanceId = ins.id AND nt.taskState = 10
LEFT JOIN wf_process_task rt ON rt.processInstanceId = ins.id AND rt.taskParentId = t.id
$condition
""");
Cnd cnd = Cnd.NEW();
if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
SqlExpressionGroup seg = new SqlExpressionGroup();
seg.or(SiteCugApply::getApplyUserName, "like", "%" + pageForm.getSearchKeyword() + "%");
seg.or(SiteCugApply::getApplyLoginName, "like", "%" + pageForm.getSearchKeyword() + "%");
cnd.and(seg);
}
cnd.andEX("si.typeId", "=", siteType);
cnd.andEX("info.siteId", "=", siteId);
cnd.and("t.taskName", "=", "c8c03407-cf26-4e0b-82f1-b2afc9c79996");
if (!StpUtil.hasRole(RoleConstant.SYSADMIN.name())) {
cnd.and("ta.actorId", "in", List.of(SecurityUtil.getUserId()));
}
if (Boolean.TRUE.equals(approval)) {
cnd.and("t.taskState", "in", List.of(ProcessTaskStateEnum.FINISHED.getCode(), ProcessTaskStateEnum.INTERRUPT.getCode()));
} else {
cnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode());
}
if (StrUtil.isNotBlank(pageForm.getPageOrderName()) && StrUtil.isNotBlank(pageForm.getPageOrderBy())) {
cnd.orderBy(resolveOrderColumn(pageForm.getPageOrderName()), PageUtil.getOrder(pageForm.getPageOrderBy()));
} else {
cnd.desc("info.createdAt");
}
cnd.groupBy("t.id");
sql.setCondition(cnd);
Pagination<NutMap> pageVO = applyService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
return Result.success(pageVO);
}
private String resolveOrderColumn(String pageOrderName) {
if (StrUtil.isBlank(pageOrderName)) {
return "info.createdAt";
}
switch (pageOrderName) {
case "siteName":
return "si.name";
case "applyUserName":
return "info.applyUserName";
case "applyLoginName":
return "info.applyLoginName";
case "yearlyBatch":
return "yearlyBatch";
case "reserveTargetName":
return "reserveTargetName";
case "reserveStartTime":
return "info.reserveStartTime";
case "reserveEndTime":
return "info.reserveEndTime";
case "applyMobile":
return "info.applyMobile";
case "curTaskName":
return "curTaskName";
case "instanceState":
return "ins.state";
default:
return "info.createdAt";
}
return Result.success(applyService.listAuditPage(pageForm, siteType, siteId, approval, AUDIT_TASK_NAME));
}
@At("/executeTask")
@@ -174,72 +53,14 @@ public class SiteCugSchoolUnionAuditController {
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission(value = {"siteCug.schoolUnionAudit", "h5.siteCug.schoolUnionAudit"}, mode = SaMode.OR)
public Result executeTask(@Param("data") String data) {
Dict args = Json.fromJson(Dict.class, data);
Long processTaskId = args.getLong(FlowConst.PROCESS_TASK_ID_KEY);
ProcessTask currentTask = processTaskService.getById(processTaskId);
if (currentTask == null) {
return Result.error("审核任务不存在");
}
SiteCugApply apply = applyService.fetch(args.getStr("applyId"));
if (apply == null) {
return Result.error("预约记录不存在");
}
List<ProcessTask> taskList = new ArrayList<>();
if (StrUtil.isNotBlank(apply.getBackOption()) && apply.getBackOption().startsWith(YEARLY_BATCH_PREFIX)) {
List<SiteCugApply> batchList = applyService.query(Cnd.where("backOption", "=", apply.getBackOption()).and("createdBy", "=", apply.getCreatedBy()));
List<String> bizIds = batchList.stream().map(SiteCugApply::getId).toList();
taskList = processTaskService.getDoingTaskByBizIdTaskName(bizIds, currentTask.getTaskName());
}
if (taskList.isEmpty()) {
taskList.add(currentTask);
}
for (ProcessTask task : taskList) {
Dict cloneArgs = args.clone();
cloneArgs.put(FlowConst.PROCESS_TASK_ID_KEY, task.getId());
flowCommonService.executeTask(cloneArgs);
}
if (taskList.size() > 1) {
return Result.success("已完成该全年预约批次的审核");
}
return Result.success("提交成功");
return applyService.executeAuditTask(data);
}
@At("/revokeTask")
@ApiOperation("撤回审核任务")
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission(value = {"siteCug.schoolUnionAudit", "h5.siteCug.schoolUnionAudit"}, mode = SaMode.OR)
public Result revokeTask(@Param("taskId") Long taskId, @Param("applyId") String applyId) {
ProcessTask currentTask = processTaskService.getById(taskId);
if (currentTask == null) {
return Result.error("撤回任务不存在");
}
SiteCugApply apply = applyService.fetch(applyId);
if (apply == null) {
return Result.error("预约记录不存在");
}
List<ProcessTask> taskList = new ArrayList<>();
if (StrUtil.isNotBlank(apply.getBackOption()) && apply.getBackOption().startsWith(YEARLY_BATCH_PREFIX)) {
List<SiteCugApply> batchList = applyService.query(Cnd.where("backOption", "=", apply.getBackOption()).and("createdBy", "=", apply.getCreatedBy()));
List<String> bizIds = batchList.stream().map(SiteCugApply::getId).toList();
taskList = processTaskService.getDoneTaskByBizIdTaskName(bizIds, currentTask.getTaskName());
}
if (taskList.isEmpty()) {
taskList.add(currentTask);
}
for (ProcessTask task : taskList) {
flowCommonService.revokeTask(task.getId());
}
if (taskList.size() > 1) {
return Result.success("已撤回该全年预约批次的审核任务");
}
return Result.success("撤回成功");
return applyService.revokeAuditTask(taskId, applyId);
}
}
@@ -100,6 +100,11 @@ public class SiteCugApply extends BaseModel {
@ColDefine(type = ColType.INT)
private Integer joinCount;
@Column
@Comment("是否需要咖啡")
@ColDefine(type = ColType.INT)
private Integer coffeeNeeded;
@Column
@Comment("预约事由")
@ColDefine(type = ColType.VARCHAR, width = 1000)
@@ -1,10 +1,14 @@
package com.budwk.app.zhgh.dayofficework.siteCug.service;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.dayofficework.siteCug.model.SiteCugApply;
import com.budwk.app.zhgh.dayofficework.siteCug.param.SiteCugRecordPageForm;
import org.nutz.dao.Cnd;
import org.nutz.dao.sql.Sql;
import org.nutz.lang.util.NutMap;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
@@ -21,4 +25,13 @@ public interface SiteCugApplyService extends BaseService<SiteCugApply> {
// 场馆预约查询页的导出逻辑统一放在 service,controller 只负责接收请求
void exportRecord(SiteCugRecordPageForm pageForm, HttpServletResponse response);
// 场馆预约审核页共用列表查询,按流程节点 taskName 区分不同审核页面
Pagination<NutMap> listAuditPage(PageForm pageForm, String siteType, String siteId, Boolean approval, String auditTaskName);
// 场馆预约审核提交共用同一套任务执行逻辑,兼容全年预约批次一起审核
Result executeAuditTask(String data);
// 场馆预约审核撤回共用同一套任务撤回逻辑,兼容全年预约批次一起撤回
Result revokeAuditTask(Long taskId, String applyId);
}
@@ -4,14 +4,25 @@ import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.param.PageForm;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.base.utils.CommonDownloadUtil;
import com.budwk.app.base.utils.PageUtil;
import com.budwk.app.flow.constant.FlowConst;
import com.budwk.app.flow.entity.ProcessInstance;
import com.budwk.app.flow.entity.ProcessTask;
import com.budwk.app.flow.enums.ProcessInstanceStateEnum;
import com.budwk.app.flow.enums.ProcessTaskStateEnum;
import com.budwk.app.flow.service.FlowCommonService;
import com.budwk.app.flow.service.ProcessTaskService;
import com.budwk.app.sys.models.SysHoliday;
import com.budwk.app.sys.models.Sys_union;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
@@ -30,6 +41,7 @@ import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.json.Json;
import org.nutz.lang.util.NutMap;
import javax.servlet.http.HttpServletResponse;
@@ -44,8 +56,14 @@ import java.util.stream.Collectors;
@IocBean(args = {"refer:dao"})
public class SiteCugApplyServiceImpl extends BaseServiceImpl<SiteCugApply> implements SiteCugApplyService {
private static final String YEARLY_BATCH_PREFIX = "YEARLY_BATCH:";
@Inject
private SysClubService sysClubService;
@Inject
private ProcessTaskService processTaskService;
@Inject
private FlowCommonService flowCommonService;
public SiteCugApplyServiceImpl(Dao dao) {
super(dao);
@@ -64,6 +82,11 @@ public class SiteCugApplyServiceImpl extends BaseServiceImpl<SiteCugApply> imple
if (StrUtil.isBlank(apply.getReserveStartTime()) || StrUtil.isBlank(apply.getReserveEndTime())) {
return Map.of(false, "预约时间不能为空");
}
if (apply.getCoffeeNeeded() == null) {
apply.setCoffeeNeeded(0);
} else if (apply.getCoffeeNeeded() != 0 && apply.getCoffeeNeeded() != 1) {
return Map.of(false, "是否需要咖啡仅支持是/否");
}
if (!DateUtil.parseDateTime(apply.getReserveEndTime()).isAfter(DateUtil.parseDateTime(apply.getReserveStartTime()))) {
return Map.of(false, "结束时间必须晚于开始时间");
}
@@ -204,6 +227,123 @@ public class SiteCugApplyServiceImpl extends BaseServiceImpl<SiteCugApply> imple
CommonDownloadUtil.download("场馆预约查询.xlsx", workbook, response);
}
@Override
public Pagination<NutMap> listAuditPage(PageForm pageForm, String siteType, String siteId, Boolean approval, String auditTaskName) {
Sql sql = Sqls.create("""
SELECT
info.*,
si.name as siteName,
CASE WHEN info.reserveType = 'club' THEN info.clubName ELSE info.applyUnionName END AS reserveTargetName,
ins.id AS instanceId,
ins.businessNo,
ins.state instanceState,
ins.variable instanceVariable,
ins.processDefineId instanceProcessDefineId,
t.id taskId,
t.taskName AS taskKey,
t.displayName taskName,
t.taskType,
t.performType taskPerformType,
t.taskState,
t.finishTime,
t.taskParentId,
t.variable taskVariable,
IFNULL(GROUP_CONCAT(DISTINCT nt.displayName),'结束') curTaskName,
CASE WHEN EXISTS (
SELECT 1 FROM wf_process_task rt
WHERE rt.processInstanceId = ins.id AND rt.taskParentId = t.id
) THEN 1 ELSE 0 END AS canRevoke,
CASE WHEN info.backOption LIKE 'YEARLY_BATCH:%' THEN 1 ELSE 0 END AS yearlyBatch,
CASE WHEN info.backOption LIKE 'YEARLY_BATCH:%' THEN (
SELECT COUNT(1) FROM site_cug_apply batch_info WHERE batch_info.backOption = info.backOption AND batch_info.createdBy = info.createdBy
) ELSE 1 END AS batchAuditCount
FROM
wf_process_task t
LEFT JOIN wf_process_instance ins ON ins.id = t.processInstanceId
LEFT JOIN site_cug_apply info ON info.id = ins.businessNo
LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = t.id
LEFT JOIN site_cug_info si ON info.siteId = si.id
LEFT JOIN wf_process_task nt ON nt.processInstanceId = ins.id AND nt.taskState = 10
LEFT JOIN wf_process_task rt ON rt.processInstanceId = ins.id AND rt.taskParentId = t.id
$condition
""");
Cnd cnd = Cnd.NEW();
if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
SqlExpressionGroup seg = new SqlExpressionGroup();
seg.or(SiteCugApply::getApplyUserName, "like", "%" + pageForm.getSearchKeyword() + "%");
seg.or(SiteCugApply::getApplyLoginName, "like", "%" + pageForm.getSearchKeyword() + "%");
cnd.and(seg);
}
cnd.andEX("si.typeId", "=", siteType);
cnd.andEX("info.siteId", "=", siteId);
cnd.and("t.taskName", "=", auditTaskName);
if (!StpUtil.hasRole(RoleConstant.SYSADMIN.name())) {
cnd.and("ta.actorId", "in", List.of(SecurityUtil.getUserId()));
}
if (Boolean.TRUE.equals(approval)) {
cnd.and("t.taskState", "in", List.of(ProcessTaskStateEnum.FINISHED.getCode(), ProcessTaskStateEnum.INTERRUPT.getCode()));
} else {
cnd.and("t.taskState", "=", ProcessTaskStateEnum.DOING.getCode());
}
if (StrUtil.isNotBlank(pageForm.getPageOrderName()) && StrUtil.isNotBlank(pageForm.getPageOrderBy())) {
cnd.orderBy(resolveAuditOrderColumn(pageForm.getPageOrderName()), PageUtil.getOrder(pageForm.getPageOrderBy()));
} else {
cnd.desc("info.createdAt");
}
cnd.groupBy("t.id");
sql.setCondition(cnd);
return listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
}
@Override
public Result executeAuditTask(String data) {
Dict args = Json.fromJson(Dict.class, data);
Long processTaskId = args.getLong(FlowConst.PROCESS_TASK_ID_KEY);
ProcessTask currentTask = processTaskService.getById(processTaskId);
if (currentTask == null) {
return Result.error("审核任务不存在");
}
SiteCugApply apply = fetch(args.getStr("applyId"));
if (apply == null) {
return Result.error("预约记录不存在");
}
List<ProcessTask> taskList = getBatchDoingTaskList(apply, currentTask);
for (ProcessTask task : taskList) {
Dict cloneArgs = args.clone();
cloneArgs.put(FlowConst.PROCESS_TASK_ID_KEY, task.getId());
flowCommonService.executeTask(cloneArgs);
}
if (taskList.size() > 1) {
return Result.success("已完成该全年预约批次的审核");
}
return Result.success("提交成功");
}
@Override
public Result revokeAuditTask(Long taskId, String applyId) {
ProcessTask currentTask = processTaskService.getById(taskId);
if (currentTask == null) {
return Result.error("撤回任务不存在");
}
SiteCugApply apply = fetch(applyId);
if (apply == null) {
return Result.error("预约记录不存在");
}
List<ProcessTask> taskList = getBatchDoneTaskList(apply, currentTask);
for (ProcessTask task : taskList) {
flowCommonService.revokeTask(task.getId());
}
if (taskList.size() > 1) {
return Result.success("已撤回该全年预约批次的审核任务");
}
return Result.success("撤回成功");
}
// 场馆预约查询导出字段统一在 service 中定义,并与当前列表展示字段保持一致
private List<ExcelExportEntity> buildRecordExportEntities() {
List<ExcelExportEntity> exportEntities = new ArrayList<>();
@@ -218,6 +358,66 @@ public class SiteCugApplyServiceImpl extends BaseServiceImpl<SiteCugApply> imple
return exportEntities;
}
private String resolveAuditOrderColumn(String pageOrderName) {
if (StrUtil.isBlank(pageOrderName)) {
return "info.createdAt";
}
switch (pageOrderName) {
case "siteName":
return "si.name";
case "applyUserName":
return "info.applyUserName";
case "applyLoginName":
return "info.applyLoginName";
case "yearlyBatch":
return "yearlyBatch";
case "reserveTargetName":
return "reserveTargetName";
case "reserveStartTime":
return "info.reserveStartTime";
case "reserveEndTime":
return "info.reserveEndTime";
case "applyMobile":
return "info.applyMobile";
case "curTaskName":
return "curTaskName";
case "instanceState":
return "ins.state";
default:
return "info.createdAt";
}
}
private List<ProcessTask> getBatchDoingTaskList(SiteCugApply apply, ProcessTask currentTask) {
List<ProcessTask> taskList = new ArrayList<>();
if (isYearlyBatchApply(apply)) {
List<SiteCugApply> batchList = query(Cnd.where("backOption", "=", apply.getBackOption()).and("createdBy", "=", apply.getCreatedBy()));
List<String> bizIds = batchList.stream().map(SiteCugApply::getId).toList();
taskList = processTaskService.getDoingTaskByBizIdTaskName(bizIds, currentTask.getTaskName());
}
if (taskList.isEmpty()) {
taskList.add(currentTask);
}
return taskList;
}
private List<ProcessTask> getBatchDoneTaskList(SiteCugApply apply, ProcessTask currentTask) {
List<ProcessTask> taskList = new ArrayList<>();
if (isYearlyBatchApply(apply)) {
List<SiteCugApply> batchList = query(Cnd.where("backOption", "=", apply.getBackOption()).and("createdBy", "=", apply.getCreatedBy()));
List<String> bizIds = batchList.stream().map(SiteCugApply::getId).toList();
taskList = processTaskService.getDoneTaskByBizIdTaskName(bizIds, currentTask.getTaskName());
}
if (taskList.isEmpty()) {
taskList.add(currentTask);
}
return taskList;
}
private boolean isYearlyBatchApply(SiteCugApply apply) {
return StrUtil.isNotBlank(apply.getBackOption()) && apply.getBackOption().startsWith(YEARLY_BATCH_PREFIX);
}
// 预约时间范围内只要包含周六或周日,就视为不可预约
private boolean containsWeekend(String reserveStartTime, String reserveEndTime) {
Date start = DateUtil.beginOfDay(DateUtil.parseDateTime(reserveStartTime));
@@ -261,19 +461,69 @@ public class SiteCugApplyServiceImpl extends BaseServiceImpl<SiteCugApply> imple
DateTime reserveStart = DateUtil.parseDateTime(reserveStartTime);
DateTime reserveEnd = DateUtil.parseDateTime(reserveEndTime);
for (NutMap range : timeRanges) {
if (range == null || StrUtil.hasBlank(range.getString("date"), range.getString("startTime"), range.getString("endTime"))) {
if (range == null || StrUtil.hasBlank(range.getString("startTime"), range.getString("endTime"))) {
continue;
}
DateTime disabledStart = DateUtil.parseDateTime(range.getString("date") + " " + range.getString("startTime") + ":00");
DateTime disabledEnd = DateUtil.parseDateTime(range.getString("date") + " " + range.getString("endTime") + ":00");
boolean overlap = reserveStart.isBefore(disabledEnd) && reserveEnd.isAfter(disabledStart);
if (overlap) {
if (isDateDisabledTime(range) && intersectsDateDisabledTime(range, reserveStart, reserveEnd)) {
return true;
}
if (isWeekDisabledTime(range) && intersectsWeekDisabledTime(range, reserveStart, reserveEnd)) {
return true;
}
}
return false;
}
private boolean isDateDisabledTime(NutMap range) {
String type = range.getString("type");
return (StrUtil.isBlank(type) || StrUtil.equals(type, "date")) && StrUtil.isNotBlank(range.getString("date"));
}
private boolean isWeekDisabledTime(NutMap range) {
return StrUtil.equals(range.getString("type"), "week") && range.get("weekDay") != null;
}
private boolean intersectsDateDisabledTime(NutMap range, DateTime reserveStart, DateTime reserveEnd) {
DateTime disabledStart = DateUtil.parseDateTime(range.getString("date") + " " + range.getString("startTime") + ":00");
DateTime disabledEnd = DateUtil.parseDateTime(range.getString("date") + " " + range.getString("endTime") + ":00");
return reserveStart.isBefore(disabledEnd) && reserveEnd.isAfter(disabledStart);
}
private boolean intersectsWeekDisabledTime(NutMap range, DateTime reserveStart, DateTime reserveEnd) {
int disabledWeekDay = parseWeekDay(range.get("weekDay"));
if (disabledWeekDay == 0) {
return false;
}
Date current = DateUtil.beginOfDay(reserveStart);
Date end = DateUtil.beginOfDay(reserveEnd);
while (current.compareTo(end) <= 0) {
DateTime currentDay = DateUtil.date(current);
if (toWeekDay(currentDay) == disabledWeekDay) {
DateTime disabledStart = DateUtil.parseDateTime(DateUtil.formatDate(currentDay) + " " + range.getString("startTime") + ":00");
DateTime disabledEnd = DateUtil.parseDateTime(DateUtil.formatDate(currentDay) + " " + range.getString("endTime") + ":00");
if (reserveStart.isBefore(disabledEnd) && reserveEnd.isAfter(disabledStart)) {
return true;
}
}
current = DateUtil.offsetDay(current, 1);
}
return false;
}
private int parseWeekDay(Object value) {
try {
int weekDay = Integer.parseInt(String.valueOf(value));
return weekDay >= 1 && weekDay <= 7 ? weekDay : 0;
} catch (Exception e) {
return 0;
}
}
private int toWeekDay(DateTime currentDay) {
int weekDay = currentDay.dayOfWeek() - 1;
return weekDay == 0 ? 7 : weekDay;
}
// 按预约类型补齐最终落库数据,避免前端传错或被绕过
private Map<Boolean, String> fillReserveTypeData(SiteCugApply apply) {
if (StrUtil.isBlank(apply.getReserveType())) {
@@ -130,7 +130,7 @@ layout("/layouts/v4/baseLayout.html"){
<work-template v-if="$auth.hasRoleOr(['SYSADMIN','SCHOOL_UNION_ADMIN'])"></work-template>
<jcdt :list="websiteNews"></jcdt>
<jcdt :list="websiteNews.grassroots"></jcdt>
</div>
</div>
@@ -148,7 +148,9 @@ layout("/layouts/v4/baseLayout.html"){
el: '#v4-home-app',
data(){
return{
websiteNews: [],
websiteNews: {
grassroots: []
},
homeBannerList: "${config.AppHomeImg!}".split(",").map(item => item.trim()).filter(item => item)
}
},
@@ -167,7 +169,7 @@ layout("/layouts/v4/baseLayout.html"){
getWebSiteNews(){
this.$axios.post('/platform/home/getNews').then(res => {
if (res.code === 0) {
this.websiteNews = res.data
this.$set(this, 'websiteNews', res.data || {grassroots: []})
}
})
}
+168 -167
View File
@@ -1,33 +1,29 @@
const jcdt = {
template: /*language=HTML*/ `
<div class="jcdt-wrapper">
<div class="jcdt-container">
<el-carousel height="360px" :interval="10000" indicator-position="none">
<el-carousel-item v-for="item,index in list" :key="index">
<div class="jcdt-title">
<span>{{ item.label }}</span>
</div>
<div class="jcdt-content">
<div class="jcdt-list">
<div class="jcdt-item" v-for="o,i in item.value" :key="i" @click="onLink(o)">
<div class="date" v-if="o.time">
<i class="el-icon-alarm-clock"></i>
<span>{{o.time}}</span>
</div>
<div style="display: flex; align-items: center">
<div>
</div>
<div class="title">{{o.text}}</div>
</div>
</div>
</div>
</div>
</el-carousel-item>
</el-carousel>
</div>
</div>
`,
template: [
'<div class="jcdt-wrapper">',
' <div class="jcdt-container">',
' <div class="jcdt-title">',
' <span>通知公告</span>',
' </div>',
' <div class="jcdt-content">',
' <div class="jcdt-list">',
' <div class="jcdt-item" v-for="item in list" :key="item.url || item.title" @click="onLink(item)">',
' <div class="date" v-if="item.date">',
' <i class="el-icon-alarm-clock"></i>',
' <span>{{item.date}}</span>',
' </div>',
' <div style="display: flex; align-items: center">',
' <div class="jcdt-item-icon">',
' <i class="fa fa-bullhorn"></i>',
' </div>',
' <div class="title">{{item.title}}</div>',
' </div>',
' </div>',
' </div>',
' </div>',
' </div>',
'</div>'
].join(''),
data() {
return {}
},
@@ -39,144 +35,149 @@ const jcdt = {
},
methods: {
onLink(item) {
const url = this.isHttpOrHttps(item.href) ? item.href : "https://xgh.cug.edu.cn/" + item.href
window.open(url)
},
isHttpOrHttps(url) {
return /^(http:\/\/|https:\/\/)/i.test(url)
if (!item.url) {
return
}
window.open(item.url)
}
},
style: /*language=CSS*/ `
.jcdt-wrapper {
width: 100%;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
}
.jcdt-container {
width: 80%;
max-width: 80%;
margin: 0 auto 10px;
background-color: #FFFFFF;
border-radius: 12px;
padding: 20px;
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.06);
}
/deep/ .jcdt-title{
font-size: 30px;
color: #000000;
display: block;
font-weight: bold;
position: relative;
text-align: center;
padding: 20px 0;
}
/deep/ .jcdt-container .jcdt-title span::before {
content: '';
width: 24px;
height: 11px;
background: url(https://www.ncu.edu.cn/images/titl.svg) no-repeat center;
background-size: 24px 11px;
display: inline-block;
margin-right: 0;
vertical-align: middle;
}
/deep/ .jcdt-container .jcdt-title span::after {
content: '';
width: 24px;
height: 11px;
background: url(https://www.ncu.edu.cn/images/titr.svg) no-repeat center;
background-size: 24px 11px;
display: inline-block;
margin-left: 0;
vertical-align: middle;
}
/deep/ .jcdt-list {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
}
/deep/ .jcdt-item {
display: flex;
flex-direction: column;
padding: 16px;
border: none;
border-bottom: 3px solid transparent;
transition: all 0.3s ease;
cursor: pointer;
height: auto;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
border-bottom-color: var(--color-primary);
background: #ffffff;
}
/deep/ .jcdt-item:hover{
transform: translateY(-4px);
}
/deep/ .jcdt-item .date {
font-size: 20px;
font-weight: bold;
white-space: nowrap;
text-align: left;
margin-bottom: 8px;
transition: opacity 0.3s ease;
}
/deep/ .jcdt-item .title {
font-size: 16px;
font-weight: 600;
/*margin-bottom: 12px;*/
line-height: 1.4;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
/*height: 45px;*/
}
/deep/ .jcdt-item .image {
width: 100%;
height: 180px;
margin: 0 0 16px 0;
border-radius: 8px;
overflow: hidden;
background: #f5f5f5;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
transition: transform 0.3s ease;
}
/deep/ .jcdt-item .image img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.3s ease;
}
/deep/ .jcdt-item .summary {
font-size: 14px;
color: #666;
line-height: 1.6;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
text-align: justify;
margin-bottom: 12px;
}
/deep/ .jcdt-item i {
color: #007aff;
}
`
style: [
'.jcdt-wrapper {',
' width: 100%;',
' background-size: cover;',
' background-position: center;',
' background-repeat: no-repeat;',
'}',
'',
'.jcdt-container {',
' width: 80%;',
' max-width: 80%;',
' margin: 0 auto 10px;',
' background-color: #FFFFFF;',
' border-radius: 12px;',
' padding: 20px;',
' box-shadow: 0 6px 20px rgba(0, 0, 0, 0.06);',
'}',
'',
'/deep/ .jcdt-title{',
' font-size: 30px;',
' color: #000000;',
' display: block;',
' font-weight: bold;',
' position: relative;',
' text-align: center;',
' padding: 20px 0;',
'}',
'',
'/deep/ .jcdt-container .jcdt-title span::before {',
" content: '';",
' width: 24px;',
' height: 11px;',
' background: url(https://www.ncu.edu.cn/images/titl.svg) no-repeat center;',
' background-size: 24px 11px;',
' display: inline-block;',
' margin-right: 0;',
' vertical-align: middle;',
'}',
'',
'/deep/ .jcdt-container .jcdt-title span::after {',
" content: '';",
' width: 24px;',
' height: 11px;',
' background: url(https://www.ncu.edu.cn/images/titr.svg) no-repeat center;',
' background-size: 24px 11px;',
' display: inline-block;',
' margin-left: 0;',
' vertical-align: middle;',
'}',
'',
'/deep/ .jcdt-list {',
' display: grid;',
' grid-template-columns: repeat(4, 1fr);',
' gap: 20px;',
'}',
'',
'/deep/ .jcdt-item {',
' display: flex;',
' flex-direction: column;',
' padding: 16px;',
' border: none;',
' border-bottom: 3px solid transparent;',
' transition: all 0.3s ease;',
' cursor: pointer;',
' height: auto;',
' border-radius: 12px;',
' box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);',
' border-bottom-color: var(--color-primary);',
' background: #ffffff;',
'}',
'',
'/deep/ .jcdt-item:hover{',
' transform: translateY(-4px);',
'}',
'',
'/deep/ .jcdt-item .date {',
' font-size: 20px;',
' font-weight: bold;',
' white-space: nowrap;',
' text-align: left;',
' margin-bottom: 8px;',
' transition: opacity 0.3s ease;',
'}',
'',
'/deep/ .jcdt-item .title {',
' font-size: 16px;',
' font-weight: 600;',
' /*margin-bottom: 12px;*/',
' line-height: 1.4;',
' display: -webkit-box;',
' -webkit-line-clamp: 2;',
' -webkit-box-orient: vertical;',
' overflow: hidden;',
' /*height: 45px;*/',
'}',
'',
'/deep/ .jcdt-item-icon {',
' color: #007aff;',
' font-size: 22px;',
' margin-right: 8px;',
'}',
'',
'/deep/ .jcdt-item .image {',
' width: 100%;',
' height: 180px;',
' margin: 0 0 16px 0;',
' border-radius: 8px;',
' overflow: hidden;',
' background: #f5f5f5;',
' display: flex;',
' align-items: center;',
' justify-content: center;',
' box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);',
' transition: transform 0.3s ease;',
'}',
'',
'/deep/ .jcdt-item .image img {',
' width: 100%;',
' height: 100%;',
' object-fit: cover;',
' transition: transform 0.3s ease;',
'}',
'',
'/deep/ .jcdt-item .summary {',
' font-size: 14px;',
' color: #666;',
' line-height: 1.6;',
' display: -webkit-box;',
' -webkit-line-clamp: 3;',
' -webkit-box-orient: vertical;',
' overflow: hidden;',
' text-align: justify;',
' margin-bottom: 12px;',
'}',
'',
'/deep/ .jcdt-item i {',
' color: #007aff;',
'}'
].join('')
};
@@ -144,7 +144,7 @@ const apply = {
</div>
</el-col>
<el-col :span="12">
<el-col :span="12">
<el-form-item label="预约开始时间" prop="reserveStartTime">
<el-date-picker
:key="'start-' + pickerRefreshKey"
@@ -172,6 +172,24 @@ const apply = {
</el-date-picker>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="是否需要咖啡" prop="coffeeNeeded">
<el-radio-group v-model="formData.coffeeNeeded">
<el-radio :label="0">否</el-radio>
<el-radio :label="1">是</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="预约人数" prop="joinCount">
<el-input-number
v-model="formData.joinCount"
:min="1"
controls-position="right"
style="width: 160px">
</el-input-number>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="批量预约">
<div style="display: flex; align-items: flex-start; column-gap: 12px; flex-wrap: wrap; line-height: 1.7;">
@@ -239,6 +257,7 @@ const apply = {
reserveStartTime: '',
reserveEndTime: '',
yearlyReserveEndDate: '',
coffeeNeeded: 0,
joinCount: 1,
applyCause: '',
},
@@ -251,7 +270,26 @@ const apply = {
applyMobile: [{required: true, message: '必填', trigger: ['blur', 'change']}],
reserveStartTime: [{required: true, message: '必填', trigger: ['blur', 'change']}],
reserveEndTime: [{required: true, message: '必填', trigger: ['blur', 'change']}],
joinCount: [{required: true, message: '必填', trigger: ['blur', 'change']}],
coffeeNeeded: [{
validator: (rule, value, callback) => {
if (value !== 0 && value !== 1) {
callback(new Error('请选择是否需要咖啡'))
return
}
callback()
},
trigger: ['blur', 'change'],
}],
joinCount: [{
validator: (rule, value, callback) => {
if (!Number.isInteger(Number(value)) || Number(value) <= 0) {
callback(new Error('预约人数必须为大于0的整数'))
return
}
callback()
},
trigger: ['blur', 'change'],
}],
applyCause: [{required: true, message: '必填', trigger: ['blur', 'change']}],
clubId: [{
validator: (rule, value, callback) => {
@@ -451,6 +489,7 @@ const apply = {
reserveStartTime: '',
reserveEndTime: '',
yearlyReserveEndDate: '',
coffeeNeeded: 0,
joinCount: 1,
applyCause: '',
}
@@ -679,10 +718,31 @@ const apply = {
}
return timeStr.length === 5 ? timeStr + ':00' : timeStr
},
getDayWeekDay(dayStr) {
const target = this.$moment(dayStr, 'YYYY-MM-DD')
if (!target.isValid()) {
return 0
}
const weekDay = target.day()
return weekDay === 0 ? 7 : weekDay
},
isDateDisabledTime(item, dayStr) {
if (!item || !item.date) {
return false
}
const type = item.type || 'date'
return type === 'date' && this.$moment(item.date).format('YYYY-MM-DD') === dayStr
},
isWeekDisabledTime(item, dayStr) {
if (!item || item.type !== 'week') {
return false
}
return Number(item.weekDay) === this.getDayWeekDay(dayStr)
},
getDisabledRangesByDay(dayStr) {
const list = Array.isArray(this.timeLimitConfig.notApplyTimeList) ? this.timeLimitConfig.notApplyTimeList : []
return list
.filter(item => item && item.date && this.$moment(item.date).format('YYYY-MM-DD') === dayStr)
.filter(item => item && item.startTime && item.endTime && (this.isDateDisabledTime(item, dayStr) || this.isWeekDisabledTime(item, dayStr)))
.sort((a, b) => this.normalizeTime(a.startTime).localeCompare(this.normalizeTime(b.startTime)))
},
buildSelectableRange(dateTimeValue) {
@@ -742,15 +802,22 @@ const apply = {
return false
},
overlapsDisabledRange(start, end) {
const list = Array.isArray(this.timeLimitConfig.notApplyTimeList) ? this.timeLimitConfig.notApplyTimeList : []
return list.some(item => {
if (!item || !item.date || !item.startTime || !item.endTime) {
return false
const current = start.clone().startOf('day')
const endDay = end.clone().startOf('day')
while (current.isSameOrBefore(endDay)) {
const dayStr = current.format('YYYY-MM-DD')
const disabledRanges = this.getDisabledRangesByDay(dayStr)
const matched = disabledRanges.some(item => {
const rangeStart = this.$moment(dayStr + ' ' + this.normalizeTime(item.startTime))
const rangeEnd = this.$moment(dayStr + ' ' + this.normalizeTime(item.endTime))
return start.isBefore(rangeEnd) && end.isAfter(rangeStart)
})
if (matched) {
return true
}
const rangeStart = this.$moment(this.$moment(item.date).format('YYYY-MM-DD') + ' ' + this.normalizeTime(item.startTime))
const rangeEnd = this.$moment(this.$moment(item.date).format('YYYY-MM-DD') + ' ' + this.normalizeTime(item.endTime))
return start.isBefore(rangeEnd) && end.isAfter(rangeStart)
})
current.add(1, 'day')
}
return false
},
validateTimeLimit(showMessage = true, startTime = this.formData.reserveStartTime, endTime = this.formData.reserveEndTime) {
if (!startTime || !endTime) {
@@ -0,0 +1,368 @@
<!--#
layout("/layouts/platform.html"){
#-->
<style>
.audit-batch-toggle {
padding: 0;
color: #409EFF;
font-size: 16px;
}
.audit-batch-empty {
display: inline-block;
width: 16px;
height: 16px;
}
.audit-batch-child-row {
background: #fafcff;
}
.audit-batch-child-label {
display: inline-flex;
align-items: center;
gap: 8px;
color: #7a8a9a;
}
.audit-batch-child-label::before {
content: "";
width: 16px;
height: 1px;
background: #c8d3df;
}
</style>
<div id="app">
<guava ref="guava">
<el-card shadow="never">
<search @search="doSearch">
<search-item label="姓名/工号">
<el-input
v-model="pageForm.searchKeyword"
placeholder="请输入姓名或工号"
clearable
@keyup.enter.native="doSearch">
</el-input>
</search-item>
<search-item label="活动场地">
<el-select
v-model="pageForm.siteId"
@change="doSearch"
style="width: 100%"
placeholder="请选择活动场地"
filterable
clearable>
<el-option
v-for="item in siteOptions"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
</search-item>
<search-item label="场地类型">
<el-select
v-model="pageForm.siteType"
@change="doSearch"
style="width: 100%"
placeholder="请选择场地类型"
filterable
clearable>
<el-option
v-for="item in typeOptions"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
</search-item>
</search>
</el-card>
<el-card shadow="never">
<table-tool label="分工会审核列表">
<el-radio-group v-model="pageForm.approval" size="small" @change="doSearch">
<el-radio-button :label="true">已审核</el-radio-button>
<el-radio-button :label="false">未审核</el-radio-button>
</el-radio-group>
</table-tool>
<el-table
:data="tableData"
:row-class-name="tableRowClassName"
@sort-change="pageOrder"
style="width: 100%">
<el-table-column label="" width="54" align="center" header-align="center">
<template v-slot="{ row }">
<el-button
v-if="row._hasFoldChildren"
class="audit-batch-toggle"
type="text"
@click="toggleBatchGroup(row)">
<i :class="row._expanded ? 'el-icon-caret-bottom' : 'el-icon-caret-right'"></i>
</el-button>
<span v-else class="audit-batch-empty"></span>
</template>
</el-table-column>
<el-table-column type="index" width="50" label="序号" :index="indexMethod"></el-table-column>
<el-table-column
v-for="column in tableColumns"
:key="column.prop"
:label="column.label"
:prop="column.prop"
:width="column.width"
:sortable="column.sortable"
align="center"
header-align="center"
show-overflow-tooltip>
<template v-slot="{ row }" v-if="column.prop === 'siteName'">
<span v-if="row._isBatchChild" class="audit-batch-child-label">{{ row.siteName }}</span>
<span v-else>{{ row.siteName }}</span>
</template>
<template v-slot="{ row }" v-else-if="column.prop === 'reserveTargetName'">
<span>{{ row.reserveType === 'club' ? (row.clubName || '-') : (row.applyUnionName || '-') }}</span>
</template>
<template v-slot="{ row }" v-else-if="column.prop === 'yearlyBatch'">
<span>{{ normalizeBatchFlag(row) ? '是' : '否' }}</span>
</template>
<template v-slot="{ row }" v-else-if="column.prop === 'instanceState'">
<enum-tag :value="row.instanceState" name="ProcessInstanceStateEnum" label_key="message" size="small"></enum-tag>
</template>
</el-table-column>
<el-table-column label="操作" fixed="right" width="220">
<template v-slot="{ row }">
<el-button @click="onView(row)" size="mini" type="primary">查看</el-button>
<el-button v-if="!row._isBatchChild && row.taskState === 10" @click="onAudit(row)" size="mini" type="primary">审核</el-button>
<el-button v-if="!row._isBatchChild && canRevoke(row)" :loading="revokeLoading" :disabled="revokeLoading" @click="onRevoke(row)" size="mini" type="danger">撤回</el-button>
</template>
</el-table-column>
</el-table>
<!--#include("/layouts/pagination.html"){}#-->
</el-card>
<template #edit>
<info ref="infoRef">
<div v-if="showApprovalForm">
<div class="process-title">
{{ formData.taskName }}
</div>
<el-form :model="formData" ref="formRef" :rules="formRules" label-width="0" class="flow-task-form">
<el-form-item
label="审批意见"
prop="tf_opinion"
:rules="[{ required: true, message: '必填', trigger: ['change', 'blur'] }]">
<user-opinion-textarea v-model="formData.tf_opinion"></user-opinion-textarea>
</el-form-item>
</el-form>
<el-row type="flex" justify="end">
<el-button @click="$refs.guava.index()" size="small">取消</el-button>
<el-button @click="handleTaskAction(6)" :loading="auditLoading" :disabled="auditLoading" size="small" type="info">退回到发起人</el-button>
<el-button @click="handleTaskAction(2)" :loading="auditLoading" :disabled="auditLoading" size="small" type="danger">拒绝申请</el-button>
<el-button @click="handleTaskAction(1)" :loading="auditLoading" :disabled="auditLoading" size="small" type="primary">同意申请</el-button>
</el-row>
</div>
</info>
</template>
</guava>
</div>
<script nonce="${cspNonce!}">
<!--#include('../common/applyInfo.js'){}#-->
new Vue({
el: '#app',
store,
mixins: [initTableMixins],
components: {
info: siteCugApplyInfo,
},
data() {
return {
pageForm: {
approval: false,
},
rawTableData: [],
expandedBatchKeys: {},
tableColumns: [
{ prop: 'siteName', label: '场地名称', sortable: 'custom' },
{ prop: 'applyUserName', label: '预约人', sortable: 'custom' },
{ prop: 'applyLoginName', label: '工号', sortable: 'custom' },
{ prop: 'yearlyBatch', label: '是否批量预约', sortable: 'custom', width: 130 },
{ prop: 'reserveTargetName', label: '预约单位', sortable: 'custom' },
{ prop: 'reserveStartTime', label: '开始时间', sortable: 'custom' },
{ prop: 'reserveEndTime', label: '结束时间', sortable: 'custom' },
{ prop: 'applyMobile', label: '联系方式', sortable: 'custom' },
{ prop: 'curTaskName', label: '当前节点', sortable: 'custom' },
{ prop: 'instanceState', label: '流程状态', sortable: 'custom', width: 120 },
],
typeOptions: [],
siteOptions: [],
formData: {},
formRules: {},
showApprovalForm: false,
auditLoading: false,
revokeLoading: false,
}
},
methods: {
normalizeBatchFlag(row) {
return row && (row.yearlyBatch === 1 || row.yearlyBatch === '1' || row.yearlyBatch === true)
},
getBatchGroupKey(row) {
if (!this.normalizeBatchFlag(row)) {
return 'single_' + row.id
}
const batchNo = row.backOption || row.id
return 'batch_' + batchNo + '_' + (row.applyUserName || '') + '_' + (row.siteName || '')
},
buildTableData(rows) {
const groups = new Map()
;(rows || []).forEach(row => {
const key = this.getBatchGroupKey(row)
if (!groups.has(key)) {
groups.set(key, [])
}
groups.get(key).push({ ...row })
})
const displayRows = []
groups.forEach((groupRows, key) => {
const sortedRows = groupRows.slice().sort((a, b) => {
return (b.reserveStartTime || '').localeCompare(a.reserveStartTime || '')
})
const parent = { ...sortedRows[0] }
const children = sortedRows.slice(1).map(item => ({
...item,
_isBatchChild: true,
_groupKey: key,
_hasFoldChildren: false,
}))
parent._groupKey = key
parent._isBatchChild = false
parent._hasFoldChildren = children.length > 0
parent._expanded = !!this.expandedBatchKeys[key]
parent._batchCount = sortedRows.length
parent._foldedRecords = children
displayRows.push(parent)
if (parent._expanded) {
displayRows.push(...children)
}
})
return displayRows
},
tableRowClassName({ row }) {
return row && row._isBatchChild ? 'audit-batch-child-row' : ''
},
toggleBatchGroup(row) {
if (!row || !row._groupKey || !row._hasFoldChildren) {
return
}
this.$set(this.expandedBatchKeys, row._groupKey, !this.expandedBatchKeys[row._groupKey])
this.tableData = this.buildTableData(this.rawTableData)
},
canRevoke(row) {
return Number(row.instanceState) === 20
},
onView(row) {
this.$refs.guava.edit(() => {
this.showApprovalForm = false
this.$refs.infoRef.onOpen(row)
})
},
onAudit(row) {
this.$refs.guava.edit(() => {
this.showApprovalForm = true
this.formData = {
processTaskId: row.taskId,
applyId: row.id,
taskName: row.curTaskName,
tf_opinion: '',
yearlyBatch: row.yearlyBatch,
batchAuditCount: row.batchAuditCount || row._batchCount,
}
this.$refs.infoRef.onOpen(row)
})
},
handleTaskAction(val) {
if (this.auditLoading) {
return
}
const message = this.formData.yearlyBatch
? ('该记录属于“预约本年”批量预约,提交后将一并审核同批次的 ' + (this.formData.batchAuditCount || 0) + ' 条预约记录,是否继续?')
: '您确定要提交吗?'
this.$confirm(message, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
this.auditLoading = true
this.$axios.post(loc() + '/executeTask', {
data: JSON.stringify({
...this.formData,
submitType: val,
}),
}).then((res) => {
if (res.code === 0) {
this.$refs.guava.index()
this.$message.success(res.msg)
this.doSearch()
}
}).finally(() => {
this.auditLoading = false
})
}).catch(() => {})
},
onRevoke(row) {
if (!this.canRevoke(row) || this.revokeLoading) {
return
}
const message = this.normalizeBatchFlag(row)
? ('该记录属于“预约本年”批量预约,撤回后将一并撤回同批次的 ' + (row.batchAuditCount || row._batchCount || 0) + ' 条审核任务,是否继续?')
: '您确定要撤回吗?'
this.$confirm(message, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'info',
}).then(() => {
this.revokeLoading = true
this.$axios.post(loc() + '/revokeTask', { taskId: row.taskId, applyId: row.id }).then((res) => {
if (res.code === 0) {
this.$message.success(res.msg)
this.doSearch()
}
}).finally(() => {
this.revokeLoading = false
})
}).catch(() => {})
},
querySiteType() {
this.$axios.post('/platform/siteCug/function/type/queryFunctionType').then((res) => {
this.typeOptions = res.data
})
},
querySites() {
this.$axios.post('/platform/siteCug/manage/querySites').then((res) => {
this.siteOptions = res.data
})
},
pageData() {
this.$axios.post(loc() + '/pageData', this.pageForm).then((res) => {
if (res.code === 0) {
this.rawTableData = Array.isArray(res.data.list) ? res.data.list : []
this.tableData = this.buildTableData(this.rawTableData)
this.pageForm.totalCount = res.data.totalCount
}
})
},
},
created() {
this.querySiteType()
this.querySites()
this.pageData()
},
})
</script>
<!--#
}
#-->
@@ -14,8 +14,9 @@ const siteCugApplyInfo = {
<el-descriptions-item label="预约主体">{{ reserveTargetName }}</el-descriptions-item>
<el-descriptions-item label="开始时间">{{ viewData.reserveStartTime }}</el-descriptions-item>
<el-descriptions-item label="结束时间">{{ viewData.reserveEndTime }}</el-descriptions-item>
<el-descriptions-item label="是否需要咖啡">{{ viewData.coffeeNeeded === 1 ? '是' : '否' }}</el-descriptions-item>
<el-descriptions-item label="预约人数">{{ viewData.joinCount || '-' }}</el-descriptions-item>
<el-descriptions-item label="联系电话">{{ viewData.applyMobile }}</el-descriptions-item>
<el-descriptions-item label="预约人数">{{ viewData.joinCount }}</el-descriptions-item>
<el-descriptions-item label="预约事由" :span="2">{{ viewData.applyCause }}</el-descriptions-item>
</el-descriptions>
<template v-for="task in doneTasks">
@@ -0,0 +1,368 @@
<!--#
layout("/layouts/platform.html"){
#-->
<style>
.audit-batch-toggle {
padding: 0;
color: #409EFF;
font-size: 16px;
}
.audit-batch-empty {
display: inline-block;
width: 16px;
height: 16px;
}
.audit-batch-child-row {
background: #fafcff;
}
.audit-batch-child-label {
display: inline-flex;
align-items: center;
gap: 8px;
color: #7a8a9a;
}
.audit-batch-child-label::before {
content: "";
width: 16px;
height: 1px;
background: #c8d3df;
}
</style>
<div id="app">
<guava ref="guava">
<el-card shadow="never">
<search @search="doSearch">
<search-item label="姓名/工号">
<el-input
v-model="pageForm.searchKeyword"
placeholder="请输入姓名或工号"
clearable
@keyup.enter.native="doSearch">
</el-input>
</search-item>
<search-item label="活动场地">
<el-select
v-model="pageForm.siteId"
@change="doSearch"
style="width: 100%"
placeholder="请选择活动场地"
filterable
clearable>
<el-option
v-for="item in siteOptions"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
</search-item>
<search-item label="场地类型">
<el-select
v-model="pageForm.siteType"
@change="doSearch"
style="width: 100%"
placeholder="请选择场地类型"
filterable
clearable>
<el-option
v-for="item in typeOptions"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
</search-item>
</search>
</el-card>
<el-card shadow="never">
<table-tool label="校工会副主席审核列表">
<el-radio-group v-model="pageForm.approval" size="small" @change="doSearch">
<el-radio-button :label="true">已审核</el-radio-button>
<el-radio-button :label="false">未审核</el-radio-button>
</el-radio-group>
</table-tool>
<el-table
:data="tableData"
:row-class-name="tableRowClassName"
@sort-change="pageOrder"
style="width: 100%">
<el-table-column label="" width="54" align="center" header-align="center">
<template v-slot="{ row }">
<el-button
v-if="row._hasFoldChildren"
class="audit-batch-toggle"
type="text"
@click="toggleBatchGroup(row)">
<i :class="row._expanded ? 'el-icon-caret-bottom' : 'el-icon-caret-right'"></i>
</el-button>
<span v-else class="audit-batch-empty"></span>
</template>
</el-table-column>
<el-table-column type="index" width="50" label="序号" :index="indexMethod"></el-table-column>
<el-table-column
v-for="column in tableColumns"
:key="column.prop"
:label="column.label"
:prop="column.prop"
:width="column.width"
:sortable="column.sortable"
align="center"
header-align="center"
show-overflow-tooltip>
<template v-slot="{ row }" v-if="column.prop === 'siteName'">
<span v-if="row._isBatchChild" class="audit-batch-child-label">{{ row.siteName }}</span>
<span v-else>{{ row.siteName }}</span>
</template>
<template v-slot="{ row }" v-else-if="column.prop === 'reserveTargetName'">
<span>{{ row.reserveType === 'club' ? (row.clubName || '-') : (row.applyUnionName || '-') }}</span>
</template>
<template v-slot="{ row }" v-else-if="column.prop === 'yearlyBatch'">
<span>{{ normalizeBatchFlag(row) ? '是' : '否' }}</span>
</template>
<template v-slot="{ row }" v-else-if="column.prop === 'instanceState'">
<enum-tag :value="row.instanceState" name="ProcessInstanceStateEnum" label_key="message" size="small"></enum-tag>
</template>
</el-table-column>
<el-table-column label="操作" fixed="right" width="220">
<template v-slot="{ row }">
<el-button @click="onView(row)" size="mini" type="primary">查看</el-button>
<el-button v-if="!row._isBatchChild && row.taskState === 10" @click="onAudit(row)" size="mini" type="primary">审核</el-button>
<el-button v-if="!row._isBatchChild && canRevoke(row)" :loading="revokeLoading" :disabled="revokeLoading" @click="onRevoke(row)" size="mini" type="danger">撤回</el-button>
</template>
</el-table-column>
</el-table>
<!--#include("/layouts/pagination.html"){}#-->
</el-card>
<template #edit>
<info ref="infoRef">
<div v-if="showApprovalForm">
<div class="process-title">
{{ formData.taskName }}
</div>
<el-form :model="formData" ref="formRef" :rules="formRules" label-width="0" class="flow-task-form">
<el-form-item
label="审批意见"
prop="tf_opinion"
:rules="[{ required: true, message: '必填', trigger: ['change', 'blur'] }]">
<user-opinion-textarea v-model="formData.tf_opinion"></user-opinion-textarea>
</el-form-item>
</el-form>
<el-row type="flex" justify="end">
<el-button @click="$refs.guava.index()" size="small">取消</el-button>
<el-button @click="handleTaskAction(6)" :loading="auditLoading" :disabled="auditLoading" size="small" type="info">退回到发起人</el-button>
<el-button @click="handleTaskAction(2)" :loading="auditLoading" :disabled="auditLoading" size="small" type="danger">拒绝申请</el-button>
<el-button @click="handleTaskAction(1)" :loading="auditLoading" :disabled="auditLoading" size="small" type="primary">同意申请</el-button>
</el-row>
</div>
</info>
</template>
</guava>
</div>
<script nonce="${cspNonce!}">
<!--#include('../common/applyInfo.js'){}#-->
new Vue({
el: '#app',
store,
mixins: [initTableMixins],
components: {
info: siteCugApplyInfo,
},
data() {
return {
pageForm: {
approval: false,
},
rawTableData: [],
expandedBatchKeys: {},
tableColumns: [
{ prop: 'siteName', label: '场地名称', sortable: 'custom' },
{ prop: 'applyUserName', label: '预约人', sortable: 'custom' },
{ prop: 'applyLoginName', label: '工号', sortable: 'custom' },
{ prop: 'yearlyBatch', label: '是否批量预约', sortable: 'custom', width: 130 },
{ prop: 'reserveTargetName', label: '预约单位', sortable: 'custom' },
{ prop: 'reserveStartTime', label: '开始时间', sortable: 'custom' },
{ prop: 'reserveEndTime', label: '结束时间', sortable: 'custom' },
{ prop: 'applyMobile', label: '联系方式', sortable: 'custom' },
{ prop: 'curTaskName', label: '当前节点', sortable: 'custom' },
{ prop: 'instanceState', label: '流程状态', sortable: 'custom', width: 120 },
],
typeOptions: [],
siteOptions: [],
formData: {},
formRules: {},
showApprovalForm: false,
auditLoading: false,
revokeLoading: false,
}
},
methods: {
normalizeBatchFlag(row) {
return row && (row.yearlyBatch === 1 || row.yearlyBatch === '1' || row.yearlyBatch === true)
},
getBatchGroupKey(row) {
if (!this.normalizeBatchFlag(row)) {
return 'single_' + row.id
}
const batchNo = row.backOption || row.id
return 'batch_' + batchNo + '_' + (row.applyUserName || '') + '_' + (row.siteName || '')
},
buildTableData(rows) {
const groups = new Map()
;(rows || []).forEach(row => {
const key = this.getBatchGroupKey(row)
if (!groups.has(key)) {
groups.set(key, [])
}
groups.get(key).push({ ...row })
})
const displayRows = []
groups.forEach((groupRows, key) => {
const sortedRows = groupRows.slice().sort((a, b) => {
return (b.reserveStartTime || '').localeCompare(a.reserveStartTime || '')
})
const parent = { ...sortedRows[0] }
const children = sortedRows.slice(1).map(item => ({
...item,
_isBatchChild: true,
_groupKey: key,
_hasFoldChildren: false,
}))
parent._groupKey = key
parent._isBatchChild = false
parent._hasFoldChildren = children.length > 0
parent._expanded = !!this.expandedBatchKeys[key]
parent._batchCount = sortedRows.length
parent._foldedRecords = children
displayRows.push(parent)
if (parent._expanded) {
displayRows.push(...children)
}
})
return displayRows
},
tableRowClassName({ row }) {
return row && row._isBatchChild ? 'audit-batch-child-row' : ''
},
toggleBatchGroup(row) {
if (!row || !row._groupKey || !row._hasFoldChildren) {
return
}
this.$set(this.expandedBatchKeys, row._groupKey, !this.expandedBatchKeys[row._groupKey])
this.tableData = this.buildTableData(this.rawTableData)
},
canRevoke(row) {
return Number(row.instanceState) === 20
},
onView(row) {
this.$refs.guava.edit(() => {
this.showApprovalForm = false
this.$refs.infoRef.onOpen(row)
})
},
onAudit(row) {
this.$refs.guava.edit(() => {
this.showApprovalForm = true
this.formData = {
processTaskId: row.taskId,
applyId: row.id,
taskName: row.curTaskName,
tf_opinion: '',
yearlyBatch: row.yearlyBatch,
batchAuditCount: row.batchAuditCount || row._batchCount,
}
this.$refs.infoRef.onOpen(row)
})
},
handleTaskAction(val) {
if (this.auditLoading) {
return
}
const message = this.formData.yearlyBatch
? ('该记录属于“预约本年”批量预约,提交后将一并审核同批次的 ' + (this.formData.batchAuditCount || 0) + ' 条预约记录,是否继续?')
: '您确定要提交吗?'
this.$confirm(message, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
this.auditLoading = true
this.$axios.post(loc() + '/executeTask', {
data: JSON.stringify({
...this.formData,
submitType: val,
}),
}).then((res) => {
if (res.code === 0) {
this.$refs.guava.index()
this.$message.success(res.msg)
this.doSearch()
}
}).finally(() => {
this.auditLoading = false
})
}).catch(() => {})
},
onRevoke(row) {
if (!this.canRevoke(row) || this.revokeLoading) {
return
}
const message = this.normalizeBatchFlag(row)
? ('该记录属于“预约本年”批量预约,撤回后将一并撤回同批次的 ' + (row.batchAuditCount || row._batchCount || 0) + ' 条审核任务,是否继续?')
: '您确定要撤回吗?'
this.$confirm(message, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'info',
}).then(() => {
this.revokeLoading = true
this.$axios.post(loc() + '/revokeTask', { taskId: row.taskId, applyId: row.id }).then((res) => {
if (res.code === 0) {
this.$message.success(res.msg)
this.doSearch()
}
}).finally(() => {
this.revokeLoading = false
})
}).catch(() => {})
},
querySiteType() {
this.$axios.post('/platform/siteCug/function/type/queryFunctionType').then((res) => {
this.typeOptions = res.data
})
},
querySites() {
this.$axios.post('/platform/siteCug/manage/querySites').then((res) => {
this.siteOptions = res.data
})
},
pageData() {
this.$axios.post(loc() + '/pageData', this.pageForm).then((res) => {
if (res.code === 0) {
this.rawTableData = Array.isArray(res.data.list) ? res.data.list : []
this.tableData = this.buildTableData(this.rawTableData)
this.pageForm.totalCount = res.data.totalCount
}
})
},
},
created() {
this.querySiteType()
this.querySites()
this.pageData()
},
})
</script>
<!--#
}
#-->
@@ -120,7 +120,7 @@
<el-col :span="12">
<el-form-item label="禁用时间">
<el-button type="primary" @click="openSetUpTime" size="small">点击设置禁用时间</el-button>
<span style="color: #c64120">您已设置{{ formData.notApplyTimeList ? formData.notApplyTimeList.length : 0 }}个禁用时间</span>
<span style="color: #c64120">您已设置{{ disabledTimeCount }}个禁用时间</span>
</el-form-item>
</el-col>
</el-row>
@@ -224,84 +224,167 @@
<el-button @click="onSubmit" type="primary">正式提交</el-button>
</el-row>
<el-dialog append-to-body :close-on-click-modal="false" :visible.sync="setUpTimeDialog" title="设置时间">
<div class="left-span-label">选择日期</div>
<el-date-picker
@change="setUpDateChange"
placeholder="请选择一个或多个日期"
style="width: 100%"
type="dates"
v-model="formData.setUpDate"
value-format="yyyy-MM-dd">
</el-date-picker>
<div class="left-span-label mt20">设置禁用时间</div>
<el-row>
<el-time-select
:picker-options="{
start: '00:00',
step: '00:05',
end: '23:55'
}"
placeholder="开始时间"
v-model="timeOneKeySet.startTime">
</el-time-select>
<el-time-select
:picker-options="{
start: '00:00',
step: '00:05',
end: '23:55'
}"
placeholder="结束时间"
v-model="timeOneKeySet.endTime">
</el-time-select>
<el-button @click="oneKeySetStartEndTime" type="primary">一键设置开始/结束时间</el-button>
</el-row>
<el-table :data="formData.notApplyTimeList" class="mt10" max-height="520px" size="mini" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55"></el-table-column>
<el-table-column label="日期" width="200">
<template v-slot="{row}">
<i class="el-icon-time"></i>
{{$moment(row.date).format('YYYY-MM-DD')}}
</template>
</el-table-column>
<el-table-column label="开始时间">
<template v-slot="{row}">
<el-dialog append-to-body :close-on-click-modal="false" :visible.sync="setUpTimeDialog" title="设置禁用时间" width="70%">
<el-tabs v-model="disabledTimeTab">
<el-tab-pane label="按日期" name="date">
<div class="left-span-label">选择日期</div>
<el-date-picker
@change="setUpDateChange"
placeholder="请选择一个或多个日期"
style="width: 100%"
type="dates"
v-model="formData.setUpDate"
value-format="yyyy-MM-dd">
</el-date-picker>
<div class="left-span-label mt20">设置禁用时间</div>
<el-row>
<el-time-select
size="mini"
:picker-options="{
start: '00:00',
step: '00:05',
end: '23:55'
}"
v-model="row.startTime">
placeholder="开始时间"
v-model="timeOneKeySet.startTime">
</el-time-select>
</template>
</el-table-column>
<el-table-column label="结束时间">
<template v-slot="{row}">
<el-time-select
size="mini"
:picker-options="{
start: '00:00',
step: '00:05',
end: '23:55'
}"
v-model="row.endTime">
placeholder="结束时间"
v-model="timeOneKeySet.endTime">
</el-time-select>
</template>
</el-table-column>
<el-table-column label="操作" width="200">
<template v-slot="{row, $index}">
<el-button
size="mini"
@click="formData.notApplyTimeList.splice($index,0,{date:row.date,startTime:'',endTime:''})"
type="primary">
新增同天时段
</el-button>
<el-button size="mini" @click="removeSetUpTableRow(row,$index)" type="danger">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-button @click="oneKeySetStartEndTime" type="primary">一键设置开始/结束时间</el-button>
</el-row>
<el-table :data="dateDisabledTimeList" class="mt10" max-height="520px" size="mini" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55"></el-table-column>
<el-table-column label="日期" width="200">
<template v-slot="{row}">
<i class="el-icon-time"></i>
{{$moment(row.date).format('YYYY-MM-DD')}}
</template>
</el-table-column>
<el-table-column label="开始时间">
<template v-slot="{row}">
<el-time-select
size="mini"
:picker-options="{
start: '00:00',
step: '00:05',
end: '23:55'
}"
v-model="row.startTime">
</el-time-select>
</template>
</el-table-column>
<el-table-column label="结束时间">
<template v-slot="{row}">
<el-time-select
size="mini"
:picker-options="{
start: '00:00',
step: '00:05',
end: '23:55'
}"
v-model="row.endTime">
</el-time-select>
</template>
</el-table-column>
<el-table-column label="操作" width="260" fixed="right">
<template v-slot="{row}">
<el-button
size="mini"
@click="addSameDayTime(row)"
type="primary">
新增同天时段
</el-button>
<el-button size="mini" @click="removeDisabledTimeRow(row)" type="danger">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="按星期" name="week">
<div class="left-span-label">选择星期</div>
<el-checkbox-group v-model="formData.setUpWeekDays" @change="setUpWeekChange">
<el-checkbox-button
v-for="item in weekOptions"
:label="item.value"
:key="item.value">
{{ item.label }}
</el-checkbox-button>
</el-checkbox-group>
<div class="left-span-label mt20">设置禁用时间</div>
<el-row>
<el-time-select
:picker-options="{
start: '00:00',
step: '00:05',
end: '23:55'
}"
placeholder="开始时间"
v-model="timeOneKeySet.startTime">
</el-time-select>
<el-time-select
:picker-options="{
start: '00:00',
step: '00:05',
end: '23:55'
}"
placeholder="结束时间"
v-model="timeOneKeySet.endTime">
</el-time-select>
<el-button @click="oneKeySetStartEndTime" type="primary">一键设置开始/结束时间</el-button>
</el-row>
<el-table :data="weekDisabledTimeList" class="mt10" max-height="520px" size="mini" @selection-change="handleWeekSelectionChange">
<el-table-column type="selection" width="55"></el-table-column>
<el-table-column label="星期" width="200">
<template v-slot="{row}">
<i class="el-icon-date"></i>
{{ row.weekName || getWeekName(row.weekDay) }}
</template>
</el-table-column>
<el-table-column label="开始时间">
<template v-slot="{row}">
<el-time-select
size="mini"
:picker-options="{
start: '00:00',
step: '00:05',
end: '23:55'
}"
v-model="row.startTime">
</el-time-select>
</template>
</el-table-column>
<el-table-column label="结束时间">
<template v-slot="{row}">
<el-time-select
size="mini"
:picker-options="{
start: '00:00',
step: '00:05',
end: '23:55'
}"
v-model="row.endTime">
</el-time-select>
</template>
</el-table-column>
<el-table-column label="操作" width="260" fixed="right">
<template v-slot="{row}">
<el-button
size="mini"
@click="addSameWeekTime(row)"
type="primary">
新增同星期时段
</el-button>
<el-button size="mini" @click="removeDisabledTimeRow(row)" type="danger">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
</el-tabs>
<el-row class="mt20" justify="end" type="flex">
<el-button @click="setUpTimeDialog = false">取消</el-button>
<el-button @click="doConfirmSetUpCourse" type="primary">确定</el-button>
@@ -349,11 +432,22 @@
reserveTimeType: [{required: true, message: '必填', trigger: ['blur', 'change']}],
},
setUpTimeDialog: false,
disabledTimeTab: 'date',
weekOptions: [
{ value: 1, label: '星期一' },
{ value: 2, label: '星期二' },
{ value: 3, label: '星期三' },
{ value: 4, label: '星期四' },
{ value: 5, label: '星期五' },
{ value: 6, label: '星期六' },
{ value: 7, label: '星期日' },
],
timeOneKeySet: {
startTime: "",
endTime: ""
},
multipleSelection: [],
weekMultipleSelection: [],
}
},
computed: {
@@ -368,6 +462,17 @@
this.$set(this.formData.openHours[0], 'timeUnit', 30)
}
return this.formData.openHours[0]
},
dateDisabledTimeList() {
this.normalizeDisabledTimeList()
return this.formData.notApplyTimeList.filter(item => this.isDateDisabledTime(item))
},
weekDisabledTimeList() {
this.normalizeDisabledTimeList()
return this.formData.notApplyTimeList.filter(item => this.isWeekDisabledTime(item))
},
disabledTimeCount() {
return Array.isArray(this.formData.notApplyTimeList) ? this.formData.notApplyTimeList.length : 0
}
},
methods: {
@@ -400,6 +505,7 @@
},
buildSubmitFormData() {
this.syncOpenHoursCache()
this.normalizeDisabledTimeList()
const submitData = clone(this.formData)
submitData.segmentedOpenHours = clone(this.segmentedOpenHoursCache || [])
submitData.fullDayOpenHour = clone(this.fullDayOpenHourCache || { startTime: '', endTime: '', timeUnit: 30 })
@@ -571,7 +677,16 @@
doConfirmSetUpCourse() {
const timeList = this.formData.notApplyTimeList
if (timeList && timeList.length > 0) {
const valid = timeList.every(v => v.date && v.startTime && v.endTime && (v.startTime < v.endTime))
const valid = timeList.every(v => {
const timeValid = v.startTime && v.endTime && (v.startTime < v.endTime)
if (!timeValid) {
return false
}
if (this.isWeekDisabledTime(v)) {
return !!v.weekDay
}
return !!v.date
})
if (!valid) {
this.$message.warning("时间不完整或者有误")
return
@@ -579,58 +694,139 @@
}
this.setUpTimeDialog = false
},
removeSetUpTableRow(row, index) {
this.formData.notApplyTimeList.splice(index, 1)
const dateArray = this.formData.notApplyTimeList.map(v => this.$moment(v.date).format("YYYY-MM-DD"))
const setUpDate = this.formData.setUpDate || []
this.formData.setUpDate = setUpDate.filter(v => {
return dateArray.includes(this.$moment(v).format("YYYY-MM-DD"))
normalizeDisabledTimeList() {
if (!Array.isArray(this.formData.notApplyTimeList)) {
this.$set(this.formData, 'notApplyTimeList', [])
}
this.formData.notApplyTimeList.forEach(item => {
if (!item) {
return
}
if (item.date && !item.type) {
this.$set(item, 'type', 'date')
}
if (item.type === 'week') {
this.$set(item, 'weekDay', Number(item.weekDay))
this.$set(item, 'weekName', item.weekName || this.getWeekName(item.weekDay))
}
})
},
isDateDisabledTime(item) {
return item && (!item.type || item.type === 'date') && item.date
},
isWeekDisabledTime(item) {
return item && item.type === 'week'
},
getWeekName(weekDay) {
const current = this.weekOptions.find(item => Number(item.value) === Number(weekDay))
return current ? current.label : ''
},
removeDisabledTimeRow(row) {
const index = this.formData.notApplyTimeList.indexOf(row)
if (index >= 0) {
this.formData.notApplyTimeList.splice(index, 1)
}
this.refreshDisabledTimeSelectCache()
},
refreshDisabledTimeSelectCache() {
const dateArray = this.dateDisabledTimeList.map(v => this.$moment(v.date).format("YYYY-MM-DD"))
const setUpDate = this.formData.setUpDate || []
this.$set(this.formData, 'setUpDate', setUpDate.filter(v => {
return dateArray.includes(this.$moment(v).format("YYYY-MM-DD"))
}))
const weekDayArray = this.weekDisabledTimeList.map(v => Number(v.weekDay))
const setUpWeekDays = this.formData.setUpWeekDays || []
this.$set(this.formData, 'setUpWeekDays', setUpWeekDays.filter(v => {
return weekDayArray.includes(Number(v))
}))
},
handleSelectionChange(val) {
this.multipleSelection = val
},
handleWeekSelectionChange(val) {
this.weekMultipleSelection = val
},
oneKeySetStartEndTime() {
if (this.multipleSelection.length === 0) {
const selection = this.disabledTimeTab === 'week' ? this.weekMultipleSelection : this.multipleSelection
if (selection.length === 0) {
this.$message.warning('请在下面表格多选框中选择需要一键设置的时间')
return
}
const { startTime, endTime } = this.timeOneKeySet
this.formData.notApplyTimeList.forEach(v => {
const selected = this.multipleSelection.find(o => o.date === v.date && o.startTime === v.startTime && o.endTime === v.endTime)
if (selected) {
this.$set(v, "startTime", startTime)
this.$set(v, "endTime", endTime)
}
selection.forEach(v => {
this.$set(v, "startTime", startTime)
this.$set(v, "endTime", endTime)
})
this.$forceUpdate()
},
setUpDateChange(val) {
const weekList = (this.formData.notApplyTimeList || []).filter(v => this.isWeekDisabledTime(v))
if (!val) {
this.formData.notApplyTimeList = []
this.$set(this.formData, 'notApplyTimeList', weekList)
return
}
if (this.formData.notApplyTimeList === undefined) {
this.$set(this.formData, "notApplyTimeList", [])
}
const dateSet = new Set(this.formData.notApplyTimeList.map(v => this.$moment(v.date).format("YYYY-MM-DD")))
const dateSet = new Set(this.dateDisabledTimeList.map(v => this.$moment(v.date).format("YYYY-MM-DD")))
val.forEach(v => {
if (!dateSet.has(v)) {
this.formData.notApplyTimeList.push({
date: v, startTime: "", endTime: ""
type: 'date', date: v, startTime: "", endTime: ""
})
}
})
this.formData.notApplyTimeList = this.formData.notApplyTimeList.filter(v => {
const dateList = this.dateDisabledTimeList.filter(v => {
return val.includes(this.$moment(v.date).format("YYYY-MM-DD"))
})
this.formData.notApplyTimeList.sort((a, b) => {
}).sort((a, b) => {
return Date.parse(a.date) - Date.parse(b.date)
})
this.$set(this.formData, 'notApplyTimeList', dateList.concat(weekList))
},
setUpWeekChange(val) {
const dateList = (this.formData.notApplyTimeList || []).filter(v => this.isDateDisabledTime(v))
const weekSet = new Set(this.weekDisabledTimeList.map(v => Number(v.weekDay)))
val.forEach(v => {
if (!weekSet.has(Number(v))) {
this.formData.notApplyTimeList.push({
type: 'week',
weekDay: Number(v),
weekName: this.getWeekName(v),
startTime: '',
endTime: ''
})
}
})
const weekList = this.weekDisabledTimeList.filter(v => {
return val.map(item => Number(item)).includes(Number(v.weekDay))
}).sort((a, b) => {
return Number(a.weekDay) - Number(b.weekDay)
})
this.$set(this.formData, 'notApplyTimeList', dateList.concat(weekList))
},
addSameDayTime(row) {
const index = this.formData.notApplyTimeList.indexOf(row)
const data = { type: 'date', date: row.date, startTime: '', endTime: '' }
if (index >= 0) {
this.formData.notApplyTimeList.splice(index + 1, 0, data)
} else {
this.formData.notApplyTimeList.push(data)
}
},
addSameWeekTime(row) {
const index = this.formData.notApplyTimeList.indexOf(row)
const data = { type: 'week', weekDay: Number(row.weekDay), weekName: this.getWeekName(row.weekDay), startTime: '', endTime: '' }
if (index >= 0) {
this.formData.notApplyTimeList.splice(index + 1, 0, data)
} else {
this.formData.notApplyTimeList.push(data)
}
},
openSetUpTime() {
this.normalizeDisabledTimeList()
if (this.formData.notApplyTimeList) {
this.$set(this.formData, 'setUpDate', this.formData.notApplyTimeList.map(o => o.date))
this.$set(this.formData, 'setUpDate', Array.from(new Set(this.dateDisabledTimeList.map(o => o.date))))
this.$set(this.formData, 'setUpWeekDays', Array.from(new Set(this.weekDisabledTimeList.map(o => Number(o.weekDay)))))
}
this.activeTab = 'siteSetting'
this.setUpTimeDialog = true
@@ -53,7 +53,11 @@ const siteInfo = {
<el-descriptions-item label="禁用时间" :span="2">
<el-table v-if="viewData.notApplyTimeList && viewData.notApplyTimeList.length > 0"
:data="viewData.notApplyTimeList" max-height="300" size="mini">
<el-table-column prop="date" label="日期"></el-table-column>
<el-table-column label="日期/星期">
<template v-slot="{row}">
{{ getDisabledTimeLabel(row) }}
</template>
</el-table-column>
<el-table-column prop="startTime" label="开始时间"></el-table-column>
<el-table-column prop="endTime" label="结束时间"></el-table-column>
</el-table>
@@ -92,6 +96,27 @@ const siteInfo = {
}
},
methods: {
getWeekName(weekDay) {
const weekNameMap = {
1: '星期一',
2: '星期二',
3: '星期三',
4: '星期四',
5: '星期五',
6: '星期六',
7: '星期日',
}
return weekNameMap[Number(weekDay)] || ''
},
getDisabledTimeLabel(item) {
if (!item) {
return '--'
}
if (item.type === 'week') {
return item.weekName || this.getWeekName(item.weekDay) || '--'
}
return item.date || '--'
},
onOpen(row) {
this.activeTab = 'basicInfo'
this.viewData = row
@@ -82,7 +82,7 @@ layout("/layouts/platform.html"){
</el-card>
<el-card shadow="never">
<table-tool label="申请列表">
<table-tool label="校工会主席审核列表">
<el-radio-group v-model="pageForm.approval" size="small" @change="doSearch">
<el-radio-button :label="true">已审核</el-radio-button>
<el-radio-button :label="false">未审核</el-radio-button>
@@ -296,7 +296,7 @@ layout("/layouts/platform.html"){
type: 'warning',
}).then(() => {
this.auditLoading = true
this.$axios.post('/platform/siteCug/schoolUnionAudit/executeTask', {
this.$axios.post(loc() + '/executeTask', {
data: JSON.stringify({
...this.formData,
submitType: val,
@@ -325,7 +325,7 @@ layout("/layouts/platform.html"){
type: 'info',
}).then(() => {
this.revokeLoading = true
this.$axios.post('/platform/siteCug/schoolUnionAudit/revokeTask', { taskId: row.taskId, applyId: row.id }).then((res) => {
this.$axios.post(loc() + '/revokeTask', { taskId: row.taskId, applyId: row.id }).then((res) => {
if (res.code === 0) {
this.$message.success(res.msg)
this.doSearch()
@@ -41,6 +41,7 @@ new Vue({
reserveStartTime: '',
reserveEndTime: '',
yearlyReserveEndDate: '',
coffeeNeeded: 0,
joinCount: 1,
applyCause: '',
},
@@ -219,6 +220,7 @@ new Vue({
reserveStartTime: '',
reserveEndTime: '',
yearlyReserveEndDate: '',
coffeeNeeded: 0,
joinCount: 1,
applyCause: '',
}
@@ -700,6 +702,48 @@ new Vue({
}
return timeStr.length === 5 ? timeStr + ':00' : timeStr
},
getWeekName(weekDay) {
const weekNameMap = {
1: '星期一',
2: '星期二',
3: '星期三',
4: '星期四',
5: '星期五',
6: '星期六',
7: '星期日',
}
return weekNameMap[Number(weekDay)] || ''
},
getDisabledTimeLabel(item) {
if (!item) {
return '--'
}
if (item.type === 'week') {
return item.weekName || this.getWeekName(item.weekDay) || '--'
}
return item.date || '--'
},
getDayWeekDay(dayStr) {
const target = this.$moment(dayStr, 'YYYY-MM-DD')
if (!target.isValid()) {
return 0
}
const weekDay = target.day()
return weekDay === 0 ? 7 : weekDay
},
isDateDisabledTime(item, dayStr) {
if (!item || !item.date) {
return false
}
const type = item.type || 'date'
return type === 'date' && this.$moment(item.date).format('YYYY-MM-DD') === dayStr
},
isWeekDisabledTime(item, dayStr) {
if (!item || item.type !== 'week') {
return false
}
return Number(item.weekDay) === this.getDayWeekDay(dayStr)
},
isDisabledDate(time) {
const day = this.$moment(time).format('YYYY-MM-DD')
const today = this.$moment().startOf('day')
@@ -715,7 +759,7 @@ new Vue({
getDisabledRangesByDay(dayStr) {
const list = Array.isArray(this.timeLimitConfig.notApplyTimeList) ? this.timeLimitConfig.notApplyTimeList : []
return list
.filter(item => item && item.date && this.$moment(item.date).format('YYYY-MM-DD') === dayStr)
.filter(item => item && item.startTime && item.endTime && (this.isDateDisabledTime(item, dayStr) || this.isWeekDisabledTime(item, dayStr)))
.sort((a, b) => this.normalizeTime(a.startTime).localeCompare(this.normalizeTime(b.startTime)))
},
isDateTimeBlocked(dateTimeStr) {
@@ -752,15 +796,22 @@ new Vue({
return false
},
overlapsDisabledRange(start, end) {
const list = Array.isArray(this.timeLimitConfig.notApplyTimeList) ? this.timeLimitConfig.notApplyTimeList : []
return list.some(item => {
if (!item || !item.date || !item.startTime || !item.endTime) {
return false
const current = start.clone().startOf('day')
const endDay = end.clone().startOf('day')
while (current.isSameOrBefore(endDay)) {
const dayStr = current.format('YYYY-MM-DD')
const disabledRanges = this.getDisabledRangesByDay(dayStr)
const matched = disabledRanges.some(item => {
const rangeStart = this.$moment(dayStr + ' ' + this.normalizeTime(item.startTime))
const rangeEnd = this.$moment(dayStr + ' ' + this.normalizeTime(item.endTime))
return start.isBefore(rangeEnd) && end.isAfter(rangeStart)
})
if (matched) {
return true
}
const rangeStart = this.$moment(this.$moment(item.date).format('YYYY-MM-DD') + ' ' + this.normalizeTime(item.startTime))
const rangeEnd = this.$moment(this.$moment(item.date).format('YYYY-MM-DD') + ' ' + this.normalizeTime(item.endTime))
return start.isBefore(rangeEnd) && end.isAfter(rangeStart)
})
current.add(1, 'day')
}
return false
},
showSubmitError(message) {
return this.$dialog.alert({
@@ -379,8 +379,8 @@ layout("/layouts/platform_h5.html"){
<div
class="disabled-time-item"
v-for="(item, index) in timeLimitConfig.notApplyTimeList"
:key="item.date + item.startTime + item.endTime + index">
{{ item.date }} {{ item.startTime }} - {{ item.endTime }}
:key="(item.type || 'date') + (item.date || item.weekDay || '') + item.startTime + item.endTime + index">
{{ getDisabledTimeLabel(item) }} {{ item.startTime }} - {{ item.endTime }}
</div>
</div>
<div v-else>暂无禁用时段</div>
@@ -458,6 +458,28 @@ layout("/layouts/platform_h5.html"){
@click="openAvailabilityPopup">
</van-field>
<div v-if="selectionSummary" class="selection-summary">当前已选:{{ selectionSummary }}</div>
<van-field
label="是否需要咖啡"
name="coffeeNeeded"
:rules="[{ validator: (value) => value === 0 || value === 1, message: '请选择是否需要咖啡' }]">
<template #input>
<van-radio-group
v-model="formData.coffeeNeeded"
direction="horizontal">
<van-radio :name="0">否</van-radio>
<van-radio :name="1">是</van-radio>
</van-radio-group>
</template>
</van-field>
<van-field
v-model="formData.joinCount"
label="预约人数"
name="joinCount"
type="number"
required
placeholder="请输入预约人数"
:rules="[{ required: true, message: '请输入预约人数' }, { validator: (value) => Number.isInteger(Number(value)) && Number(value) > 0, message: '预约人数必须为大于0的整数' }]">
</van-field>
<van-cell title="批量预约">
<template #default>
<van-switch v-model="yearlyReserve" size="22px"></van-switch>
@@ -0,0 +1,335 @@
<!--#
layout("/layouts/platform_h5.html"){
#-->
<style scoped>
.site-cug-audit-page {
height: 100vh;
overflow: hidden;
display: flex;
flex-direction: column;
background: #f4f6f8;
box-sizing: border-box;
}
.site-cug-audit-sticky {
flex-shrink: 0;
background: #f4f6f8;
}
.site-cug-audit-list-scroll {
min-height: 0;
flex: 1;
overflow-y: auto;
padding-bottom: 18px;
-webkit-overflow-scrolling: touch;
box-sizing: border-box;
}
.site-cug-audit-search /deep/ .van-search__content,
.site-cug-audit-search .van-search__content {
border-radius: 8px;
background: #ffffff;
}
.site-cug-card-tags {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-bottom: 8px;
}
.site-cug-tag {
display: inline-flex;
align-items: center;
min-height: 22px;
padding: 0 8px;
border-radius: 4px;
background: #eef6ff;
color: #0b75bd;
font-size: 12px;
}
.site-cug-tag.gray {
background: #f1f5f9;
color: #64748b;
}
.site-cug-tag.warn {
background: #fff3e5;
color: #d97706;
}
.action-btn.disabled {
opacity: 0.45;
pointer-events: none;
}
.action-btn.loading {
opacity: 0.9;
}
.action-btn .van-loading {
margin-right: 4px;
}
</style>
<div id="app" class="site-cug-audit-page" v-cloak>
<van-nav-bar title="分工会审核" left-text="返回" left-arrow @click-left="historyBack" fixed placeholder></van-nav-bar>
<van-sticky offset-top="46px" class="site-cug-audit-sticky">
<van-search
class="site-cug-audit-search"
v-model="pageForm.searchKeyword"
show-action
clearable
input-align="left"
placeholder="请输入姓名/工号/场地搜索"
@search="doSearch"
@clear="doSearch">
<template #action>
<div @click="doSearch">搜索</div>
</template>
</van-search>
<van-dropdown-menu :close-on-click-outside="false" :close-on-click-overlay="false">
<van-dropdown-item v-model="pageForm.siteType" :options="typeOptions" :multiple="false" @change="doSearch"></van-dropdown-item>
<van-dropdown-item v-model="pageForm.siteId" :options="siteOptions" :multiple="false" @change="doSearch"></van-dropdown-item>
</van-dropdown-menu>
<van-tabs v-model="pageForm.approvalText" @change="onApprovalTabChange">
<van-tab title="已审核" name="1"></van-tab>
<van-tab title="未审核" name="0"></van-tab>
</van-tabs>
</van-sticky>
<div class="site-cug-audit-list-scroll">
<table-list api="/platform/siteCug/branchUnionAudit/pageData" :page_form.sync="pageForm" ref="tableListRef" title="applyUserName" @ready="onReady">
<template v-slot="{index,row}">
<div class="site-cug-card-tags">
<span v-if="row.applyLoginName" class="site-cug-tag">{{ row.applyLoginName }}</span>
<span class="site-cug-tag gray">{{ reserveTypeText(row) }}</span>
<span v-if="row.yearlyBatch" class="site-cug-tag warn">预约本年</span>
</div>
<table-column label="场地名称">{{row.siteName}}</table-column>
<table-column label="预约主体">{{row.reserveType === 'club' ? (row.clubName || '-') : (row.applyUnionName || '-')}}</table-column>
<table-column label="所属单位">{{row.applyUnitName}}</table-column>
<table-column label="开始时间">{{row.reserveStartTime}}</table-column>
<table-column label="结束时间">{{row.reserveEndTime}}</table-column>
<table-column label="当前节点">{{row.curTaskName || '-'}}</table-column>
</template>
<template #actions="{index,row}">
<div class="action-btn" @click="onView(row)">
<i class="fa fa-eye"></i>
<span>查看</span>
</div>
<div class="action-btn" v-if="row.taskState === 10" @click="onAudit(row)">
<i class="fa fa-edit"></i>
<span>审核</span>
</div>
<div class="action-btn delete" v-if="canRevoke(row)" :class="{ loading: revokeLoading }" @click="onRevoke(row)">
<van-loading v-if="revokeLoading" size="14px" color="#fff"></van-loading>
<i v-else class="fa fa-reply"></i>
<span>撤回</span>
</div>
</template>
</table-list>
</div>
<info ref="infoRef">
<div v-if="showApprovalForm">
<div class="process-title">{{formData.taskName}}</div>
<van-form ref="formRef">
<van-field
v-model="formData.tf_opinion"
name="tf_opinion"
label="审批意见"
placeholder="请输入审批意见"
:rules="[{ required: true, message: '请填写审批意见' }]"
required
maxlength="100"
show-word-limit
></van-field>
</van-form>
<div style="display:flex;justify-content:space-between;column-gap:10px;padding:10px;">
<van-button type="info" block :loading="auditLoading" :disabled="auditLoading" @click="handleTaskAction(6)">退回</van-button>
<van-button type="danger" block :loading="auditLoading" :disabled="auditLoading" @click="handleTaskAction(2)">不同意</van-button>
<van-button type="primary" block :loading="auditLoading" :disabled="auditLoading" @click="handleTaskAction(1)">同意</van-button>
</div>
</div>
</info>
</div>
<script nonce="${cspNonce!}">
<!--#include('../common/applyInfo.js'){}#-->
new Vue({
el: '#app',
store,
components: {
info: siteCugApplyInfoH5,
},
data() {
return {
pageForm: {
pageNumber: 1,
pageSize: 10,
totalCount: 0,
searchKeyword: '',
approvalText: '0',
approval: false,
siteType: null,
siteId: null,
},
typeOptions: [
{
text: '全部类型',
value: null,
}
],
siteOptions: [
{
text: '全部场地',
value: null,
}
],
formData: {},
showApprovalForm: false,
auditLoading: false,
revokeLoading: false,
}
},
methods: {
historyBack,
async onReady() {
await this.querySiteType()
await this.querySites()
this.doSearch()
},
onApprovalTabChange(val) {
this.pageForm.approval = val === '1'
this.doSearch()
},
canRevoke(row) {
return Number(row.instanceState) === 20
},
reserveTypeText(row) {
return row && row.reserveType === 'club' ? '协会预约' : '分工会预约'
},
onView(row) {
this.showApprovalForm = false
this.$refs.infoRef.onOpen(row)
},
onAudit(row) {
this.showApprovalForm = true
this.formData = {
processTaskId: row.taskId,
applyId: row.id,
taskName: row.curTaskName,
tf_opinion: '',
yearlyBatch: row.yearlyBatch,
batchAuditCount: row.batchAuditCount,
}
this.$refs.infoRef.onOpen(row)
},
async handleTaskAction(submitType) {
if (this.auditLoading) {
return
}
try {
await this.$refs.formRef.validate()
this.$dialog.confirm({
title: '提示',
message: this.formData.yearlyBatch
? ('该记录属于“预约本年”批量预约,提交后将一并审核同批次的 ' + (this.formData.batchAuditCount || 0) + ' 条预约记录,是否继续?')
: '您确定要提交吗?',
}).then(() => {
this.auditLoading = true
this.$axios.post('/platform/siteCug/branchUnionAudit/executeTask', {
data: JSON.stringify({
...this.formData,
submitType: submitType,
})
}).then((res) => {
if (res.code === 0) {
this.$refs.infoRef.onClose()
this.$toast.success(res.msg || '提交成功')
this.doSearch()
} else {
this.$dialog.alert({
title: '提示',
message: res.msg || '提交失败',
}).catch(() => {})
}
}).catch((err) => {
this.$dialog.alert({
title: '提示',
message: (err && err.msg) || '提交失败',
}).catch(() => {})
}).finally(() => {
this.auditLoading = false
})
}).catch(() => {})
} catch (e) {
}
},
onRevoke(row) {
if (!this.canRevoke(row) || this.revokeLoading) {
return
}
this.$dialog.confirm({
title: '提示',
message: row.yearlyBatch
? ('该记录属于“预约本年”批量预约,撤回后将一并撤回同批次的 ' + (row.batchAuditCount || 0) + ' 条审核任务,是否继续?')
: '您确定要撤回吗?',
}).then(() => {
this.revokeLoading = true
this.$axios.post('/platform/siteCug/branchUnionAudit/revokeTask', { taskId: row.taskId, applyId: row.id }).then((res) => {
if (res.code === 0) {
this.$toast.success(res.msg || '撤回成功')
this.doSearch()
} else {
this.$dialog.alert({
title: '提示',
message: res.msg || '撤回失败',
}).catch(() => {})
}
}).catch((err) => {
this.$dialog.alert({
title: '提示',
message: (err && err.msg) || '撤回失败',
}).catch(() => {})
}).finally(() => {
this.revokeLoading = false
})
}).catch(() => {})
},
querySiteType() {
return this.$axios.post('/platform/siteCug/function/type/queryFunctionType').then((res) => {
const list = Array.isArray(res.data) ? res.data : []
this.typeOptions = [
{
text: '全部类型',
value: null,
}
].concat(list.map((item) => ({
text: item.name,
value: item.id,
})))
})
},
querySites() {
return this.$axios.post('/platform/siteCug/manage/querySites').then((res) => {
const list = Array.isArray(res.data) ? res.data : []
this.siteOptions = [
{
text: '全部场地',
value: null,
}
].concat(list.map((item) => ({
text: item.name,
value: item.id,
})))
})
},
doSearch() {
this.$nextTick(() => {
this.pageForm.pageNumber = 1
this.pageForm.totalCount = 0
this.$refs.tableListRef.doSearch()
})
},
},
})
</script>
<!--#
}
#-->
@@ -13,6 +13,8 @@ const siteCugApplyInfoH5 = {
<van-cell title="预约主体">{{ reserveTargetName }}</van-cell>
<van-cell title="开始时间">{{ viewData.reserveStartTime }}</van-cell>
<van-cell title="结束时间">{{ viewData.reserveEndTime }}</van-cell>
<van-cell title="是否需要咖啡">{{ viewData.coffeeNeeded ? '是' : '否' }}</van-cell>
<van-cell title="预约人数">{{ viewData.joinCount || '-' }}</van-cell>
<van-cell title="联系电话">{{ viewData.applyMobile }}</van-cell>
<van-cell title="预约事由" class="direction-column-cell">{{ viewData.applyCause }}</van-cell>
</van-cell-group>
@@ -46,14 +46,14 @@ const siteInfo = {
<table class="table-class">
<thead>
<tr>
<th>日期</th>
<th>日期/星期</th>
<th>开始时间</th>
<th>结束时间</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in viewData.notApplyTimeList" :key="item.date + item.startTime + item.endTime + index">
<td>{{ item.date }}</td>
<tr v-for="(item, index) in viewData.notApplyTimeList" :key="(item.type || 'date') + (item.date || item.weekDay || '') + item.startTime + item.endTime + index">
<td>{{ getDisabledTimeLabel(item) }}</td>
<td>{{ item.startTime }}</td>
<td>{{ item.endTime }}</td>
</tr>
@@ -71,6 +71,27 @@ const siteInfo = {
}
},
methods: {
getWeekName(weekDay) {
const weekNameMap = {
1: '星期一',
2: '星期二',
3: '星期三',
4: '星期四',
5: '星期五',
6: '星期六',
7: '星期日',
}
return weekNameMap[Number(weekDay)] || ''
},
getDisabledTimeLabel(item) {
if (!item) {
return '--'
}
if (item.type === 'week') {
return item.weekName || this.getWeekName(item.weekDay) || '--'
}
return item.date || '--'
},
onOpen(row) {
this.viewData = row
this.visible = true
@@ -0,0 +1,335 @@
<!--#
layout("/layouts/platform_h5.html"){
#-->
<style scoped>
.site-cug-audit-page {
height: 100vh;
overflow: hidden;
display: flex;
flex-direction: column;
background: #f4f6f8;
box-sizing: border-box;
}
.site-cug-audit-sticky {
flex-shrink: 0;
background: #f4f6f8;
}
.site-cug-audit-list-scroll {
min-height: 0;
flex: 1;
overflow-y: auto;
padding-bottom: 18px;
-webkit-overflow-scrolling: touch;
box-sizing: border-box;
}
.site-cug-audit-search /deep/ .van-search__content,
.site-cug-audit-search .van-search__content {
border-radius: 8px;
background: #ffffff;
}
.site-cug-card-tags {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-bottom: 8px;
}
.site-cug-tag {
display: inline-flex;
align-items: center;
min-height: 22px;
padding: 0 8px;
border-radius: 4px;
background: #eef6ff;
color: #0b75bd;
font-size: 12px;
}
.site-cug-tag.gray {
background: #f1f5f9;
color: #64748b;
}
.site-cug-tag.warn {
background: #fff3e5;
color: #d97706;
}
.action-btn.disabled {
opacity: 0.45;
pointer-events: none;
}
.action-btn.loading {
opacity: 0.9;
}
.action-btn .van-loading {
margin-right: 4px;
}
</style>
<div id="app" class="site-cug-audit-page" v-cloak>
<van-nav-bar title="校工会副主席审核" left-text="返回" left-arrow @click-left="historyBack" fixed placeholder></van-nav-bar>
<van-sticky offset-top="46px" class="site-cug-audit-sticky">
<van-search
class="site-cug-audit-search"
v-model="pageForm.searchKeyword"
show-action
clearable
input-align="left"
placeholder="请输入姓名/工号/场地搜索"
@search="doSearch"
@clear="doSearch">
<template #action>
<div @click="doSearch">搜索</div>
</template>
</van-search>
<van-dropdown-menu :close-on-click-outside="false" :close-on-click-overlay="false">
<van-dropdown-item v-model="pageForm.siteType" :options="typeOptions" :multiple="false" @change="doSearch"></van-dropdown-item>
<van-dropdown-item v-model="pageForm.siteId" :options="siteOptions" :multiple="false" @change="doSearch"></van-dropdown-item>
</van-dropdown-menu>
<van-tabs v-model="pageForm.approvalText" @change="onApprovalTabChange">
<van-tab title="已审核" name="1"></van-tab>
<van-tab title="未审核" name="0"></van-tab>
</van-tabs>
</van-sticky>
<div class="site-cug-audit-list-scroll">
<table-list api="/platform/siteCug/deputySchoolUnion/pageData" :page_form.sync="pageForm" ref="tableListRef" title="applyUserName" @ready="onReady">
<template v-slot="{index,row}">
<div class="site-cug-card-tags">
<span v-if="row.applyLoginName" class="site-cug-tag">{{ row.applyLoginName }}</span>
<span class="site-cug-tag gray">{{ reserveTypeText(row) }}</span>
<span v-if="row.yearlyBatch" class="site-cug-tag warn">预约本年</span>
</div>
<table-column label="场地名称">{{row.siteName}}</table-column>
<table-column label="预约主体">{{row.reserveType === 'club' ? (row.clubName || '-') : (row.applyUnionName || '-')}}</table-column>
<table-column label="所属单位">{{row.applyUnitName}}</table-column>
<table-column label="开始时间">{{row.reserveStartTime}}</table-column>
<table-column label="结束时间">{{row.reserveEndTime}}</table-column>
<table-column label="当前节点">{{row.curTaskName || '-'}}</table-column>
</template>
<template #actions="{index,row}">
<div class="action-btn" @click="onView(row)">
<i class="fa fa-eye"></i>
<span>查看</span>
</div>
<div class="action-btn" v-if="row.taskState === 10" @click="onAudit(row)">
<i class="fa fa-edit"></i>
<span>审核</span>
</div>
<div class="action-btn delete" v-if="canRevoke(row)" :class="{ loading: revokeLoading }" @click="onRevoke(row)">
<van-loading v-if="revokeLoading" size="14px" color="#fff"></van-loading>
<i v-else class="fa fa-reply"></i>
<span>撤回</span>
</div>
</template>
</table-list>
</div>
<info ref="infoRef">
<div v-if="showApprovalForm">
<div class="process-title">{{formData.taskName}}</div>
<van-form ref="formRef">
<van-field
v-model="formData.tf_opinion"
name="tf_opinion"
label="审批意见"
placeholder="请输入审批意见"
:rules="[{ required: true, message: '请填写审批意见' }]"
required
maxlength="100"
show-word-limit
></van-field>
</van-form>
<div style="display:flex;justify-content:space-between;column-gap:10px;padding:10px;">
<van-button type="info" block :loading="auditLoading" :disabled="auditLoading" @click="handleTaskAction(6)">退回</van-button>
<van-button type="danger" block :loading="auditLoading" :disabled="auditLoading" @click="handleTaskAction(2)">不同意</van-button>
<van-button type="primary" block :loading="auditLoading" :disabled="auditLoading" @click="handleTaskAction(1)">同意</van-button>
</div>
</div>
</info>
</div>
<script nonce="${cspNonce!}">
<!--#include('../common/applyInfo.js'){}#-->
new Vue({
el: '#app',
store,
components: {
info: siteCugApplyInfoH5,
},
data() {
return {
pageForm: {
pageNumber: 1,
pageSize: 10,
totalCount: 0,
searchKeyword: '',
approvalText: '0',
approval: false,
siteType: null,
siteId: null,
},
typeOptions: [
{
text: '全部类型',
value: null,
}
],
siteOptions: [
{
text: '全部场地',
value: null,
}
],
formData: {},
showApprovalForm: false,
auditLoading: false,
revokeLoading: false,
}
},
methods: {
historyBack,
async onReady() {
await this.querySiteType()
await this.querySites()
this.doSearch()
},
onApprovalTabChange(val) {
this.pageForm.approval = val === '1'
this.doSearch()
},
canRevoke(row) {
return Number(row.instanceState) === 20
},
reserveTypeText(row) {
return row && row.reserveType === 'club' ? '协会预约' : '分工会预约'
},
onView(row) {
this.showApprovalForm = false
this.$refs.infoRef.onOpen(row)
},
onAudit(row) {
this.showApprovalForm = true
this.formData = {
processTaskId: row.taskId,
applyId: row.id,
taskName: row.curTaskName,
tf_opinion: '',
yearlyBatch: row.yearlyBatch,
batchAuditCount: row.batchAuditCount,
}
this.$refs.infoRef.onOpen(row)
},
async handleTaskAction(submitType) {
if (this.auditLoading) {
return
}
try {
await this.$refs.formRef.validate()
this.$dialog.confirm({
title: '提示',
message: this.formData.yearlyBatch
? ('该记录属于“预约本年”批量预约,提交后将一并审核同批次的 ' + (this.formData.batchAuditCount || 0) + ' 条预约记录,是否继续?')
: '您确定要提交吗?',
}).then(() => {
this.auditLoading = true
this.$axios.post('/platform/siteCug/deputySchoolUnion/executeTask', {
data: JSON.stringify({
...this.formData,
submitType: submitType,
})
}).then((res) => {
if (res.code === 0) {
this.$refs.infoRef.onClose()
this.$toast.success(res.msg || '提交成功')
this.doSearch()
} else {
this.$dialog.alert({
title: '提示',
message: res.msg || '提交失败',
}).catch(() => {})
}
}).catch((err) => {
this.$dialog.alert({
title: '提示',
message: (err && err.msg) || '提交失败',
}).catch(() => {})
}).finally(() => {
this.auditLoading = false
})
}).catch(() => {})
} catch (e) {
}
},
onRevoke(row) {
if (!this.canRevoke(row) || this.revokeLoading) {
return
}
this.$dialog.confirm({
title: '提示',
message: row.yearlyBatch
? ('该记录属于“预约本年”批量预约,撤回后将一并撤回同批次的 ' + (row.batchAuditCount || 0) + ' 条审核任务,是否继续?')
: '您确定要撤回吗?',
}).then(() => {
this.revokeLoading = true
this.$axios.post('/platform/siteCug/deputySchoolUnion/revokeTask', { taskId: row.taskId, applyId: row.id }).then((res) => {
if (res.code === 0) {
this.$toast.success(res.msg || '撤回成功')
this.doSearch()
} else {
this.$dialog.alert({
title: '提示',
message: res.msg || '撤回失败',
}).catch(() => {})
}
}).catch((err) => {
this.$dialog.alert({
title: '提示',
message: (err && err.msg) || '撤回失败',
}).catch(() => {})
}).finally(() => {
this.revokeLoading = false
})
}).catch(() => {})
},
querySiteType() {
return this.$axios.post('/platform/siteCug/function/type/queryFunctionType').then((res) => {
const list = Array.isArray(res.data) ? res.data : []
this.typeOptions = [
{
text: '全部类型',
value: null,
}
].concat(list.map((item) => ({
text: item.name,
value: item.id,
})))
})
},
querySites() {
return this.$axios.post('/platform/siteCug/manage/querySites').then((res) => {
const list = Array.isArray(res.data) ? res.data : []
this.siteOptions = [
{
text: '全部场地',
value: null,
}
].concat(list.map((item) => ({
text: item.name,
value: item.id,
})))
})
},
doSearch() {
this.$nextTick(() => {
this.pageForm.pageNumber = 1
this.pageForm.totalCount = 0
this.$refs.tableListRef.doSearch()
})
},
},
})
</script>
<!--#
}
#-->
@@ -65,7 +65,7 @@ layout("/layouts/platform_h5.html"){
</style>
<div id="app" class="site-cug-audit-page" v-cloak>
<van-nav-bar title="校工会审核" left-text="返回" left-arrow @click-left="historyBack" fixed placeholder></van-nav-bar>
<van-nav-bar title="校工会主席审核" left-text="返回" left-arrow @click-left="historyBack" fixed placeholder></van-nav-bar>
<van-sticky offset-top="46px" class="site-cug-audit-sticky">
<van-search