疗休养优化1.2

This commit is contained in:
2026-06-15 17:43:33 +08:00
parent 6b892bb026
commit 850f4742a2
33 changed files with 1303 additions and 180 deletions
@@ -0,0 +1,3 @@
-- Add signup ledger age field for mobile confirmation snapshot.
ALTER TABLE `tour_ledger`
ADD COLUMN `age` int DEFAULT NULL COMMENT '年龄' AFTER `gender`;
@@ -0,0 +1,4 @@
-- 疗休养配置增加PC首页报名浮动入口控制字段。
ALTER TABLE `tour_setting`
ADD COLUMN `homeSignupEntryEnabled` tinyint(1) DEFAULT 0 COMMENT '是否展示PC首页报名入口' AFTER `enabled`,
ADD COLUMN `homeSignupEntryImage` varchar(500) DEFAULT NULL COMMENT 'PC首页报名入口图片' AFTER `homeSignupEntryEnabled`;
@@ -23,6 +23,8 @@ CREATE TABLE IF NOT EXISTS `tour_setting` (
`allowFamily` tinyint(1) DEFAULT 0 COMMENT '是否允许携带家属',
`fillBedInfo` tinyint(1) DEFAULT 1 COMMENT '是否填报床位信息',
`enabled` tinyint(1) DEFAULT 1 COMMENT '是否启用',
`homeSignupEntryEnabled` tinyint(1) DEFAULT 0 COMMENT '是否展示PC首页报名入口',
`homeSignupEntryImage` varchar(500) DEFAULT NULL COMMENT 'PC首页报名入口图片',
`serviceNotice` text COMMENT '服务须知',
`createdBy` varchar(32) DEFAULT NULL COMMENT '创建人',
`createdAt` bigint DEFAULT NULL COMMENT '创建时间',
@@ -87,6 +89,8 @@ CREATE TABLE IF NOT EXISTS `tour_user_assignment` (
`loginName` varchar(50) DEFAULT NULL COMMENT '工号',
`userName` varchar(100) DEFAULT NULL COMMENT '姓名',
`gender` varchar(20) DEFAULT NULL COMMENT '性别',
`age` int DEFAULT NULL COMMENT '年龄',
`idCard` varchar(30) DEFAULT NULL COMMENT '身份证号',
`mobile` varchar(30) DEFAULT NULL COMMENT '手机号',
`unitId` varchar(32) DEFAULT NULL COMMENT '所在单位ID',
`unitName` varchar(100) DEFAULT NULL COMMENT '所在单位',
@@ -0,0 +1,4 @@
-- Add mobile H5 signup confirmation fields to tour user assignment.
ALTER TABLE `tour_user_assignment`
ADD COLUMN `age` int DEFAULT NULL COMMENT '年龄' AFTER `gender`,
ADD COLUMN `idCard` varchar(30) DEFAULT NULL COMMENT '身份证号' AFTER `age`;
@@ -13,6 +13,8 @@ CREATE TABLE IF NOT EXISTS `tour_user_assignment` (
`loginName` varchar(50) DEFAULT NULL COMMENT '工号',
`userName` varchar(100) DEFAULT NULL COMMENT '姓名',
`gender` varchar(20) DEFAULT NULL COMMENT '性别',
`age` int DEFAULT NULL COMMENT '年龄',
`idCard` varchar(30) DEFAULT NULL COMMENT '身份证号',
`mobile` varchar(30) DEFAULT NULL COMMENT '手机号',
`unitId` varchar(32) DEFAULT NULL COMMENT '所在单位ID',
`unitName` varchar(100) DEFAULT NULL COMMENT '所在单位',
Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

@@ -93,6 +93,41 @@ layout("/layouts/v4/baseLayout.html"){
font-weight: 500;
}
.tour-home-float {
position: fixed;
left: 0;
top: 0;
z-index: 99999;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 8px 24px rgba(15, 23, 42, 0.22);
cursor: pointer;
background: #ffffff;
will-change: transform;
}
.tour-home-float img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
.tour-home-float__title {
position: absolute;
left: 0;
right: 0;
bottom: 0;
padding: 8px 10px;
color: #ffffff;
font-size: 15px;
font-weight: 700;
line-height: 1.2;
background: linear-gradient(180deg, rgba(15, 23, 42, 0), rgba(15, 23, 42, 0.75));
text-shadow: 0 1px 3px rgba(15, 23, 42, 0.45);
box-sizing: border-box;
}
</style>
<div class="v4-container" id="v4-home-app">
@@ -133,6 +168,16 @@ layout("/layouts/v4/baseLayout.html"){
<jcdt :list="websiteNews"></jcdt>
</div>
<div
v-for="item in tourHomeFloatEntries"
:key="item.id"
class="tour-home-float"
:style="tourHomeFloatStyle(item)"
@click="openTourSignup(item)">
<img :src="item.imageUrl" :alt="item.title || ''">
<div class="tour-home-float__title">{{ item.title }}</div>
</div>
</div>
<script nonce="${cspNonce!}">
@@ -149,6 +194,9 @@ layout("/layouts/v4/baseLayout.html"){
data(){
return{
websiteNews: [],
tourHomeFloatEntries: [],
tourHomeFloatFrame: null,
tourHomeFloatLastTime: 0,
homeBannerList: "${config.AppHomeImg!}".split(",").map(item => item.trim()).filter(item => item)
}
},
@@ -162,6 +210,10 @@ layout("/layouts/v4/baseLayout.html"){
},
mounted() {
this.getWebSiteNews()
this.loadTourHomeSignupEntry()
},
beforeDestroy() {
this.stopTourHomeFloat()
},
methods: {
getWebSiteNews(){
@@ -170,6 +222,109 @@ layout("/layouts/v4/baseLayout.html"){
this.websiteNews = res.data
}
})
},
loadTourHomeSignupEntry() {
this.$axios.post('/platform/home/listTourHomeSignupEntry').then(res => {
if (res.code === 0) {
this.initTourHomeFloatEntries(res.data || [])
}
})
},
initTourHomeFloatEntries(rows) {
this.stopTourHomeFloat()
// 疗休养首页浮动入口从左上角开始飘动,多条配置时使用轻微偏移避免完全重叠。
const width = 285
const height = 177
const bounds = this.getTourHomeFloatBounds(width, height)
this.tourHomeFloatEntries = (rows || []).map((item, index) => {
const offset = index * 28
return Object.assign({}, item, {
width: width,
height: height,
x: Math.min(bounds.minX + offset, bounds.maxX),
y: Math.min(bounds.minY + offset, bounds.maxY),
vx: (index % 2 === 0 ? 1 : -1) * (0.028 + index * 0.004),
vy: (index % 3 === 0 ? 1 : -1) * (0.022 + index * 0.003)
})
})
if (this.tourHomeFloatEntries.length > 0) {
this.startTourHomeFloat()
}
},
startTourHomeFloat() {
this.tourHomeFloatLastTime = 0
const step = (timestamp) => {
if (!this.tourHomeFloatEntries.length) {
this.tourHomeFloatFrame = null
return
}
if (!this.tourHomeFloatLastTime) {
this.tourHomeFloatLastTime = timestamp
}
const delta = Math.min(timestamp - this.tourHomeFloatLastTime, 40)
this.tourHomeFloatLastTime = timestamp
this.moveTourHomeFloat(delta)
this.tourHomeFloatFrame = window.requestAnimationFrame(step)
}
this.tourHomeFloatFrame = window.requestAnimationFrame(step)
},
stopTourHomeFloat() {
if (this.tourHomeFloatFrame) {
window.cancelAnimationFrame(this.tourHomeFloatFrame)
this.tourHomeFloatFrame = null
}
this.tourHomeFloatLastTime = 0
},
moveTourHomeFloat(delta) {
this.tourHomeFloatEntries.forEach((item) => {
const bounds = this.getTourHomeFloatBounds(item.width, item.height)
let nextX = item.x + item.vx * delta
let nextY = item.y + item.vy * delta
let nextVx = item.vx
let nextVy = item.vy
if (nextX <= bounds.minX || nextX >= bounds.maxX) {
nextVx = -nextVx
nextX = Math.min(Math.max(nextX, bounds.minX), bounds.maxX)
}
if (nextY <= bounds.minY || nextY >= bounds.maxY) {
nextVy = -nextVy
nextY = Math.min(Math.max(nextY, bounds.minY), bounds.maxY)
}
this.$set(item, "x", nextX)
this.$set(item, "y", nextY)
this.$set(item, "vx", nextVx)
this.$set(item, "vy", nextVy)
})
},
getTourHomeFloatBounds(width, height) {
// 根据固定导航和首页内容容器计算浮动范围,确保图片只在首页内容可视区域内飘动。
const viewportWidth = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0)
const viewportHeight = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0)
const header = document.querySelector(".v4-header")
const container = document.querySelector("#container")
const headerRect = header ? header.getBoundingClientRect() : { bottom: 0 }
const containerRect = container ? container.getBoundingClientRect() : { left: 0 }
const padding = 4
const minX = Math.max(containerRect.left + padding, padding)
const minY = Math.max(headerRect.bottom + padding, padding)
return {
minX: minX,
minY: minY,
maxX: Math.max(viewportWidth - width - padding, minX),
maxY: Math.max(viewportHeight - height - padding, minY)
}
},
tourHomeFloatStyle(item) {
return {
width: item.width + "px",
height: item.height + "px",
transform: "translate(" + item.x + "px, " + item.y + "px)"
}
},
openTourSignup(item) {
// 首页浮动入口点击后打开新页面,避免打断当前首页浏览位置。
const url = item && item.href ? item.href : "/platform/tour/signup"
window.open(url, "_blank")
}
}
})
@@ -153,13 +153,21 @@ layout("/layouts/platform.html"){
</div>
<div class="candidate-toolbar">
<el-input
v-model="candidateForm.keyword"
<el-select
v-model="selectedCandidateIds"
multiple
filterable
remote
clearable
placeholder="姓名/工号"
style="width: 240px"
@keyup.enter.native="candidateSearch">
</el-input>
collapse-tags
reserve-keyword
:remote-method="remoteCandidateSearch"
:loading="candidateSelectLoading"
placeholder="请选择姓名/工号"
style="width: 360px"
@change="candidateUserSelectChange">
<el-option v-for="item in candidateUserOptions" :key="item.userId" :label="candidateOptionLabel(item)" :value="item.userId"></el-option>
</el-select>
<el-button type="primary" icon="el-icon-search" @click="candidateSearch">查询</el-button>
<el-button @click="resetCandidateSearch">重置</el-button>
<span class="candidate-selected">已选 {{ selectedCandidates.length }} 人</span>
@@ -312,8 +320,11 @@ layout("/layouts/platform.html"){
},
assignDialogVisible: false,
candidateLoading: false,
candidateSelectLoading: false,
candidateData: [],
candidateUserOptions: [],
selectedCandidates: [],
selectedCandidateIds: [],
assignSubmitting: false,
selectMatterDialogVisible: false,
selectMatterSubmitting: false,
@@ -447,6 +458,8 @@ layout("/layouts/platform.html"){
this.quotaInfo = this.defaultQuotaInfo()
this.candidateData = []
this.selectedCandidates = []
this.selectedCandidateIds = []
this.candidateUserOptions = []
this.assignDialogVisible = true
this.loadAssignSettingOptions()
},
@@ -457,6 +470,8 @@ layout("/layouts/platform.html"){
this.quotaInfo = this.defaultQuotaInfo()
this.candidateData = []
this.selectedCandidates = []
this.selectedCandidateIds = []
this.candidateUserOptions = []
this.assignSubmitting = false
},
assignYearChange() {
@@ -464,12 +479,18 @@ layout("/layouts/platform.html"){
this.assignForm.matterId = ""
this.assignMatterOptions = []
this.quotaInfo = this.defaultQuotaInfo()
this.selectedCandidateIds = []
this.candidateUserOptions = []
this.candidateForm.keyword = ""
this.clearCandidateSelection()
this.loadAssignSettingOptions()
},
assignSettingChange() {
this.assignForm.matterId = ""
this.assignMatterOptions = []
this.selectedCandidateIds = []
this.candidateUserOptions = []
this.candidateForm.keyword = ""
this.clearCandidateSelection()
this.loadAssignMatterOptions()
this.loadQuotaInfo()
@@ -536,12 +557,14 @@ layout("/layouts/platform.html"){
pageNumber: this.candidateForm.pageNumber,
pageSize: this.candidateForm.pageSize,
settingId: this.assignForm.settingId,
keyword: this.candidateForm.keyword
keyword: (this.selectedCandidateIds || []).length > 0 ? "" : this.candidateForm.keyword,
userIds: JSON.stringify(this.selectedCandidateIds || [])
}).then((res) => {
if (res.code === 0) {
const data = res.data || {}
this.candidateData = data.list || []
this.candidateForm.totalCount = data.totalCount || 0
this.mergeCandidateOptions(this.candidateData)
} else {
this.$message.warning(res.msg || "候选人员查询失败")
}
@@ -551,11 +574,16 @@ layout("/layouts/platform.html"){
},
candidateSearch() {
this.candidateForm.pageNumber = 1
this.clearCandidateSelection()
this.loadCandidatePageData()
},
resetCandidateSearch() {
this.candidateForm.keyword = ""
this.candidateForm.pageNumber = 1
this.selectedCandidateIds = []
this.selectedCandidates = []
this.candidateUserOptions = []
this.clearCandidateSelection()
this.loadCandidatePageData()
},
candidateSizeChange(size) {
@@ -569,6 +597,51 @@ layout("/layouts/platform.html"){
},
candidateSelectionChange(rows) {
this.selectedCandidates = rows || []
this.mergeCandidateOptions(this.selectedCandidates)
},
remoteCandidateSearch(keyword) {
if (!this.assignForm.settingId) {
this.candidateUserOptions = []
return
}
// 人员选择器复用候选人员接口,后端会限定为当前登录人所在分工会会员。
this.candidateForm.keyword = keyword || ""
this.candidateSelectLoading = true
this.$axios.post(loc() + "/candidatePageData", {
pageNumber: 1,
pageSize: 20,
settingId: this.assignForm.settingId,
keyword: keyword || ""
}).then((res) => {
if (res.code === 0) {
const data = res.data || {}
this.candidateUserOptions = this.mergeOptionLists(this.selectedCandidates, data.list || [])
}
}).finally(() => {
this.candidateSelectLoading = false
})
},
candidateUserSelectChange(userIds) {
// 人员选择器仅作为查询条件;最终分配人员仍由下方列表勾选决定。
this.selectedCandidateIds = userIds || []
},
candidateOptionLabel(item) {
if (!item) {
return ""
}
return (item.userName || "") + (item.loginName ? "" + item.loginName + "" : "")
},
mergeCandidateOptions(list) {
this.candidateUserOptions = this.mergeOptionLists(this.candidateUserOptions, list || [])
},
mergeOptionLists(first, second) {
const map = {}
;(first || []).concat(second || []).forEach(item => {
if (item && item.userId) {
map[item.userId] = Object.assign({}, map[item.userId] || {}, item)
}
})
return Object.keys(map).map(key => map[key])
},
clearCandidateSelection() {
this.selectedCandidates = []
@@ -189,7 +189,7 @@ layout("/layouts/platform.html"){
</el-col>
<el-col :span="8">
<el-form-item label="联系方式" prop="contactPhone">
<el-input v-model="batchForm.contactPhone" maxlength="11" placeholder="请输入联系方式"></el-input>
<el-input v-model="batchForm.contactPhone" maxlength="30" placeholder="请输入联系方式"></el-input>
</el-form-item>
</el-col>
</el-row>
@@ -239,12 +239,10 @@ layout("/layouts/platform.html"){
callback(new Error("必填"))
}
}
const validateMobile = (rule, value, callback) => {
const mobileReg = /^1[3-9]\d{9}$/
const validateContactPhone = (rule, value, callback) => {
// 线路联系人电话允许填写座机、分机或其他联系说明,此处只校验必填。
if (!value) {
callback(new Error("必填"))
} else if (!mobileReg.test(value)) {
callback(new Error("手机号格式不正确"))
} else {
callback()
}
@@ -328,7 +326,7 @@ layout("/layouts/platform.html"){
travelStartTime: [{ validator: validateBatchTime, trigger: ["blur", "change"] }],
travelEndTime: [{ validator: validateBatchTime, trigger: ["blur", "change"] }],
contactName: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
contactPhone: [{ validator: validateMobile, trigger: ["blur", "change"] }],
contactPhone: [{ validator: validateContactPhone, trigger: ["blur", "change"] }],
minGroupPeople: [{ validator: validatePeople, trigger: ["blur", "change"] }],
maxGroupPeople: [{ validator: validatePeople, trigger: ["blur", "change"] }],
estimatedCost: [{ validator: validateMoney, trigger: ["blur", "change"] }]
@@ -157,13 +157,21 @@ layout("/layouts/platform.html"){
<el-select v-model="candidateForm.unionId" clearable filterable placeholder="所属分工会" style="width: 220px" @change="candidateSearch">
<el-option v-for="item in unionOptions" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
<el-input
v-model="candidateForm.keyword"
<el-select
v-model="selectedCandidateIds"
multiple
filterable
remote
clearable
placeholder="姓名/工号"
style="width: 220px"
@keyup.enter.native="candidateSearch">
</el-input>
collapse-tags
reserve-keyword
:remote-method="remoteCandidateSearch"
:loading="candidateSelectLoading"
placeholder="请选择姓名/工号"
style="width: 360px"
@change="candidateUserSelectChange">
<el-option v-for="item in candidateUserOptions" :key="item.userId" :label="candidateOptionLabel(item)" :value="item.userId"></el-option>
</el-select>
<el-button type="primary" icon="el-icon-search" @click="candidateSearch">查询</el-button>
<el-button @click="resetCandidateSearch">重置</el-button>
<span class="candidate-selected">已选 {{ selectedCandidates.length }} 人</span>
@@ -302,8 +310,11 @@ layout("/layouts/platform.html"){
unionOptions: [],
assignDialogVisible: false,
candidateLoading: false,
candidateSelectLoading: false,
candidateData: [],
candidateUserOptions: [],
selectedCandidates: [],
selectedCandidateIds: [],
assignSubmitting: false,
selectMatterDialogVisible: false,
selectMatterSubmitting: false,
@@ -437,6 +448,8 @@ layout("/layouts/platform.html"){
this.candidateForm = this.defaultCandidateForm()
this.candidateData = []
this.selectedCandidates = []
this.selectedCandidateIds = []
this.candidateUserOptions = []
this.assignDialogVisible = true
this.loadAssignSettingOptions()
},
@@ -446,12 +459,17 @@ layout("/layouts/platform.html"){
this.assignMatterOptions = []
this.candidateData = []
this.selectedCandidates = []
this.selectedCandidateIds = []
this.candidateUserOptions = []
this.assignSubmitting = false
},
assignYearChange() {
this.assignForm.settingId = ""
this.assignForm.matterId = ""
this.assignMatterOptions = []
this.selectedCandidateIds = []
this.candidateUserOptions = []
this.candidateForm.keyword = ""
this.clearCandidateSelection()
this.loadAssignSettingOptions()
this.loadCandidatePageData()
@@ -459,6 +477,9 @@ layout("/layouts/platform.html"){
assignSettingChange() {
this.assignForm.matterId = ""
this.assignMatterOptions = []
this.selectedCandidateIds = []
this.candidateUserOptions = []
this.candidateForm.keyword = ""
this.clearCandidateSelection()
this.loadAssignMatterOptions()
this.loadCandidatePageData()
@@ -506,7 +527,8 @@ layout("/layouts/platform.html"){
pageSize: this.candidateForm.pageSize,
settingId: this.assignForm.settingId,
unionId: this.candidateForm.unionId,
keyword: this.candidateForm.keyword
keyword: (this.selectedCandidateIds || []).length > 0 ? "" : this.candidateForm.keyword,
userIds: JSON.stringify(this.selectedCandidateIds || [])
}).then((res) => {
if (res.code === 0) {
const data = res.data || {}
@@ -514,6 +536,7 @@ layout("/layouts/platform.html"){
assignmentMatterId: ""
}))
this.candidateForm.totalCount = data.totalCount || 0
this.mergeCandidateOptions(this.candidateData)
} else {
this.$message.warning(res.msg || "候选人员查询失败")
}
@@ -528,12 +551,17 @@ layout("/layouts/platform.html"){
},
candidateSearch() {
this.candidateForm.pageNumber = 1
this.clearCandidateSelection()
this.loadCandidatePageData()
},
resetCandidateSearch() {
this.candidateForm.unionId = ""
this.candidateForm.keyword = ""
this.candidateForm.pageNumber = 1
this.selectedCandidateIds = []
this.selectedCandidates = []
this.candidateUserOptions = []
this.clearCandidateSelection()
this.loadCandidatePageData()
},
candidateSizeChange(size) {
@@ -555,10 +583,56 @@ layout("/layouts/platform.html"){
}
})
this.selectedCandidates = rows || []
this.mergeCandidateOptions(this.selectedCandidates)
if (this.selectedCandidates.length <= 0) {
this.assignForm.matterId = ""
}
},
remoteCandidateSearch(keyword) {
if (!this.assignForm.settingId) {
this.candidateUserOptions = []
return
}
// 人员选择器复用候选人员接口,校工会可按分工会过滤,也可不选分工会查询全部会员。
this.candidateForm.keyword = keyword || ""
this.candidateSelectLoading = true
this.$axios.post(loc() + "/candidatePageData", {
pageNumber: 1,
pageSize: 20,
settingId: this.assignForm.settingId,
unionId: this.candidateForm.unionId,
keyword: keyword || ""
}).then((res) => {
if (res.code === 0) {
const data = res.data || {}
this.candidateUserOptions = this.mergeOptionLists(this.selectedCandidates, data.list || [])
}
}).finally(() => {
this.candidateSelectLoading = false
})
},
candidateUserSelectChange(userIds) {
// 人员选择器仅作为查询条件;最终分配人员仍由下方列表勾选决定。
this.selectedCandidateIds = userIds || []
},
candidateOptionLabel(item) {
if (!item) {
return ""
}
return (item.userName || "") + (item.loginName ? "" + item.loginName + "" : "")
},
mergeCandidateOptions(list) {
this.candidateUserOptions = this.mergeOptionLists(this.candidateUserOptions, list || [])
},
mergeOptionLists(first, second) {
const map = {}
;(first || []).concat(second || []).forEach(item => {
if (item && item.userId) {
map[item.userId] = Object.assign({}, map[item.userId] || {}, item)
}
})
return Object.keys(map).map(key => map[key])
},
isCandidateSelected(row) {
return !!row && this.selectedCandidates.some(item => item.userId === row.userId)
},
@@ -241,6 +241,26 @@ layout("/layouts/platform.html"){
<el-switch v-model="formData.enabled" active-text="启用" inactive-text="停用"></el-switch>
</el-form-item>
</el-col>
<el-col :span="24">
<div class="tour-setting-basic-divider"></div>
</el-col>
<el-col :span="12">
<el-form-item label="首页报名入口" prop="homeSignupEntryEnabled">
<el-switch v-model="formData.homeSignupEntryEnabled" active-text="展示" inactive-text="不展示"></el-switch>
</el-form-item>
</el-col>
<el-col v-if="formData.homeSignupEntryEnabled" :span="12">
<el-form-item label="入口图片" prop="homeSignupEntryImage">
<file-upload
style="--upload-width: 220px;--upload-height:120px"
:value.sync="formData.homeSignupEntryImage"
:upload_number="1"
upload_mode="image"
upload_result_category="interval"
upload_result_type="url">
</file-upload>
</el-form-item>
</el-col>
<el-col :span="24">
<div class="tour-setting-basic-divider"></div>
<div class="tour-boarding-panel">
@@ -621,6 +641,8 @@ layout("/layouts/platform.html"){
allowFamily: false,
fillBedInfo: true,
enabled: true,
homeSignupEntryEnabled: false,
homeSignupEntryImage: "",
lots: [],
unionQuotas: [],
serviceNotice: ""
@@ -693,6 +715,9 @@ layout("/layouts/platform.html"){
if (this.formData.fillBedInfo === null || this.formData.fillBedInfo === undefined) {
this.$set(this.formData, "fillBedInfo", true)
}
// 上一年配置只延用业务规则,首页浮动报名入口需要当年重新确认。
this.$set(this.formData, "homeSignupEntryEnabled", false)
this.$set(this.formData, "homeSignupEntryImage", "")
this.lotDeleteList = []
this.$message.success("已延用上一年配置信息")
this.$nextTick(() => this.$refs.form && this.$refs.form.clearValidate())
@@ -720,6 +745,8 @@ layout("/layouts/platform.html"){
allowFamily: false,
fillBedInfo: true,
enabled: true,
homeSignupEntryEnabled: false,
homeSignupEntryImage: "",
lots: [],
unionQuotas: [],
serviceNotice: ""
@@ -738,6 +765,14 @@ layout("/layouts/platform.html"){
if (this.formData.fillBedInfo === null || this.formData.fillBedInfo === undefined) {
this.$set(this.formData, "fillBedInfo", true)
}
if (this.formData.homeSignupEntryEnabled === null || this.formData.homeSignupEntryEnabled === undefined) {
this.$set(this.formData, "homeSignupEntryEnabled", false)
}
if (!this.formData.homeSignupEntryImage) {
this.$set(this.formData, "homeSignupEntryImage", "")
} else {
this.$set(this.formData, "homeSignupEntryImage", this.normalizeHomeSignupEntryImage(this.formData.homeSignupEntryImage))
}
this.formData.cycleStartYear = this.formData.cycleStartYear ? String(this.formData.cycleStartYear) : ""
this.formData.cycleEndYear = this.formData.cycleEndYear ? String(this.formData.cycleEndYear) : ""
this.formData.lots = (this.formData.lots || []).map(item => Object.assign({}, item, {
@@ -765,6 +800,12 @@ layout("/layouts/platform.html"){
if (submitData.outProvinceRatioType !== "固定人数") {
submitData.outProvinceFixedPeople = 0
}
if (!submitData.homeSignupEntryEnabled) {
submitData.homeSignupEntryImage = ""
} else {
// 入口图片只允许保存一张,兼容历史多路径数据时只取第一张。
submitData.homeSignupEntryImage = this.normalizeHomeSignupEntryImage(submitData.homeSignupEntryImage)
}
submitData.lots = JSON.stringify(this.formData.lots || [])
submitData.unionQuotas = JSON.stringify(this.formData.unionQuotas || [])
submitData.lotDeleteList = JSON.stringify(this.lotDeleteList)
@@ -782,6 +823,21 @@ layout("/layouts/platform.html"){
})
})
},
normalizeHomeSignupEntryImage(value) {
// file-upload 组件单图场景保存为逗号分隔字符串,这里统一裁剪为第一张图片。
if (!value) {
return ""
}
if (Array.isArray(value)) {
return value.length > 0 ? value[0] : ""
}
const imageList = String(value).split(",").map(function (item) {
return item.trim()
}).filter(function (item) {
return !!item
})
return imageList.length > 0 ? imageList[0] : ""
},
addLot() {
if (!this.formData.lots) {
this.$set(this.formData, "lots", [])
@@ -116,11 +116,10 @@ const select = {
</template>
<el-form-item :prop="'times.' + $index + '.contactPhone'"
:rules="[
{ required: true, message: '手机号码不能为空', trigger: 'blur' },
{ pattern: /^1[34578]\\d{9}$/, message: '手机号码格式不正确', trigger: 'blur' }
]"
{ required: true, message: '联系方式不能为空', trigger: 'blur' }
]"
label-width="0">
<el-input placeholder="请输入联系方式" clearable maxlength="11" v-model="row.contactPhone"></el-input>
<el-input placeholder="请输入联系方式" clearable maxlength="30" v-model="row.contactPhone"></el-input>
</el-form-item>
</el-descriptions-item>
<el-descriptions-item label="最少参与教工">
@@ -534,7 +534,7 @@ layout("/layouts/platform_h5.html"){
</div>
</div>
<div class="tour-bottom-bar">
<van-button v-if="signupForm.id" type="danger" plain block round :loading="cancelLoading" @click="cancelSignup">取消报名</van-button>
<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>
@@ -733,7 +733,7 @@ layout("/layouts/platform_h5.html"){
return this.isApprovalRow(row) && this.toBoolean(row.canRevoke)
},
showCancel(row) {
return !this.isApprovalRow(row) || this.canModify(row)
return this.toBoolean(row && row.canCancelSignup) && (!this.isApprovalRow(row) || this.canModify(row))
},
// 退出疗休养报名依赖人员分配记录,已退出的数据不再展示入口。
showLeaveTour(row) {
@@ -832,8 +832,10 @@ layout("/layouts/platform_h5.html"){
travelAgencyName: matter.travelAgencyName || "",
boardingPlace: matter.defaultBoardingPlace || "",
travelPeriod: matter.travelPeriod || "",
signupEndTime: matter.signupEndTime || "",
travelStartTime: matter.travelStartTime || "",
travelEndTime: matter.travelEndTime || "",
canCancelSignup: this.toBoolean(matter.canCancelSignup),
allowOverReimbursement: matter.allowOverReimbursement,
overCostReimbursed: false,
hasFamily: false,
@@ -851,8 +853,10 @@ layout("/layouts/platform_h5.html"){
travelAgencyName: matter.travelAgencyName || ledger.travelAgencyName || "",
boardingPlace: ledger.boardingPlace || matter.defaultBoardingPlace || "",
travelPeriod: matter.travelPeriod || "",
signupEndTime: matter.signupEndTime || "",
travelStartTime: matter.travelStartTime || "",
travelEndTime: matter.travelEndTime || "",
canCancelSignup: this.toBoolean(matter.canCancelSignup),
allowOverReimbursement: matter.allowOverReimbursement,
overCostReimbursed: ledger.overCostReimbursed === true || ledger.overCostReimbursed === 1 || ledger.overCostReimbursed === "1",
hasFamily: ledger.hasFamily === true || ledger.hasFamily === 1 || ledger.hasFamily === "1"
@@ -383,6 +383,7 @@ layout("/layouts/platform_h5.html"){
const data = res.data || {}
const matter = data.matter || {}
const staff = data.staff || {}
const assignment = data.assignment || {}
const ledger = data.ledger || {}
const directRelative = data.directRelative || {}
this.boardingPlaceOptions = this.parseBoardingPlaceOptions(matter.boardingPlaceOptions || "")
@@ -409,7 +410,7 @@ layout("/layouts/platform_h5.html"){
hotelName: "",
travelAgencyId: matter.travelAgencyId || "",
travelAgencyName: matter.travelAgencyName || "",
boardingPlace: matter.defaultBoardingPlace || "",
boardingPlace: "",
travelPeriod: matter.travelPeriod || "",
travelStartTime: matter.travelStartTime || "",
travelEndTime: matter.travelEndTime || "",
@@ -419,7 +420,7 @@ layout("/layouts/platform_h5.html"){
intendedRoommate: "",
bedType: "",
bedInfo: ""
}, staff, ledger, {
}, staff, assignment, ledger, {
year: matter.year,
matterId: matter.matterId || ledger.matterId || "",
lineId: matter.lineId || ledger.lineId || "",
@@ -428,7 +429,7 @@ layout("/layouts/platform_h5.html"){
directFamilyUnitLine: matter.directFamilyUnitLine,
travelAgencyId: matter.travelAgencyId || ledger.travelAgencyId || "",
travelAgencyName: matter.travelAgencyName || ledger.travelAgencyName || "",
boardingPlace: ledger.boardingPlace || matter.defaultBoardingPlace || "",
boardingPlace: ledger.boardingPlace || assignment.boardingPlace || "",
travelPeriod: matter.travelPeriod || "",
travelStartTime: matter.travelStartTime || "",
travelEndTime: matter.travelEndTime || "",
@@ -450,7 +451,6 @@ layout("/layouts/platform_h5.html"){
this.familyData = []
this.signupForm.hasFamily = false
}
this.ensureBoardingPlace()
})
},
parseBoardingPlaceOptions(value) {
@@ -466,12 +466,6 @@ layout("/layouts/platform_h5.html"){
return String(value).split(",").map((item) => item.trim()).filter((item) => item)
}
},
ensureBoardingPlace() {
if (!this.signupForm) return
if (!this.signupForm.boardingPlace && this.boardingPlaceOptions.length === 1) {
this.$set(this.signupForm, "boardingPlace", this.boardingPlaceOptions[0])
}
},
emptyFamily() {
return {
familyName: "",
@@ -597,6 +591,26 @@ layout("/layouts/platform_h5.html"){
}
return true
},
validateStaffRequired() {
// 报名人身份证和手机号为移动端提交必填项,提交前统一清理空格并拦截空值。
const idCard = this.signupForm && this.signupForm.idCard ? String(this.signupForm.idCard).trim().toUpperCase() : ""
const mobile = this.signupForm && this.signupForm.mobile ? String(this.signupForm.mobile).trim() : ""
this.$set(this.signupForm, "idCard", idCard)
this.$set(this.signupForm, "mobile", mobile)
if (!idCard) {
vant.Toast("请填写身份证号码")
return false
}
if (!this.isValidIdCard(idCard)) {
vant.Toast("请输入正确的身份证号码")
return false
}
if (!mobile) {
vant.Toast("请填写手机号")
return false
}
return true
},
validateDirectRelative() {
if (!this.isDirectFamilyLine(this.signupForm)) return true
if (!this.directRelativeForm.relativeName) {
@@ -644,6 +658,7 @@ layout("/layouts/platform_h5.html"){
},
submitSignup() {
if (this.pageLoading || this.submitLoading) return
if (!this.validateStaffRequired()) return
if (this.boardingPlaceOptions.length > 0 && !this.signupForm.boardingPlace) {
vant.Toast("请选择乘车地点")
return
@@ -665,7 +680,7 @@ layout("/layouts/platform_h5.html"){
message: res.msg || "恭喜您已报名成功",
confirmButtonColor: "#1867b0"
}).then(() => {
window.location.replace("/platform/tour/signup/h5/signup")
window.location.replace("/platform/tour/signup/h5/notice")
})
} else {
this.showSubmitError(res)
@@ -0,0 +1,234 @@
<!--#
layout("/layouts/platform_h5.html"){
#-->
<style scoped>
.tour-confirm-page {
min-height: 100vh;
padding: 12px 12px 84px;
background: #f4f6f8;
box-sizing: border-box;
}
.tour-confirm-card {
overflow: hidden;
border-radius: 8px;
background: #ffffff;
box-shadow: 0 2px 10px rgba(15, 23, 42, 0.06);
}
.tour-confirm-tip {
margin: 10px 2px 0;
color: #ee8b00;
font-size: 12px;
line-height: 1.6;
}
.tour-confirm-footer {
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: 20;
padding: 10px 14px calc(10px + env(safe-area-inset-bottom));
border-top: 1px solid #edf0f4;
background: #ffffff;
box-sizing: border-box;
}
</style>
<div id="app">
<van-nav-bar title="信息确认" left-text="返回" left-arrow @click-left="historyBack" fixed placeholder></van-nav-bar>
<div class="tour-confirm-page">
<van-loading v-if="loading" size="24px" vertical>加载中...</van-loading>
<template v-else>
<div class="tour-confirm-card">
<van-field label="姓名" required v-model="form.userName" maxlength="100" placeholder="请填写姓名"></van-field>
<van-field label="性别" required readonly clickable is-link v-model="form.gender" placeholder="请选择性别" @click="genderPickerVisible=true"></van-field>
<van-field label="年龄" required readonly v-model="form.age" placeholder="根据身份证号自动计算"></van-field>
<van-field label="身份证号" required v-model="form.idCard" maxlength="18" placeholder="请填写身份证号" @blur="normalizeIdCard"></van-field>
<van-field label="手机号" required v-model="form.mobile" type="tel" maxlength="30" placeholder="请填写手机号"></van-field>
<van-field
label="乘车地点"
required
:readonly="boardingPlaceOptions.length > 0"
:clickable="boardingPlaceOptions.length > 0"
:is-link="boardingPlaceOptions.length > 0"
v-model="form.boardingPlace"
placeholder="请选择或填写乘车地点"
@click="openBoardingPlacePicker">
</van-field>
</div>
<div class="tour-confirm-tip">
请确认本人报名基础信息无误后,即可选择路线报名。
</div>
</template>
</div>
<div class="tour-confirm-footer">
<van-button type="info" block round :loading="submitLoading" @click="submitConfirm">我已确认</van-button>
</div>
<van-popup v-model="genderPickerVisible" position="bottom">
<van-picker show-toolbar :columns="genderColumns" @confirm="confirmGender" @cancel="genderPickerVisible=false"></van-picker>
</van-popup>
<van-popup v-model="boardingPlacePickerVisible" position="bottom">
<van-picker show-toolbar :columns="boardingPlaceOptions" @confirm="confirmBoardingPlace" @cancel="boardingPlacePickerVisible=false"></van-picker>
</van-popup>
</div>
<script nonce="${cspNonce!}">
const vue = new Vue({
el: "#app",
store,
data() {
return {
loading: false,
submitLoading: false,
existsAssignment: false,
form: {
userName: "",
gender: "",
age: "",
idCard: "",
mobile: "",
boardingPlace: ""
},
genderColumns: ["男", "女"],
boardingPlaceOptions: [],
genderPickerVisible: false,
boardingPlacePickerVisible: false
}
},
methods: {
parseBoardingPlaceOptions(value) {
if (!value) return []
try {
const list = JSON.parse(value)
if (!Array.isArray(list)) return []
return list.map(function (item) {
if (typeof item === "string") return item
return item && item.name ? item.name : ""
}).filter(function (item) {
return !!item
})
} catch (e) {
return String(value).split(",").map(function (item) {
return item.trim()
}).filter(function (item) {
return !!item
})
}
},
normalizeIdCard() {
this.form.idCard = this.form.idCard ? String(this.form.idCard).trim().toUpperCase() : ""
this.form.age = this.calcAgeByIdCard(this.form.idCard)
},
isValidIdCard(value) {
if (!value) return false
const idCard = String(value).trim().toUpperCase()
return /^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dX]$/.test(idCard)
},
calcAgeByIdCard(value) {
if (!this.isValidIdCard(value)) {
return ""
}
const idCard = String(value).trim().toUpperCase()
const year = Number(idCard.substring(6, 10))
const month = Number(idCard.substring(10, 12))
const day = Number(idCard.substring(12, 14))
const today = new Date()
let age = today.getFullYear() - year
const currentMonth = today.getMonth() + 1
const currentDay = today.getDate()
if (currentMonth < month || (currentMonth === month && currentDay < day)) {
age--
}
return age >= 0 ? String(age) : ""
},
loadInfo() {
this.loading = true
this.$axios.post("/platform/tour/signup/h5/confirmInfo").then((res) => {
this.loading = false
if (res.code !== 0) {
vant.Toast(res.msg || "信息加载失败")
return
}
const data = res.data || {}
const form = data.form || {}
this.existsAssignment = data.existsAssignment === true || data.existsAssignment === 1 || data.existsAssignment === "1"
if (!this.existsAssignment) {
window.location.replace("/platform/tour/signup/h5/signup")
return
}
this.boardingPlaceOptions = this.parseBoardingPlaceOptions(data.boardingPlaceOptions || "")
this.form = Object.assign({}, this.form, form, {
age: form.idCard ? this.calcAgeByIdCard(form.idCard) : ""
})
}).catch(() => {
this.loading = false
})
},
openBoardingPlacePicker() {
if (this.boardingPlaceOptions.length === 0) return
this.boardingPlacePickerVisible = true
},
confirmGender(value) {
this.form.gender = value
this.genderPickerVisible = false
},
confirmBoardingPlace(value) {
this.form.boardingPlace = value
this.boardingPlacePickerVisible = false
},
validateForm() {
this.normalizeIdCard()
const requiredFields = [
{ key: "userName", message: "请填写姓名" },
{ key: "gender", message: "请选择性别" },
{ key: "idCard", message: "请填写身份证号" },
{ key: "age", message: "请填写正确的身份证号" },
{ key: "mobile", message: "请填写手机号" },
{ key: "boardingPlace", message: "请填写乘车地点" }
]
for (let i = 0; i < requiredFields.length; i++) {
const item = requiredFields[i]
if (this.form[item.key] === null || this.form[item.key] === undefined || String(this.form[item.key]).trim() === "") {
vant.Toast(item.message)
return false
}
}
if (!this.isValidIdCard(this.form.idCard)) {
vant.Toast("请输入正确的身份证号")
return false
}
this.form.age = this.calcAgeByIdCard(this.form.idCard)
return true
},
submitConfirm() {
if (this.loading || this.submitLoading) return
if (!this.validateForm()) return
this.submitLoading = true
this.$axios.post("/platform/tour/signup/h5/saveConfirmInfo", this.form).then((res) => {
this.submitLoading = false
if (res.code === 0) {
window.location.replace("/platform/tour/signup/h5/signup")
} else {
vant.Toast(res.msg || "信息确认失败")
}
}).catch(() => {
this.submitLoading = false
})
}
},
created() {
this.loadInfo()
}
})
</script>
<!--#
}
#-->
@@ -0,0 +1,42 @@
<!--#
layout("/layouts/platform_h5.html"){
#-->
<style scoped>
.tour-entry-page {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: #f4f6f8;
}
</style>
<div id="app" class="tour-entry-page">
<van-loading size="24px" vertical>加载中...</van-loading>
</div>
<script nonce="${cspNonce!}">
const vue = new Vue({
el: "#app",
store,
methods: {
routeByAssignment() {
this.$axios.post("/platform/tour/signup/h5/confirmInfo").then((res) => {
const data = res.code === 0 ? (res.data || {}) : {}
const existsAssignment = data.existsAssignment === true || data.existsAssignment === 1 || data.existsAssignment === "1"
window.location.replace(existsAssignment ? "/platform/tour/signup/h5/confirm" : "/platform/tour/signup/h5/noAssignment")
}).catch(() => {
window.location.replace("/platform/tour/signup/h5/noAssignment")
})
}
},
created() {
this.routeByAssignment()
}
})
</script>
<!--#
}
#-->
@@ -6,83 +6,32 @@ layout("/layouts/platform_h5.html"){
.tour-signup-h5 {
min-height: 100vh;
background: #f5f7fb;
padding-bottom: 80px;
padding: 10px 10px 80px;
box-sizing: border-box;
}
.tour-signup-banner {
position: relative;
height: 193px;
overflow: hidden;
background: #0f74bc;
color: #ffffff;
}
.tour-signup-swipe {
width: 100%;
height: 100%;
}
.tour-signup-swipe img {
display: block;
width: 100%;
height: 193px;
object-fit: cover;
}
.tour-signup-banner::after {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(90deg, rgba(5, 83, 143, 0.72) 0%, rgba(5, 83, 143, 0.32) 48%, rgba(5, 83, 143, 0.08) 100%);
pointer-events: none;
}
.tour-signup-banner__text {
position: absolute;
left: 16px;
right: 16px;
bottom: 20px;
z-index: 1;
}
.tour-signup-title {
font-size: 22px;
font-weight: 700;
line-height: 1.35;
}
.tour-signup-subtitle {
margin-top: 8px;
color: rgba(255, 255, 255, 0.86);
font-size: 13px;
line-height: 1.5;
}
.tour-signup-card {
margin: 10px 12px 0;
display: flex;
flex-direction: column;
min-height: calc(100vh - 160px);
max-height: calc(100vh - 160px);
margin: 0;
padding: 14px 14px 16px;
border-radius: 8px;
background: #ffffff;
box-shadow: 0 8px 20px rgba(15, 23, 42, 0.08);
box-sizing: border-box;
}
.tour-signup-card__header {
display: flex;
align-items: center;
justify-content: space-between;
justify-content: flex-end;
gap: 10px;
padding-bottom: 12px;
border-bottom: 1px solid #eef2f7;
}
.tour-signup-card__title {
color: #111827;
font-size: 17px;
font-weight: 700;
line-height: 1.4;
}
.tour-signup-card__year {
flex-shrink: 0;
color: #0f74bc;
@@ -91,13 +40,19 @@ layout("/layouts/platform_h5.html"){
}
.tour-signup-notice {
margin-top: 14px;
color: #334155;
font-size: 15px;
line-height: 1.8;
word-break: break-word;
}
.tour-signup-card__body {
flex: 1;
min-height: 0;
margin-top: 14px;
overflow-y: auto;
}
.tour-signup-notice /deep/ img,
.tour-signup-notice /deep/ video {
max-width: 100%;
@@ -140,7 +95,10 @@ layout("/layouts/platform_h5.html"){
}
.tour-signup-empty {
padding: 34px 0 26px;
min-height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.tour-signup-footer {
@@ -156,48 +114,38 @@ layout("/layouts/platform_h5.html"){
</style>
<div id="app" class="tour-signup-h5">
<van-nav-bar title="疗休养报名" left-text="返回" left-arrow @click-left="historyBack" fixed placeholder></van-nav-bar>
<div class="tour-signup-banner">
<van-swipe class="tour-signup-swipe" :autoplay="3500" indicator-color="white">
<van-swipe-item v-for="item in bannerList" :key="item">
<img :src="item" alt="疗休养报名">
</van-swipe-item>
</van-swipe>
<div class="tour-signup-banner__text">
<div class="tour-signup-title">疗休养报名</div>
</div>
</div>
<van-nav-bar title="须知提醒" left-text="返回" left-arrow @click-left="historyBack" fixed placeholder></van-nav-bar>
<div class="tour-signup-card">
<div class="tour-signup-card__header">
<div class="tour-signup-card__title">服务须知</div>
<div class="tour-signup-card__year">{{ setting.year || currentYear }}年度</div>
</div>
<van-skeleton title :row="8" :loading="loading">
<div v-if="setting.serviceNotice" class="tour-signup-notice">
<div v-if="serviceNoticePdfHref" class="tour-pdf-preview">
<div v-if="pdfPageCount" class="tour-pdf-toolbar">
已加载 {{ pdfRenderedPages }} / {{ pdfPageCount }} 页
<div class="tour-signup-card__body">
<van-skeleton title :row="8" :loading="loading">
<div v-if="setting.serviceNotice" class="tour-signup-notice">
<div v-if="serviceNoticePdfHref" class="tour-pdf-preview">
<div v-if="pdfPageCount" class="tour-pdf-toolbar">
已加载 {{ pdfRenderedPages }} / {{ pdfPageCount }} 页
</div>
<div ref="pdfPreviewContainer" class="tour-pdf-pages"></div>
<div v-if="pdfLoading && pdfRenderedPages === 0" class="tour-pdf-status">
<van-loading size="22px" vertical>正在加载第一页...</van-loading>
</div>
<div v-else-if="pdfLoading" class="tour-pdf-status">
正在继续加载后续页面...
</div>
<div v-if="pdfError" class="tour-pdf-status">{{ pdfError }}</div>
</div>
<div ref="pdfPreviewContainer" class="tour-pdf-pages"></div>
<div v-if="pdfLoading && pdfRenderedPages === 0" class="tour-pdf-status">
<van-loading size="22px" vertical>正在加载第一页...</van-loading>
</div>
<div v-else-if="pdfLoading" class="tour-pdf-status">
正在继续加载后续页面...
</div>
<div v-if="pdfError" class="tour-pdf-status">{{ pdfError }}</div>
<div v-else v-html="setting.serviceNotice"></div>
</div>
<div v-else v-html="setting.serviceNotice"></div>
</div>
<van-empty v-else class="tour-signup-empty" description="暂无服务须知"></van-empty>
</van-skeleton>
<van-empty v-else class="tour-signup-empty" description="暂无须知"></van-empty>
</van-skeleton>
</div>
</div>
<div class="tour-signup-footer">
<van-button block type="info" color="#0f74bc" round @click="confirmRead">我已阅读</van-button>
<van-button block type="info" color="#0f74bc" round @click="confirmRead">我已知晓</van-button>
</div>
</div>
@@ -217,10 +165,6 @@ layout("/layouts/platform_h5.html"){
pdfPageCount: 0,
pdfRenderedPages: 0,
pdfRenderToken: 0,
bannerList: [
"/assets/platform/images/tour/tour-h5-banner-1.jpg",
"/assets/platform/images/tour/tour-h5-banner-2.jpg"
],
setting: {
year: "",
configName: "",
@@ -379,7 +323,7 @@ layout("/layouts/platform_h5.html"){
this.renderServiceNoticePdf()
})
} else {
vant.Toast(res.msg || "服务须知加载失败")
vant.Toast(res.msg || "须知加载失败")
}
}).catch(() => {
this.loading = false
@@ -200,7 +200,7 @@ layout("/layouts/platform_h5.html"){
</div>
<div class="tour-detail-meta-row">
<span class="tour-detail-meta-label"><span>联系方式</span><span></span></span>
<span class="tour-detail-meta-value">{{ lineDetail.contactPhone || signupDetail.contactPhone || '暂无' }}</span>
<span class="tour-detail-meta-value">{{ signupDetail.contactPhone || lineDetail.contactPhone || '暂无' }}</span>
</div>
</div>
</div>
@@ -306,7 +306,7 @@ layout("/layouts/platform_h5.html"){
}
return this.canModifySignup() ? "修改报名" : "审核中"
}
return "我要核对信息"
return "我要报名"
},
detailActionType() {
return this.canRevokeSignup() ? "danger" : "info"
@@ -462,7 +462,7 @@ layout("/layouts/platform_h5.html"){
</style>
<div id="app" class="tour-line-page">
<van-nav-bar title="疗休养线路" left-text="返回" left-arrow @click-left="historyBack('/platform/tour/signup/h5')" fixed placeholder></van-nav-bar>
<van-nav-bar title="疗休养线路" left-text="返回" left-arrow @click-left="goHome" fixed placeholder></van-nav-bar>
<div class="tour-line-toolbar">
<div class="tour-line-search-row">
@@ -668,6 +668,9 @@ layout("/layouts/platform_h5.html"){
}
},
methods: {
goHome() {
window.location.replace("/platform/h5/home")
},
thumbUrl(row) {
const thumb = this.resolveThumbPath(row && (row.lineMobileThumb || row.agencyMobileThumb))
if (!thumb) {
@@ -817,7 +820,7 @@ layout("/layouts/platform_h5.html"){
}
vant.Dialog.confirm({
title: "提示",
message: "是否确认退出本次疗休,退出后将取消报名资格!",
message: "是否确认退出本次疗休,退出后将取消报名资格!",
confirmButtonColor: "#ee2f2f"
}).then(() => {
this.$axios.post("/platform/tour/signup/h5/doLeaveTour", { assignmentId: row.assignmentId }).then((res) => {
@@ -837,7 +840,7 @@ layout("/layouts/platform_h5.html"){
}
vant.Dialog.confirm({
title: "提示",
message: "是否确认取消本线路?",
message: "是否确认取消本线路报名",
confirmButtonColor: "#ee2f2f"
}).then(() => {
this.$axios.post("/platform/tour/signup/h5/doCancelLine", { ledgerId: row.ledgerId }).then((res) => {
@@ -73,7 +73,7 @@ const home = {
</div>
</div>
<div class="policy-panel">
<div class="policy-panel" @click="enterPolicy">
<div class="policy-panel__title">政策文件</div>
<div class="policy-panel__action">
<span>更多</span>
@@ -120,6 +120,7 @@ const home = {
return {
activityOptions: [],
quickEntries: [],
disabledHomeModules: ["劳模先进", "普惠信息", "我的课堂", "政策文件"],
classroomCourses: [],
classroomCourseIndex: 0,
classroomCourseTimer: null,
@@ -374,16 +375,40 @@ const home = {
}, 3000)
},
menuClick(item) {
if (this.isHomeModuleDisabled(item && item.name)) {
this.showDisabledHomeModuleNotice()
return
}
this.$pjaxReplace(item.href)
},
featureClick(item) {
if (this.isHomeModuleDisabled(item && item.name)) {
this.showDisabledHomeModuleNotice()
return
}
if (item.href) {
this.$pjaxReplace(item.href)
}
},
enterClassroom() {
if (this.isHomeModuleDisabled("我的课堂")) {
this.showDisabledHomeModuleNotice()
return
}
this.$pjaxReplace("/platform/learning/course/h5")
},
enterPolicy() {
if (this.isHomeModuleDisabled("政策文件")) {
this.showDisabledHomeModuleNotice()
}
},
isHomeModuleDisabled(moduleName) {
// 首页临时停用模块统一在点击入口拦截,保留原模块展示和后台数据配置。
return moduleName && this.disabledHomeModules.indexOf(moduleName) !== -1
},
showDisabledHomeModuleNotice() {
this.$toast("暂未启用")
},
bannerChange(index) {
this.activeBannerIndex = index
},