This commit is contained in:
zhouhefeng
2026-04-24 16:42:07 +08:00
parent 693925a088
commit 08ee03f802
2 changed files with 630 additions and 43 deletions
@@ -2,9 +2,9 @@ const apply = {
template: /*language=HTML*/ ` template: /*language=HTML*/ `
<div> <div>
<el-row :gutter="20" class="mb10"> <el-row :gutter="20" class="mb10">
<el-col :span="24" style="text-align: center"> <el-col :span="24" style="text-align: center;">
<div style="font-size: large;color: #303133"> <div style="font-size: large; color: #303133;">
您正在预约【<span style="color: #409EFF">{{ row.name }}</span>】活动场地 您正在预约【<span style="color: #409EFF;">{{ row.name }}</span>】活动场地
</div> </div>
</el-col> </el-col>
</el-row> </el-row>
@@ -28,26 +28,33 @@ const apply = {
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="预约类型" prop="reserveType"> <el-form-item label="预约类型" prop="reserveType">
<el-radio-group v-model="formData.reserveType" @change="reserveTypeChange"> <el-radio-group v-if="availableReserveTypes.length" v-model="formData.reserveType" @change="reserveTypeChange" style="display: none;">
<el-radio-button label="union">分工会预约</el-radio-button> <el-radio-button label="union">分工会预约</el-radio-button>
<el-radio-button label="club">协会预约</el-radio-button> <el-radio-button label="club">协会预约</el-radio-button>
</el-radio-group> </el-radio-group>
<el-radio-group v-if="availableReserveTypes.length" v-model="formData.reserveType" @change="reserveTypeChange">
<el-radio-button
v-for="item in availableReserveTypeOptions"
:key="item.value"
:label="item.value">
{{ item.label }}
</el-radio-button>
</el-radio-group>
<span v-else style="color: #909399;">当前无可用的预约权限</span>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12" v-if="formData.reserveType === 'union'">
<el-form-item label="分工会" prop="applyUnionName" v-if="formData.reserveType === 'union'"> <el-form-item label="分工会" prop="applyUnionName">
<el-input readonly v-model="formData.applyUnionName" <el-input readonly v-model="formData.applyUnionName" placeholder="自动读取当前登录人的分工会"></el-input>
placeholder="自动读取当前登录人的分工会"></el-input>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12" v-if="formData.reserveType === 'club'">
<el-form-item <el-form-item
label="协会" label="协会"
prop="clubId" prop="clubId"
v-if="formData.reserveType === 'club'"
:rules="[{ required: true, message: '请选择您管理的协会', trigger: ['change', 'blur'] }]"> :rules="[{ required: true, message: '请选择您管理的协会', trigger: ['change', 'blur'] }]">
<el-select v-model="formData.clubId" placeholder="请选择您管理的协会" filterable clearable <el-select v-model="formData.clubId" placeholder="请选择您管理的协会" filterable clearable
style="width: 100%" @change="clubChange"> style="width: 100%;" @change="clubChange">
<el-option <el-option
v-for="item in clubOptions" v-for="item in clubOptions"
:key="item.id" :key="item.id"
@@ -62,6 +69,81 @@ const apply = {
<el-input maxlength="11" placeholder="请输入联系电话" v-model="formData.applyMobile"></el-input> <el-input maxlength="11" placeholder="请输入联系电话" v-model="formData.applyMobile"></el-input>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24">
<div class="apply-visual-panel">
<div class="apply-visual-panel__toolbar">
<div>
<div class="apply-visual-panel__title">可预约时段</div>
<div class="apply-visual-panel__subtitle">{{ scheduleRuleText }}</div>
</div>
<div class="apply-visual-panel__actions">
<el-tag size="small" type="success">{{ reserveModeText }}</el-tag>
<el-date-picker
v-model="scheduleDate"
type="date"
value-format="yyyy-MM-dd"
placeholder="请选择预约日期"
:picker-options="{ disabledDate: isDisabledDate }"
@change="onScheduleDateChange">
</el-date-picker>
</div>
</div>
<div class="apply-visual-panel__legend">
<span class="apply-legend__item"><i class="apply-legend__dot apply-legend__dot--available"></i>未预约</span>
<span class="apply-legend__item"><i class="apply-legend__dot apply-legend__dot--reserved"></i>已预约</span>
<span class="apply-legend__item"><i class="apply-legend__dot apply-legend__dot--selected"></i>已选择</span>
<span class="apply-legend__item"><i class="apply-legend__dot apply-legend__dot--closed"></i>不可预约</span>
</div>
<el-skeleton :loading="availabilityLoading" animated>
<template slot="template">
<div style="padding-top: 8px;">
<el-skeleton-item variant="p" style="width: 100%; height: 80px;"></el-skeleton-item>
</div>
</template>
<template>
<el-empty v-if="!availabilityBlocks.length" description="当前日期暂无可选时段"></el-empty>
<div v-else-if="isSegmentedMode">
<div v-for="group in segmentedBlockGroups" :key="group.groupIndex" class="apply-block-group">
<div class="apply-block-group__title">{{ group.groupLabel }}</div>
<div class="apply-block-grid">
<button
v-for="block in group.blocks"
:key="block.key"
type="button"
:class="getBlockClass(block)"
@click="onAvailabilityBlockClick(block)">
<span>{{ block.label }}</span>
</button>
</div>
</div>
</div>
<div v-else class="apply-block-group">
<div class="apply-block-group__title">全天候时段</div>
<div class="apply-block-grid">
<button
v-for="block in availabilityBlocks"
:key="block.key"
type="button"
:class="getBlockClass(block)"
@click="onAvailabilityBlockClick(block)">
<span>{{ block.label }}</span>
</button>
</div>
</div>
</template>
</el-skeleton>
<div class="apply-visual-panel__summary" v-if="selectionSummary">
当前选择:{{ selectionSummary }}
</div>
</div>
</el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="预约开始时间" prop="reserveStartTime"> <el-form-item label="预约开始时间" prop="reserveStartTime">
<el-date-picker <el-date-picker
@@ -71,7 +153,7 @@ const apply = {
value-format="yyyy-MM-dd HH:mm:ss" value-format="yyyy-MM-dd HH:mm:ss"
placeholder="请选择预约开始时间" placeholder="请选择预约开始时间"
:picker-options="startPickerOptions" :picker-options="startPickerOptions"
style="width: 100%" style="width: 100%;"
@change="timeFieldChange('reserveStartTime')"> @change="timeFieldChange('reserveStartTime')">
</el-date-picker> </el-date-picker>
</el-form-item> </el-form-item>
@@ -85,21 +167,30 @@ const apply = {
value-format="yyyy-MM-dd HH:mm:ss" value-format="yyyy-MM-dd HH:mm:ss"
placeholder="请选择预约结束时间" placeholder="请选择预约结束时间"
:picker-options="endPickerOptions" :picker-options="endPickerOptions"
style="width: 100%" style="width: 100%;"
@change="timeFieldChange('reserveEndTime')"> @change="timeFieldChange('reserveEndTime')">
</el-date-picker> </el-date-picker>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="24"> <el-col :span="24">
<el-form-item label="批量预约"> <el-form-item label="批量预约">
<div style="display:flex;align-items:flex-start;column-gap:12px;flex-wrap:wrap;line-height:1.7;"> <div style="display: flex; align-items: flex-start; column-gap: 12px; flex-wrap: wrap; line-height: 1.7;">
<el-checkbox v-model="yearlyReserve">预约本年后续每周同一时段</el-checkbox> <el-checkbox v-model="yearlyReserve">预约本年后续每周同一时段</el-checkbox>
<span style="color:#909399;"> <span style="color: #909399;">
例如先选某个周一 09:00-11:00,勾选后会自动预约本年剩余所有周一的这个时段。 例如先选某个周一 09:00-11:00,勾选后会自动预约本年剩余所有周一的这个时段。
</span> </span>
</div> </div>
<div v-if="yearlyReserve && yearlyReserveSummary" style="margin-top:8px;color:#E6A23C;"> <div v-if="yearlyReserve" style="margin-top: 10px; max-width: 320px;">
{{ yearlyReserveSummary }} <el-date-picker
v-model="formData.yearlyReserveEndDate"
type="date"
value-format="yyyy-MM-dd"
placeholder="请选择批量预约截止日期"
:picker-options="yearlyReserveEndDatePickerOptions">
</el-date-picker>
</div>
<div v-if="yearlyReserve && batchReserveSummary" style="margin-top: 8px; color: #E6A23C;">
{{ batchReserveSummary }}
</div> </div>
</el-form-item> </el-form-item>
</el-col> </el-col>
@@ -124,20 +215,30 @@ const apply = {
row: {}, row: {},
clubOptions: [], clubOptions: [],
yearlyReserve: false, yearlyReserve: false,
scheduleDate: '',
availabilityLoading: false,
selectedBlockKeys: [],
timeLimitConfig: { timeLimitConfig: {
filterHolidays: false, filterHolidays: false,
holidayList: [], holidayList: [],
notApplyTimeList: [], notApplyTimeList: [],
}, },
availabilityData: {
date: '',
reserveTimeType: 1,
reserveTimeTypeName: '分段预约',
blocks: [],
},
pickerRefreshKey: 0, pickerRefreshKey: 0,
formData: { formData: {
reserveType: 'union', reserveType: '',
applyUnionId: '', applyUnionId: '',
applyUnionName: '', applyUnionName: '',
clubId: '', clubId: '',
clubName: '', clubName: '',
reserveStartTime: '', reserveStartTime: '',
reserveEndTime: '', reserveEndTime: '',
yearlyReserveEndDate: '',
joinCount: 1, joinCount: 1,
applyCause: '', applyCause: '',
}, },
@@ -152,17 +253,77 @@ const apply = {
reserveEndTime: [{required: true, message: '必填', trigger: ['blur', 'change']}], reserveEndTime: [{required: true, message: '必填', trigger: ['blur', 'change']}],
joinCount: [{required: true, message: '必填', trigger: ['blur', 'change']}], joinCount: [{required: true, message: '必填', trigger: ['blur', 'change']}],
applyCause: [{required: true, message: '必填', trigger: ['blur', 'change']}], applyCause: [{required: true, message: '必填', trigger: ['blur', 'change']}],
clubId: [{ validator: (rule, value, callback) => { clubId: [{
validator: (rule, value, callback) => {
if (this.formData.reserveType === 'club' && !value) { if (this.formData.reserveType === 'club' && !value) {
callback(new Error('请选择您管理的协会')) callback(new Error('请选择您管理的协会'))
return return
} }
callback() callback()
}, trigger: ['blur', 'change']}], },
trigger: ['blur', 'change']
}],
}, },
} }
}, },
computed: { computed: {
canApplyUnion() {
return this.$auth.hasPermission('siteCug.apply.union')
},
canApplyClub() {
return this.$auth.hasPermission('siteCug.apply.club')
},
availableReserveTypes() {
const result = []
if (this.canApplyUnion) {
result.push('union')
}
if (this.canApplyClub) {
result.push('club')
}
return result
},
availableReserveTypeOptions() {
const result = []
if (this.canApplyUnion) {
result.push({value: 'union', label: '分工会预约'})
}
if (this.canApplyClub) {
result.push({value: 'club', label: '协会预约'})
}
return result
},
availabilityBlocks() {
return Array.isArray(this.availabilityData.blocks) ? this.availabilityData.blocks : []
},
isSegmentedMode() {
return Number(this.availabilityData.reserveTimeType || this.row.reserveTimeType || 1) === 1
},
reserveModeText() {
return this.availabilityData.reserveTimeTypeName || (this.isSegmentedMode ? '分段预约' : '全天候预约')
},
scheduleRuleText() {
if (this.isSegmentedMode) {
return '分段预约场地需按场次时间单位选择一个或多个连续时段,不能跨间隔跳选。'
}
return '全天候预约场地需按预约时间单位选择一个或多个连续时段,开始和结束时间会自动回填。'
},
segmentedBlockGroups() {
const groups = []
this.availabilityBlocks.forEach(block => {
let target = groups.find(item => item.groupIndex === block.groupIndex)
if (!target) {
target = {
groupIndex: block.groupIndex,
groupLabel: block.groupLabel,
blocks: [],
}
groups.push(target)
}
target.blocks.push(block)
})
return groups
},
startPickerOptions() { startPickerOptions() {
return { return {
disabledDate: (time) => this.isDisabledDate(time), disabledDate: (time) => this.isDisabledDate(time),
@@ -175,27 +336,38 @@ const apply = {
selectableRange: this.buildSelectableRange(this.formData.reserveEndTime), selectableRange: this.buildSelectableRange(this.formData.reserveEndTime),
} }
}, },
yearlyReserveEndDatePickerOptions() {
return {
disabledDate: (time) => {
if (!this.formData.reserveStartTime) {
return false
}
return this.$moment(time).format('YYYY-MM-DD') < this.formData.reserveStartTime.slice(0, 10)
},
}
},
yearlyReserveDates() { yearlyReserveDates() {
if (!this.yearlyReserve || !this.formData.reserveStartTime || !this.formData.reserveEndTime) { if (!this.yearlyReserve || !this.formData.reserveStartTime || !this.formData.reserveEndTime || !this.formData.yearlyReserveEndDate) {
return [] return []
} }
const start = this.$moment(this.formData.reserveStartTime) const start = this.$moment(this.formData.reserveStartTime)
const end = this.$moment(this.formData.reserveEndTime) const end = this.$moment(this.formData.reserveEndTime)
if (!start.isValid() || !end.isValid() || !end.isAfter(start)) { const reserveEndDate = this.$moment(this.formData.yearlyReserveEndDate, 'YYYY-MM-DD')
if (!start.isValid() || !end.isValid() || !end.isAfter(start) || !reserveEndDate.isValid() || reserveEndDate.isBefore(start, 'day')) {
return [] return []
} }
const result = [] const result = []
const current = start.clone().startOf('day') const current = start.clone().startOf('day')
const endOfYear = start.clone().endOf('year').startOf('day') const reserveEndDay = reserveEndDate.clone().startOf('day')
const targetWeekDay = start.day() const targetWeekDay = start.day()
const startClock = start.format('HH:mm:ss') const startClock = start.format('HH:mm:ss')
const endClock = end.format('HH:mm:ss') const endClock = end.format('HH:mm:ss')
while (current.isSameOrBefore(endOfYear, 'day')) { while (current.isSameOrBefore(reserveEndDay, 'day')) {
if (current.day() === targetWeekDay) { if (current.day() === targetWeekDay) {
const day = current.format('YYYY-MM-DD') const day = current.format('YYYY-MM-DD')
const startTime = day + ' ' + startClock const startTime = day + ' ' + startClock
const endTime = day + ' ' + endClock const endTime = day + ' ' + endClock
if (this.validateTimeLimit(false, startTime, endTime)) { if (this.validateTimeLimit(false, startTime, endTime) && this.validateGraphSelection(false, startTime, endTime, day)) {
result.push(day) result.push(day)
} }
} }
@@ -203,6 +375,21 @@ const apply = {
} }
return result return result
}, },
batchReserveSummary() {
if (!this.yearlyReserve) {
return ''
}
if (!this.formData.yearlyReserveEndDate) {
return '请选择批量预约截止日期'
}
if (!this.formData.reserveStartTime || !this.formData.reserveEndTime) {
return '请先选择开始和结束时间,再批量预约截止日期内的同星期时段'
}
if (!this.yearlyReserveDates.length) {
return '当前时间段在截止日期内无法生成可批量预约的日期'
}
return '将从 ' + this.yearlyReserveDates[0] + ' 开始,自动预约到 ' + this.formData.yearlyReserveEndDate + ',共 ' + this.yearlyReserveDates.length + ' 个时段'
},
yearlyReserveSummary() { yearlyReserveSummary() {
if (!this.yearlyReserve) { if (!this.yearlyReserve) {
return '' return ''
@@ -215,13 +402,39 @@ const apply = {
} }
return '将从 ' + this.yearlyReserveDates[0] + ' 开始,自动预约到 ' + this.yearlyReserveDates[this.yearlyReserveDates.length - 1] + ',共 ' + this.yearlyReserveDates.length + ' 个时段' return '将从 ' + this.yearlyReserveDates[0] + ' 开始,自动预约到 ' + this.yearlyReserveDates[this.yearlyReserveDates.length - 1] + ',共 ' + this.yearlyReserveDates.length + ' 个时段'
}, },
batchReserveSummary() {
if (!this.yearlyReserve) {
return ''
}
if (!this.formData.yearlyReserveEndDate) {
return '请选择批量预约截止日期'
}
if (!this.formData.reserveStartTime || !this.formData.reserveEndTime) {
return '请先选择开始和结束时间,再设置批量预约截止日期'
}
if (!this.yearlyReserveDates.length) {
return '当前截止日期内没有可批量预约的同星期时段'
}
return '将从 ' + this.formData.reserveStartTime.slice(0, 10) + ' 开始,自动预约到 ' + this.formData.yearlyReserveEndDate
},
selectionSummary() {
if (!this.formData.reserveStartTime || !this.formData.reserveEndTime) {
return ''
}
return this.formData.reserveStartTime + ' 至 ' + this.formData.reserveEndTime
},
},
watch: {
yearlyReserve(val) {
if (val) {
this.syncYearlyReserveEndDate(true)
}
},
}, },
methods: { methods: {
async onOpen(row) { createDefaultFormData(row) {
this.row = row const reserveType = this.getDefaultReserveType()
this.clubOptions = [] return {
this.yearlyReserve = false
this.formData = {
siteId: row.id, siteId: row.id,
applyUserId: this.$store.state.user.id, applyUserId: this.$store.state.user.id,
applyUserName: this.$store.state.user.username, applyUserName: this.$store.state.user.username,
@@ -229,18 +442,71 @@ const apply = {
applyLoginName: this.$store.state.user.loginname, applyLoginName: this.$store.state.user.loginname,
applyUnitId: this.$store.state.user?.unit?.id, applyUnitId: this.$store.state.user?.unit?.id,
applyUnitName: this.$store.state.user?.unit?.name, applyUnitName: this.$store.state.user?.unit?.name,
reserveType: 'union', reserveType,
applyUnionId: this.$store.state.user?.union?.id, applyUnionId: reserveType === 'union' ? (this.$store.state.user?.union?.id || '') : '',
applyUnionName: this.$store.state.user?.union?.name, applyUnionName: reserveType === 'union' ? (this.$store.state.user?.union?.name || '') : '',
clubId: '', clubId: '',
clubName: '', clubName: '',
applyMobile: this.$store.state.user.mobile, applyMobile: this.$store.state.user.mobile,
reserveStartTime: '', reserveStartTime: '',
reserveEndTime: '', reserveEndTime: '',
yearlyReserveEndDate: '',
joinCount: 1, joinCount: 1,
applyCause: '', applyCause: '',
} }
},
getDefaultReserveType() {
return this.availableReserveTypes[0] || ''
},
async onOpen(row) {
this.row = row || {}
this.clubOptions = []
this.yearlyReserve = false
this.selectedBlockKeys = []
this.scheduleDate = ''
this.availabilityData = {
date: '',
reserveTimeType: Number(row.reserveTimeType || 1),
reserveTimeTypeName: row.reserveTimeTypeName || (Number(row.reserveTimeType || 1) === 2 ? '全天候预约' : '分段预约'),
blocks: [],
}
this.formData = this.createDefaultFormData(row || {})
if (this.formData.reserveType) {
await this.reserveTypeChange(this.formData.reserveType)
}
this.syncYearlyReserveEndDate(true)
await this.queryTimeLimitConfig() await this.queryTimeLimitConfig()
this.scheduleDate = this.getInitialScheduleDate()
await this.queryAvailability()
},
getDefaultYearlyReserveEndDate() {
const base = this.formData.reserveStartTime
? this.$moment(this.formData.reserveStartTime)
: (this.scheduleDate ? this.$moment(this.scheduleDate, 'YYYY-MM-DD') : this.$moment())
if (!base.isValid()) {
return this.$moment().endOf('month').format('YYYY-MM-DD')
}
return base.clone().endOf('month').format('YYYY-MM-DD')
},
syncYearlyReserveEndDate(force = false) {
const defaultDate = this.getDefaultYearlyReserveEndDate()
if (force || !this.formData.yearlyReserveEndDate) {
this.$set(this.formData, 'yearlyReserveEndDate', defaultDate)
return
}
if (this.formData.reserveStartTime && this.formData.yearlyReserveEndDate < this.formData.reserveStartTime.slice(0, 10)) {
this.$set(this.formData, 'yearlyReserveEndDate', defaultDate)
}
},
getInitialScheduleDate() {
let current = this.$moment().startOf('day')
for (let i = 0; i < 90; i++) {
if (!this.isDisabledDate(current.toDate())) {
return current.format('YYYY-MM-DD')
}
current = current.add(1, 'day')
}
return this.$moment().add(1, 'day').format('YYYY-MM-DD')
}, },
async queryTimeLimitConfig() { async queryTimeLimitConfig() {
const fallback = { const fallback = {
@@ -266,7 +532,57 @@ const apply = {
} }
this.pickerRefreshKey += 1 this.pickerRefreshKey += 1
}, },
async queryAvailability(date) {
const targetDate = date || this.scheduleDate
if (!targetDate || !this.row.id) {
this.availabilityData.blocks = []
return
}
this.availabilityLoading = true
try {
const res = await this.$axios.post('/platform/siteCug/apply/availability', {
siteId: this.row.id,
date: targetDate,
})
if (res.code === 0 && res.data) {
this.availabilityData = {
date: res.data.date || targetDate,
reserveTimeType: Number(res.data.reserveTimeType || this.row.reserveTimeType || 1),
reserveTimeTypeName: res.data.reserveTimeTypeName || this.reserveModeText,
blocks: Array.isArray(res.data.blocks) ? res.data.blocks : [],
}
} else {
this.availabilityData = {
date: targetDate,
reserveTimeType: Number(this.row.reserveTimeType || 1),
reserveTimeTypeName: Number(this.row.reserveTimeType || 1) === 2 ? '全天候预约' : '分段预约',
blocks: [],
}
this.$message.warning((res && res.msg) || '预约时段加载失败')
}
} catch (e) {
this.availabilityData = {
date: targetDate,
reserveTimeType: Number(this.row.reserveTimeType || 1),
reserveTimeTypeName: Number(this.row.reserveTimeType || 1) === 2 ? '全天候预约' : '分段预约',
blocks: [],
}
this.$message.warning('预约时段加载失败,请稍后重试')
} finally {
this.availabilityLoading = false
this.syncSelectionFromFields()
}
},
async onScheduleDateChange(value) {
this.scheduleDate = value || ''
this.clearSelection(true)
await this.queryAvailability(value)
},
async reserveTypeChange(value) { async reserveTypeChange(value) {
if (!this.availableReserveTypes.includes(value)) {
this.formData.reserveType = this.getDefaultReserveType()
return
}
if (value === 'union') { if (value === 'union') {
this.formData.applyUnionId = this.$store.state.user?.union?.id || '' this.formData.applyUnionId = this.$store.state.user?.union?.id || ''
this.formData.applyUnionName = this.$store.state.user?.union?.name || '' this.formData.applyUnionName = this.$store.state.user?.union?.name || ''
@@ -288,19 +604,44 @@ const apply = {
try { try {
const res = await this.$axios.post('/platform/club/examine/apply/getClubsByRole') const res = await this.$axios.post('/platform/club/examine/apply/getClubsByRole')
this.clubOptions = Array.isArray(res.data) ? res.data : [] this.clubOptions = Array.isArray(res.data) ? res.data : []
this.syncManagedClubSelection()
if (!this.clubOptions.length) { if (!this.clubOptions.length) {
this.$message.warning('您当前没有可预约的协会管理权限') this.$message.warning('您当前没有可预约的协会管理权限')
} }
} catch (e) { } catch (e) {
this.clubOptions = [] this.clubOptions = []
this.formData.clubId = ''
this.formData.clubName = ''
this.$message.warning('协会列表加载失败,请稍后重试') this.$message.warning('协会列表加载失败,请稍后重试')
} }
}, },
syncManagedClubSelection() {
if (!this.clubOptions.length) {
this.formData.clubId = ''
this.formData.clubName = ''
return
}
const currentClub = this.clubOptions.find(item => item.id === this.formData.clubId)
if (currentClub) {
this.formData.clubName = currentClub.clubName || ''
return
}
if (this.clubOptions.length === 1) {
this.formData.clubId = this.clubOptions[0].id
this.formData.clubName = this.clubOptions[0].clubName || ''
return
}
this.formData.clubId = ''
this.formData.clubName = ''
},
clubChange(clubId) { clubChange(clubId) {
const club = this.clubOptions.find(item => item.id === clubId) const club = this.clubOptions.find(item => item.id === clubId)
this.formData.clubName = club ? club.clubName : '' this.formData.clubName = club ? club.clubName : ''
}, },
async normalizeReserveTypeData() { async normalizeReserveTypeData() {
if (!this.availableReserveTypes.includes(this.formData.reserveType)) {
this.formData.reserveType = this.getDefaultReserveType()
}
if (this.formData.reserveType === 'union') { if (this.formData.reserveType === 'union') {
this.formData.applyUnionId = this.$store.state.user?.union?.id || '' this.formData.applyUnionId = this.$store.state.user?.union?.id || ''
this.formData.applyUnionName = this.$store.state.user?.union?.name || '' this.formData.applyUnionName = this.$store.state.user?.union?.name || ''
@@ -417,6 +758,15 @@ const apply = {
} }
const start = this.$moment(startTime) const start = this.$moment(startTime)
const end = this.$moment(endTime) const end = this.$moment(endTime)
if (!start.isValid() || !end.isValid()) {
return false
}
if (start.format('YYYY-MM-DD') !== end.format('YYYY-MM-DD')) {
if (showMessage) {
this.$message.warning('预约时间必须在同一天内')
}
return false
}
if (this.isDateTimeBlocked(startTime) || this.isDateTimeBlocked(endTime)) { if (this.isDateTimeBlocked(startTime) || this.isDateTimeBlocked(endTime)) {
if (showMessage) { if (showMessage) {
this.$message.warning('预约时间不能选择周末、节假日或禁用时间') this.$message.warning('预约时间不能选择周末、节假日或禁用时间')
@@ -437,9 +787,173 @@ const apply = {
} }
return true return true
}, },
timeFieldChange(field) { clearSelection(clearTimeFields = false) {
this.selectedBlockKeys = []
if (clearTimeFields) {
this.formData.reserveStartTime = ''
this.formData.reserveEndTime = ''
}
},
getSelectedIndices() {
return this.availabilityBlocks
.map((block, index) => this.selectedBlockKeys.includes(block.key) ? index : -1)
.filter(index => index > -1)
},
getBlockClass(block) {
return {
'apply-block': true,
'apply-block--available': block.status === 'available' && !this.selectedBlockKeys.includes(block.key),
'apply-block--reserved': block.status === 'reserved',
'apply-block--closed': block.status === 'closed',
'apply-block--selected': this.selectedBlockKeys.includes(block.key),
}
},
onAvailabilityBlockClick(block) {
if (!block || !block.key) {
return
}
if (block.status !== 'available' && !this.selectedBlockKeys.includes(block.key)) {
this.$message.warning(block.status === 'reserved' ? '该时段已被预约,请选择其他时段' : '该时段当前不可预约')
return
}
const index = this.availabilityBlocks.findIndex(item => item.key === block.key)
if (index < 0) {
return
}
this.toggleContinuousSelection(index)
},
toggleContinuousSelection(index) {
const selectedIndices = this.getSelectedIndices()
if (!selectedIndices.length) {
this.applySelectionRange(index, index)
return
}
const selectedStart = selectedIndices[0]
const selectedEnd = selectedIndices[selectedIndices.length - 1]
if (selectedIndices.includes(index)) {
if (selectedIndices.length === 1) {
this.clearSelection(true)
return
}
if (index === selectedStart) {
this.applySelectionRange(selectedStart + 1, selectedEnd)
return
}
if (index === selectedEnd) {
this.applySelectionRange(selectedStart, selectedEnd - 1)
return
}
this.applySelectionRange(index, index)
return
}
const nextStart = Math.min(index, selectedStart)
const nextEnd = Math.max(index, selectedEnd)
if (!this.canSelectRange(nextStart, nextEnd)) {
this.$message.warning(this.isSegmentedMode ? '分段预约只能连续选择一个或多个时间段,不能跨间隔跳选' : '全天候预约只能连续选择一个时间段')
return
}
this.applySelectionRange(nextStart, nextEnd)
},
canSelectRange(startIndex, endIndex) {
if (startIndex < 0 || endIndex >= this.availabilityBlocks.length || startIndex > endIndex) {
return false
}
for (let i = startIndex; i <= endIndex; i++) {
const block = this.availabilityBlocks[i]
if (!block || block.status !== 'available') {
return false
}
if (i > startIndex) {
const prev = this.availabilityBlocks[i - 1]
if (!prev || prev.endDateTime !== block.startDateTime) {
return false
}
}
}
return true
},
applySelectionRange(startIndex, endIndex) {
const selected = this.availabilityBlocks.slice(startIndex, endIndex + 1)
this.selectedBlockKeys = selected.map(item => item.key)
if (selected.length) {
this.formData.reserveStartTime = selected[0].startDateTime
this.formData.reserveEndTime = selected[selected.length - 1].endDateTime
this.scheduleDate = selected[0].startDateTime.slice(0, 10)
}
},
findRangeIndicesByFields(startTime, endTime) {
if (!startTime || !endTime) {
return null
}
for (let i = 0; i < this.availabilityBlocks.length; i++) {
const block = this.availabilityBlocks[i]
if (block.status !== 'available' || block.startDateTime !== startTime) {
continue
}
let currentEnd = block.endDateTime
if (currentEnd === endTime) {
return [i, i]
}
for (let j = i + 1; j < this.availabilityBlocks.length; j++) {
const prev = this.availabilityBlocks[j - 1]
const next = this.availabilityBlocks[j]
if (next.status !== 'available' || prev.endDateTime !== next.startDateTime) {
break
}
currentEnd = next.endDateTime
if (currentEnd === endTime) {
return [i, j]
}
}
}
return null
},
syncSelectionFromFields() {
const startTime = this.formData.reserveStartTime
const endTime = this.formData.reserveEndTime
if (!startTime || !endTime) {
this.selectedBlockKeys = []
return
}
if (startTime.slice(0, 10) !== this.scheduleDate || endTime.slice(0, 10) !== this.scheduleDate) {
this.selectedBlockKeys = []
return
}
const range = this.findRangeIndicesByFields(startTime, endTime)
if (!range) {
this.selectedBlockKeys = []
return
}
this.selectedBlockKeys = this.availabilityBlocks.slice(range[0], range[1] + 1).map(item => item.key)
},
validateGraphSelection(showMessage = true, startTime = this.formData.reserveStartTime, endTime = this.formData.reserveEndTime, targetDay = this.scheduleDate) {
if (!startTime || !endTime) {
return true
}
if (!targetDay || startTime.slice(0, 10) !== targetDay || endTime.slice(0, 10) !== targetDay) {
if (showMessage) {
this.$message.warning('预约时间必须与当前选择的预约日期一致')
}
return false
}
const range = this.findRangeIndicesByFields(startTime, endTime)
if (range) {
return true
}
if (showMessage) {
this.$message.warning(this.isSegmentedMode
? '分段预约请按场次时间单位选择一个或多个连续时段'
: '全天候预约请按预约时间单位选择一个或多个连续时段')
}
return false
},
async timeFieldChange(field) {
const value = this.formData[field] const value = this.formData[field]
if (!value) { if (!value) {
if (field === 'reserveStartTime') {
this.syncYearlyReserveEndDate(true)
}
this.syncSelectionFromFields()
return return
} }
if (this.isDateTimeBlocked(value)) { if (this.isDateTimeBlocked(value)) {
@@ -447,16 +961,38 @@ const apply = {
this.$set(this.formData, field, '') this.$set(this.formData, field, '')
return return
} }
const day = this.$moment(value).format('YYYY-MM-DD')
if (day !== this.scheduleDate) {
this.scheduleDate = day
await this.queryAvailability(day)
}
if (this.formData.reserveStartTime && this.formData.reserveEndTime && !this.validateTimeLimit()) { if (this.formData.reserveStartTime && this.formData.reserveEndTime && !this.validateTimeLimit()) {
this.$set(this.formData, field, '') this.$set(this.formData, field, '')
return
} }
if (field === 'reserveStartTime') {
this.syncYearlyReserveEndDate()
}
this.syncSelectionFromFields()
}, },
validateYearlyReserve() { validateYearlyReserve() {
if (!this.yearlyReserve) { if (!this.yearlyReserve) {
return true return true
} }
if (!this.formData.yearlyReserveEndDate) {
this.$message.warning('请选择批量预约截止日期')
return false
}
if (this.formData.reserveStartTime && this.formData.yearlyReserveEndDate < this.formData.reserveStartTime.slice(0, 10)) {
this.$message.warning('批量预约截止日期不能早于开始日期')
return false
}
if (!this.yearlyReserveDates.length) { if (!this.yearlyReserveDates.length) {
this.$message.warning('?????????????????????') this.$message.warning('当前选择的时间段在截止日期内无法生成可批量预约的日期')
return false
}
if (!this.yearlyReserveDates.length) {
this.$message.warning('当前选择的时间段无法生成本年批量预约日期')
return false return false
} }
return true return true
@@ -465,10 +1001,58 @@ const apply = {
return this.yearlyReserve ? '/platform/siteCug/apply/submitYearly' : '/platform/siteCug/apply/submit' return this.yearlyReserve ? '/platform/siteCug/apply/submitYearly' : '/platform/siteCug/apply/submit'
}, },
buildSubmitConfirmMessage() { buildSubmitConfirmMessage() {
const baseText = '您已选择 ' + this.formData.reserveStartTime + ' 至 ' + this.formData.reserveEndTime
if (!this.yearlyReserve) { if (!this.yearlyReserve) {
return '您确定提交' return baseText + ',是否确定提交预约'
} }
return '将一次性预约本年后续 ' + this.yearlyReserveDates.length + ' 个同星期时段,确认提交' return baseText + '。系统将继续预约本年后续 ' + this.yearlyReserveDates.length + ' 个同星期时段,是否确定提交?'
},
validateYearlyReserve() {
if (!this.yearlyReserve) {
return true
}
if (!this.formData.yearlyReserveEndDate) {
this.$message.warning('请选择批量预约截止日期')
return false
}
if (this.formData.reserveStartTime && this.formData.yearlyReserveEndDate < this.formData.reserveStartTime.slice(0, 10)) {
this.$message.warning('批量预约截止日期不能早于开始日期')
return false
}
if (!this.yearlyReserveDates.length) {
this.$message.warning('当前选择的时间段在截止日期内无法生成可批量预约的日期')
return false
}
return true
},
buildSubmitConfirmMessage() {
const baseText = '您已选择 ' + this.formData.reserveStartTime + ' 至 ' + this.formData.reserveEndTime
if (!this.yearlyReserve) {
return baseText + ',是否确定提交预约?'
}
return baseText + '。系统将继续预约到 ' + this.formData.yearlyReserveEndDate + ',共 ' + this.yearlyReserveDates.length + ' 个同星期时段,是否确定提交?'
},
batchReserveSummary() {
if (!this.yearlyReserve) {
return ''
}
if (!this.formData.yearlyReserveEndDate) {
return '请选择批量预约截止日期'
}
if (!this.formData.reserveStartTime || !this.formData.reserveEndTime) {
return '请先选择开始和结束时间,再设置批量预约截止日期'
}
if (!this.yearlyReserveDates.length) {
return '当前截止日期内没有可批量预约的同星期时段'
}
return '将从 ' + this.yearlyReserveDates[0] + ' 开始,自动预约到 ' + this.formData.yearlyReserveEndDate
},
buildSubmitConfirmMessage() {
const baseText = '您已选择 ' + this.formData.reserveStartTime + ' 至 ' + this.formData.reserveEndTime
if (!this.yearlyReserve) {
return baseText + ',是否确定提交预约?'
}
return baseText + '。系统将继续预约到 ' + this.formData.yearlyReserveEndDate + ',是否确定提交?'
}, },
onSubmit() { onSubmit() {
this.$refs.formRef.validate(async (valid) => { this.$refs.formRef.validate(async (valid) => {
@@ -503,6 +1087,9 @@ const apply = {
if (!this.validateTimeLimit()) { if (!this.validateTimeLimit()) {
return return
} }
if (!this.validateGraphSelection()) {
return
}
if (!this.validateYearlyReserve()) { if (!this.validateYearlyReserve()) {
return return
} }
@@ -107,7 +107,7 @@ layout("/layouts/platform.html"){
<script nonce="${cspNonce!}"> <script nonce="${cspNonce!}">
<!--#include('../manage/info.js'){}#--> <!--#include('../manage/info.js'){}#-->
<!--#include('apply_v2.js'){}#--> <!--#include('apply.js'){}#-->
const vue = new Vue({ const vue = new Vue({
el: "#app", el: "#app",
mixins: [initTableMixins], mixins: [initTableMixins],