疗休养优化
This commit is contained in:
@@ -5,7 +5,6 @@ import cn.hutool.core.date.DateUtil;
|
|||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.hutool.http.useragent.UserAgent;
|
import cn.hutool.http.useragent.UserAgent;
|
||||||
import cn.hutool.http.useragent.UserAgentUtil;
|
import cn.hutool.http.useragent.UserAgentUtil;
|
||||||
import cn.hutool.json.JSONObject;
|
|
||||||
import com.budwk.app.base.constant.RoleConstant;
|
import com.budwk.app.base.constant.RoleConstant;
|
||||||
import com.budwk.app.base.result.Result;
|
import com.budwk.app.base.result.Result;
|
||||||
import com.budwk.app.sys.models.Sys_home_activity;
|
import com.budwk.app.sys.models.Sys_home_activity;
|
||||||
@@ -287,76 +286,67 @@ public class SysHomeController {
|
|||||||
// @CacheDefaults(cacheName = RedisConstant.PLATFORM_REDIS_WKCACHE_PREFIX + "web_news", isHash = true)
|
// @CacheDefaults(cacheName = RedisConstant.PLATFORM_REDIS_WKCACHE_PREFIX + "web_news", isHash = true)
|
||||||
// @CacheResult(cacheKey = "news", ignoreNull = true, cacheLiveTime = 60 * 60 * 24)
|
// @CacheResult(cacheKey = "news", ignoreNull = true, cacheLiveTime = 60 * 60 * 24)
|
||||||
public Result getNews() {
|
public Result getNews() {
|
||||||
// 定义要爬取的URL
|
|
||||||
String domain = "https://cdgh.ncu.edu.cn/";
|
String domain = "https://cdgh.ncu.edu.cn/";
|
||||||
try {
|
try {
|
||||||
// 使用Jsoup连接到URL并获取页面内容
|
|
||||||
Document doc = Jsoup.connect(domain).get();
|
Document doc = Jsoup.connect(domain).get();
|
||||||
|
|
||||||
JSONObject root = new JSONObject();
|
List<NutMap> news = new ArrayList<>();
|
||||||
|
news.add(NutMap.NEW().setv("label", "新闻快讯")
|
||||||
// 1. 新闻快讯(来自 .list1)
|
.setv("value", parseHomeNewsItems(domain, doc.select(".group1 .list1 li"), "a.text", ".title")));
|
||||||
List<JSONObject> newsArray = new ArrayList<>();
|
news.add(NutMap.NEW().setv("label", "通知公告")
|
||||||
Elements newsItems = doc.select(".group1 .list1 li");
|
.setv("value", parseHomeNewsItems(domain, doc.select(".group2 .list2 li"), "a.title", "")));
|
||||||
for (Element item : newsItems) {
|
news.add(NutMap.NEW().setv("label", "基层风采")
|
||||||
String date = item.select(".date").text().trim();
|
.setv("value", parseHomeNewsItems(domain, doc.select(".group3 .pic-list1 li"), "a.img-scale", ".title")));
|
||||||
Element link = item.select("a.text").first();
|
return Result.success(news);
|
||||||
String title = link.select(".title").text().trim();
|
|
||||||
String url = link.attr("href").trim();
|
|
||||||
|
|
||||||
JSONObject node = new JSONObject();
|
|
||||||
node.set("title", title);
|
|
||||||
node.set("date", date);
|
|
||||||
node.set("url", domain + url);
|
|
||||||
newsArray.add(node);
|
|
||||||
}
|
|
||||||
root.set("news", newsArray);
|
|
||||||
|
|
||||||
// 2. 通知公告(来自 .list2)
|
|
||||||
List<JSONObject> noticesArray = new ArrayList<>();
|
|
||||||
Elements noticeItems = doc.select(".group2 .list2 li");
|
|
||||||
for (Element item : noticeItems) {
|
|
||||||
String date = item.select(".date").text().trim();
|
|
||||||
Element link = item.select("a.title").first();
|
|
||||||
String title = link.text().trim();
|
|
||||||
String url = link.attr("href").trim();
|
|
||||||
JSONObject node = new JSONObject();
|
|
||||||
node.set("title", title);
|
|
||||||
node.set("date", date);
|
|
||||||
node.set("url", domain + url);
|
|
||||||
noticesArray.add(node);
|
|
||||||
}
|
|
||||||
root.set("notices", noticesArray);
|
|
||||||
|
|
||||||
// 3. 基层风采(来自 .pic-list1)
|
|
||||||
List<JSONObject> grassrootsArray = new ArrayList<>();
|
|
||||||
Elements grassrootsItems = doc.select(".group3 .pic-list1 li");
|
|
||||||
for (Element item : grassrootsItems) {
|
|
||||||
Element link = item.select("a.img-scale").first();
|
|
||||||
String date = link.select(".date").text().trim();
|
|
||||||
String title = link.select(".title").text().trim();
|
|
||||||
String url = link.attr("href").trim();
|
|
||||||
String summary = link.select(".info").text().trim();
|
|
||||||
|
|
||||||
String imageUrl = "";
|
|
||||||
Element img = link.select("img").first();
|
|
||||||
if (img != null) {
|
|
||||||
imageUrl = img.attr("src").trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
JSONObject node = new JSONObject();
|
|
||||||
node.set("title", title);
|
|
||||||
node.set("date", date);
|
|
||||||
node.set("url", domain + url);
|
|
||||||
node.set("summary", summary);
|
|
||||||
node.set("image", domain + imageUrl);
|
|
||||||
grassrootsArray.add(node);
|
|
||||||
}
|
|
||||||
root.set("grassroots", grassrootsArray);
|
|
||||||
return Result.success(root);
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error(e);
|
log.error(e);
|
||||||
return Result.success();
|
return Result.success(Collections.emptyList());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the NCU union website list nodes to the PC v4 home carousel structure.
|
||||||
|
* The migrated jcdt component renders label/value and item href/time/text fields.
|
||||||
|
*
|
||||||
|
* @param domain base website domain used to complete relative links
|
||||||
|
* @param items news list nodes parsed from the source page
|
||||||
|
* @param linkSelector selector for the clickable element inside each list node
|
||||||
|
* @param titleSelector selector for the title inside the clickable element, blank means using link text
|
||||||
|
* @return normalized news items for the PC v4 home page
|
||||||
|
*/
|
||||||
|
private List<NutMap> parseHomeNewsItems(String domain, Elements items, String linkSelector, String titleSelector) {
|
||||||
|
List<NutMap> rows = new ArrayList<>();
|
||||||
|
for (Element item : items) {
|
||||||
|
Element link = item.select(linkSelector).first();
|
||||||
|
if (link == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String title = StrUtil.isBlank(titleSelector) ? link.text().trim() : link.select(titleSelector).text().trim();
|
||||||
|
String href = link.attr("href").trim();
|
||||||
|
String time = item.select(".date").text().trim();
|
||||||
|
rows.add(NutMap.NEW()
|
||||||
|
.setv("href", completeNewsUrl(domain, href))
|
||||||
|
.setv("time", time)
|
||||||
|
.setv("text", title));
|
||||||
|
}
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Completes relative news links while preserving absolute website links.
|
||||||
|
*
|
||||||
|
* @param domain base website domain
|
||||||
|
* @param href original href from the source page
|
||||||
|
* @return absolute link for opening the news detail page
|
||||||
|
*/
|
||||||
|
private String completeNewsUrl(String domain, String href) {
|
||||||
|
if (StrUtil.isBlank(href) || StrUtil.startWithIgnoreCase(href, "http://")
|
||||||
|
|| StrUtil.startWithIgnoreCase(href, "https://")) {
|
||||||
|
return href;
|
||||||
|
}
|
||||||
|
if (href.startsWith("/")) {
|
||||||
|
return domain + href.substring(1);
|
||||||
|
}
|
||||||
|
return domain + href;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-34
@@ -193,14 +193,15 @@ public class TourGroupController {
|
|||||||
t.id,
|
t.id,
|
||||||
t.jobNo,
|
t.jobNo,
|
||||||
t.userName,
|
t.userName,
|
||||||
'' AS mobile,
|
COALESCE(NULLIF(t.mobile, ''), NULLIF(vu.mobile, ''), '') AS mobile,
|
||||||
t.idCard,
|
COALESCE(NULLIF(t.idCard, ''), NULLIF(vu.idCard, ''), '') AS idCard,
|
||||||
t.unionId,
|
t.unionId,
|
||||||
t.unionName,
|
t.unionName,
|
||||||
t.signupTime
|
t.signupTime
|
||||||
FROM tour_ledger t
|
FROM tour_ledger t
|
||||||
INNER JOIN tour_matter m ON m.id = t.matterId
|
INNER JOIN tour_matter m ON m.id = t.matterId
|
||||||
INNER JOIN tour_line l ON l.id = m.lineId
|
INNER JOIN tour_line l ON l.id = m.lineId
|
||||||
|
LEFT JOIN vw_user vu ON vu.loginname = t.jobNo
|
||||||
$condition
|
$condition
|
||||||
ORDER BY $orderColumn $orderBy, t.signupTime DESC, t.createdAt DESC
|
ORDER BY $orderColumn $orderBy, t.signupTime DESC, t.createdAt DESC
|
||||||
""");
|
""");
|
||||||
@@ -212,7 +213,6 @@ public class TourGroupController {
|
|||||||
tourLedgerService.dao().execute(listSql);
|
tourLedgerService.dao().execute(listSql);
|
||||||
|
|
||||||
List<NutMap> list = listSql.getList(NutMap.class);
|
List<NutMap> list = listSql.getList(NutMap.class);
|
||||||
fillSignupMobile(list);
|
|
||||||
fillSignupFamilies(list);
|
fillSignupFamilies(list);
|
||||||
Pagination<NutMap> pagination = new Pagination<>(pageForm.getPageNumber(), pageForm.getPageSize(), count, list);
|
Pagination<NutMap> pagination = new Pagination<>(pageForm.getPageNumber(), pageForm.getPageSize(), count, list);
|
||||||
return Result.success(pagination);
|
return Result.success(pagination);
|
||||||
@@ -300,8 +300,8 @@ public class TourGroupController {
|
|||||||
t.id AS ledgerId,
|
t.id AS ledgerId,
|
||||||
t.jobNo,
|
t.jobNo,
|
||||||
t.userName,
|
t.userName,
|
||||||
t.idCard,
|
COALESCE(NULLIF(t.idCard, ''), NULLIF(vu.idCard, ''), '') AS idCard,
|
||||||
vu.mobile,
|
COALESCE(NULLIF(t.mobile, ''), NULLIF(vu.mobile, ''), '') AS mobile,
|
||||||
t.unionName,
|
t.unionName,
|
||||||
l.lineName,
|
l.lineName,
|
||||||
CASE
|
CASE
|
||||||
@@ -526,35 +526,6 @@ public class TourGroupController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fillSignupMobile(List<NutMap> list) {
|
|
||||||
if (list == null || list.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
List<String> jobNos = new ArrayList<>();
|
|
||||||
for (NutMap item : list) {
|
|
||||||
String jobNo = item.getString("jobNo", "");
|
|
||||||
if (StrUtil.isNotBlank(jobNo) && !jobNos.contains(jobNo)) {
|
|
||||||
jobNos.add(jobNo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (jobNos.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Sql sql = Sqls.create("SELECT loginname, mobile FROM vw_user WHERE loginname IN (@jobNos)");
|
|
||||||
sql.setParam("jobNos", jobNos.toArray(new String[0]));
|
|
||||||
sql.setCallback(Sqls.callback.maps());
|
|
||||||
tourLedgerService.dao().execute(sql);
|
|
||||||
|
|
||||||
Map<String, String> mobileMap = new HashMap<>();
|
|
||||||
for (NutMap user : sql.getList(NutMap.class)) {
|
|
||||||
mobileMap.put(user.getString("loginname", ""), user.getString("mobile", ""));
|
|
||||||
}
|
|
||||||
for (NutMap item : list) {
|
|
||||||
item.put("mobile", mobileMap.getOrDefault(item.getString("jobNo", ""), ""));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Cnd buildQueryCnd(Integer year, String lineName, String lineType, String unionId) {
|
private Cnd buildQueryCnd(Integer year, String lineName, String lineType, String unionId) {
|
||||||
Cnd cnd = Cnd.NEW();
|
Cnd cnd = Cnd.NEW();
|
||||||
cnd.and("m.delFlag", "=", false);
|
cnd.and("m.delFlag", "=", false);
|
||||||
|
|||||||
+25
-6
@@ -133,8 +133,8 @@ public class TourLedgerController {
|
|||||||
COALESCE(NULLIF(l.lineName, ''), t.lineName) AS currentLineName,
|
COALESCE(NULLIF(l.lineName, ''), t.lineName) AS currentLineName,
|
||||||
COALESCE(NULLIF(vu.sex, ''), t.gender, '') AS currentGender,
|
COALESCE(NULLIF(vu.sex, ''), t.gender, '') AS currentGender,
|
||||||
vu.age AS age,
|
vu.age AS age,
|
||||||
COALESCE(NULLIF(vu.idCard, ''), t.idCard, '') AS currentIdCard,
|
COALESCE(NULLIF(t.idCard, ''), NULLIF(vu.idCard, ''), '') AS currentIdCard,
|
||||||
COALESCE(NULLIF(vu.mobile, ''), '') AS mobile,
|
COALESCE(NULLIF(t.mobile, ''), NULLIF(vu.mobile, ''), '') AS currentMobile,
|
||||||
IFNULL(f.familyCount, 0) AS familyCount,
|
IFNULL(f.familyCount, 0) AS familyCount,
|
||||||
CASE
|
CASE
|
||||||
WHEN IFNULL(m.travelStartTime, '') <> '' AND IFNULL(m.travelEndTime, '') <> ''
|
WHEN IFNULL(m.travelStartTime, '') <> '' AND IFNULL(m.travelEndTime, '') <> ''
|
||||||
@@ -188,6 +188,7 @@ public class TourLedgerController {
|
|||||||
item.put("lineName", item.getString("currentLineName"));
|
item.put("lineName", item.getString("currentLineName"));
|
||||||
item.put("gender", item.getString("currentGender"));
|
item.put("gender", item.getString("currentGender"));
|
||||||
item.put("idCard", item.getString("currentIdCard"));
|
item.put("idCard", item.getString("currentIdCard"));
|
||||||
|
item.put("mobile", item.getString("currentMobile"));
|
||||||
});
|
});
|
||||||
Pagination<NutMap> pagination = new Pagination<>(pageForm.getPageNumber(), pageForm.getPageSize(), count, list);
|
Pagination<NutMap> pagination = new Pagination<>(pageForm.getPageNumber(), pageForm.getPageSize(), count, list);
|
||||||
return Result.success(pagination);
|
return Result.success(pagination);
|
||||||
@@ -206,6 +207,7 @@ public class TourLedgerController {
|
|||||||
if (!canAccessLedgerUnion(ledger.getUnionId())) {
|
if (!canAccessLedgerUnion(ledger.getUnionId())) {
|
||||||
return Result.error("无权查看该台账记录");
|
return Result.error("无权查看该台账记录");
|
||||||
}
|
}
|
||||||
|
fillLedgerContactFallback(ledger);
|
||||||
ledger.setLineName(getCurrentLineName(ledger));
|
ledger.setLineName(getCurrentLineName(ledger));
|
||||||
Cnd familyCnd = Cnd.NEW();
|
Cnd familyCnd = Cnd.NEW();
|
||||||
familyCnd.and(TourLedgerFamily::getLedgerId, "=", id);
|
familyCnd.and(TourLedgerFamily::getLedgerId, "=", id);
|
||||||
@@ -392,8 +394,8 @@ public class TourLedgerController {
|
|||||||
t.userName,
|
t.userName,
|
||||||
COALESCE(NULLIF(vu.sex, ''), t.gender, '') AS gender,
|
COALESCE(NULLIF(vu.sex, ''), t.gender, '') AS gender,
|
||||||
vu.age AS age,
|
vu.age AS age,
|
||||||
COALESCE(NULLIF(vu.idCard, ''), t.idCard, '') AS idCard,
|
COALESCE(NULLIF(t.idCard, ''), NULLIF(vu.idCard, ''), '') AS idCard,
|
||||||
COALESCE(NULLIF(vu.mobile, ''), '') AS mobile,
|
COALESCE(NULLIF(t.mobile, ''), NULLIF(vu.mobile, ''), '') AS mobile,
|
||||||
t.unitName,
|
t.unitName,
|
||||||
t.boardingPlace,
|
t.boardingPlace,
|
||||||
COALESCE(NULLIF(l.lineName, ''), t.lineName, '') AS lineName,
|
COALESCE(NULLIF(l.lineName, ''), t.lineName, '') AS lineName,
|
||||||
@@ -491,11 +493,11 @@ public class TourLedgerController {
|
|||||||
t.id,
|
t.id,
|
||||||
t.jobNo,
|
t.jobNo,
|
||||||
t.userName,
|
t.userName,
|
||||||
t.idCard,
|
COALESCE(NULLIF(t.idCard, ''), NULLIF(vu.idCard, ''), '') AS idCard,
|
||||||
t.unionId,
|
t.unionId,
|
||||||
COALESCE(NULLIF(t.unionName, ''), su.name, vu.unionName, '') AS unionName,
|
COALESCE(NULLIF(t.unionName, ''), su.name, vu.unionName, '') AS unionName,
|
||||||
COALESCE(NULLIF(su.unionCode, ''), vu.unionCode, '') AS unionCode,
|
COALESCE(NULLIF(su.unionCode, ''), vu.unionCode, '') AS unionCode,
|
||||||
COALESCE(NULLIF(vu.mobile, ''), '') AS mobile,
|
COALESCE(NULLIF(t.mobile, ''), NULLIF(vu.mobile, ''), '') AS mobile,
|
||||||
COALESCE(NULLIF(l.lineName, ''), t.lineName, '') AS lineName,
|
COALESCE(NULLIF(l.lineName, ''), t.lineName, '') AS lineName,
|
||||||
CASE
|
CASE
|
||||||
WHEN IFNULL(m.travelStartTime, '') <> '' AND IFNULL(m.travelEndTime, '') <> ''
|
WHEN IFNULL(m.travelStartTime, '') <> '' AND IFNULL(m.travelEndTime, '') <> ''
|
||||||
@@ -948,6 +950,23 @@ public class TourLedgerController {
|
|||||||
return StrUtil.isNotBlank(currentUnionId) && StrUtil.equals(currentUnionId, unionId);
|
return StrUtil.isNotBlank(currentUnionId) && StrUtil.equals(currentUnionId, unionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void fillLedgerContactFallback(TourLedger ledger) {
|
||||||
|
if (ledger == null || (StrUtil.isNotBlank(ledger.getIdCard()) && StrUtil.isNotBlank(ledger.getMobile()))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Detail views prefer ledger contact data; blank ledger fields fall back to the user profile snapshot for display only.
|
||||||
|
View_user user = tourLedgerService.dao().fetch(View_user.class, Cnd.where(View_user::getLoginname, "=", ledger.getJobNo()));
|
||||||
|
if (user == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (StrUtil.isBlank(ledger.getIdCard())) {
|
||||||
|
ledger.setIdCard(user.getIdCard());
|
||||||
|
}
|
||||||
|
if (StrUtil.isBlank(ledger.getMobile())) {
|
||||||
|
ledger.setMobile(user.getMobile());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void appendApprovedWorkflowFilter(Cnd cnd) {
|
private void appendApprovedWorkflowFilter(Cnd cnd) {
|
||||||
SqlExpressionGroup group = new SqlExpressionGroup();
|
SqlExpressionGroup group = new SqlExpressionGroup();
|
||||||
group.or("ins.id", "IS", null);
|
group.or("ins.id", "IS", null);
|
||||||
|
|||||||
+111
-2
@@ -27,6 +27,7 @@ import com.budwk.app.zhgh.dayofficework.tour.models.TourLedgerFamily;
|
|||||||
import com.budwk.app.zhgh.dayofficework.tour.models.TourLine;
|
import com.budwk.app.zhgh.dayofficework.tour.models.TourLine;
|
||||||
import com.budwk.app.zhgh.dayofficework.tour.models.TourMatter;
|
import com.budwk.app.zhgh.dayofficework.tour.models.TourMatter;
|
||||||
import com.budwk.app.zhgh.dayofficework.tour.models.TourSetting;
|
import com.budwk.app.zhgh.dayofficework.tour.models.TourSetting;
|
||||||
|
import com.budwk.app.zhgh.dayofficework.tour.models.TourUserAssignment;
|
||||||
import com.budwk.app.zhgh.dayofficework.tour.service.TourLedgerDirectRelativeService;
|
import com.budwk.app.zhgh.dayofficework.tour.service.TourLedgerDirectRelativeService;
|
||||||
import com.budwk.app.zhgh.dayofficework.tour.service.TourLedgerFamilyService;
|
import com.budwk.app.zhgh.dayofficework.tour.service.TourLedgerFamilyService;
|
||||||
import com.budwk.app.zhgh.dayofficework.tour.service.TourLedgerService;
|
import com.budwk.app.zhgh.dayofficework.tour.service.TourLedgerService;
|
||||||
@@ -147,8 +148,13 @@ public class TourMySignupController {
|
|||||||
(SELECT MAX(id) FROM wf_process_task WHERE processInstanceId = ins.id AND taskName = 'startTask' AND taskState IN (10, 20)) AS startTaskId,
|
(SELECT MAX(id) FROM wf_process_task WHERE processInstanceId = ins.id AND taskName = 'startTask' AND taskState IN (10, 20)) AS startTaskId,
|
||||||
IF(IFNULL(l.directFamilyUnitLine, 0) = 1 OR dr.id IS NOT NULL, 1, 0) AS directFamilyUnitLine,
|
IF(IFNULL(l.directFamilyUnitLine, 0) = 1 OR dr.id IS NOT NULL, 1, 0) AS directFamilyUnitLine,
|
||||||
dr.id AS directRelativeId,
|
dr.id AS directRelativeId,
|
||||||
|
COALESCE(NULLIF(t.idCard, ''), NULLIF(vu.idCard, ''), '') AS currentIdCard,
|
||||||
|
COALESCE(NULLIF(t.mobile, ''), NULLIF(vu.mobile, ''), '') AS currentMobile,
|
||||||
IFNULL(GROUP_CONCAT(DISTINCT task.displayName), IF(ins.id IS NULL, '', '结束')) AS curTaskName,
|
IFNULL(GROUP_CONCAT(DISTINCT task.displayName), IF(ins.id IS NULL, '', '结束')) AS curTaskName,
|
||||||
IFNULL(f.familyCount, 0) AS familyCount,
|
IFNULL(f.familyCount, 0) AS familyCount,
|
||||||
|
a.id AS assignmentId,
|
||||||
|
IFNULL(a.cancelled, 0) AS assignmentCancelled,
|
||||||
|
m.travelStartTime AS actionTravelStartTime,
|
||||||
COALESCE(m.travelEndTime, tp.travelEndTime) AS travelEndTime,
|
COALESCE(m.travelEndTime, tp.travelEndTime) AS travelEndTime,
|
||||||
CASE
|
CASE
|
||||||
WHEN IFNULL(m.travelStartTime, '') <> '' AND IFNULL(m.travelEndTime, '') <> ''
|
WHEN IFNULL(m.travelStartTime, '') <> '' AND IFNULL(m.travelEndTime, '') <> ''
|
||||||
@@ -160,6 +166,7 @@ public class TourMySignupController {
|
|||||||
LEFT JOIN wf_process_task task ON task.processInstanceId = ins.id AND task.taskState = 10
|
LEFT JOIN wf_process_task task ON task.processInstanceId = ins.id AND task.taskState = 10
|
||||||
LEFT JOIN tour_line l ON l.id = t.lineId AND l.delFlag = 0
|
LEFT JOIN tour_line l ON l.id = t.lineId AND l.delFlag = 0
|
||||||
LEFT JOIN tour_ledger_direct_relative dr ON dr.ledgerId = t.id AND dr.delFlag = 0
|
LEFT JOIN tour_ledger_direct_relative dr ON dr.ledgerId = t.id AND dr.delFlag = 0
|
||||||
|
LEFT JOIN vw_user vu ON vu.loginname = t.jobNo
|
||||||
LEFT JOIN (
|
LEFT JOIN (
|
||||||
SELECT ledgerId, COUNT(1) AS familyCount
|
SELECT ledgerId, COUNT(1) AS familyCount
|
||||||
FROM tour_ledger_family
|
FROM tour_ledger_family
|
||||||
@@ -167,6 +174,10 @@ public class TourMySignupController {
|
|||||||
GROUP BY ledgerId
|
GROUP BY ledgerId
|
||||||
) f ON f.ledgerId = t.id
|
) f ON f.ledgerId = t.id
|
||||||
LEFT JOIN tour_matter m ON m.id = t.matterId AND m.delFlag = 0
|
LEFT JOIN tour_matter m ON m.id = t.matterId AND m.delFlag = 0
|
||||||
|
LEFT JOIN tour_user_assignment a ON a.matterId = t.matterId
|
||||||
|
AND a.userId = @currentUserId
|
||||||
|
AND a.loginName = t.jobNo
|
||||||
|
AND a.delFlag = 0
|
||||||
LEFT JOIN (
|
LEFT JOIN (
|
||||||
SELECT
|
SELECT
|
||||||
`year`,
|
`year`,
|
||||||
@@ -192,13 +203,20 @@ public class TourMySignupController {
|
|||||||
ORDER BY $orderColumn $orderBy, t.`year` DESC, t.signupTime DESC, t.createdAt DESC
|
ORDER BY $orderColumn $orderBy, t.`year` DESC, t.signupTime DESC, t.createdAt DESC
|
||||||
""");
|
""");
|
||||||
listSql.setCondition(cnd);
|
listSql.setCondition(cnd);
|
||||||
|
listSql.setParam("currentUserId", SecurityUtil.getUserId());
|
||||||
listSql.setVar("orderColumn", getOrderColumn(pageForm.getPageOrderName()));
|
listSql.setVar("orderColumn", getOrderColumn(pageForm.getPageOrderName()));
|
||||||
listSql.setVar("orderBy", "descending".equals(pageForm.getPageOrderBy()) ? "desc" : "asc");
|
listSql.setVar("orderBy", "descending".equals(pageForm.getPageOrderBy()) ? "desc" : "asc");
|
||||||
listSql.setPager(tourLedgerService.dao().createPager(pageForm.getPageNumber(), pageForm.getPageSize()));
|
listSql.setPager(tourLedgerService.dao().createPager(pageForm.getPageNumber(), pageForm.getPageSize()));
|
||||||
listSql.setCallback(Sqls.callback.maps());
|
listSql.setCallback(Sqls.callback.maps());
|
||||||
tourLedgerService.dao().execute(listSql);
|
tourLedgerService.dao().execute(listSql);
|
||||||
|
|
||||||
Pagination<NutMap> pagination = new Pagination<>(pageForm.getPageNumber(), pageForm.getPageSize(), count, listSql.getList(NutMap.class));
|
List<NutMap> list = listSql.getList(NutMap.class);
|
||||||
|
list.forEach(item -> {
|
||||||
|
item.put("idCard", item.getString("currentIdCard"));
|
||||||
|
item.put("mobile", item.getString("currentMobile"));
|
||||||
|
});
|
||||||
|
appendMySignupActionFlags(list);
|
||||||
|
Pagination<NutMap> pagination = new Pagination<>(pageForm.getPageNumber(), pageForm.getPageSize(), count, list);
|
||||||
return Result.success(pagination);
|
return Result.success(pagination);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,6 +227,7 @@ public class TourMySignupController {
|
|||||||
if (ledger == null) {
|
if (ledger == null) {
|
||||||
return Result.error("报名记录不存在");
|
return Result.error("报名记录不存在");
|
||||||
}
|
}
|
||||||
|
fillLedgerContactFallback(ledger);
|
||||||
Cnd familyCnd = Cnd.where(TourLedgerFamily::getDelFlag, "=", false)
|
Cnd familyCnd = Cnd.where(TourLedgerFamily::getDelFlag, "=", false)
|
||||||
.and(TourLedgerFamily::getLedgerId, "=", ledger.getId());
|
.and(TourLedgerFamily::getLedgerId, "=", ledger.getId());
|
||||||
familyCnd.asc(TourLedgerFamily::getCreatedAt);
|
familyCnd.asc(TourLedgerFamily::getCreatedAt);
|
||||||
@@ -279,6 +298,7 @@ public class TourMySignupController {
|
|||||||
if (ledger == null) {
|
if (ledger == null) {
|
||||||
return Result.error("报名记录不存在");
|
return Result.error("报名记录不存在");
|
||||||
}
|
}
|
||||||
|
fillLedgerContactFallback(ledger);
|
||||||
if (StrUtil.isBlank(ledger.getMatterId())) {
|
if (StrUtil.isBlank(ledger.getMatterId())) {
|
||||||
return Result.error("历史报名记录缺少事项信息,不能修改");
|
return Result.error("历史报名记录缺少事项信息,不能修改");
|
||||||
}
|
}
|
||||||
@@ -536,6 +556,29 @@ public class TourMySignupController {
|
|||||||
return Result.success().addMsg("取消报名成功");
|
return Result.success().addMsg("取消报名成功");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移动端我的疗休养退出报名入口。
|
||||||
|
* 仅允许当前登录人操作自己的人员分配记录,具体退出截止天数和台账清理逻辑复用 service 层。
|
||||||
|
*
|
||||||
|
* @param assignmentId 人员分配记录ID
|
||||||
|
* @return 退出结果
|
||||||
|
*/
|
||||||
|
@At
|
||||||
|
@Aop(TransAop.READ_COMMITTED)
|
||||||
|
@SaCheckPermission(value = {"tour.mysignup", "h5.tour.mysignup"}, mode = SaMode.OR)
|
||||||
|
public Result doLeaveTour(String assignmentId) {
|
||||||
|
TourUserAssignment assignment = fetchOwnAssignment(assignmentId);
|
||||||
|
if (assignment == null) {
|
||||||
|
return Result.error("人员分配记录不存在或无权操作");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
tourUserAssignmentService.cancelAssignment(assignment.getId());
|
||||||
|
return Result.success().addMsg("退出疗休养报名成功");
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return Result.error(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@At
|
@At
|
||||||
@Aop(TransAop.READ_COMMITTED)
|
@Aop(TransAop.READ_COMMITTED)
|
||||||
@SaCheckPermission(value = {"tour.mysignup", "h5.tour.mysignup"}, mode = SaMode.OR)
|
@SaCheckPermission(value = {"tour.mysignup", "h5.tour.mysignup"}, mode = SaMode.OR)
|
||||||
@@ -605,6 +648,55 @@ public class TourMySignupController {
|
|||||||
.and(TourLedger::getJobNo, "=", currentJobNo()));
|
.and(TourLedger::getJobNo, "=", currentJobNo()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询当前登录人的人员分配记录,避免移动端传入他人分配ID进行退出。
|
||||||
|
*/
|
||||||
|
private TourUserAssignment fetchOwnAssignment(String assignmentId) {
|
||||||
|
if (StrUtil.isBlank(assignmentId)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return tourUserAssignmentService.fetch(Cnd.where(TourUserAssignment::getId, "=", assignmentId)
|
||||||
|
.and(TourUserAssignment::getUserId, "=", SecurityUtil.getUserId())
|
||||||
|
.and(TourUserAssignment::getLoginName, "=", currentJobNo())
|
||||||
|
.and(TourUserAssignment::getDelFlag, "=", false));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 给移动端我的疗休养列表追加操作按钮显示标识,退出截止时间与 service 层最终校验保持一致。
|
||||||
|
*/
|
||||||
|
private void appendMySignupActionFlags(List<NutMap> list) {
|
||||||
|
if (Lang.isEmpty(list)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int cancelDeadlineDays = tourUserAssignmentService.getCancelDeadlineDays();
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
for (NutMap item : list) {
|
||||||
|
boolean assignmentCancelled = item.getBoolean("assignmentCancelled", false);
|
||||||
|
String assignmentId = item.getString("assignmentId", "");
|
||||||
|
item.put("cancelDeadlineDays", cancelDeadlineDays);
|
||||||
|
item.put("canLeaveTour", StrUtil.isNotBlank(assignmentId)
|
||||||
|
&& !assignmentCancelled
|
||||||
|
&& isWithinLeaveTimeRange(item.getString("actionTravelStartTime", ""), cancelDeadlineDays, now));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断当前时间是否处于“出行开始前N天”到“出行开始前”的可退出范围内。
|
||||||
|
*/
|
||||||
|
private boolean isWithinLeaveTimeRange(String travelStartTime, int cancelDeadlineDays, LocalDateTime now) {
|
||||||
|
try {
|
||||||
|
String normalized = normalizeDateTime(travelStartTime, false);
|
||||||
|
if (StrUtil.isBlank(normalized)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
LocalDateTime startTime = LocalDateTime.parse(normalized, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
||||||
|
LocalDateTime deadline = startTime.minusDays(cancelDeadlineDays);
|
||||||
|
return !now.isBefore(deadline) && now.isBefore(startTime);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Result checkSignup(TourLedger ledger) {
|
private Result checkSignup(TourLedger ledger) {
|
||||||
if (ledger == null) {
|
if (ledger == null) {
|
||||||
return Result.error("参数错误");
|
return Result.error("参数错误");
|
||||||
@@ -1037,13 +1129,30 @@ public class TourMySignupController {
|
|||||||
ledger.setJobNo(user == null ? SecurityUtil.getUserLoginname() : defaultIfBlank(user.getLoginname(), SecurityUtil.getUserLoginname()));
|
ledger.setJobNo(user == null ? SecurityUtil.getUserLoginname() : defaultIfBlank(user.getLoginname(), SecurityUtil.getUserLoginname()));
|
||||||
ledger.setUserName(user == null ? SecurityUtil.getUserUsername() : defaultIfBlank(user.getUsername(), SecurityUtil.getUserUsername()));
|
ledger.setUserName(user == null ? SecurityUtil.getUserUsername() : defaultIfBlank(user.getUsername(), SecurityUtil.getUserUsername()));
|
||||||
ledger.setGender(user == null ? ledger.getGender() : defaultIfBlank(user.getSex(), ledger.getGender()));
|
ledger.setGender(user == null ? ledger.getGender() : defaultIfBlank(user.getSex(), ledger.getGender()));
|
||||||
ledger.setIdCard(user == null ? ledger.getIdCard() : defaultIfBlank(user.getIdCard(), ledger.getIdCard()));
|
ledger.setIdCard(user == null ? ledger.getIdCard() : defaultIfBlank(ledger.getIdCard(), user.getIdCard()));
|
||||||
ledger.setUnitId(user == null ? SecurityUtil.getUnitId() : defaultIfBlank(user.getUnitId(), SecurityUtil.getUnitId()));
|
ledger.setUnitId(user == null ? SecurityUtil.getUnitId() : defaultIfBlank(user.getUnitId(), SecurityUtil.getUnitId()));
|
||||||
ledger.setUnitName(user == null ? ledger.getUnitName() : defaultIfBlank(user.getUnitName(), ledger.getUnitName()));
|
ledger.setUnitName(user == null ? ledger.getUnitName() : defaultIfBlank(user.getUnitName(), ledger.getUnitName()));
|
||||||
ledger.setUnionId(user == null ? SecurityUtil.getUnionId() : defaultIfBlank(user.getUnionId(), ledger.getUnionId()));
|
ledger.setUnionId(user == null ? SecurityUtil.getUnionId() : defaultIfBlank(user.getUnionId(), ledger.getUnionId()));
|
||||||
ledger.setUnionName(user == null ? ledger.getUnionName() : defaultIfBlank(user.getUnionName(), ledger.getUnionName()));
|
ledger.setUnionName(user == null ? ledger.getUnionName() : defaultIfBlank(user.getUnionName(), ledger.getUnionName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void fillLedgerContactFallback(TourLedger ledger) {
|
||||||
|
if (ledger == null || (StrUtil.isNotBlank(ledger.getIdCard()) && StrUtil.isNotBlank(ledger.getMobile()))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// My signup detail keeps ledger contact data first; blank contact fields use the user profile snapshot for display only.
|
||||||
|
View_user user = tourLedgerService.dao().fetch(View_user.class, Cnd.where(View_user::getLoginname, "=", ledger.getJobNo()));
|
||||||
|
if (user == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (StrUtil.isBlank(ledger.getIdCard())) {
|
||||||
|
ledger.setIdCard(user.getIdCard());
|
||||||
|
}
|
||||||
|
if (StrUtil.isBlank(ledger.getMobile())) {
|
||||||
|
ledger.setMobile(user.getMobile());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void clearBedInfo(TourLedger ledger, List<TourLedgerFamily> familyList) {
|
private void clearBedInfo(TourLedger ledger, List<TourLedgerFamily> familyList) {
|
||||||
if (ledger != null) {
|
if (ledger != null) {
|
||||||
ledger.setBedType("");
|
ledger.setBedType("");
|
||||||
|
|||||||
+19
@@ -7,6 +7,7 @@ import com.budwk.app.base.page.Pagination;
|
|||||||
import com.budwk.app.base.param.PageForm;
|
import com.budwk.app.base.param.PageForm;
|
||||||
import com.budwk.app.base.result.Result;
|
import com.budwk.app.base.result.Result;
|
||||||
import com.budwk.app.flow.enums.ProcessTaskStateEnum;
|
import com.budwk.app.flow.enums.ProcessTaskStateEnum;
|
||||||
|
import com.budwk.app.sys.views.View_user;
|
||||||
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
|
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
|
||||||
import com.budwk.app.zhgh.dayofficework.tour.models.TourLedger;
|
import com.budwk.app.zhgh.dayofficework.tour.models.TourLedger;
|
||||||
import com.budwk.app.zhgh.dayofficework.tour.models.TourLedgerDirectRelative;
|
import com.budwk.app.zhgh.dayofficework.tour.models.TourLedgerDirectRelative;
|
||||||
@@ -171,6 +172,7 @@ public class TourSchoolUnionApprovalController {
|
|||||||
if (ledger == null) {
|
if (ledger == null) {
|
||||||
return Result.error("报名记录不存在或无权查看");
|
return Result.error("报名记录不存在或无权查看");
|
||||||
}
|
}
|
||||||
|
fillLedgerContactFallback(ledger);
|
||||||
ledger.setLineName(getCurrentLineName(ledger));
|
ledger.setLineName(getCurrentLineName(ledger));
|
||||||
Cnd familyCnd = Cnd.where(TourLedgerFamily::getLedgerId, "=", ledger.getId())
|
Cnd familyCnd = Cnd.where(TourLedgerFamily::getLedgerId, "=", ledger.getId())
|
||||||
.and(TourLedgerFamily::getDelFlag, "=", false);
|
.and(TourLedgerFamily::getDelFlag, "=", false);
|
||||||
@@ -465,6 +467,23 @@ public class TourSchoolUnionApprovalController {
|
|||||||
return sql.getInt() > 0 ? tourLedgerService.fetch(id) : null;
|
return sql.getInt() > 0 ? tourLedgerService.fetch(id) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void fillLedgerContactFallback(TourLedger ledger) {
|
||||||
|
if (ledger == null || (StrUtil.isNotBlank(ledger.getIdCard()) && StrUtil.isNotBlank(ledger.getMobile()))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Approval detail keeps ledger contact data first; blank contact fields use the user profile snapshot for display only.
|
||||||
|
View_user user = tourLedgerService.dao().fetch(View_user.class, Cnd.where(View_user::getLoginname, "=", ledger.getJobNo()));
|
||||||
|
if (user == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (StrUtil.isBlank(ledger.getIdCard())) {
|
||||||
|
ledger.setIdCard(user.getIdCard());
|
||||||
|
}
|
||||||
|
if (StrUtil.isBlank(ledger.getMobile())) {
|
||||||
|
ledger.setMobile(user.getMobile());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private String getCurrentLineName(TourLedger ledger) {
|
private String getCurrentLineName(TourLedger ledger) {
|
||||||
if (ledger == null) {
|
if (ledger == null) {
|
||||||
return "";
|
return "";
|
||||||
|
|||||||
+145
-17
@@ -26,6 +26,7 @@ import com.budwk.app.zhgh.dayofficework.tour.models.TourLedgerFamily;
|
|||||||
import com.budwk.app.zhgh.dayofficework.tour.models.TourLine;
|
import com.budwk.app.zhgh.dayofficework.tour.models.TourLine;
|
||||||
import com.budwk.app.zhgh.dayofficework.tour.models.TourMatter;
|
import com.budwk.app.zhgh.dayofficework.tour.models.TourMatter;
|
||||||
import com.budwk.app.zhgh.dayofficework.tour.models.TourSetting;
|
import com.budwk.app.zhgh.dayofficework.tour.models.TourSetting;
|
||||||
|
import com.budwk.app.zhgh.dayofficework.tour.models.TourUserAssignment;
|
||||||
import com.budwk.app.zhgh.dayofficework.tour.service.TourLedgerDirectRelativeService;
|
import com.budwk.app.zhgh.dayofficework.tour.service.TourLedgerDirectRelativeService;
|
||||||
import com.budwk.app.zhgh.dayofficework.tour.service.TourLedgerFamilyService;
|
import com.budwk.app.zhgh.dayofficework.tour.service.TourLedgerFamilyService;
|
||||||
import com.budwk.app.zhgh.dayofficework.tour.service.TourLedgerService;
|
import com.budwk.app.zhgh.dayofficework.tour.service.TourLedgerService;
|
||||||
@@ -167,6 +168,8 @@ public class TourSignupController {
|
|||||||
m.`year`,
|
m.`year`,
|
||||||
m.unionId,
|
m.unionId,
|
||||||
COALESCE(u.name, '校工会') AS unionName,
|
COALESCE(u.name, '校工会') AS unionName,
|
||||||
|
m.signupStartTime,
|
||||||
|
m.signupEndTime,
|
||||||
m.lineId,
|
m.lineId,
|
||||||
l.lineName,
|
l.lineName,
|
||||||
l.lineType,
|
l.lineType,
|
||||||
@@ -185,6 +188,8 @@ public class TourSignupController {
|
|||||||
my.instanceState,
|
my.instanceState,
|
||||||
my.taskKey,
|
my.taskKey,
|
||||||
my.startTaskId,
|
my.startTaskId,
|
||||||
|
my.assignmentId,
|
||||||
|
IFNULL(my.assignmentCancelled, 0) AS assignmentCancelled,
|
||||||
IFNULL(my.canRevoke, 0) AS canRevoke,
|
IFNULL(my.canRevoke, 0) AS canRevoke,
|
||||||
CASE WHEN my.ledgerId IS NULL THEN 0 ELSE 1 END AS signed
|
CASE WHEN my.ledgerId IS NULL THEN 0 ELSE 1 END AS signed
|
||||||
FROM tour_matter m
|
FROM tour_matter m
|
||||||
@@ -217,9 +222,12 @@ public class TourSignupController {
|
|||||||
MAX(ins.id) AS instanceId,
|
MAX(ins.id) AS instanceId,
|
||||||
MAX(ins.state) AS instanceState,
|
MAX(ins.state) AS instanceState,
|
||||||
MAX(task.taskName) AS taskKey,
|
MAX(task.taskName) AS taskKey,
|
||||||
|
MAX(ua.id) AS assignmentId,
|
||||||
|
MAX(IFNULL(ua.cancelled, 0)) AS assignmentCancelled,
|
||||||
MAX((SELECT MAX(st.id) FROM wf_process_task st WHERE st.processInstanceId = ins.id AND st.taskName = 'startTask' AND st.taskState IN (10, 20))) AS startTaskId,
|
MAX((SELECT MAX(st.id) FROM wf_process_task st WHERE st.processInstanceId = ins.id AND st.taskName = 'startTask' AND st.taskState IN (10, 20))) AS startTaskId,
|
||||||
MAX(IF((SELECT taskName FROM wf_process_task tt WHERE tt.id = task.taskParentId) = 'startTask', 1, 0)) AS canRevoke
|
MAX(IF((SELECT taskName FROM wf_process_task tt WHERE tt.id = task.taskParentId) = 'startTask', 1, 0)) AS canRevoke
|
||||||
FROM tour_ledger t
|
FROM tour_ledger t
|
||||||
|
LEFT JOIN tour_user_assignment ua ON ua.matterId = t.matterId AND ua.loginName = t.jobNo AND ua.delFlag = 0
|
||||||
LEFT JOIN wf_process_instance ins ON ins.businessNo = t.id AND ins.state <> @abandonState
|
LEFT JOIN wf_process_instance ins ON ins.businessNo = t.id AND ins.state <> @abandonState
|
||||||
LEFT JOIN wf_process_task task ON task.processInstanceId = ins.id AND task.taskState = 10
|
LEFT JOIN wf_process_task task ON task.processInstanceId = ins.id AND task.taskState = 10
|
||||||
WHERE t.delFlag = 0
|
WHERE t.delFlag = 0
|
||||||
@@ -240,17 +248,73 @@ public class TourSignupController {
|
|||||||
listSql.setCallback(Sqls.callback.maps());
|
listSql.setCallback(Sqls.callback.maps());
|
||||||
tourMatterService.dao().execute(listSql);
|
tourMatterService.dao().execute(listSql);
|
||||||
|
|
||||||
Pagination<NutMap> pagination = new Pagination<>(pageForm.getPageNumber(), pageForm.getPageSize(), count, listSql.getList(NutMap.class));
|
List<NutMap> list = listSql.getList(NutMap.class);
|
||||||
|
appendH5LineActionFlags(list);
|
||||||
|
Pagination<NutMap> pagination = new Pagination<>(pageForm.getPageNumber(), pageForm.getPageSize(), count, list);
|
||||||
return Result.success(pagination);
|
return Result.success(pagination);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移动端退出疗休养入口,复用人员分配退出逻辑并限定只能操作当前登录人的分配记录。
|
||||||
|
*
|
||||||
|
* @param assignmentId 人员分配记录ID
|
||||||
|
* @return 退出结果
|
||||||
|
*/
|
||||||
|
@At("/h5/doLeaveTour")
|
||||||
|
@Aop(TransAop.READ_COMMITTED)
|
||||||
|
@SaCheckPermission("h5.tour.signup")
|
||||||
|
public Result h5DoLeaveTour(String assignmentId) {
|
||||||
|
TourUserAssignment assignment = fetchCurrentUserAssignment(assignmentId);
|
||||||
|
if (assignment == null) {
|
||||||
|
return Result.error("人员分配记录不存在或无权操作");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
tourUserAssignmentService.cancelAssignment(assignment.getId());
|
||||||
|
return Result.success();
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return Result.error(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移动端取消当前线路,删除本人的报名台账并清空人员分配中的线路快照。
|
||||||
|
*
|
||||||
|
* @param ledgerId 报名台账ID
|
||||||
|
* @return 取消结果
|
||||||
|
*/
|
||||||
|
@At("/h5/doCancelLine")
|
||||||
|
@Aop(TransAop.READ_COMMITTED)
|
||||||
|
@SaCheckPermission("h5.tour.signup")
|
||||||
|
public Result h5DoCancelLine(String ledgerId) {
|
||||||
|
if (StrUtil.isBlank(ledgerId)) {
|
||||||
|
return Result.error("报名台账参数错误");
|
||||||
|
}
|
||||||
|
TourLedger ledger = tourLedgerService.fetch(Cnd.where(TourLedger::getId, "=", ledgerId)
|
||||||
|
.and(TourLedger::getJobNo, "=", currentJobNo())
|
||||||
|
.and(TourLedger::getDelFlag, "=", false));
|
||||||
|
if (ledger == null) {
|
||||||
|
return Result.error("报名台账不存在或无权操作");
|
||||||
|
}
|
||||||
|
TourMatter matter = tourMatterService.fetch(ledger.getMatterId());
|
||||||
|
Result signupTimeResult = checkSignupTime(matter);
|
||||||
|
if (signupTimeResult != null) {
|
||||||
|
return signupTimeResult;
|
||||||
|
}
|
||||||
|
tourLedgerFamilyService.clear(Cnd.where(TourLedgerFamily::getLedgerId, "=", ledger.getId()));
|
||||||
|
tourLedgerDirectRelativeService.clear(Cnd.where(TourLedgerDirectRelative::getLedgerId, "=", ledger.getId()));
|
||||||
|
tourLedgerService.delete(ledger.getId());
|
||||||
|
tourUserAssignmentService.clearExistingAssignmentMatterAfterLedgerDelete(ledger.getMatterId(), ledger.getJobNo());
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
@At
|
@At
|
||||||
@SaCheckPermission(value = {"tour.signup", "h5.tour.signup"}, mode = SaMode.OR)
|
@SaCheckPermission(value = {"tour.signup", "h5.tour.signup"}, mode = SaMode.OR)
|
||||||
public Result pageData(PageForm pageForm, Integer year, String lineName, String lineType, String unionId) {
|
public Result pageData(PageForm pageForm, Integer year, String lineName, String lineType, String unionId, String travelPeriod) {
|
||||||
if (StrUtil.isBlank(pageForm.getPageOrderName())) {
|
if (StrUtil.isBlank(pageForm.getPageOrderName())) {
|
||||||
pageForm.defaultSortAsc("lineName");
|
pageForm.defaultSortAsc("lineName");
|
||||||
}
|
}
|
||||||
Cnd cnd = buildQueryCnd(year, lineName, lineType, unionId);
|
Cnd cnd = buildQueryCnd(year, lineName, lineType, unionId);
|
||||||
|
appendTravelPeriodCnd(cnd, travelPeriod);
|
||||||
String currentJobNo = currentJobNo();
|
String currentJobNo = currentJobNo();
|
||||||
|
|
||||||
Sql countSql = Sqls.create("""
|
Sql countSql = Sqls.create("""
|
||||||
@@ -375,9 +439,10 @@ public class TourSignupController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询移动端选择路线页的出行时间筛选项,按 yyyyMMdd-yyyyMMdd 格式拼接事项出行起止时间并去重。
|
* Queries distinct travel periods for PC signup and H5 line selection.
|
||||||
|
* The value uses yyyyMMdd-yyyyMMdd so the frontend can pass it back as a stable filter key.
|
||||||
*/
|
*/
|
||||||
@At("/h5/travelPeriodOptions")
|
@At({"/h5/travelPeriodOptions", "/travelPeriodOptions"})
|
||||||
@SaCheckPermission(value = {"tour.signup", "h5.tour.signup"}, mode = SaMode.OR)
|
@SaCheckPermission(value = {"tour.signup", "h5.tour.signup"}, mode = SaMode.OR)
|
||||||
public Result h5TravelPeriodOptions(Integer year, String lineType, Boolean directFamilyUnitLine) {
|
public Result h5TravelPeriodOptions(Integer year, String lineType, Boolean directFamilyUnitLine) {
|
||||||
Cnd cnd = buildQueryCnd(year, null, null, null);
|
Cnd cnd = buildQueryCnd(year, null, null, null);
|
||||||
@@ -490,20 +555,11 @@ public class TourSignupController {
|
|||||||
.and(TourLedger::getJobNo, "=", staff.getString("jobNo"));
|
.and(TourLedger::getJobNo, "=", staff.getString("jobNo"));
|
||||||
ledgerCnd.desc(TourLedger::getCreatedAt);
|
ledgerCnd.desc(TourLedger::getCreatedAt);
|
||||||
TourLedger ledger = tourLedgerService.fetch(ledgerCnd);
|
TourLedger ledger = tourLedgerService.fetch(ledgerCnd);
|
||||||
TourMatter matterEntity = tourMatterService.fetch(matter.getString("matterId"));
|
// Viewing a line should only load detail data; signup rules are enforced when the user starts or submits signup.
|
||||||
TourLedger ruleLedger = new TourLedger();
|
|
||||||
ruleLedger.setId(ledger == null ? null : ledger.getId());
|
|
||||||
ruleLedger.setYear(matterEntity == null ? matter.getInt("year") : matterEntity.getYear());
|
|
||||||
ruleLedger.setMatterId(matter.getString("matterId"));
|
|
||||||
ruleLedger.setLineId(matter.getString("lineId"));
|
|
||||||
ruleLedger.setLineName(matter.getString("lineName"));
|
|
||||||
Result ruleResult = checkSignupRule(ruleLedger, matterEntity, ledger);
|
|
||||||
if (ruleResult != null) {
|
|
||||||
return ruleResult;
|
|
||||||
}
|
|
||||||
List<TourLedgerFamily> families = Collections.emptyList();
|
List<TourLedgerFamily> families = Collections.emptyList();
|
||||||
TourLedgerDirectRelative directRelative = null;
|
TourLedgerDirectRelative directRelative = null;
|
||||||
if (ledger != null) {
|
if (ledger != null) {
|
||||||
|
fillLedgerContactFallback(ledger, staff);
|
||||||
Cnd familyCnd = Cnd.where(TourLedgerFamily::getDelFlag, "=", false)
|
Cnd familyCnd = Cnd.where(TourLedgerFamily::getDelFlag, "=", false)
|
||||||
.and(TourLedgerFamily::getLedgerId, "=", ledger.getId());
|
.and(TourLedgerFamily::getLedgerId, "=", ledger.getId());
|
||||||
familyCnd.asc(TourLedgerFamily::getCreatedAt);
|
familyCnd.asc(TourLedgerFamily::getCreatedAt);
|
||||||
@@ -793,7 +849,7 @@ public class TourSignupController {
|
|||||||
return workflowResult;
|
return workflowResult;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Result.success().addMsg(approvalRequired ? (update ? "修改已提交,等待审核" : "申请已提交,等待审核") : (update ? "修改成功" : "报名成功"));
|
return Result.success().addMsg(approvalRequired ? (update ? "修改已提交,等待审核" : "申请已提交,等待审核") : (update ? "修改成功" : "恭喜您已报名成功"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@At
|
@At
|
||||||
@@ -1199,6 +1255,65 @@ public class TourSignupController {
|
|||||||
return setting != null && "ASSIGNED_USER".equals(setting.getSignupEligibilityMode());
|
return setting != null && "ASSIGNED_USER".equals(setting.getSignupEligibilityMode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void appendH5LineActionFlags(List<NutMap> list) {
|
||||||
|
if (Lang.isEmpty(list)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int cancelDeadlineDays = tourUserAssignmentService.getCancelDeadlineDays();
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
for (NutMap item : list) {
|
||||||
|
boolean signed = item.getBoolean("signed", false);
|
||||||
|
boolean assignmentCancelled = item.getBoolean("assignmentCancelled", false);
|
||||||
|
String assignmentId = item.getString("assignmentId", "");
|
||||||
|
String ledgerId = item.getString("ledgerId", "");
|
||||||
|
// Button visibility is calculated on the server so H5 follows the same time rules as backend submit actions.
|
||||||
|
item.put("cancelDeadlineDays", cancelDeadlineDays);
|
||||||
|
item.put("canLeaveTour", signed && StrUtil.isNotBlank(assignmentId) && !assignmentCancelled
|
||||||
|
&& isWithinLeaveTimeRange(item.getString("travelStartTime", ""), cancelDeadlineDays, now));
|
||||||
|
item.put("canCancelLine", signed && StrUtil.isNotBlank(ledgerId)
|
||||||
|
&& isWithinSignupRange(item.getString("signupStartTime", ""), item.getString("signupEndTime", ""), now));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isWithinLeaveTimeRange(String travelStartTime, int cancelDeadlineDays, LocalDateTime now) {
|
||||||
|
LocalDateTime startTime = parseDateTimeValue(travelStartTime, false);
|
||||||
|
if (startTime == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
LocalDateTime deadline = startTime.minusDays(cancelDeadlineDays);
|
||||||
|
return !now.isBefore(deadline) && now.isBefore(startTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isWithinSignupRange(String signupStartTime, String signupEndTime, LocalDateTime now) {
|
||||||
|
LocalDateTime startTime = parseDateTimeValue(signupStartTime, false);
|
||||||
|
LocalDateTime endTime = parseDateTimeValue(signupEndTime, true);
|
||||||
|
if (startTime == null || endTime == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !now.isBefore(startTime) && !now.isAfter(endTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
private LocalDateTime parseDateTimeValue(String value, boolean endOfDay) {
|
||||||
|
String normalized = normalizeDateTime(value, endOfDay);
|
||||||
|
if (StrUtil.isBlank(normalized)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return LocalDateTime.parse(normalized.substring(0, 19), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private TourUserAssignment fetchCurrentUserAssignment(String assignmentId) {
|
||||||
|
if (StrUtil.isBlank(assignmentId)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return tourLedgerService.dao().fetch(TourUserAssignment.class, Cnd.where(TourUserAssignment::getId, "=", assignmentId)
|
||||||
|
.and(TourUserAssignment::getUserId, "=", SecurityUtil.getUserId())
|
||||||
|
.and(TourUserAssignment::getDelFlag, "=", false));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 统一校验疗休养事项报名时间,确保资格名单校验在时间校验通过后再执行。
|
* 统一校验疗休养事项报名时间,确保资格名单校验在时间校验通过后再执行。
|
||||||
*/
|
*/
|
||||||
@@ -1497,13 +1612,26 @@ public class TourSignupController {
|
|||||||
ledger.setJobNo(user == null ? SecurityUtil.getUserLoginname() : defaultIfBlank(user.getLoginname(), SecurityUtil.getUserLoginname()));
|
ledger.setJobNo(user == null ? SecurityUtil.getUserLoginname() : defaultIfBlank(user.getLoginname(), SecurityUtil.getUserLoginname()));
|
||||||
ledger.setUserName(user == null ? SecurityUtil.getUserUsername() : defaultIfBlank(user.getUsername(), SecurityUtil.getUserUsername()));
|
ledger.setUserName(user == null ? SecurityUtil.getUserUsername() : defaultIfBlank(user.getUsername(), SecurityUtil.getUserUsername()));
|
||||||
ledger.setGender(user == null ? ledger.getGender() : defaultIfBlank(user.getSex(), ledger.getGender()));
|
ledger.setGender(user == null ? ledger.getGender() : defaultIfBlank(user.getSex(), ledger.getGender()));
|
||||||
ledger.setIdCard(user == null ? ledger.getIdCard() : defaultIfBlank(user.getIdCard(), ledger.getIdCard()));
|
ledger.setIdCard(user == null ? ledger.getIdCard() : defaultIfBlank(ledger.getIdCard(), user.getIdCard()));
|
||||||
ledger.setUnitId(user == null ? SecurityUtil.getUnitId() : defaultIfBlank(user.getUnitId(), SecurityUtil.getUnitId()));
|
ledger.setUnitId(user == null ? SecurityUtil.getUnitId() : defaultIfBlank(user.getUnitId(), SecurityUtil.getUnitId()));
|
||||||
ledger.setUnitName(user == null ? ledger.getUnitName() : defaultIfBlank(user.getUnitName(), ledger.getUnitName()));
|
ledger.setUnitName(user == null ? ledger.getUnitName() : defaultIfBlank(user.getUnitName(), ledger.getUnitName()));
|
||||||
ledger.setUnionId(user == null ? SecurityUtil.getUnionId() : defaultIfBlank(user.getUnionId(), SecurityUtil.getUnionId()));
|
ledger.setUnionId(user == null ? SecurityUtil.getUnionId() : defaultIfBlank(user.getUnionId(), SecurityUtil.getUnionId()));
|
||||||
ledger.setUnionName(user == null ? ledger.getUnionName() : defaultIfBlank(user.getUnionName(), ledger.getUnionName()));
|
ledger.setUnionName(user == null ? ledger.getUnionName() : defaultIfBlank(user.getUnionName(), ledger.getUnionName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void fillLedgerContactFallback(TourLedger ledger, NutMap staff) {
|
||||||
|
if (ledger == null || staff == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Signup detail keeps saved ledger contact data first; blank contact fields use the user profile snapshot for display only.
|
||||||
|
if (StrUtil.isBlank(ledger.getIdCard())) {
|
||||||
|
ledger.setIdCard(staff.getString("idCard", ""));
|
||||||
|
}
|
||||||
|
if (StrUtil.isBlank(ledger.getMobile())) {
|
||||||
|
ledger.setMobile(staff.getString("mobile", ""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private List<TourLedgerFamily> parseFamilies(String families) {
|
private List<TourLedgerFamily> parseFamilies(String families) {
|
||||||
if (StrUtil.isBlank(families)) {
|
if (StrUtil.isBlank(families)) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
|
|||||||
+19
@@ -7,6 +7,7 @@ import com.budwk.app.base.page.Pagination;
|
|||||||
import com.budwk.app.base.param.PageForm;
|
import com.budwk.app.base.param.PageForm;
|
||||||
import com.budwk.app.base.result.Result;
|
import com.budwk.app.base.result.Result;
|
||||||
import com.budwk.app.flow.enums.ProcessTaskStateEnum;
|
import com.budwk.app.flow.enums.ProcessTaskStateEnum;
|
||||||
|
import com.budwk.app.sys.views.View_user;
|
||||||
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
|
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
|
||||||
import com.budwk.app.zhgh.dayofficework.tour.models.TourLedger;
|
import com.budwk.app.zhgh.dayofficework.tour.models.TourLedger;
|
||||||
import com.budwk.app.zhgh.dayofficework.tour.models.TourLedgerDirectRelative;
|
import com.budwk.app.zhgh.dayofficework.tour.models.TourLedgerDirectRelative;
|
||||||
@@ -171,6 +172,7 @@ public class TourUnionApprovalController {
|
|||||||
if (ledger == null) {
|
if (ledger == null) {
|
||||||
return Result.error("报名记录不存在或无权查看");
|
return Result.error("报名记录不存在或无权查看");
|
||||||
}
|
}
|
||||||
|
fillLedgerContactFallback(ledger);
|
||||||
ledger.setLineName(getCurrentLineName(ledger));
|
ledger.setLineName(getCurrentLineName(ledger));
|
||||||
Cnd familyCnd = Cnd.where(TourLedgerFamily::getLedgerId, "=", ledger.getId())
|
Cnd familyCnd = Cnd.where(TourLedgerFamily::getLedgerId, "=", ledger.getId())
|
||||||
.and(TourLedgerFamily::getDelFlag, "=", false);
|
.and(TourLedgerFamily::getDelFlag, "=", false);
|
||||||
@@ -465,6 +467,23 @@ public class TourUnionApprovalController {
|
|||||||
return sql.getInt() > 0 ? tourLedgerService.fetch(id) : null;
|
return sql.getInt() > 0 ? tourLedgerService.fetch(id) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void fillLedgerContactFallback(TourLedger ledger) {
|
||||||
|
if (ledger == null || (StrUtil.isNotBlank(ledger.getIdCard()) && StrUtil.isNotBlank(ledger.getMobile()))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Approval detail keeps ledger contact data first; blank contact fields use the user profile snapshot for display only.
|
||||||
|
View_user user = tourLedgerService.dao().fetch(View_user.class, Cnd.where(View_user::getLoginname, "=", ledger.getJobNo()));
|
||||||
|
if (user == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (StrUtil.isBlank(ledger.getIdCard())) {
|
||||||
|
ledger.setIdCard(user.getIdCard());
|
||||||
|
}
|
||||||
|
if (StrUtil.isBlank(ledger.getMobile())) {
|
||||||
|
ledger.setMobile(user.getMobile());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private String getCurrentLineName(TourLedger ledger) {
|
private String getCurrentLineName(TourLedger ledger) {
|
||||||
if (ledger == null) {
|
if (ledger == null) {
|
||||||
return "";
|
return "";
|
||||||
|
|||||||
+2
-2
@@ -416,9 +416,9 @@ public class TourUnionLedgerController {
|
|||||||
SELECT
|
SELECT
|
||||||
t.id,
|
t.id,
|
||||||
t.userName,
|
t.userName,
|
||||||
t.idCard,
|
COALESCE(NULLIF(t.idCard, ''), NULLIF(vu.idCard, ''), '') AS idCard,
|
||||||
t.unionName,
|
t.unionName,
|
||||||
vu.mobile,
|
COALESCE(NULLIF(t.mobile, ''), NULLIF(vu.mobile, ''), '') AS mobile,
|
||||||
m.travelStartTime,
|
m.travelStartTime,
|
||||||
m.travelEndTime,
|
m.travelEndTime,
|
||||||
m.estimatedCost,
|
m.estimatedCost,
|
||||||
|
|||||||
@@ -51,6 +51,11 @@ public class TourLedger extends BaseModel implements Serializable {
|
|||||||
@ColDefine(type = ColType.VARCHAR, width = 30)
|
@ColDefine(type = ColType.VARCHAR, width = 30)
|
||||||
private String idCard;
|
private String idCard;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@Comment("手机号")
|
||||||
|
@ColDefine(type = ColType.VARCHAR, width = 30)
|
||||||
|
private String mobile;
|
||||||
|
|
||||||
@Column
|
@Column
|
||||||
@Comment("所在单位ID")
|
@Comment("所在单位ID")
|
||||||
@ColDefine(type = ColType.VARCHAR, width = 32)
|
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||||
|
|||||||
+7
@@ -272,6 +272,13 @@ public interface TourUserAssignmentService extends BaseService<TourUserAssignmen
|
|||||||
*/
|
*/
|
||||||
void cancelAssignment(String id);
|
void cancelAssignment(String id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询疗休养退出取消的出行开始前限制天数,供移动端按钮展示和实际退出校验保持一致。
|
||||||
|
*
|
||||||
|
* @return 出行开始前允许退出的天数
|
||||||
|
*/
|
||||||
|
int getCancelDeadlineDays();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将人员分配记录从已退出恢复为未退出。
|
* 将人员分配记录从已退出恢复为未退出。
|
||||||
* 只恢复人员分配表状态,不自动恢复已删除的报名台账。
|
* 只恢复人员分配表状态,不自动恢复已删除的报名台账。
|
||||||
|
|||||||
+6
-3
@@ -886,12 +886,15 @@ public class TourUserAssignmentServiceImpl extends BaseServiceImpl<TourUserAssig
|
|||||||
LocalDateTime travelStartTime = parseTravelStartTime(matter.getTravelStartTime());
|
LocalDateTime travelStartTime = parseTravelStartTime(matter.getTravelStartTime());
|
||||||
int days = getCancelDeadlineDays();
|
int days = getCancelDeadlineDays();
|
||||||
LocalDateTime deadline = travelStartTime.minusDays(days);
|
LocalDateTime deadline = travelStartTime.minusDays(days);
|
||||||
if (LocalDateTime.now().isAfter(deadline)) {
|
LocalDateTime now = LocalDateTime.now();
|
||||||
throw new IllegalArgumentException("当前路线已超过取消时间,需在出行开始前" + days + "天取消");
|
// 退出报名仅允许在“出行开始前N天”到“出行开始前”之间办理,出行开始时刻不能再退出。
|
||||||
|
if (now.isBefore(deadline) || !now.isBefore(travelStartTime)) {
|
||||||
|
throw new IllegalArgumentException("当前不在退出时间范围内,需在出行开始前" + days + "天至出行开始前办理");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getCancelDeadlineDays() {
|
@Override
|
||||||
|
public int getCancelDeadlineDays() {
|
||||||
try {
|
try {
|
||||||
Sys_config config = sysConfigService == null ? null : sysConfigService.getValueByKey(TOUR_CANCEL_DEADLINE_DAYS_KEY);
|
Sys_config config = sysConfigService == null ? null : sysConfigService.getValueByKey(TOUR_CANCEL_DEADLINE_DAYS_KEY);
|
||||||
String value = config == null ? "" : config.getConfigValue();
|
String value = config == null ? "" : config.getConfigValue();
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
-- Add signup ledger mobile field; signup mobile is stored with the ledger and does not update user profile data.
|
||||||
|
ALTER TABLE `tour_ledger`
|
||||||
|
ADD COLUMN `mobile` varchar(30) DEFAULT NULL COMMENT '手机号' AFTER `idCard`;
|
||||||
@@ -128,7 +128,7 @@ layout("/layouts/v4/baseLayout.html"){
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<work-template v-if="$auth.hasRoleOr(['SYSADMIN','SCHOOL_UNION_ADMIN'])"></work-template>
|
<!-- <work-template v-if="$auth.hasRoleOr(['SYSADMIN','SCHOOL_UNION_ADMIN'])"></work-template>-->
|
||||||
|
|
||||||
<jcdt :list="websiteNews"></jcdt>
|
<jcdt :list="websiteNews"></jcdt>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+1
-1
@@ -80,7 +80,7 @@ layout("/layouts/platform.html"){
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="分配时间" prop="assignedAt" width="170" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
<el-table-column label="分配时间" prop="assignedAt" width="170" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
|
||||||
<el-table-column label="操作" width="320" align="center" header-align="center" fixed="right">
|
<el-table-column label="操作" width="380" align="center" header-align="center" fixed="right">
|
||||||
<template slot-scope="{row}">
|
<template slot-scope="{row}">
|
||||||
<el-button v-if="!row.matterId && row.personType !== 'BACKUP'" size="mini" type="primary" @click="openSelectMatter(row)">选择路线</el-button>
|
<el-button v-if="!row.matterId && row.personType !== 'BACKUP'" size="mini" type="primary" @click="openSelectMatter(row)">选择路线</el-button>
|
||||||
<el-button size="mini" type="primary" :loading="personTypeSwitching === row.id" @click="switchPersonType(row, row.personType === 'BACKUP' ? 'FORMAL' : 'BACKUP')">{{ row.personType === 'BACKUP' ? '转为正式' : '转为替补' }}</el-button>
|
<el-button size="mini" type="primary" :loading="personTypeSwitching === row.id" @click="switchPersonType(row, row.personType === 'BACKUP' ? 'FORMAL' : 'BACKUP')">{{ row.personType === 'BACKUP' ? '转为正式' : '转为替补' }}</el-button>
|
||||||
|
|||||||
@@ -42,7 +42,23 @@ layout("/layouts/platform.html"){
|
|||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
<el-card class="mt10" shadow="never">
|
<el-card class="mt10" shadow="never">
|
||||||
<table-tool :app="this" label="线路列表"></table-tool>
|
<div class="tour-list-header">
|
||||||
|
<table-tool :app="this" label="线路列表"></table-tool>
|
||||||
|
<div class="tour-period-area" v-if="travelPeriodOptions.length > 0">
|
||||||
|
<div class="tour-period-filter">
|
||||||
|
<button
|
||||||
|
v-for="item in travelPeriodOptions"
|
||||||
|
:key="item.value"
|
||||||
|
type="button"
|
||||||
|
class="tour-period-chip"
|
||||||
|
:class="{'tour-period-chip--active': pageForm.travelPeriod === item.value}"
|
||||||
|
@click="selectTravelPeriod(item)">
|
||||||
|
{{ item.value }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="tour-period-tip">可选择指定出行时间,快速查看对应路线</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<el-table
|
<el-table
|
||||||
v-loading="tableLoading"
|
v-loading="tableLoading"
|
||||||
@@ -470,6 +486,62 @@ layout("/layouts/platform.html"){
|
|||||||
.over-cost-form-item .el-radio__input.is-checked + .el-radio__label {
|
.over-cost-form-item .el-radio__input.is-checked + .el-radio__label {
|
||||||
color: #f56c6c;
|
color: #f56c6c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tour-list-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tour-list-header /deep/ .wk-table-tool,
|
||||||
|
.tour-list-header .wk-table-tool {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tour-period-area {
|
||||||
|
flex: 1 1 0;
|
||||||
|
min-width: 240px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tour-period-filter {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tour-period-tip {
|
||||||
|
margin-top: 6px;
|
||||||
|
color: #f56c6c;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tour-period-chip {
|
||||||
|
min-width: 118px;
|
||||||
|
height: 34px;
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 0 14px;
|
||||||
|
background: #ffffff;
|
||||||
|
color: #606266;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 32px;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
box-sizing: border-box;
|
||||||
|
transition: all 0.18s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tour-period-chip:hover,
|
||||||
|
.tour-period-chip--active {
|
||||||
|
border-color: #0076c8;
|
||||||
|
background: #0076c8;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script nonce="${cspNonce!}">
|
<script nonce="${cspNonce!}">
|
||||||
@@ -481,6 +553,7 @@ layout("/layouts/platform.html"){
|
|||||||
return {
|
return {
|
||||||
unionOptions: [],
|
unionOptions: [],
|
||||||
lineTypeOptions: [],
|
lineTypeOptions: [],
|
||||||
|
travelPeriodOptions: [],
|
||||||
signupVisible: false,
|
signupVisible: false,
|
||||||
signupLoading: false,
|
signupLoading: false,
|
||||||
cancelLoading: false,
|
cancelLoading: false,
|
||||||
@@ -505,17 +578,36 @@ layout("/layouts/platform.html"){
|
|||||||
year: currentYear,
|
year: currentYear,
|
||||||
lineName: "",
|
lineName: "",
|
||||||
lineType: "",
|
lineType: "",
|
||||||
unionId: ""
|
unionId: "",
|
||||||
|
travelPeriod: ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
resetSearch() {
|
resetSearch() {
|
||||||
this.pageForm.year = moment().format("YYYY")
|
const currentYear = moment().format("YYYY")
|
||||||
|
const yearChanged = this.pageForm.year !== currentYear
|
||||||
|
this.pageForm.year = currentYear
|
||||||
this.pageForm.lineName = ""
|
this.pageForm.lineName = ""
|
||||||
this.pageForm.lineType = ""
|
this.pageForm.lineType = ""
|
||||||
this.pageForm.unionId = ""
|
this.pageForm.unionId = ""
|
||||||
|
this.pageForm.travelPeriod = ""
|
||||||
|
if (yearChanged) {
|
||||||
|
return
|
||||||
|
}
|
||||||
this.loadLineTypeOptions()
|
this.loadLineTypeOptions()
|
||||||
|
this.loadTravelPeriodOptions(true).then(() => {
|
||||||
|
this.doSearch()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
selectTravelPeriod(item) {
|
||||||
|
const travelPeriod = item && item.value ? item.value : ""
|
||||||
|
if (this.pageForm.travelPeriod === travelPeriod) {
|
||||||
|
this.pageForm.travelPeriod = ""
|
||||||
|
this.doSearch()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.pageForm.travelPeriod = travelPeriod
|
||||||
this.doSearch()
|
this.doSearch()
|
||||||
},
|
},
|
||||||
signupActionText(row) {
|
signupActionText(row) {
|
||||||
@@ -1026,6 +1118,31 @@ layout("/layouts/platform.html"){
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
loadTravelPeriodOptions(defaultFirst) {
|
||||||
|
return this.$axios.post(loc() + "/travelPeriodOptions", {
|
||||||
|
year: this.pageForm.year
|
||||||
|
}).then((res) => {
|
||||||
|
if (res.code !== 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.travelPeriodOptions = (res.data || []).map((item) => {
|
||||||
|
const value = item.travelPeriod || ""
|
||||||
|
return {
|
||||||
|
value: value,
|
||||||
|
name: value || "未设置出行时间"
|
||||||
|
}
|
||||||
|
}).filter((item) => item.value).sort((a, b) => {
|
||||||
|
return String(a.value).localeCompare(String(b.value))
|
||||||
|
})
|
||||||
|
if (defaultFirst) {
|
||||||
|
this.pageForm.travelPeriod = this.travelPeriodOptions.length > 0 ? this.travelPeriodOptions[0].value : ""
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
if (defaultFirst) {
|
||||||
|
this.pageForm.travelPeriod = ""
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
loadDirectRelativeOptions() {
|
loadDirectRelativeOptions() {
|
||||||
this.$axios.post(loc() + "/directRelativeOptions").then((res) => {
|
this.$axios.post(loc() + "/directRelativeOptions").then((res) => {
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
@@ -1054,12 +1171,18 @@ layout("/layouts/platform.html"){
|
|||||||
this.loadDirectRelativeOptions()
|
this.loadDirectRelativeOptions()
|
||||||
this.loadBedTypeOptions()
|
this.loadBedTypeOptions()
|
||||||
this.loadFamilyRelationshipOptions()
|
this.loadFamilyRelationshipOptions()
|
||||||
this.pageData()
|
this.loadTravelPeriodOptions(true).then(() => {
|
||||||
|
this.pageData()
|
||||||
|
})
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
"pageForm.year"() {
|
"pageForm.year"() {
|
||||||
this.pageForm.lineType = ""
|
this.pageForm.lineType = ""
|
||||||
|
this.pageForm.travelPeriod = ""
|
||||||
this.loadLineTypeOptions()
|
this.loadLineTypeOptions()
|
||||||
|
this.loadTravelPeriodOptions(true).then(() => {
|
||||||
|
this.doSearch()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -378,6 +378,7 @@ layout("/layouts/platform_h5.html"){
|
|||||||
<van-button size="small" type="info" plain @click="openView(row)">查看</van-button>
|
<van-button size="small" type="info" plain @click="openView(row)">查看</van-button>
|
||||||
<van-button v-if="canModify(row) || isTravelEnded(row)" size="small" type="info" :disabled="isTravelEnded(row)" @click="openSignup(row)">修改</van-button>
|
<van-button v-if="canModify(row) || isTravelEnded(row)" size="small" type="info" :disabled="isTravelEnded(row)" @click="openSignup(row)">修改</van-button>
|
||||||
<van-button v-if="canRevoke(row)" size="small" type="danger" plain @click="revokeSignup(row)">撤回</van-button>
|
<van-button v-if="canRevoke(row)" size="small" type="danger" plain @click="revokeSignup(row)">撤回</van-button>
|
||||||
|
<van-button v-if="showLeaveTour(row)" size="small" type="danger" plain :loading="leaveLoading" @click="leaveTour(row)">退出疗休养报名</van-button>
|
||||||
<van-button v-if="showCancel(row)" size="small" type="danger" plain :disabled="isTravelEnded(row)" @click="cancelByRow(row)">取消报名</van-button>
|
<van-button v-if="showCancel(row)" size="small" type="danger" plain :disabled="isTravelEnded(row)" @click="cancelByRow(row)">取消报名</van-button>
|
||||||
<van-button v-if="showExport(row)" size="small" type="info" plain :disabled="!canExport(row)" @click="downloadExport(row, 'cost')">报销申请</van-button>
|
<van-button v-if="showExport(row)" size="small" type="info" plain :disabled="!canExport(row)" @click="downloadExport(row, 'cost')">报销申请</van-button>
|
||||||
<van-button v-if="showDirectRelativeExport(row)" size="small" type="info" plain :disabled="!canDirectRelativeExport(row)" @click="downloadExport(row, 'direct')">亲属线路申请</van-button>
|
<van-button v-if="showDirectRelativeExport(row)" size="small" type="info" plain :disabled="!canDirectRelativeExport(row)" @click="downloadExport(row, 'direct')">亲属线路申请</van-button>
|
||||||
@@ -584,6 +585,7 @@ layout("/layouts/platform_h5.html"){
|
|||||||
signupVisible: false,
|
signupVisible: false,
|
||||||
signupLoading: false,
|
signupLoading: false,
|
||||||
cancelLoading: false,
|
cancelLoading: false,
|
||||||
|
leaveLoading: false,
|
||||||
allowFamily: false,
|
allowFamily: false,
|
||||||
fillBedInfo: true,
|
fillBedInfo: true,
|
||||||
signupForm: {},
|
signupForm: {},
|
||||||
@@ -733,6 +735,10 @@ layout("/layouts/platform_h5.html"){
|
|||||||
showCancel(row) {
|
showCancel(row) {
|
||||||
return !this.isApprovalRow(row) || this.canModify(row)
|
return !this.isApprovalRow(row) || this.canModify(row)
|
||||||
},
|
},
|
||||||
|
// 退出疗休养报名依赖人员分配记录,已退出的数据不再展示入口。
|
||||||
|
showLeaveTour(row) {
|
||||||
|
return this.toBoolean(row && row.canLeaveTour)
|
||||||
|
},
|
||||||
showExport(row) {
|
showExport(row) {
|
||||||
return this.toBoolean(row && row.overCostReimbursed)
|
return this.toBoolean(row && row.overCostReimbursed)
|
||||||
},
|
},
|
||||||
@@ -1075,6 +1081,32 @@ layout("/layouts/platform_h5.html"){
|
|||||||
})
|
})
|
||||||
}).catch(() => {})
|
}).catch(() => {})
|
||||||
},
|
},
|
||||||
|
leaveTour(row) {
|
||||||
|
if (!row || !row.assignmentId) {
|
||||||
|
vant.Toast("暂无可退出的疗休养报名")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
vant.Dialog.confirm({
|
||||||
|
title: "温馨提醒",
|
||||||
|
message: "是否确认退出本次疗休养报名,退出后将取消报名资格!",
|
||||||
|
confirmButtonColor: "#ee2f2f"
|
||||||
|
}).then(() => {
|
||||||
|
this.leaveLoading = true
|
||||||
|
this.$axios.post("/platform/tour/mysignup/doLeaveTour", {
|
||||||
|
assignmentId: row.assignmentId
|
||||||
|
}).then((res) => {
|
||||||
|
this.leaveLoading = false
|
||||||
|
if (res.code === 0) {
|
||||||
|
vant.Toast.success(res.msg || "退出疗休养报名成功")
|
||||||
|
this.doSearch()
|
||||||
|
} else {
|
||||||
|
vant.Dialog.alert({ title: "提示", message: res.msg || "退出失败", confirmButtonColor: "#1867b0" })
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
this.leaveLoading = false
|
||||||
|
})
|
||||||
|
}).catch(() => {})
|
||||||
|
},
|
||||||
revokeSignup(row) {
|
revokeSignup(row) {
|
||||||
vant.Dialog.confirm({
|
vant.Dialog.confirm({
|
||||||
title: "温馨提醒",
|
title: "温馨提醒",
|
||||||
@@ -1106,6 +1138,7 @@ layout("/layouts/platform_h5.html"){
|
|||||||
this.fillBedInfo = true
|
this.fillBedInfo = true
|
||||||
this.signupLoading = false
|
this.signupLoading = false
|
||||||
this.cancelLoading = false
|
this.cancelLoading = false
|
||||||
|
this.leaveLoading = false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
|
|||||||
@@ -128,10 +128,10 @@ layout("/layouts/platform_h5.html"){
|
|||||||
<div class="tour-apply-title">教职工信息</div>
|
<div class="tour-apply-title">教职工信息</div>
|
||||||
<van-field label="姓名" readonly v-model="signupForm.userName"></van-field>
|
<van-field label="姓名" readonly v-model="signupForm.userName"></van-field>
|
||||||
<van-field label="工号" readonly v-model="signupForm.jobNo"></van-field>
|
<van-field label="工号" readonly v-model="signupForm.jobNo"></van-field>
|
||||||
<van-field label="身份证号码" v-model="signupForm.idCard" maxlength="30" placeholder="请填写身份证号码"></van-field>
|
<van-field label="身份证号码" required v-model="signupForm.idCard" maxlength="30" placeholder="请填写身份证号码"></van-field>
|
||||||
<van-field label="性别" readonly v-model="signupForm.gender"></van-field>
|
<van-field label="性别" readonly v-model="signupForm.gender"></van-field>
|
||||||
<van-field label="年龄" readonly :value="staffAge(signupForm)"></van-field>
|
<van-field label="年龄" readonly :value="staffAge(signupForm)"></van-field>
|
||||||
<van-field label="手机号" v-model="signupForm.mobile" type="tel" maxlength="30" placeholder="请填写手机号"></van-field>
|
<van-field label="手机号" required v-model="signupForm.mobile" type="tel" maxlength="30" placeholder="请填写手机号"></van-field>
|
||||||
<van-field label="所属工会" readonly v-model="signupForm.unionName"></van-field>
|
<van-field label="所属工会" readonly v-model="signupForm.unionName"></van-field>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -142,6 +142,7 @@ layout("/layouts/platform_h5.html"){
|
|||||||
<van-field v-if="signupForm.travelPeriod" label="出行时间" readonly v-model="signupForm.travelPeriod"></van-field>
|
<van-field v-if="signupForm.travelPeriod" label="出行时间" readonly v-model="signupForm.travelPeriod"></van-field>
|
||||||
<van-field
|
<van-field
|
||||||
label="乘车地点"
|
label="乘车地点"
|
||||||
|
required
|
||||||
readonly
|
readonly
|
||||||
clickable
|
clickable
|
||||||
is-link
|
is-link
|
||||||
@@ -620,6 +621,27 @@ layout("/layouts/platform_h5.html"){
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
|
showSubmitError(error) {
|
||||||
|
// Keep signup submit errors local so the global axios interceptor does not show a duplicate toast.
|
||||||
|
vant.Toast.clear()
|
||||||
|
const responseData = error && error.response && error.response.data ? error.response.data : null
|
||||||
|
const message = responseData && (responseData.msg || responseData.message)
|
||||||
|
? (responseData.msg || responseData.message)
|
||||||
|
: (error && (error.msg || error.message) ? (error.msg || error.message) : "报名提交失败")
|
||||||
|
vant.Toast({
|
||||||
|
message: message,
|
||||||
|
duration: 3000
|
||||||
|
})
|
||||||
|
},
|
||||||
|
submitSignupRequest(form) {
|
||||||
|
// Use raw axios for this submit only; the page controls backend error text and toast duration locally.
|
||||||
|
return axios.post("/platform/tour/signup/doSignup", $.param(form), {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
|
||||||
|
"x-requested-with": "XMLHttpRequest"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
submitSignup() {
|
submitSignup() {
|
||||||
if (this.pageLoading || this.submitLoading) return
|
if (this.pageLoading || this.submitLoading) return
|
||||||
if (this.boardingPlaceOptions.length > 0 && !this.signupForm.boardingPlace) {
|
if (this.boardingPlaceOptions.length > 0 && !this.signupForm.boardingPlace) {
|
||||||
@@ -634,26 +656,23 @@ layout("/layouts/platform_h5.html"){
|
|||||||
directRelative: this.isDirectFamilyLine(this.signupForm) ? JSON.stringify(this.directRelativeForm) : ""
|
directRelative: this.isDirectFamilyLine(this.signupForm) ? JSON.stringify(this.directRelativeForm) : ""
|
||||||
})
|
})
|
||||||
this.submitLoading = true
|
this.submitLoading = true
|
||||||
this.$axios.post("/platform/tour/signup/doSignup", form).then((res) => {
|
this.submitSignupRequest(form).then((resp) => {
|
||||||
this.submitLoading = false
|
this.submitLoading = false
|
||||||
|
const res = resp.data || {}
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
vant.Dialog.alert({
|
vant.Dialog.alert({
|
||||||
title: "提示",
|
title: "提示",
|
||||||
message: res.msg || "报名成功",
|
message: res.msg || "恭喜您已报名成功",
|
||||||
confirmButtonColor: "#1867b0"
|
confirmButtonColor: "#1867b0"
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
window.location.replace("/platform/tour/signup/h5/signup")
|
window.location.replace("/platform/tour/signup/h5/signup")
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
vant.Dialog.alert({
|
this.showSubmitError(res)
|
||||||
title: "提示",
|
|
||||||
message: res.msg || "报名失败",
|
|
||||||
confirmButtonColor: "#1867b0"
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}).catch(() => {
|
}).catch((error) => {
|
||||||
this.submitLoading = false
|
this.submitLoading = false
|
||||||
vant.Toast("报名提交失败")
|
this.showSubmitError(error)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -229,7 +229,7 @@ layout("/layouts/platform_h5.html"){
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tour-detail-footer">
|
<div class="tour-detail-footer">
|
||||||
<van-button :type="detailActionType()" block round :disabled="detailActionDisabled()" @click="handleDetailAction">
|
<van-button :type="detailActionType()" block round @click="handleDetailAction">
|
||||||
{{ detailActionText() }}
|
{{ detailActionText() }}
|
||||||
</van-button>
|
</van-button>
|
||||||
</div>
|
</div>
|
||||||
@@ -311,13 +311,7 @@ layout("/layouts/platform_h5.html"){
|
|||||||
detailActionType() {
|
detailActionType() {
|
||||||
return this.canRevokeSignup() ? "danger" : "info"
|
return this.canRevokeSignup() ? "danger" : "info"
|
||||||
},
|
},
|
||||||
detailActionDisabled() {
|
|
||||||
return this.isSigned() && this.isApprovalSignup() && (this.isFinishedSignup() || this.isRejectedSignup() || (!this.canRevokeSignup() && !this.canModifySignup()))
|
|
||||||
},
|
|
||||||
handleDetailAction() {
|
handleDetailAction() {
|
||||||
if (this.detailActionDisabled()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (this.canRevokeSignup()) {
|
if (this.canRevokeSignup()) {
|
||||||
this.revokeSignup()
|
this.revokeSignup()
|
||||||
return
|
return
|
||||||
@@ -522,39 +516,8 @@ layout("/layouts/platform_h5.html"){
|
|||||||
vant.Toast("报名事项信息不完整")
|
vant.Toast("报名事项信息不完整")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const goApply = () => {
|
// 线路详情底部按钮不做报名资格前置校验,所有业务规则统一放到最终提交报名时处理。
|
||||||
window.location.href = "/platform/tour/signup/h5/apply?matterId=" + encodeURIComponent(this.matterId)
|
window.location.href = "/platform/tour/signup/h5/apply?matterId=" + encodeURIComponent(this.matterId)
|
||||||
}
|
|
||||||
this.$axios.post("/platform/tour/signup/signupEligibilityNotice", { matterId: this.matterId }).then((res) => {
|
|
||||||
if (res.code !== 0) {
|
|
||||||
vant.Dialog.alert({
|
|
||||||
title: "温馨提醒",
|
|
||||||
message: res.msg || "当前不能报名",
|
|
||||||
confirmButtonColor: "#1867b0"
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const data = res.data || {}
|
|
||||||
if (!data.noticeRequired) {
|
|
||||||
goApply()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (data.canApply) {
|
|
||||||
vant.Dialog.confirm({
|
|
||||||
title: "温馨提醒",
|
|
||||||
message: data.message || "当前可报名,是否继续报名?",
|
|
||||||
confirmButtonText: "继续报名",
|
|
||||||
cancelButtonText: "稍后报名",
|
|
||||||
confirmButtonColor: "#1867b0"
|
|
||||||
}).then(goApply).catch(() => {})
|
|
||||||
} else {
|
|
||||||
vant.Dialog.alert({
|
|
||||||
title: "温馨提醒",
|
|
||||||
message: data.message || "当前不能报名",
|
|
||||||
confirmButtonColor: "#1867b0"
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
revokeSignup() {
|
revokeSignup() {
|
||||||
vant.Dialog.confirm({
|
vant.Dialog.confirm({
|
||||||
|
|||||||
@@ -326,13 +326,12 @@ layout("/layouts/platform_h5.html"){
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tour-line-title-row {
|
.tour-line-title-row {
|
||||||
display: flex;
|
position: relative;
|
||||||
align-items: center;
|
padding-right: 76px;
|
||||||
gap: 6px;
|
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tour-line-title {
|
.tour-line-title-left {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
color: #0f172a;
|
color: #0f172a;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
@@ -341,9 +340,15 @@ layout("/layouts/platform_h5.html"){
|
|||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tour-line-title {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
.tour-line-type-badge {
|
.tour-line-type-badge {
|
||||||
height: 18px;
|
height: 18px;
|
||||||
flex-shrink: 0;
|
display: inline-block;
|
||||||
|
margin-left: 5px;
|
||||||
|
vertical-align: 2px;
|
||||||
padding: 0 5px;
|
padding: 0 5px;
|
||||||
border: 1px solid #ffd6a8;
|
border: 1px solid #ffd6a8;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
@@ -355,6 +360,21 @@ layout("/layouts/platform_h5.html"){
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tour-line-signup-count {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 22px;
|
||||||
|
padding: 0 7px;
|
||||||
|
border-radius: 11px;
|
||||||
|
background: #d9fde8;
|
||||||
|
color: #2fcf6b;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 22px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
.tour-line-tags {
|
.tour-line-tags {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@@ -389,7 +409,9 @@ layout("/layouts/platform_h5.html"){
|
|||||||
|
|
||||||
.tour-line-actions {
|
.tour-line-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
gap: 8px;
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -410,6 +432,12 @@ layout("/layouts/platform_h5.html"){
|
|||||||
background: #ee2f2f;
|
background: #ee2f2f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tour-line-action--plain {
|
||||||
|
border-color: #1e88e5;
|
||||||
|
background: #ffffff;
|
||||||
|
color: #1e88e5;
|
||||||
|
}
|
||||||
|
|
||||||
.tour-line-action--disabled {
|
.tour-line-action--disabled {
|
||||||
border-color: #d1d5db;
|
border-color: #d1d5db;
|
||||||
background: #e5e7eb;
|
background: #e5e7eb;
|
||||||
@@ -509,6 +537,7 @@ layout("/layouts/platform_h5.html"){
|
|||||||
<van-list
|
<van-list
|
||||||
v-model="loading"
|
v-model="loading"
|
||||||
:finished="finished"
|
:finished="finished"
|
||||||
|
:immediate-check="false"
|
||||||
finished-text="没有更多了"
|
finished-text="没有更多了"
|
||||||
@load="loadData">
|
@load="loadData">
|
||||||
<div class="tour-line-list">
|
<div class="tour-line-list">
|
||||||
@@ -526,11 +555,14 @@ layout("/layouts/platform_h5.html"){
|
|||||||
</div>
|
</div>
|
||||||
<div class="tour-line-main">
|
<div class="tour-line-main">
|
||||||
<div class="tour-line-title-row">
|
<div class="tour-line-title-row">
|
||||||
<div class="tour-line-title">{{ row.lineName || '未命名线路' }}</div>
|
<div class="tour-line-title-left">
|
||||||
<span v-if="lineTypeBadge(row.lineType)" class="tour-line-type-badge">{{ lineTypeBadge(row.lineType) }}</span>
|
<div class="tour-line-title">{{ row.lineName || '未命名线路' }}</div>
|
||||||
<span v-if="isSigned(row)" class="tour-line-signed-icon">
|
<span v-if="lineTypeBadge(row.lineType)" class="tour-line-type-badge">{{ lineTypeBadge(row.lineType) }}</span>
|
||||||
<van-icon name="good-job"></van-icon>
|
<span v-if="isSigned(row)" class="tour-line-signed-icon">
|
||||||
</span>
|
<van-icon name="good-job"></van-icon>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<span v-if="hasSignupCount(row)" class="tour-line-signup-count">已报名 {{ row.signupCount }}人</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="tour-line-tags">
|
<div class="tour-line-tags">
|
||||||
<span v-if="row.lotName" class="tour-line-tag">{{ row.lotName }}</span>
|
<span v-if="row.lotName" class="tour-line-tag">{{ row.lotName }}</span>
|
||||||
@@ -556,6 +588,20 @@ layout("/layouts/platform_h5.html"){
|
|||||||
@click.stop="handleSignupAction(row)">
|
@click.stop="handleSignupAction(row)">
|
||||||
{{ signupActionText(row) }}
|
{{ signupActionText(row) }}
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
v-if="showLeaveTourAction(row)"
|
||||||
|
type="button"
|
||||||
|
class="tour-line-action tour-line-action--danger"
|
||||||
|
@click.stop="leaveTour(row)">
|
||||||
|
退出疗休养
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
v-if="showCancelLineAction(row)"
|
||||||
|
type="button"
|
||||||
|
class="tour-line-action tour-line-action--plain"
|
||||||
|
@click.stop="cancelLine(row)">
|
||||||
|
取消本线路
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -596,7 +642,6 @@ layout("/layouts/platform_h5.html"){
|
|||||||
list: [],
|
list: [],
|
||||||
loading: false,
|
loading: false,
|
||||||
finished: false,
|
finished: false,
|
||||||
lineChecking: false,
|
|
||||||
showUnionPopup: false,
|
showUnionPopup: false,
|
||||||
unionList: [
|
unionList: [
|
||||||
{id: "", name: "全部分工会"}
|
{id: "", name: "全部分工会"}
|
||||||
@@ -680,6 +725,9 @@ layout("/layouts/platform_h5.html"){
|
|||||||
isSigned(row) {
|
isSigned(row) {
|
||||||
return !!(row && (row.signed === true || row.signed === 1 || row.signed === "1" || row.ledgerId))
|
return !!(row && (row.signed === true || row.signed === 1 || row.signed === "1" || row.ledgerId))
|
||||||
},
|
},
|
||||||
|
hasSignupCount(row) {
|
||||||
|
return row && row.signupCount !== undefined && row.signupCount !== null && row.signupCount !== ""
|
||||||
|
},
|
||||||
isApprovalSignup(row) {
|
isApprovalSignup(row) {
|
||||||
return !!(row && this.isSigned(row) && row.instanceId)
|
return !!(row && this.isSigned(row) && row.instanceId)
|
||||||
},
|
},
|
||||||
@@ -714,7 +762,13 @@ layout("/layouts/platform_h5.html"){
|
|||||||
return "报名"
|
return "报名"
|
||||||
},
|
},
|
||||||
showSignupAction(row) {
|
showSignupAction(row) {
|
||||||
return this.isSigned(row) && this.canModifySignup(row)
|
return false
|
||||||
|
},
|
||||||
|
showLeaveTourAction(row) {
|
||||||
|
return this.isSigned(row) && this.toBoolean(row && row.canLeaveTour) && !!(row && row.assignmentId)
|
||||||
|
},
|
||||||
|
showCancelLineAction(row) {
|
||||||
|
return this.isSigned(row) && this.toBoolean(row && row.canCancelLine) && !!(row && row.ledgerId)
|
||||||
},
|
},
|
||||||
signupActionDisabled(row) {
|
signupActionDisabled(row) {
|
||||||
return !!(this.isSigned(row) && this.isApprovalSignup(row) && (this.isRejectedSignup(row) || (!this.canRevokeSignup(row) && !this.canModifySignup(row))))
|
return !!(this.isSigned(row) && this.isApprovalSignup(row) && (this.isRejectedSignup(row) || (!this.canRevokeSignup(row) && !this.canModifySignup(row))))
|
||||||
@@ -737,39 +791,8 @@ layout("/layouts/platform_h5.html"){
|
|||||||
vant.Toast("报名事项信息不完整")
|
vant.Toast("报名事项信息不完整")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const go = () => {
|
// 进入报名页不再提前做资格校验,所有业务校验统一放到最终提交报名时处理。
|
||||||
window.location.href = "/platform/tour/signup/h5/apply?matterId=" + encodeURIComponent(row.matterId)
|
window.location.href = "/platform/tour/signup/h5/apply?matterId=" + encodeURIComponent(row.matterId)
|
||||||
}
|
|
||||||
this.$axios.post("/platform/tour/signup/signupEligibilityNotice", { matterId: row.matterId }).then((res) => {
|
|
||||||
if (res.code !== 0) {
|
|
||||||
vant.Dialog.alert({
|
|
||||||
title: "温馨提醒",
|
|
||||||
message: res.msg || "当前不能报名",
|
|
||||||
confirmButtonColor: "#1867b0"
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const data = res.data || {}
|
|
||||||
if (!data.noticeRequired) {
|
|
||||||
go()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (data.canApply) {
|
|
||||||
vant.Dialog.confirm({
|
|
||||||
title: "温馨提醒",
|
|
||||||
message: data.message || "当前可报名,是否继续报名?",
|
|
||||||
confirmButtonText: "继续报名",
|
|
||||||
cancelButtonText: "稍后报名",
|
|
||||||
confirmButtonColor: "#1867b0"
|
|
||||||
}).then(go).catch(() => {})
|
|
||||||
} else {
|
|
||||||
vant.Dialog.alert({
|
|
||||||
title: "温馨提醒",
|
|
||||||
message: data.message || "当前不能报名",
|
|
||||||
confirmButtonColor: "#1867b0"
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
revokeSignup(row) {
|
revokeSignup(row) {
|
||||||
vant.Dialog.confirm({
|
vant.Dialog.confirm({
|
||||||
@@ -787,6 +810,46 @@ layout("/layouts/platform_h5.html"){
|
|||||||
})
|
})
|
||||||
}).catch(() => {})
|
}).catch(() => {})
|
||||||
},
|
},
|
||||||
|
leaveTour(row) {
|
||||||
|
if (!row || !row.assignmentId) {
|
||||||
|
vant.Toast("人员分配信息不完整")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
vant.Dialog.confirm({
|
||||||
|
title: "提示",
|
||||||
|
message: "是否确认退出本次疗休养,退出后将取消报名资格!",
|
||||||
|
confirmButtonColor: "#ee2f2f"
|
||||||
|
}).then(() => {
|
||||||
|
this.$axios.post("/platform/tour/signup/h5/doLeaveTour", { assignmentId: row.assignmentId }).then((res) => {
|
||||||
|
if (res.code === 0) {
|
||||||
|
vant.Toast.success(res.msg || "退出成功")
|
||||||
|
this.doSearch()
|
||||||
|
} else {
|
||||||
|
vant.Dialog.alert({ title: "提示", message: res.msg || "退出失败", confirmButtonColor: "#1867b0" })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}).catch(() => {})
|
||||||
|
},
|
||||||
|
cancelLine(row) {
|
||||||
|
if (!row || !row.ledgerId) {
|
||||||
|
vant.Toast("报名台账信息不完整")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
vant.Dialog.confirm({
|
||||||
|
title: "提示",
|
||||||
|
message: "是否确认取消本线路?",
|
||||||
|
confirmButtonColor: "#ee2f2f"
|
||||||
|
}).then(() => {
|
||||||
|
this.$axios.post("/platform/tour/signup/h5/doCancelLine", { ledgerId: row.ledgerId }).then((res) => {
|
||||||
|
if (res.code === 0) {
|
||||||
|
vant.Toast.success(res.msg || "取消成功")
|
||||||
|
this.doSearch()
|
||||||
|
} else {
|
||||||
|
vant.Dialog.alert({ title: "提示", message: res.msg || "取消失败", confirmButtonColor: "#1867b0" })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}).catch(() => {})
|
||||||
|
},
|
||||||
doSearch() {
|
doSearch() {
|
||||||
this.pageForm.pageNumber = 1
|
this.pageForm.pageNumber = 1
|
||||||
this.pageForm.totalCount = 0
|
this.pageForm.totalCount = 0
|
||||||
@@ -799,16 +862,18 @@ layout("/layouts/platform_h5.html"){
|
|||||||
this.pageForm.lineType = active ? "" : lineType
|
this.pageForm.lineType = active ? "" : lineType
|
||||||
this.pageForm.directFamilyUnitLine = null
|
this.pageForm.directFamilyUnitLine = null
|
||||||
this.pageForm.travelPeriod = ""
|
this.pageForm.travelPeriod = ""
|
||||||
this.loadTravelPeriodOptions()
|
this.loadTravelPeriodOptions(true).then(() => {
|
||||||
this.doSearch()
|
this.doSearch()
|
||||||
|
})
|
||||||
},
|
},
|
||||||
toggleDirectFamilyLine() {
|
toggleDirectFamilyLine() {
|
||||||
const active = this.pageForm.directFamilyUnitLine === true
|
const active = this.pageForm.directFamilyUnitLine === true
|
||||||
this.pageForm.directFamilyUnitLine = active ? null : true
|
this.pageForm.directFamilyUnitLine = active ? null : true
|
||||||
this.pageForm.lineType = ""
|
this.pageForm.lineType = ""
|
||||||
this.pageForm.travelPeriod = ""
|
this.pageForm.travelPeriod = ""
|
||||||
this.loadTravelPeriodOptions()
|
this.loadTravelPeriodOptions(true).then(() => {
|
||||||
this.doSearch()
|
this.doSearch()
|
||||||
|
})
|
||||||
},
|
},
|
||||||
openUnionPopup() {
|
openUnionPopup() {
|
||||||
this.showUnionPopup = true
|
this.showUnionPopup = true
|
||||||
@@ -849,8 +914,8 @@ layout("/layouts/platform_h5.html"){
|
|||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
loadTravelPeriodOptions() {
|
loadTravelPeriodOptions(defaultFirst) {
|
||||||
this.$axios.post("/platform/tour/signup/h5/travelPeriodOptions", {
|
return this.$axios.post("/platform/tour/signup/h5/travelPeriodOptions", {
|
||||||
year: this.pageForm.year,
|
year: this.pageForm.year,
|
||||||
lineType: this.pageForm.lineType,
|
lineType: this.pageForm.lineType,
|
||||||
directFamilyUnitLine: this.pageForm.directFamilyUnitLine
|
directFamilyUnitLine: this.pageForm.directFamilyUnitLine
|
||||||
@@ -865,8 +930,15 @@ layout("/layouts/platform_h5.html"){
|
|||||||
name: item.travelPeriod || "未设置出行时间"
|
name: item.travelPeriod || "未设置出行时间"
|
||||||
}
|
}
|
||||||
}).filter((item) => item.value).sort((a, b) => {
|
}).filter((item) => item.value).sort((a, b) => {
|
||||||
return String(b.value).localeCompare(String(a.value))
|
return String(a.value).localeCompare(String(b.value))
|
||||||
})
|
})
|
||||||
|
if (defaultFirst) {
|
||||||
|
this.pageForm.travelPeriod = this.travelPeriodList.length > 0 ? this.travelPeriodList[0].value : ""
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
if (defaultFirst) {
|
||||||
|
this.pageForm.travelPeriod = ""
|
||||||
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
loadData() {
|
loadData() {
|
||||||
@@ -901,44 +973,18 @@ layout("/layouts/platform_h5.html"){
|
|||||||
vant.Toast("线路信息不完整")
|
vant.Toast("线路信息不完整")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (this.lineChecking) {
|
// 点击线路只进入详情页,报名资格和业务规则统一在提交报名按钮处校验。
|
||||||
return
|
window.location.href = "/platform/tour/signup/h5/lineInfo?matterId="
|
||||||
}
|
+ encodeURIComponent(row.matterId)
|
||||||
this.lineChecking = true
|
+ "&lineId="
|
||||||
axios.post("/platform/tour/signup/signupDetail", $.param({ matterId: row.matterId }), {
|
+ encodeURIComponent(row.lineId)
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
|
|
||||||
"x-requested-with": "XMLHttpRequest"
|
|
||||||
}
|
|
||||||
}).then((resp) => {
|
|
||||||
this.lineChecking = false
|
|
||||||
const res = resp.data || {}
|
|
||||||
if (res.code !== 0) {
|
|
||||||
vant.Dialog.alert({
|
|
||||||
title: "温馨提醒",
|
|
||||||
message: res.msg || "当前线路暂不能报名",
|
|
||||||
confirmButtonColor: "#1867b0"
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
window.location.href = "/platform/tour/signup/h5/lineInfo?matterId="
|
|
||||||
+ encodeURIComponent(row.matterId)
|
|
||||||
+ "&lineId="
|
|
||||||
+ encodeURIComponent(row.lineId)
|
|
||||||
}).catch((err) => {
|
|
||||||
this.lineChecking = false
|
|
||||||
vant.Dialog.alert({
|
|
||||||
title: "温馨提醒",
|
|
||||||
message: err && err.msg ? err.msg : "当前线路暂不能报名",
|
|
||||||
confirmButtonColor: "#1867b0"
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.loadUnionOptions()
|
this.loadUnionOptions()
|
||||||
this.loadTravelPeriodOptions()
|
this.loadTravelPeriodOptions(true).then(() => {
|
||||||
this.loadData()
|
this.loadData()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -575,11 +575,7 @@ const home = {
|
|||||||
|
|
||||||
.quick-scroll {
|
.quick-scroll {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow-x: auto;
|
overflow: visible;
|
||||||
overflow-y: hidden;
|
|
||||||
-webkit-overflow-scrolling: touch;
|
|
||||||
scrollbar-width: none;
|
|
||||||
-ms-overflow-style: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.quick-scroll::-webkit-scrollbar {
|
.quick-scroll::-webkit-scrollbar {
|
||||||
@@ -588,10 +584,9 @@ const home = {
|
|||||||
|
|
||||||
.quick-scroll-grid {
|
.quick-scroll-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-auto-flow: column;
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||||
grid-template-rows: repeat(2, 82px);
|
grid-auto-rows: 82px;
|
||||||
grid-auto-columns: 25%;
|
width: 100%;
|
||||||
min-width: 100%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.quick-entry-item {
|
.quick-entry-item {
|
||||||
|
|||||||
Reference in New Issue
Block a user