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*/ `
<div>
<el-row :gutter="20" class="mb10">
<el-col :span="24" style="text-align: center">
<div style="font-size: large;color: #303133">
您正在预约【<span style="color: #409EFF">{{ row.name }}</span>】活动场地
<el-col :span="24" style="text-align: center;">
<div style="font-size: large; color: #303133;">
您正在预约【<span style="color: #409EFF;">{{ row.name }}</span>】活动场地
</div>
</el-col>
</el-row>
@@ -28,26 +28,33 @@ const apply = {
</el-col>
<el-col :span="12">
<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="club">协会预约</el-radio-button>
</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-col>
<el-col :span="12">
<el-form-item label="分工会" prop="applyUnionName" v-if="formData.reserveType === 'union'">
<el-input readonly v-model="formData.applyUnionName"
placeholder="自动读取当前登录人的分工会"></el-input>
<el-col :span="12" v-if="formData.reserveType === 'union'">
<el-form-item label="分工会" prop="applyUnionName">
<el-input readonly v-model="formData.applyUnionName" placeholder="自动读取当前登录人的分工会"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-col :span="12" v-if="formData.reserveType === 'club'">
<el-form-item
label="协会"
prop="clubId"
v-if="formData.reserveType === 'club'"
:rules="[{ required: true, message: '请选择您管理的协会', trigger: ['change', 'blur'] }]">
<el-select v-model="formData.clubId" placeholder="请选择您管理的协会" filterable clearable
style="width: 100%" @change="clubChange">
style="width: 100%;" @change="clubChange">
<el-option
v-for="item in clubOptions"
:key="item.id"
@@ -62,6 +69,81 @@ const apply = {
<el-input maxlength="11" placeholder="请输入联系电话" v-model="formData.applyMobile"></el-input>
</el-form-item>
</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-form-item label="预约开始时间" prop="reserveStartTime">
<el-date-picker
@@ -71,7 +153,7 @@ const apply = {
value-format="yyyy-MM-dd HH:mm:ss"
placeholder="请选择预约开始时间"
:picker-options="startPickerOptions"
style="width: 100%"
style="width: 100%;"
@change="timeFieldChange('reserveStartTime')">
</el-date-picker>
</el-form-item>
@@ -85,21 +167,30 @@ const apply = {
value-format="yyyy-MM-dd HH:mm:ss"
placeholder="请选择预约结束时间"
:picker-options="endPickerOptions"
style="width: 100%"
style="width: 100%;"
@change="timeFieldChange('reserveEndTime')">
</el-date-picker>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="批量预约">
<div style="display:flex;align-items:flex-start;column-gap:12px;flex-wrap:wrap;line-height:1.7;">
<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>
<span style="color:#909399;">
<span style="color: #909399;">
例如先选某个周一 09:00-11:00,勾选后会自动预约本年剩余所有周一的这个时段。
</span>
</div>
<div v-if="yearlyReserve && yearlyReserveSummary" style="margin-top:8px;color:#E6A23C;">
{{ yearlyReserveSummary }}
<div v-if="yearlyReserve" style="margin-top: 10px; max-width: 320px;">
<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>
</el-form-item>
</el-col>
@@ -124,20 +215,30 @@ const apply = {
row: {},
clubOptions: [],
yearlyReserve: false,
scheduleDate: '',
availabilityLoading: false,
selectedBlockKeys: [],
timeLimitConfig: {
filterHolidays: false,
holidayList: [],
notApplyTimeList: [],
},
availabilityData: {
date: '',
reserveTimeType: 1,
reserveTimeTypeName: '分段预约',
blocks: [],
},
pickerRefreshKey: 0,
formData: {
reserveType: 'union',
reserveType: '',
applyUnionId: '',
applyUnionName: '',
clubId: '',
clubName: '',
reserveStartTime: '',
reserveEndTime: '',
yearlyReserveEndDate: '',
joinCount: 1,
applyCause: '',
},
@@ -152,17 +253,77 @@ const apply = {
reserveEndTime: [{required: true, message: '必填', trigger: ['blur', 'change']}],
joinCount: [{required: true, message: '必填', trigger: ['blur', 'change']}],
applyCause: [{required: true, message: '必填', trigger: ['blur', 'change']}],
clubId: [{ validator: (rule, value, callback) => {
if (this.formData.reserveType === 'club' && !value) {
callback(new Error('请选择您管理的协会'))
return
}
callback()
}, trigger: ['blur', 'change']}],
clubId: [{
validator: (rule, value, callback) => {
if (this.formData.reserveType === 'club' && !value) {
callback(new Error('请选择您管理的协会'))
return
}
callback()
},
trigger: ['blur', 'change']
}],
},
}
},
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() {
return {
disabledDate: (time) => this.isDisabledDate(time),
@@ -175,27 +336,38 @@ const apply = {
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() {
if (!this.yearlyReserve || !this.formData.reserveStartTime || !this.formData.reserveEndTime) {
if (!this.yearlyReserve || !this.formData.reserveStartTime || !this.formData.reserveEndTime || !this.formData.yearlyReserveEndDate) {
return []
}
const start = this.$moment(this.formData.reserveStartTime)
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 []
}
const result = []
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 startClock = start.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) {
const day = current.format('YYYY-MM-DD')
const startTime = day + ' ' + startClock
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)
}
}
@@ -203,6 +375,21 @@ const apply = {
}
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() {
if (!this.yearlyReserve) {
return ''
@@ -215,13 +402,39 @@ const apply = {
}
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: {
async onOpen(row) {
this.row = row
this.clubOptions = []
this.yearlyReserve = false
this.formData = {
createDefaultFormData(row) {
const reserveType = this.getDefaultReserveType()
return {
siteId: row.id,
applyUserId: this.$store.state.user.id,
applyUserName: this.$store.state.user.username,
@@ -229,18 +442,71 @@ const apply = {
applyLoginName: this.$store.state.user.loginname,
applyUnitId: this.$store.state.user?.unit?.id,
applyUnitName: this.$store.state.user?.unit?.name,
reserveType: 'union',
applyUnionId: this.$store.state.user?.union?.id,
applyUnionName: this.$store.state.user?.union?.name,
reserveType,
applyUnionId: reserveType === 'union' ? (this.$store.state.user?.union?.id || '') : '',
applyUnionName: reserveType === 'union' ? (this.$store.state.user?.union?.name || '') : '',
clubId: '',
clubName: '',
applyMobile: this.$store.state.user.mobile,
reserveStartTime: '',
reserveEndTime: '',
yearlyReserveEndDate: '',
joinCount: 1,
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()
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() {
const fallback = {
@@ -266,7 +532,57 @@ const apply = {
}
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) {
if (!this.availableReserveTypes.includes(value)) {
this.formData.reserveType = this.getDefaultReserveType()
return
}
if (value === 'union') {
this.formData.applyUnionId = this.$store.state.user?.union?.id || ''
this.formData.applyUnionName = this.$store.state.user?.union?.name || ''
@@ -288,19 +604,44 @@ const apply = {
try {
const res = await this.$axios.post('/platform/club/examine/apply/getClubsByRole')
this.clubOptions = Array.isArray(res.data) ? res.data : []
this.syncManagedClubSelection()
if (!this.clubOptions.length) {
this.$message.warning('您当前没有可预约的协会管理权限')
}
} catch (e) {
this.clubOptions = []
this.formData.clubId = ''
this.formData.clubName = ''
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) {
const club = this.clubOptions.find(item => item.id === clubId)
this.formData.clubName = club ? club.clubName : ''
},
async normalizeReserveTypeData() {
if (!this.availableReserveTypes.includes(this.formData.reserveType)) {
this.formData.reserveType = this.getDefaultReserveType()
}
if (this.formData.reserveType === 'union') {
this.formData.applyUnionId = this.$store.state.user?.union?.id || ''
this.formData.applyUnionName = this.$store.state.user?.union?.name || ''
@@ -417,6 +758,15 @@ const apply = {
}
const start = this.$moment(startTime)
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 (showMessage) {
this.$message.warning('预约时间不能选择周末、节假日或禁用时间')
@@ -437,9 +787,173 @@ const apply = {
}
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]
if (!value) {
if (field === 'reserveStartTime') {
this.syncYearlyReserveEndDate(true)
}
this.syncSelectionFromFields()
return
}
if (this.isDateTimeBlocked(value)) {
@@ -447,16 +961,38 @@ const apply = {
this.$set(this.formData, field, '')
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()) {
this.$set(this.formData, field, '')
return
}
if (field === 'reserveStartTime') {
this.syncYearlyReserveEndDate()
}
this.syncSelectionFromFields()
},
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('?????????????????????')
this.$message.warning('当前选择的时间段在截止日期内无法生成可批量预约的日期')
return false
}
if (!this.yearlyReserveDates.length) {
this.$message.warning('当前选择的时间段无法生成本年批量预约日期')
return false
}
return true
@@ -465,10 +1001,58 @@ const apply = {
return this.yearlyReserve ? '/platform/siteCug/apply/submitYearly' : '/platform/siteCug/apply/submit'
},
buildSubmitConfirmMessage() {
const baseText = '您已选择 ' + this.formData.reserveStartTime + ' 至 ' + this.formData.reserveEndTime
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() {
this.$refs.formRef.validate(async (valid) => {
@@ -503,6 +1087,9 @@ const apply = {
if (!this.validateTimeLimit()) {
return
}
if (!this.validateGraphSelection()) {
return
}
if (!this.validateYearlyReserve()) {
return
}
@@ -107,7 +107,7 @@ layout("/layouts/platform.html"){
<script nonce="${cspNonce!}">
<!--#include('../manage/info.js'){}#-->
<!--#include('apply_v2.js'){}#-->
<!--#include('apply.js'){}#-->
const vue = new Vue({
el: "#app",
mixins: [initTableMixins],