|
@@ -299,41 +299,41 @@
|
|
|
</el-col>
|
|
</el-col>
|
|
|
|
|
|
|
|
<el-col :span="12" class="col-right">
|
|
<el-col :span="12" class="col-right">
|
|
|
- <div class="card card-mid-right">
|
|
|
|
|
- <div class="card-header">
|
|
|
|
|
- <el-icon class="card-icon icon-green"><Tickets /></el-icon>
|
|
|
|
|
- <span class="card-title">总名额限制(按自然周和月)</span>
|
|
|
|
|
- <el-button
|
|
|
|
|
- type="primary"
|
|
|
|
|
- size="small"
|
|
|
|
|
- :disabled="isDisabled || totalLimits.length >= 3"
|
|
|
|
|
- @click="openTotalLimitDialog"
|
|
|
|
|
- >
|
|
|
|
|
- <el-icon><Plus /></el-icon>
|
|
|
|
|
- 添加
|
|
|
|
|
- </el-button>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="card-body card-body-limit">
|
|
|
|
|
- <div v-if="totalLimits.length === 0" class="empty-limits-inline">
|
|
|
|
|
- <el-empty description="暂无限制,点击右上角添加" :image-size="40" />
|
|
|
|
|
- </div>
|
|
|
|
|
- <div v-else class="limit-list-inline">
|
|
|
|
|
- <div v-for="(limit, index) in totalLimits" :key="index" class="limit-item-inline">
|
|
|
|
|
- <span class="limit-type-badge">{{ getLimitTypeLabel(limit.type) }}</span>
|
|
|
|
|
- <span class="limit-count-num">{{ limit.count }} <em>次</em></span>
|
|
|
|
|
- <el-button
|
|
|
|
|
- type="danger"
|
|
|
|
|
- link
|
|
|
|
|
- size="small"
|
|
|
|
|
- :disabled="isDisabled"
|
|
|
|
|
- @click="removeTotalLimit(index)"
|
|
|
|
|
- >
|
|
|
|
|
- <el-icon><Delete /></el-icon>
|
|
|
|
|
- </el-button>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
+<!-- <div class="card card-mid-right">-->
|
|
|
|
|
+<!-- <div class="card-header">-->
|
|
|
|
|
+<!-- <el-icon class="card-icon icon-green"><Tickets /></el-icon>-->
|
|
|
|
|
+<!-- <span class="card-title">总名额限制(按自然周和月)</span>-->
|
|
|
|
|
+<!-- <el-button-->
|
|
|
|
|
+<!-- type="primary"-->
|
|
|
|
|
+<!-- size="small"-->
|
|
|
|
|
+<!-- :disabled="isDisabled || totalLimits.length >= 3"-->
|
|
|
|
|
+<!-- @click="openTotalLimitDialog"-->
|
|
|
|
|
+<!-- >-->
|
|
|
|
|
+<!-- <el-icon><Plus /></el-icon>-->
|
|
|
|
|
+<!-- 添加-->
|
|
|
|
|
+<!-- </el-button>-->
|
|
|
|
|
+<!-- </div>-->
|
|
|
|
|
+<!-- <div class="card-body card-body-limit">-->
|
|
|
|
|
+<!-- <div v-if="totalLimits.length === 0" class="empty-limits-inline">-->
|
|
|
|
|
+<!-- <el-empty description="暂无限制,点击右上角添加" :image-size="40" />-->
|
|
|
|
|
+<!-- </div>-->
|
|
|
|
|
+<!-- <div v-else class="limit-list-inline">-->
|
|
|
|
|
+<!-- <div v-for="(limit, index) in totalLimits" :key="index" class="limit-item-inline">-->
|
|
|
|
|
+<!-- <span class="limit-type-badge">{{ getLimitTypeLabel(limit.type) }}</span>-->
|
|
|
|
|
+<!-- <span class="limit-count-num">{{ limit.count }} <em>次</em></span>-->
|
|
|
|
|
+<!-- <el-button-->
|
|
|
|
|
+<!-- type="danger"-->
|
|
|
|
|
+<!-- link-->
|
|
|
|
|
+<!-- size="small"-->
|
|
|
|
|
+<!-- :disabled="isDisabled"-->
|
|
|
|
|
+<!-- @click="removeTotalLimit(index)"-->
|
|
|
|
|
+<!-- >-->
|
|
|
|
|
+<!-- <el-icon><Delete /></el-icon>-->
|
|
|
|
|
+<!-- </el-button>-->
|
|
|
|
|
+<!-- </div>-->
|
|
|
|
|
+<!-- </div>-->
|
|
|
|
|
+<!-- </div>-->
|
|
|
|
|
+<!-- </div>-->
|
|
|
|
|
|
|
|
<div class="card card-bottom-right">
|
|
<div class="card card-bottom-right">
|
|
|
<div class="card-header">
|
|
<div class="card-header">
|
|
@@ -518,6 +518,7 @@
|
|
|
import { ref, reactive, onMounted, computed } from 'vue'
|
|
import { ref, reactive, onMounted, computed } from 'vue'
|
|
|
import { Setting, Calendar, User, Plus, Delete, Tickets, Minus } from '@element-plus/icons-vue'
|
|
import { Setting, Calendar, User, Plus, Delete, Tickets, Minus } from '@element-plus/icons-vue'
|
|
|
import type { FormInstance, FormRules } from 'element-plus'
|
|
import type { FormInstance, FormRules } from 'element-plus'
|
|
|
|
|
+import { ElMessageBox } from 'element-plus'
|
|
|
import { useUserStore } from '@/store/modules/user'
|
|
import { useUserStore } from '@/store/modules/user'
|
|
|
import {
|
|
import {
|
|
|
appointmentConfigADD,
|
|
appointmentConfigADD,
|
|
@@ -542,6 +543,8 @@ type CycleType = 'daily' | 'weekly' | 'monthly' | 'custom'
|
|
|
|
|
|
|
|
type AppointmentItem = {
|
|
type AppointmentItem = {
|
|
|
id: number
|
|
id: number
|
|
|
|
|
+ // 后端返回的真实 id,新增的 item 没有此字段;提交时回传给后端,删除时进入 deleteIds
|
|
|
|
|
+ serverId?: number
|
|
|
startTime: string
|
|
startTime: string
|
|
|
endTime: string
|
|
endTime: string
|
|
|
limitCount: number
|
|
limitCount: number
|
|
@@ -621,6 +624,8 @@ const dateTypeToCycleType = (dateType: string): CycleType => {
|
|
|
const totalLimits = ref<LimitItem[]>([])
|
|
const totalLimits = ref<LimitItem[]>([])
|
|
|
const personLimits = ref<LimitItem[]>([])
|
|
const personLimits = ref<LimitItem[]>([])
|
|
|
const appointmentGroups = ref<AppointmentGroup[]>([])
|
|
const appointmentGroups = ref<AppointmentGroup[]>([])
|
|
|
|
|
+// 被删除的旧 item 后端 id 列表,提交时一并传给后端做物理删除
|
|
|
|
|
+const deleteIds = ref<number[]>([])
|
|
|
|
|
|
|
|
const totalLimitDialogVisible = ref(false)
|
|
const totalLimitDialogVisible = ref(false)
|
|
|
const personLimitDialogVisible = ref(false)
|
|
const personLimitDialogVisible = ref(false)
|
|
@@ -729,13 +734,20 @@ const addAppointmentGroup = () => {
|
|
|
id: itemIdCounter,
|
|
id: itemIdCounter,
|
|
|
startTime: '08:00',
|
|
startTime: '08:00',
|
|
|
endTime: '09:00',
|
|
endTime: '09:00',
|
|
|
- limitCount: 100
|
|
|
|
|
|
|
+ limitCount: 20
|
|
|
}
|
|
}
|
|
|
]
|
|
]
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const removeGroup = (index: number) => {
|
|
const removeGroup = (index: number) => {
|
|
|
|
|
+ const group = appointmentGroups.value[index]
|
|
|
|
|
+ // 该组下所有带 serverId 的 item 都是后端旧数据,删除时记录 id 交给后端物理删除
|
|
|
|
|
+ group.items.forEach((item) => {
|
|
|
|
|
+ if (item.serverId) {
|
|
|
|
|
+ deleteIds.value.push(item.serverId)
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
appointmentGroups.value.splice(index, 1)
|
|
appointmentGroups.value.splice(index, 1)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -769,11 +781,16 @@ const addItem = (group: AppointmentGroup) => {
|
|
|
id: itemIdCounter,
|
|
id: itemIdCounter,
|
|
|
startTime: newStart,
|
|
startTime: newStart,
|
|
|
endTime: newEnd,
|
|
endTime: newEnd,
|
|
|
- limitCount: 100
|
|
|
|
|
|
|
+ limitCount: 20
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const removeItem = (group: AppointmentGroup, index: number) => {
|
|
const removeItem = (group: AppointmentGroup, index: number) => {
|
|
|
|
|
+ const item = group.items[index]
|
|
|
|
|
+ // 带 serverId 表示是后端旧数据,删除时记录 id 交给后端物理删除
|
|
|
|
|
+ if (item.serverId) {
|
|
|
|
|
+ deleteIds.value.push(item.serverId)
|
|
|
|
|
+ }
|
|
|
group.items.splice(index, 1)
|
|
group.items.splice(index, 1)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -891,6 +908,30 @@ const removePersonLimit = (index: number) => {
|
|
|
personLimits.value.splice(index, 1)
|
|
personLimits.value.splice(index, 1)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+const validatePersonLimits = (): string | null => {
|
|
|
|
|
+ const daily = personLimits.value.find((item) => item.type === 1)
|
|
|
|
|
+ const weekly = personLimits.value.find((item) => item.type === 2)
|
|
|
|
|
+ const monthly = personLimits.value.find((item) => item.type === 3)
|
|
|
|
|
+
|
|
|
|
|
+ if (daily && daily.count <= 0) {
|
|
|
|
|
+ return '单人每日名额限制必须大于0'
|
|
|
|
|
+ }
|
|
|
|
|
+ if (weekly && weekly.count <= 0) {
|
|
|
|
|
+ return '单人每周名额限制必须大于0'
|
|
|
|
|
+ }
|
|
|
|
|
+ if (monthly && monthly.count <= 0) {
|
|
|
|
|
+ return '单人每月名额限制必须大于0'
|
|
|
|
|
+ }
|
|
|
|
|
+ if (monthly && weekly && monthly.count <= weekly.count) {
|
|
|
|
|
+ return '单人每月名额限制必须大于每周名额限制'
|
|
|
|
|
+ }
|
|
|
|
|
+ if (weekly && daily && weekly.count <= daily.count) {
|
|
|
|
|
+ return '单人每周名额限制必须大于每日名额限制'
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return null
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
const validateAdvanceDays = (_rule: any, value: any, callback: any) => {
|
|
const validateAdvanceDays = (_rule: any, value: any, callback: any) => {
|
|
|
if (!value || value < 1) {
|
|
if (!value || value < 1) {
|
|
|
callback(new Error('提前预约天数最少为1'))
|
|
callback(new Error('提前预约天数最少为1'))
|
|
@@ -948,6 +989,8 @@ const parseGroups = (groups: AppointmentGroupDO[]): AppointmentGroup[] => {
|
|
|
itemIdCounter++
|
|
itemIdCounter++
|
|
|
group.items.push({
|
|
group.items.push({
|
|
|
id: itemIdCounter,
|
|
id: itemIdCounter,
|
|
|
|
|
+ // 保留后端真实 id,用于更新时回传和删除时入 deleteIds
|
|
|
|
|
+ serverId: g.id,
|
|
|
startTime: g.beginTime || '',
|
|
startTime: g.beginTime || '',
|
|
|
endTime: g.endTime || '',
|
|
endTime: g.endTime || '',
|
|
|
limitCount: g.limitCount || 100
|
|
limitCount: g.limitCount || 100
|
|
@@ -983,6 +1026,8 @@ const getSetting = async () => {
|
|
|
})
|
|
})
|
|
|
groupIdCounter = 0
|
|
groupIdCounter = 0
|
|
|
itemIdCounter = 0
|
|
itemIdCounter = 0
|
|
|
|
|
+ // 重新拉取配置后,本地 deleteIds 已无意义,清空避免脏数据
|
|
|
|
|
+ deleteIds.value = []
|
|
|
appointmentGroups.value = parseGroups(res.group || [])
|
|
appointmentGroups.value = parseGroups(res.group || [])
|
|
|
if (appointmentGroups.value.length > 0) {
|
|
if (appointmentGroups.value.length > 0) {
|
|
|
groupIdCounter = Math.max(...appointmentGroups.value.map((g) => g.id))
|
|
groupIdCounter = Math.max(...appointmentGroups.value.map((g) => g.id))
|
|
@@ -1112,23 +1157,28 @@ const validateGroups = (): string | null => {
|
|
|
const handleSubmit = async () => {
|
|
const handleSubmit = async () => {
|
|
|
if (!formRef.value) return
|
|
if (!formRef.value) return
|
|
|
if (!organizationId) {
|
|
if (!organizationId) {
|
|
|
- message.error('请选择机构!')
|
|
|
|
|
|
|
+ await ElMessageBox.alert('请选择机构!', '警告', { type: 'warning' })
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
if (!formData.advanceDays || formData.advanceDays < 1) {
|
|
if (!formData.advanceDays || formData.advanceDays < 1) {
|
|
|
- message.error('提前预约天数最少为1')
|
|
|
|
|
|
|
+ await ElMessageBox.alert('提前预约天数最少为1', '警告', { type: 'warning' })
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
const groupError = validateGroups()
|
|
const groupError = validateGroups()
|
|
|
if (groupError) {
|
|
if (groupError) {
|
|
|
- message.error(groupError)
|
|
|
|
|
|
|
+ await ElMessageBox.alert(groupError, '警告', { type: 'warning' })
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ const personLimitError = validatePersonLimits()
|
|
|
|
|
+ if (personLimitError) {
|
|
|
|
|
+ await ElMessageBox.alert(personLimitError, '警告', { type: 'warning' })
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
for (const group of appointmentGroups.value) {
|
|
for (const group of appointmentGroups.value) {
|
|
|
for (let i = 0; i < group.items.length; i++) {
|
|
for (let i = 0; i < group.items.length; i++) {
|
|
|
const item = group.items[i]
|
|
const item = group.items[i]
|
|
|
if (!item.limitCount || item.limitCount <= 0) {
|
|
if (!item.limitCount || item.limitCount <= 0) {
|
|
|
- message.error('限制人数必须大于0')
|
|
|
|
|
|
|
+ await ElMessageBox.alert('限制人数必须大于0', '警告', { type: 'warning' })
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
if (
|
|
if (
|
|
@@ -1136,12 +1186,20 @@ const handleSubmit = async () => {
|
|
|
item.endTime &&
|
|
item.endTime &&
|
|
|
timeToMinutes(item.startTime) >= timeToMinutes(item.endTime)
|
|
timeToMinutes(item.startTime) >= timeToMinutes(item.endTime)
|
|
|
) {
|
|
) {
|
|
|
- message.error('存在开始时间晚于或等于结束时间的预约项,请检查')
|
|
|
|
|
|
|
+ await ElMessageBox.alert(
|
|
|
|
|
+ '存在开始时间晚于或等于结束时间的预约项,请检查',
|
|
|
|
|
+ '警告',
|
|
|
|
|
+ { type: 'warning' }
|
|
|
|
|
+ )
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
for (let j = i + 1; j < group.items.length; j++) {
|
|
for (let j = i + 1; j < group.items.length; j++) {
|
|
|
if (isOverlap(item, group.items[j])) {
|
|
if (isOverlap(item, group.items[j])) {
|
|
|
- message.error('同一预约组内存在时间段重叠的预约项,请检查')
|
|
|
|
|
|
|
+ await ElMessageBox.alert(
|
|
|
|
|
+ '同一预约组内存在时间段重叠的预约项,请检查',
|
|
|
|
|
+ '警告',
|
|
|
|
|
+ { type: 'warning' }
|
|
|
|
|
+ )
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -1163,10 +1221,12 @@ const handleSubmit = async () => {
|
|
|
config[field.total] = limit.count
|
|
config[field.total] = limit.count
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
|
- personLimits.value.forEach((limit) => {
|
|
|
|
|
- const field = limitTypeToFieldMap[limit.type]
|
|
|
|
|
|
|
+ // 单人名额:未设置或删除时传 -1(后端表示无限制)
|
|
|
|
|
+ ;[1, 2, 3].forEach((type) => {
|
|
|
|
|
+ const field = limitTypeToFieldMap[type]
|
|
|
if (field) {
|
|
if (field) {
|
|
|
- config[field.person] = limit.count
|
|
|
|
|
|
|
+ const limit = personLimits.value.find((item) => item.type === type)
|
|
|
|
|
+ config[field.person] = limit ? limit.count : -1
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
|
const group: AppointmentGroupDO[] = []
|
|
const group: AppointmentGroupDO[] = []
|
|
@@ -1178,6 +1238,8 @@ const handleSubmit = async () => {
|
|
|
const enable = g.enable || 0
|
|
const enable = g.enable || 0
|
|
|
g.items.forEach((item) => {
|
|
g.items.forEach((item) => {
|
|
|
group.push({
|
|
group.push({
|
|
|
|
|
+ // 旧数据带回后端 id 用于更新;新增数据不带 id,后端按新增处理
|
|
|
|
|
+ ...(item.serverId ? { id: item.serverId } : {}),
|
|
|
dateType,
|
|
dateType,
|
|
|
weekDays,
|
|
weekDays,
|
|
|
monthDays,
|
|
monthDays,
|
|
@@ -1190,7 +1252,7 @@ const handleSubmit = async () => {
|
|
|
})
|
|
})
|
|
|
})
|
|
})
|
|
|
})
|
|
})
|
|
|
- const params = { config, group }
|
|
|
|
|
|
|
+ const params = { config, group, deleteIds: deleteIds.value }
|
|
|
const res = configId.value
|
|
const res = configId.value
|
|
|
? await appointmentConfigUpdate(params)
|
|
? await appointmentConfigUpdate(params)
|
|
|
: await appointmentConfigADD(params)
|
|
: await appointmentConfigADD(params)
|
|
@@ -1200,7 +1262,7 @@ const handleSubmit = async () => {
|
|
|
}
|
|
}
|
|
|
} catch (error: any) {
|
|
} catch (error: any) {
|
|
|
if (error?.message) {
|
|
if (error?.message) {
|
|
|
- message.error(error.message)
|
|
|
|
|
|
|
+ await ElMessageBox.alert(error.message, '警告', { type: 'warning' })
|
|
|
}
|
|
}
|
|
|
} finally {
|
|
} finally {
|
|
|
submitting.value = false
|
|
submitting.value = false
|