南大三十年教职工疗休养

This commit is contained in:
2026-07-04 01:08:45 +08:00
parent f637e02c08
commit 67c21e5dfa
5 changed files with 255 additions and 97 deletions
@@ -34,6 +34,7 @@ import com.budwk.app.zhgh.dayofficework.thirtyTeachTour.service.ThirtyTeachTourL
import com.budwk.app.zhgh.dayofficework.thirtyTeachTour.service.ThirtyTeachTourMatterService;
import com.budwk.app.zhgh.dayofficework.thirtyTeachTour.service.ThirtyTeachTourUserAssignmentService;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
@@ -445,12 +446,14 @@ public class ThirtyTeachTourMySignupController {
* @param ledger 报名台账主信息
* @param families 家属信息 JSON 数组
* @param directRelative 直系亲属线路报名信息 JSON
* @param boardingPlaceOnly H5 修改页只更新乘车地点时传 true,避免修改其他只读报名信息
* @return 修改结果;直系亲属线路、超额报销等场景会返回待审核提示
*/
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission(value = {"thirtyTeachTour.mysignup", "h5.thirtyTeachTour.mysignup"}, mode = SaMode.OR)
public Result doSignup(ThirtyTeachTourLedger ledger, @Param("families") String families, @Param("directRelative") String directRelative) {
public Result doSignup(ThirtyTeachTourLedger ledger, @Param("families") String families, @Param("directRelative") String directRelative,
@Param("boardingPlaceOnly") Boolean boardingPlaceOnly) {
Result checkResult = checkSignup(ledger);
if (checkResult != null) {
return checkResult;
@@ -462,6 +465,9 @@ public class ThirtyTeachTourMySignupController {
if (oldLedger == null) {
return Result.error("报名记录不存在");
}
if (Boolean.TRUE.equals(boardingPlaceOnly)) {
return updateH5BoardingPlaceOnly(ledger, oldLedger);
}
List<ThirtyTeachTourLedgerFamily> familyList = parseFamilies(families);
Result familyResult = checkFamilies(familyList);
if (familyResult != null) {
@@ -541,6 +547,41 @@ public class ThirtyTeachTourMySignupController {
return Result.success().addMsg(approvalRequired ? "修改已提交,等待审核" : "修改成功");
}
/**
* H5 我的疗休养修改页仅允许调整乘车地点,其他报名、家属和直系亲属信息均以原台账为准。
* 保存时同步当前人员分配记录中的乘车地点,保证移动端列表、人员分配和台账展示一致。
*
* @param submitLedger 页面提交的台账信息,仅读取 id 和 boardingPlace
* @param oldLedger 当前登录人的原台账记录
* @return 乘车地点保存结果
*/
private Result updateH5BoardingPlaceOnly(ThirtyTeachTourLedger submitLedger, ThirtyTeachTourLedger oldLedger) {
if (oldLedger == null || StrUtil.isBlank(oldLedger.getMatterId())) {
return Result.error("报名记录缺少事项信息,不能修改");
}
ThirtyTeachTourMatter matter = tourMatterService.fetch(oldLedger.getMatterId());
if (matter == null || Boolean.TRUE.equals(matter.getDelFlag()) || !Boolean.TRUE.equals(matter.getEnabled())) {
return Result.error("报名出行时段不存在或已停用");
}
if (isTravelEnded(matter.getTravelEndTime())) {
return Result.error("线路出行已结束,不能修改");
}
ThirtyTeachTourSetting setting = tourLedgerService.dao().fetch(ThirtyTeachTourSetting.class, matter.getSettingId());
oldLedger.setBoardingPlace(submitLedger == null ? "" : submitLedger.getBoardingPlace());
Result boardingPlaceResult = normalizeBoardingPlace(oldLedger, matter, setting);
if (boardingPlaceResult != null) {
return boardingPlaceResult;
}
// H5 修改页除乘车地点外全部只读,数据库更新也只落乘车地点字段,避免前端绕过只读限制。
tourLedgerService.update(Chain.make("boardingPlace", oldLedger.getBoardingPlace()),
Cnd.where(ThirtyTeachTourLedger::getId, "=", oldLedger.getId())
.and(ThirtyTeachTourLedger::getJobNo, "=", currentJobNo())
.and(ThirtyTeachTourLedger::getDelFlag, "=", false));
tourUserAssignmentService.backfillExistingAssignmentAfterSignup(matter.getSettingId(), matter.getId(), SecurityUtil.getUserId(),
oldLedger.getBoardingPlace(), oldLedger.getPhotoFiles(), oldLedger.getCurrentPeriodPhotoFiles());
return Result.success().addMsg("修改成功");
}
@At
@Aop(TransAop.READ_COMMITTED)
@SaCheckPermission(value = {"thirtyTeachTour.mysignup", "h5.thirtyTeachTour.mysignup"}, mode = SaMode.OR)
@@ -698,11 +698,13 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
sql.setParam("unionId", unionId);
List<NutMap> rows = listMap(sql);
NutMap quotaInfo = Lang.isEmpty(rows) ? emptyQuotaInfo() : rows.get(0);
int branchTotalQuota = branchPeriodQuotaTotal(settingId, unionId);
int formalQuota = defaultInt(quotaInfo.getInt("formalQuota"));
int backupQuota = defaultInt(quotaInfo.getInt("backupQuota"));
int formalUsed = defaultInt(quotaInfo.getInt("formalUsed"));
int backupUsed = defaultInt(quotaInfo.getInt("backupUsed"));
return NutMap.NEW()
.addv("branchTotalQuota", branchTotalQuota)
.addv("formalQuota", formalQuota)
.addv("backupQuota", backupQuota)
.addv("formalUsed", formalUsed)
@@ -2072,6 +2074,7 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
private NutMap emptyQuotaInfo() {
return NutMap.NEW()
.addv("branchTotalQuota", 0)
.addv("formalQuota", 0)
.addv("backupQuota", 0)
.addv("formalUsed", 0)
@@ -2080,6 +2083,29 @@ public class ThirtyTeachTourUserAssignmentServiceImpl extends BaseServiceImpl<Th
.addv("backupRemaining", 0);
}
/**
* 统计当前配置下当前分工会所有出行时间段分配人员名额的汇总数量,用于人员分配弹窗展示分工会总名额。
*/
private int branchPeriodQuotaTotal(String settingId, String unionId) {
if (StrUtil.isBlank(settingId) || StrUtil.isBlank(unionId)) {
return 0;
}
Sql sql = Sqls.create("""
SELECT COALESCE(SUM(formalQuota), 0) AS branchTotalQuota
FROM thirty_teach_tour_setting_period_quota
WHERE settingId = @settingId
AND unionId = @unionId
AND delFlag = 0
""");
sql.setParam("settingId", settingId);
sql.setParam("unionId", unionId);
List<NutMap> rows = listMap(sql);
if (Lang.isEmpty(rows)) {
return 0;
}
return defaultInt(rows.get(0).getInt("branchTotalQuota"));
}
private NutMap emptyPeriodQuotaInfo(String periodId) {
return NutMap.NEW()
.addv("periodId", periodId)
@@ -73,7 +73,7 @@ layout("/layouts/platform.html"){
<el-table-column label="工号" prop="loginName" width="120" align="center" header-align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="所在单位" prop="unitName" min-width="180" align="center" header-align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="线路" prop="lineName" min-width="200" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="出行时间" prop="travelPeriod" min-width="180" align="center" header-align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="出行时间" prop="travelPeriod" min-width="180" align="center" header-align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="出行时段" prop="periodName" min-width="180" align="center" header-align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="乘车地点" prop="boardingPlace" min-width="140" align="center" header-align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="已上传图片数量" width="140" align="center" header-align="center">
@@ -159,7 +159,7 @@ layout("/layouts/platform.html"){
</el-form-item>
</el-col>
<el-col :span="18" v-if="assignForm.personType === 'FORMAL'">
<el-form-item label="出行时间">
<el-form-item label="出行时间">
<div class="period-button-list">
<el-button
v-for="item in assignPeriodOptions"
@@ -177,6 +177,10 @@ layout("/layouts/platform.html"){
</el-form>
<div class="quota-bar">
<div class="quota-item quota-item-total">
<span class="quota-label">分工会总名额</span>
<span>{{ quotaInfo.branchTotalQuota }}</span>
</div>
<div class="quota-item">
<span class="quota-label">正式名额</span>
<span>{{ quotaInfo.formalQuota }}</span>
@@ -424,6 +428,9 @@ layout("/layouts/platform.html"){
color: #303133;
background: #fafafa;
}
.quota-item-total {
min-width: 150px;
}
.quota-label {
font-weight: 600;
margin-right: 10px;
@@ -503,6 +510,7 @@ layout("/layouts/platform.html"){
assignMatterOptions: [],
assignPeriodOptions: [],
quotaInfo: {
branchTotalQuota: 0,
formalQuota: 0,
backupQuota: 0,
formalUsed: 0,
@@ -511,6 +519,7 @@ layout("/layouts/platform.html"){
backupRemaining: 0
},
assignDialogVisible: false,
assignOpenToken: 0,
candidateLoading: false,
candidateSelectLoading: false,
candidateData: [],
@@ -617,6 +626,7 @@ layout("/layouts/platform.html"){
},
defaultQuotaInfo() {
return {
branchTotalQuota: 0,
formalQuota: 0,
backupQuota: 0,
formalUsed: 0,
@@ -726,21 +736,11 @@ layout("/layouts/platform.html"){
window.location.href = loc() + "/exportData?" + params.toString()
},
openAssign() {
const token = this.nextAssignOpenToken()
const year = this.pageForm.year || moment().format("YYYY")
this.assignForm = this.defaultAssignForm(year)
this.candidateForm = this.defaultCandidateForm()
this.assignPeriodOptions = []
this.quotaInfo = this.defaultQuotaInfo()
this.candidateData = []
this.selectedCandidates = []
this.selectedCandidateIds = []
this.candidateUserOptions = []
this.assignDialogVisible = true
this.loadAssignSettingOptions()
},
resetAssignDialog() {
this.assignForm = this.defaultAssignForm(moment().format("YYYY"))
this.candidateForm = this.defaultCandidateForm()
this.assignSettingOptions = []
this.assignMatterOptions = []
this.assignPeriodOptions = []
this.quotaInfo = this.defaultQuotaInfo()
@@ -749,8 +749,37 @@ layout("/layouts/platform.html"){
this.selectedCandidateIds = []
this.candidateUserOptions = []
this.assignSubmitting = false
this.candidateLoading = false
this.candidateSelectLoading = false
this.assignDialogVisible = true
this.loadAssignSettingOptions(token)
},
resetAssignDialog() {
this.assignOpenToken += 1
this.assignForm = this.defaultAssignForm(moment().format("YYYY"))
this.candidateForm = this.defaultCandidateForm()
this.assignSettingOptions = []
this.assignMatterOptions = []
this.assignPeriodOptions = []
this.quotaInfo = this.defaultQuotaInfo()
this.candidateData = []
this.selectedCandidates = []
this.selectedCandidateIds = []
this.candidateUserOptions = []
this.assignSubmitting = false
this.candidateLoading = false
this.candidateSelectLoading = false
},
// 人员分配弹窗每次打开都生成新的批次标记,旧请求返回时不再覆盖新弹窗数据。
nextAssignOpenToken() {
this.assignOpenToken += 1
return this.assignOpenToken
},
isCurrentAssignOpen(token) {
return this.assignDialogVisible && token === this.assignOpenToken
},
assignYearChange() {
const token = this.assignOpenToken
this.assignForm.settingId = ""
this.assignForm.matterId = ""
this.assignForm.periodId = ""
@@ -761,9 +790,10 @@ layout("/layouts/platform.html"){
this.candidateUserOptions = []
this.candidateForm.keyword = ""
this.clearCandidateSelection()
this.loadAssignSettingOptions()
this.loadAssignSettingOptions(token)
},
assignSettingChange() {
const token = this.assignOpenToken
this.assignForm.matterId = ""
this.assignForm.periodId = ""
this.assignMatterOptions = []
@@ -772,30 +802,32 @@ layout("/layouts/platform.html"){
this.candidateUserOptions = []
this.candidateForm.keyword = ""
this.clearCandidateSelection()
this.loadAssignMatterOptions()
this.loadAssignTravelPeriods(true)
this.loadCandidatePageData()
this.loadAssignMatterOptions(token)
this.loadAssignTravelPeriods(token)
this.loadCandidatePageData(token)
},
assignPersonTypeChange() {
if (this.assignForm.personType === "BACKUP") {
this.assignForm.matterId = ""
}
this.loadQuotaInfo()
this.loadQuotaInfo(this.assignOpenToken)
},
loadAssignSettingOptions() {
loadAssignSettingOptions(token) {
token = token || this.assignOpenToken
this.$axios.post(loc() + "/settingOptions", {year: this.assignForm.year}).then((res) => {
if (!this.isCurrentAssignOpen(token)) return
if (res.code === 0) {
this.assignSettingOptions = res.data || []
// 人员分配弹窗独立默认取当前年度第一条配置,再加载名额、线路和候选人员。
if (!this.assignForm.settingId && this.assignSettingOptions.length > 0) {
this.assignForm.settingId = this.assignSettingOptions[0].id
this.loadAssignMatterOptions()
this.loadAssignTravelPeriods(true)
this.loadCandidatePageData()
this.loadAssignMatterOptions(token)
this.loadAssignTravelPeriods(token)
this.loadCandidatePageData(token)
} else if (this.assignForm.settingId) {
this.loadAssignMatterOptions()
this.loadAssignTravelPeriods(true)
this.loadCandidatePageData()
this.loadAssignMatterOptions(token)
this.loadAssignTravelPeriods(token)
this.loadCandidatePageData(token)
} else {
this.assignMatterOptions = []
this.assignPeriodOptions = []
@@ -806,46 +838,50 @@ layout("/layouts/platform.html"){
}
})
},
loadAssignTravelPeriods(defaultFirst) {
loadAssignTravelPeriods(token) {
token = token || this.assignOpenToken
if (!this.assignForm.settingId) {
this.assignPeriodOptions = []
this.assignForm.periodId = ""
this.loadQuotaInfo()
this.loadQuotaInfo(token)
return
}
this.$axios.post(loc() + "/travelPeriodOptions", {settingId: this.assignForm.settingId}).then((res) => {
if (!this.isCurrentAssignOpen(token)) return
if (res.code === 0) {
this.assignPeriodOptions = res.data || []
const hasCurrent = this.assignPeriodOptions.some(item => item.id === this.assignForm.periodId)
if ((defaultFirst || !hasCurrent) && this.assignPeriodOptions.length > 0) {
this.assignForm.periodId = this.assignPeriodOptions[0].id
} else if (!hasCurrent) {
if (!hasCurrent) {
this.assignForm.periodId = ""
}
} else {
this.assignPeriodOptions = []
this.assignForm.periodId = ""
}
this.loadQuotaInfo()
this.loadQuotaInfo(token)
})
},
loadAssignMatterOptions() {
loadAssignMatterOptions(token) {
token = token || this.assignOpenToken
if (!this.assignForm.settingId) {
this.assignMatterOptions = []
return
}
this.$axios.post(loc() + "/matterOptions", {settingId: this.assignForm.settingId}).then((res) => {
if (!this.isCurrentAssignOpen(token)) return
if (res.code === 0) {
this.assignMatterOptions = res.data || []
}
})
},
loadQuotaInfo() {
loadQuotaInfo(token) {
token = token || this.assignOpenToken
if (!this.assignForm.settingId) {
this.quotaInfo = this.defaultQuotaInfo()
return
}
this.$axios.post(loc() + "/quotaInfo", {settingId: this.assignForm.settingId}).then((res) => {
if (!this.isCurrentAssignOpen(token)) return
if (res.code === 0) {
const baseQuota = Object.assign(this.defaultQuotaInfo(), res.data || {})
if (this.assignForm.personType === "FORMAL") {
@@ -855,16 +891,18 @@ layout("/layouts/platform.html"){
}
this.quotaInfo = baseQuota
if (this.assignForm.personType === "FORMAL" && this.assignForm.periodId) {
this.loadPeriodQuotaInfo()
this.loadPeriodQuotaInfo(token)
}
}
})
},
loadPeriodQuotaInfo() {
loadPeriodQuotaInfo(token) {
token = token || this.assignOpenToken
this.$axios.post(loc() + "/periodQuotaInfo", {
settingId: this.assignForm.settingId,
periodId: this.assignForm.periodId
}).then((res) => {
if (!this.isCurrentAssignOpen(token)) return
if (res.code === 0) {
const periodQuota = res.data || {}
this.quotaInfo = Object.assign({}, this.quotaInfo, {
@@ -876,13 +914,29 @@ layout("/layouts/platform.html"){
})
},
toggleAssignPeriod(item) {
const token = this.assignOpenToken
this.assignForm.periodId = this.assignForm.periodId === item.id ? "" : item.id
// 选择出行时间段时提醒先核对分配批次,避免批次线路时间与时间段不一致。
if (this.assignForm.periodId) {
this.$message({
type: "warning",
message: this.assignPeriodCheckMessage(),
duration: 5000
})
}
this.candidateForm.pageNumber = 1
this.clearCandidateSelection()
this.loadQuotaInfo()
this.loadCandidatePageData()
this.loadQuotaInfo(token)
this.loadCandidatePageData(token)
},
loadCandidatePageData() {
assignPeriodCheckMessage() {
if (!this.assignForm.matterId) {
return "请选择分配批次,并确认选中的 “分配批次” 与 “出行时间段” 出行时间是否一致"
}
return "请确认选中的 “分配批次” 与 “出行时间段” 出行时间是否一致"
},
loadCandidatePageData(token) {
token = token || this.assignOpenToken
if (!this.assignForm.settingId) {
this.candidateData = []
this.candidateForm.totalCount = 0
@@ -897,6 +951,7 @@ layout("/layouts/platform.html"){
keyword: (this.selectedCandidateIds || []).length > 0 ? "" : this.candidateForm.keyword,
userIds: JSON.stringify(this.selectedCandidateIds || [])
}).then((res) => {
if (!this.isCurrentAssignOpen(token)) return
if (res.code === 0) {
const data = res.data || {}
this.candidateData = (data.list || []).map(item => Object.assign({}, item, {
@@ -909,7 +964,9 @@ layout("/layouts/platform.html"){
this.$message.warning(res.msg || "候选人员查询失败")
}
}).finally(() => {
this.candidateLoading = false
if (this.isCurrentAssignOpen(token)) {
this.candidateLoading = false
}
})
},
candidateSearch() {
@@ -948,6 +1005,7 @@ layout("/layouts/platform.html"){
// 人员选择器复用候选人员接口,后端会限定为当前登录人所在分工会会员。
this.candidateForm.keyword = keyword || ""
this.candidateSelectLoading = true
const token = this.assignOpenToken
this.$axios.post(loc() + "/candidatePageData", {
pageNumber: 1,
pageSize: 20,
@@ -955,12 +1013,15 @@ layout("/layouts/platform.html"){
retiringSoon: this.normalizeCandidateFilterValue(this.candidateForm.retiringSoon),
keyword: keyword || ""
}).then((res) => {
if (!this.isCurrentAssignOpen(token)) return
if (res.code === 0) {
const data = res.data || {}
this.candidateUserOptions = this.mergeOptionLists(this.selectedCandidates, data.list || [])
}
}).finally(() => {
this.candidateSelectLoading = false
if (this.isCurrentAssignOpen(token)) {
this.candidateSelectLoading = false
}
})
},
candidateUserSelectChange(userIds) {
@@ -1149,7 +1210,7 @@ layout("/layouts/platform.html"){
return
}
if (this.assignForm.personType === "FORMAL" && !this.assignForm.periodId) {
this.$message.warning("请选择出行时间")
this.$message.warning("请选择出行时间")
return
}
this.$confirm("确定保存当前人员分配吗?", "提示", {
@@ -91,7 +91,7 @@ layout("/layouts/platform.html"){
<el-table-column label="所属分工会" prop="unionName" min-width="160" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="所在单位" prop="unitName" min-width="180" align="center" header-align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="线路" prop="lineName" min-width="200" sortable="custom" align="center" header-align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="出行时间" prop="travelPeriod" min-width="180" align="center" header-align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="出行时间" prop="travelPeriod" min-width="180" align="center" header-align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="出行时段" prop="periodName" min-width="180" align="center" header-align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="乘车地点" prop="boardingPlace" min-width="140" align="center" header-align="center" show-overflow-tooltip></el-table-column>
<el-table-column label="已上传图片数量" width="140" align="center" header-align="center">
@@ -475,6 +475,7 @@ layout("/layouts/platform.html"){
assignMatterOptions: [],
unionOptions: [],
assignDialogVisible: false,
assignOpenToken: 0,
remindDialogVisible: false,
remindSubmitting: false,
candidateLoading: false,
@@ -760,28 +761,47 @@ layout("/layouts/platform.html"){
})
},
openAssign() {
const token = this.nextAssignOpenToken()
const year = this.pageForm.year || moment().format("YYYY")
this.assignForm = this.defaultAssignForm(year)
this.assignForm.matterId = ""
this.candidateForm = this.defaultCandidateForm()
this.candidateData = []
this.selectedCandidates = []
this.selectedCandidateIds = []
this.candidateUserOptions = []
this.assignDialogVisible = true
this.loadAssignSettingOptions()
},
resetAssignDialog() {
this.assignForm = this.defaultAssignForm(moment().format("YYYY"))
this.candidateForm = this.defaultCandidateForm()
this.assignSettingOptions = []
this.assignMatterOptions = []
this.candidateData = []
this.selectedCandidates = []
this.selectedCandidateIds = []
this.candidateUserOptions = []
this.assignSubmitting = false
this.candidateLoading = false
this.candidateSelectLoading = false
this.assignDialogVisible = true
this.loadAssignSettingOptions(token)
},
resetAssignDialog() {
this.assignOpenToken += 1
this.assignForm = this.defaultAssignForm(moment().format("YYYY"))
this.candidateForm = this.defaultCandidateForm()
this.assignSettingOptions = []
this.assignMatterOptions = []
this.candidateData = []
this.selectedCandidates = []
this.selectedCandidateIds = []
this.candidateUserOptions = []
this.assignSubmitting = false
this.candidateLoading = false
this.candidateSelectLoading = false
},
// 人员分配弹窗每次打开都生成新的批次标记,旧请求返回时不再覆盖新弹窗数据。
nextAssignOpenToken() {
this.assignOpenToken += 1
return this.assignOpenToken
},
isCurrentAssignOpen(token) {
return this.assignDialogVisible && token === this.assignOpenToken
},
assignYearChange() {
const token = this.assignOpenToken
this.assignForm.settingId = ""
this.assignForm.matterId = ""
this.assignMatterOptions = []
@@ -789,31 +809,34 @@ layout("/layouts/platform.html"){
this.candidateUserOptions = []
this.candidateForm.keyword = ""
this.clearCandidateSelection()
this.loadAssignSettingOptions()
this.loadCandidatePageData()
this.loadAssignSettingOptions(token)
this.loadCandidatePageData(token)
},
assignSettingChange() {
const token = this.assignOpenToken
this.assignForm.matterId = ""
this.assignMatterOptions = []
this.selectedCandidateIds = []
this.candidateUserOptions = []
this.candidateForm.keyword = ""
this.clearCandidateSelection()
this.loadAssignMatterOptions()
this.loadCandidatePageData()
this.loadAssignMatterOptions(token)
this.loadCandidatePageData(token)
},
loadAssignSettingOptions() {
loadAssignSettingOptions(token) {
token = token || this.assignOpenToken
this.$axios.post(loc() + "/settingOptions", {year: this.assignForm.year}).then((res) => {
if (!this.isCurrentAssignOpen(token)) return
if (res.code === 0) {
this.assignSettingOptions = res.data || []
// 人员分配弹窗独立于主列表筛选,默认取当前年度第一条可用配置。
if (!this.assignForm.settingId && this.assignSettingOptions.length > 0) {
this.assignForm.settingId = this.assignSettingOptions[0].id
this.loadAssignMatterOptions()
this.loadCandidatePageData()
this.loadAssignMatterOptions(token)
this.loadCandidatePageData(token)
} else if (this.assignForm.settingId) {
this.loadAssignMatterOptions()
this.loadCandidatePageData()
this.loadAssignMatterOptions(token)
this.loadCandidatePageData(token)
} else {
this.assignMatterOptions = []
this.candidateData = []
@@ -822,18 +845,21 @@ layout("/layouts/platform.html"){
}
})
},
loadAssignMatterOptions() {
loadAssignMatterOptions(token) {
token = token || this.assignOpenToken
if (!this.assignForm.settingId) {
this.assignMatterOptions = []
return
}
this.$axios.post(loc() + "/matterOptions", {settingId: this.assignForm.settingId}).then((res) => {
if (!this.isCurrentAssignOpen(token)) return
if (res.code === 0) {
this.assignMatterOptions = res.data || []
}
})
},
loadCandidatePageData() {
loadCandidatePageData(token) {
token = token || this.assignOpenToken
if (!this.assignForm.settingId) {
this.candidateData = []
this.candidateForm.totalCount = 0
@@ -851,6 +877,7 @@ layout("/layouts/platform.html"){
keyword: (this.selectedCandidateIds || []).length > 0 ? "" : this.candidateForm.keyword,
userIds: JSON.stringify(this.selectedCandidateIds || [])
}).then((res) => {
if (!this.isCurrentAssignOpen(token)) return
if (res.code === 0) {
const data = res.data || {}
this.candidateData = (data.list || []).map(item => Object.assign({}, item, {
@@ -864,7 +891,9 @@ layout("/layouts/platform.html"){
this.$message.warning(res.msg || "候选人员查询失败")
}
}).finally(() => {
this.candidateLoading = false
if (this.isCurrentAssignOpen(token)) {
this.candidateLoading = false
}
})
},
assignMatterChange(value) {
@@ -922,6 +951,7 @@ layout("/layouts/platform.html"){
// 人员选择器复用候选人员接口,校工会可按分工会过滤,也可不选分工会查询全部会员。
this.candidateForm.keyword = keyword || ""
this.candidateSelectLoading = true
const token = this.assignOpenToken
this.$axios.post(loc() + "/candidatePageData", {
pageNumber: 1,
pageSize: 20,
@@ -932,12 +962,15 @@ layout("/layouts/platform.html"){
lastThirtyJoined: this.normalizeCandidateFilterValue(this.candidateForm.lastThirtyJoined),
keyword: keyword || ""
}).then((res) => {
if (!this.isCurrentAssignOpen(token)) return
if (res.code === 0) {
const data = res.data || {}
this.candidateUserOptions = this.mergeOptionLists(this.selectedCandidates, data.list || [])
}
}).finally(() => {
this.candidateSelectLoading = false
if (this.isCurrentAssignOpen(token)) {
this.candidateSelectLoading = false
}
})
},
candidateUserSelectChange(userIds) {
@@ -379,7 +379,6 @@ layout("/layouts/platform_h5.html"){
<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="showLeaveThirtyTeachTour(row)" size="small" type="danger" plain :loading="leaveLoading" @click="leaveThirtyTeachTour(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="showDirectRelativeExport(row)" size="small" type="info" plain :disabled="!canDirectRelativeExport(row)" @click="downloadExport(row, 'direct')">亲属线路申请</van-button>
</div>
@@ -470,8 +469,9 @@ layout("/layouts/platform_h5.html"){
<div class="tour-section-title">教职工信息</div>
<van-field label="姓名" readonly v-model="signupForm.userName"></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="身份证号" readonly v-model="signupForm.idCard"></van-field>
<van-field label="年龄" readonly :value="staffAge(signupForm)"></van-field>
<van-field label="手机号" readonly v-model="signupForm.mobile"></van-field>
<van-field label="所在工会" readonly v-model="signupForm.unionName"></van-field>
</div>
@@ -480,13 +480,13 @@ layout("/layouts/platform_h5.html"){
<van-field label="报名线路" readonly v-model="signupForm.lineName"></van-field>
<van-field label="线路类型" readonly v-model="signupForm.lineType"></van-field>
<van-field label="出行时间" readonly v-model="signupForm.travelPeriod"></van-field>
<van-field readonly clickable is-link label="乘车地点" v-model="signupForm.boardingPlace" placeholder="请选择乘车地点" @click="openBoardingPlacePicker"></van-field>
<van-field v-if="fillBedInfo" readonly clickable is-link label="床型" v-model="signupForm.bedType" placeholder="请选择床型" @click="openBedPicker('self')"></van-field>
<van-field v-if="fillBedInfo" label="床位信息" v-model="signupForm.bedInfo" maxlength="100" placeholder="请输入床位信息"></van-field>
<van-field v-if="fillBedInfo" label="意向拼床人" v-model="signupForm.intendedRoommate" maxlength="100" placeholder="请输入意向拼床人"></van-field>
<van-field required readonly clickable is-link label="乘车地点" v-model="signupForm.boardingPlace" placeholder="请选择乘车地点" @click="openBoardingPlacePicker"></van-field>
<van-field v-if="fillBedInfo" label="床型" readonly v-model="signupForm.bedType"></van-field>
<van-field v-if="fillBedInfo" label="床位信息" readonly v-model="signupForm.bedInfo"></van-field>
<van-field v-if="fillBedInfo" label="意向拼床人" readonly v-model="signupForm.intendedRoommate"></van-field>
<van-field v-if="canApplyOverReimbursement(signupForm)" class="tour-over-cost" label="报销超出费用">
<template #input>
<van-radio-group v-model="signupForm.overCostReimbursed" direction="horizontal">
<van-radio-group v-model="signupForm.overCostReimbursed" direction="horizontal" disabled>
<van-radio :name="true"></van-radio>
<van-radio :name="false"></van-radio>
</van-radio-group>
@@ -499,12 +499,12 @@ layout("/layouts/platform_h5.html"){
<div v-if="isDirectFamilyLine(signupForm)" class="tour-section">
<div class="tour-section-title">直系亲属线路</div>
<van-field label="亲属姓名" v-model="directRelativeForm.relativeName" maxlength="100" placeholder="请输入亲属姓名"></van-field>
<van-field label="所在单位" v-model="directRelativeForm.unitName" maxlength="100" placeholder="请输入所在单位"></van-field>
<van-field readonly clickable is-link label="亲属关系" v-model="directRelativeForm.relationshipName" placeholder="请选择亲属关系" @click="openDirectRelativePicker"></van-field>
<van-field label="线路名称" v-model="directRelativeForm.lineName" maxlength="100" placeholder="请输入线路名称"></van-field>
<van-field label="出行开始" v-model="directRelativeForm.travelStartTime" type="date" placeholder="请选择出行开始日期"></van-field>
<van-field label="出行结束" v-model="directRelativeForm.travelEndTime" type="date" placeholder="请选择出行结束日期"></van-field>
<van-field label="亲属姓名" readonly v-model="directRelativeForm.relativeName"></van-field>
<van-field label="所在单位" readonly v-model="directRelativeForm.unitName"></van-field>
<van-field label="亲属关系" readonly v-model="directRelativeForm.relationshipName"></van-field>
<van-field label="线路名称" readonly v-model="directRelativeForm.lineName"></van-field>
<van-field label="出行开始" readonly v-model="directRelativeForm.travelStartTime"></van-field>
<van-field label="出行结束" readonly v-model="directRelativeForm.travelEndTime"></van-field>
</div>
<div v-if="allowFamily && !isDirectFamilyLine(signupForm)" class="tour-section">
@@ -512,29 +512,26 @@ layout("/layouts/platform_h5.html"){
<div v-for="(item,index) in familyData" :key="index" class="tour-family-card">
<div class="tour-family-head">
<span>家属{{ index + 1 }}</span>
<van-button size="mini" type="danger" plain @click="removeFamily(index)">删除</van-button>
</div>
<van-field label="姓名" v-model="item.familyName" maxlength="100" placeholder="请填写姓名"></van-field>
<van-field label="年龄" v-model.number="item.age" type="digit" placeholder="请填写年龄"></van-field>
<van-field label="姓名" readonly v-model="item.familyName"></van-field>
<van-field label="年龄" readonly v-model="item.age"></van-field>
<van-field label="性别">
<template #input>
<van-radio-group v-model="item.gender" direction="horizontal">
<van-radio-group v-model="item.gender" direction="horizontal" disabled>
<van-radio name="男"></van-radio>
<van-radio name="女"></van-radio>
</van-radio-group>
</template>
</van-field>
<van-field label="身份证号" v-model="item.idCard" maxlength="18" placeholder="请填写身份证号" @blur="normalizeFamilyIdCard(item)"></van-field>
<van-field readonly clickable is-link label="关系" v-model="item.relationship" placeholder="请选择关系" @click="openRelationshipPicker(index)"></van-field>
<van-field v-if="fillBedInfo" readonly clickable is-link label="床型" v-model="item.bedType" placeholder="请选择床型" @click="openBedPicker('family', index)"></van-field>
<van-field v-if="fillBedInfo" label="床位" v-model="item.bedInfo" maxlength="100" placeholder="请输入床位信息"></van-field>
<van-field v-if="fillBedInfo" label="意向拼床人" v-model="item.intendedRoommate" maxlength="100" placeholder="请输入意向拼床人"></van-field>
<van-field label="身份证号" readonly v-model="item.idCard"></van-field>
<van-field label="关系" readonly v-model="item.relationship"></van-field>
<van-field v-if="fillBedInfo" label="床型" readonly v-model="item.bedType"></van-field>
<van-field v-if="fillBedInfo" label="床位" readonly v-model="item.bedInfo"></van-field>
<van-field v-if="fillBedInfo" label="意向拼床人" readonly v-model="item.intendedRoommate"></van-field>
</div>
<van-cell title="添加家属" is-link @click="addFamily"></van-cell>
</div>
</div>
<div class="tour-bottom-bar">
<van-button v-if="showCancel(signupForm)" type="danger" plain block round :loading="cancelLoading" @click="cancelSignup">取消报名</van-button>
<van-button type="info" block round :loading="signupLoading" @click="submitSignup">提交</van-button>
</div>
</van-popup>
@@ -623,7 +620,7 @@ layout("/layouts/platform_h5.html"){
return this.directRelativeOptions.map(item => ({ text: item.name || item.code, item }))
},
boardingPlaceColumns() {
return this.boardingPlaceOptions
return [""].concat(this.boardingPlaceOptions || [])
}
},
methods: {
@@ -986,7 +983,7 @@ layout("/layouts/platform_h5.html"){
this.boardingPlacePickerVisible = true
},
confirmBoardingPlace(value) {
this.signupForm.boardingPlace = value
this.signupForm.boardingPlace = value || ""
this.boardingPlacePickerVisible = false
},
normalizeFamilyIdCard(row) {
@@ -1032,11 +1029,11 @@ layout("/layouts/platform_h5.html"){
vant.Toast("请选择乘车地点")
return
}
if (!this.validateFamilies() || !this.validateDirectRelative()) return
const families = this.allowFamily && this.signupForm.hasFamily ? this.familyData : []
// H5 修改页当前只允许调整乘车地点,家属、直系亲属等历史报名信息仅展示不参与提交校验。
const form = Object.assign({}, this.signupForm, {
families: JSON.stringify(families),
directRelative: this.isDirectFamilyLine(this.signupForm) ? JSON.stringify(this.directRelativeForm) : ""
boardingPlaceOnly: true,
families: "[]",
directRelative: ""
})
this.signupLoading = true
this.$axios.post("/platform/thirtyTeachTour/mysignup/doSignup", form).then((res) => {