|
@@ -0,0 +1,757 @@
|
|
|
|
|
+<template>
|
|
|
|
|
+ <ContentWrap>
|
|
|
|
|
+ <el-form
|
|
|
|
|
+ ref="formRef"
|
|
|
|
|
+ :model="formData"
|
|
|
|
|
+ :rules="formRules"
|
|
|
|
|
+ label-width="120px"
|
|
|
|
|
+ class="setting-form"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-row :gutter="20" class="main-row">
|
|
|
|
|
+ <el-col :span="12" class="col-left">
|
|
|
|
|
+ <div class="card card-top-left">
|
|
|
|
|
+ <div class="card-header">
|
|
|
|
|
+ <el-icon class="card-icon icon-blue"><Setting /></el-icon>
|
|
|
|
|
+ <span class="card-title">基础设置</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="card-body">
|
|
|
|
|
+ <el-form-item label="放号规则" prop="ruleType">
|
|
|
|
|
+ <el-radio-group v-model="formData.ruleType">
|
|
|
|
|
+ <el-radio :value="1">时间循环预约</el-radio>
|
|
|
|
|
+ <el-radio :value="2">固定时间段预约</el-radio>
|
|
|
|
|
+ </el-radio-group>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+
|
|
|
|
|
+ <template v-if="formData.ruleType === 1">
|
|
|
|
|
+ <el-form-item label="开始预约日" prop="startDay">
|
|
|
|
|
+ <el-select v-model="formData.startDay" placeholder="请选择" style="width: 100%">
|
|
|
|
|
+ <el-option label="当天" :value="0" />
|
|
|
|
|
+ <el-option label="未来1天" :value="1" />
|
|
|
|
|
+ <el-option label="未来2天" :value="2" />
|
|
|
|
|
+ <el-option label="未来3天" :value="3" />
|
|
|
|
|
+ <el-option label="未来4天" :value="4" />
|
|
|
|
|
+ <el-option label="未来5天" :value="5" />
|
|
|
|
|
+ <el-option label="未来6天" :value="6" />
|
|
|
|
|
+ <el-option label="未来7天" :value="7" />
|
|
|
|
|
+ <el-option label="未来8天" :value="8" />
|
|
|
|
|
+ <el-option label="未来9天" :value="9" />
|
|
|
|
|
+ <el-option label="未来10天" :value="10" />
|
|
|
|
|
+ <el-option label="未来11天" :value="11" />
|
|
|
|
|
+ <el-option label="未来12天" :value="12" />
|
|
|
|
|
+ <el-option label="未来3天" :value="13" />
|
|
|
|
|
+ <el-option label="未来14天" :value="14" />
|
|
|
|
|
+ <el-option label="未来15天" :value="15" />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="提前预约天数" prop="advanceDays">
|
|
|
|
|
+ <el-input-number
|
|
|
|
|
+ v-model="formData.advanceDays"
|
|
|
|
|
+ :min="1"
|
|
|
|
|
+ :max="30"
|
|
|
|
|
+ style="width: 100%"
|
|
|
|
|
+ ><template #suffix>
|
|
|
|
|
+ <span>天</span>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-input-number>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </template>
|
|
|
|
|
+
|
|
|
|
|
+ <template v-else>
|
|
|
|
|
+ <el-form-item label="放号开始日期" prop="releaseStartDate">
|
|
|
|
|
+ <el-date-picker
|
|
|
|
|
+ v-model="formData.releaseStartDate"
|
|
|
|
|
+ type="date"
|
|
|
|
|
+ placeholder="选择开始日期"
|
|
|
|
|
+ value-format="YYYY-MM-DD"
|
|
|
|
|
+ style="width: 100%"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="放号结束日期" prop="releaseEndDate">
|
|
|
|
|
+ <el-date-picker
|
|
|
|
|
+ v-model="formData.releaseEndDate"
|
|
|
|
|
+ type="date"
|
|
|
|
|
+ placeholder="选择结束日期"
|
|
|
|
|
+ value-format="YYYY-MM-DD"
|
|
|
|
|
+ style="width: 100%"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="card card-bottom-left">
|
|
|
|
|
+ <div class="card-header">
|
|
|
|
|
+ <el-icon class="card-icon icon-green"><Calendar /></el-icon>
|
|
|
|
|
+ <span class="card-title">日期限制</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="card-body">
|
|
|
|
|
+ <el-form-item label="开启日期限制" prop="dateLimitEnabled">
|
|
|
|
|
+ <el-switch
|
|
|
|
|
+ v-model="formData.dateLimitEnabled"
|
|
|
|
|
+ active-text="开启"
|
|
|
|
|
+ inactive-text="关闭"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+
|
|
|
|
|
+ <template v-if="formData.dateLimitEnabled">
|
|
|
|
|
+ <el-form-item label="限制开始日期" prop="limitStartDate">
|
|
|
|
|
+ <el-date-picker
|
|
|
|
|
+ v-model="formData.limitStartDate"
|
|
|
|
|
+ type="date"
|
|
|
|
|
+ placeholder="选择开始日期"
|
|
|
|
|
+ value-format="YYYY-MM-DD"
|
|
|
|
|
+ style="width: 100%"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="限制关闭日期" prop="limitEndDate">
|
|
|
|
|
+ <el-date-picker
|
|
|
|
|
+ v-model="formData.limitEndDate"
|
|
|
|
|
+ type="date"
|
|
|
|
|
+ placeholder="选择关闭日期"
|
|
|
|
|
+ value-format="YYYY-MM-DD"
|
|
|
|
|
+ style="width: 100%"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <div v-if="formData.ruleType === 2" class="tip-row" style="width: 100%">
|
|
|
|
|
+ <el-alert
|
|
|
|
|
+ type="warning"
|
|
|
|
|
+ :closable="false"
|
|
|
|
|
+ show-icon
|
|
|
|
|
+ title="结束日期必须大于开始日期"
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+
|
|
|
|
|
+ <el-col :span="12" class="col-right">
|
|
|
|
|
+ <div class="card card-top-right">
|
|
|
|
|
+ <div class="card-header">
|
|
|
|
|
+ <el-icon class="card-icon icon-orange"><Clock /></el-icon>
|
|
|
|
|
+ <span class="card-title">时间配置</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="card-body">
|
|
|
|
|
+ <el-form-item label="每天开始时间" prop="dailyStartTime">
|
|
|
|
|
+ <el-time-picker
|
|
|
|
|
+ v-model="formData.dailyStartTime"
|
|
|
|
|
+ format="HH:mm"
|
|
|
|
|
+ value-format="HH:mm"
|
|
|
|
|
+ placeholder="选择开始时间"
|
|
|
|
|
+ style="width: 100%"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="每天结束时间" prop="dailyEndTime">
|
|
|
|
|
+ <el-time-picker
|
|
|
|
|
+ v-model="formData.dailyEndTime"
|
|
|
|
|
+ format="HH:mm"
|
|
|
|
|
+ value-format="HH:mm"
|
|
|
|
|
+ placeholder="选择结束时间"
|
|
|
|
|
+ style="width: 100%"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="time-preview" style="width: 100%">
|
|
|
|
|
+ <div class="preview-item">
|
|
|
|
|
+ <span class="preview-label">每日预约时长</span>
|
|
|
|
|
+ <span class="preview-value">{{ dailyDuration }}</span>
|
|
|
|
|
+ </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="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" @click="removeTotalLimit(index)">
|
|
|
|
|
+ <el-icon><Delete /></el-icon>
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="card card-bottom-right">
|
|
|
|
|
+ <div class="card-header">
|
|
|
|
|
+ <el-icon class="card-icon icon-purple"><User /></el-icon>
|
|
|
|
|
+ <span class="card-title">单人名额限制</span>
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ type="primary"
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ :disabled="personLimits.length >= 3"
|
|
|
|
|
+ @click="openPersonLimitDialog"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-icon><Plus /></el-icon>
|
|
|
|
|
+ 添加
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="card-body card-body-limit">
|
|
|
|
|
+ <div v-if="personLimits.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 personLimits" :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" @click="removePersonLimit(index)">
|
|
|
|
|
+ <el-icon><Delete /></el-icon>
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ </el-row>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="form-footer">
|
|
|
|
|
+ <el-button type="primary" size="large" :loading="submitting" @click="handleSubmit">
|
|
|
|
|
+ 保存设置
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ <el-button size="large" @click="handleReset">重置</el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-form>
|
|
|
|
|
+
|
|
|
|
|
+ <el-dialog v-model="totalLimitDialogVisible" title="添加总名额限制" width="420px" align-center>
|
|
|
|
|
+ <el-form :model="totalLimitForm" label-width="100px" style="margin-bottom: 20px">
|
|
|
|
|
+ <el-form-item label="限制类型" required>
|
|
|
|
|
+ <el-select v-model="totalLimitForm.type" placeholder="请选择" style="width: 100%">
|
|
|
|
|
+ <el-option
|
|
|
|
|
+ v-for="item in availableTotalTypes"
|
|
|
|
|
+ :key="item.value"
|
|
|
|
|
+ :label="item.label"
|
|
|
|
|
+ :value="item.value"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="限制次数" required>
|
|
|
|
|
+ <el-input-number
|
|
|
|
|
+ v-model="totalLimitForm.count"
|
|
|
|
|
+ :min="1"
|
|
|
|
|
+ :max="9999"
|
|
|
|
|
+ style="width: 100%"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-form>
|
|
|
|
|
+ <template #footer>
|
|
|
|
|
+ <el-button @click="totalLimitDialogVisible = false">取消</el-button>
|
|
|
|
|
+ <el-button type="primary" @click="confirmTotalLimit">确定</el-button>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-dialog>
|
|
|
|
|
+
|
|
|
|
|
+ <el-dialog
|
|
|
|
|
+ v-model="personLimitDialogVisible"
|
|
|
|
|
+ title="添加单人名额限制"
|
|
|
|
|
+ width="420px"
|
|
|
|
|
+ align-center
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-form :model="personLimitForm" label-width="100px" style="margin-bottom: 20px">
|
|
|
|
|
+ <el-form-item label="限制类型" required>
|
|
|
|
|
+ <el-select v-model="personLimitForm.type" placeholder="请选择" style="width: 100%">
|
|
|
|
|
+ <el-option
|
|
|
|
|
+ v-for="item in availablePersonTypes"
|
|
|
|
|
+ :key="item.value"
|
|
|
|
|
+ :label="item.label"
|
|
|
|
|
+ :value="item.value"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="限制次数" required>
|
|
|
|
|
+ <el-input-number
|
|
|
|
|
+ v-model="personLimitForm.count"
|
|
|
|
|
+ :min="1"
|
|
|
|
|
+ :max="9999"
|
|
|
|
|
+ style="width: 100%"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-form>
|
|
|
|
|
+ <template #footer>
|
|
|
|
|
+ <el-button @click="personLimitDialogVisible = false">取消</el-button>
|
|
|
|
|
+ <el-button type="primary" @click="confirmPersonLimit">确定</el-button>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-dialog>
|
|
|
|
|
+ </ContentWrap>
|
|
|
|
|
+</template>
|
|
|
|
|
+
|
|
|
|
|
+<script setup lang="ts">
|
|
|
|
|
+import { ref, reactive, onMounted, computed } from 'vue'
|
|
|
|
|
+import { Setting, Calendar, Clock, User, Plus, Delete, Tickets } from '@element-plus/icons-vue'
|
|
|
|
|
+import type { FormInstance, FormRules } from 'element-plus'
|
|
|
|
|
+import fetchHttp from '@/config/axios/fetchHttp'
|
|
|
|
|
+import { useUserStore } from '@/store/modules/user'
|
|
|
|
|
+
|
|
|
|
|
+const userStore = useUserStore()
|
|
|
|
|
+const message = useMessage()
|
|
|
|
|
+
|
|
|
|
|
+const formRef = ref<FormInstance>()
|
|
|
|
|
+const submitting = ref(false)
|
|
|
|
|
+const tenantIds = ref<number[]>([])
|
|
|
|
|
+let organizationId = 0
|
|
|
|
|
+
|
|
|
|
|
+type LimitItem = { type: number; count: number }
|
|
|
|
|
+
|
|
|
|
|
+const limitTypeOptions = [
|
|
|
|
|
+ { label: '每天', value: 1 },
|
|
|
|
|
+ { label: '每周', value: 2 },
|
|
|
|
|
+ { label: '每月', value: 3 }
|
|
|
|
|
+]
|
|
|
|
|
+
|
|
|
|
|
+const totalLimits = ref<LimitItem[]>([])
|
|
|
|
|
+const personLimits = ref<LimitItem[]>([])
|
|
|
|
|
+
|
|
|
|
|
+const totalLimitDialogVisible = ref(false)
|
|
|
|
|
+const personLimitDialogVisible = ref(false)
|
|
|
|
|
+const totalLimitForm = reactive<LimitItem>({ type: 1, count: 1 })
|
|
|
|
|
+const personLimitForm = reactive<LimitItem>({ type: 1, count: 1 })
|
|
|
|
|
+
|
|
|
|
|
+const availableTotalTypes = computed(() => {
|
|
|
|
|
+ const usedTypes = totalLimits.value.map((item) => item.type)
|
|
|
|
|
+ return limitTypeOptions.filter((item) => !usedTypes.includes(item.value))
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+const availablePersonTypes = computed(() => {
|
|
|
|
|
+ const usedTypes = personLimits.value.map((item) => item.type)
|
|
|
|
|
+ return limitTypeOptions.filter((item) => !usedTypes.includes(item.value))
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+const getLimitTypeLabel = (type: number) => {
|
|
|
|
|
+ const found = limitTypeOptions.find((item) => item.value === type)
|
|
|
|
|
+ return found ? found.label : ''
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const defaultFormData = () => ({
|
|
|
|
|
+ ruleType: 1,
|
|
|
|
|
+ startDay: 0,
|
|
|
|
|
+ advanceDays: 6,
|
|
|
|
|
+ releaseStartDate: '',
|
|
|
|
|
+ releaseEndDate: '',
|
|
|
|
|
+ dateLimitEnabled: false,
|
|
|
|
|
+ limitStartDate: '',
|
|
|
|
|
+ limitEndDate: '',
|
|
|
|
|
+ holidayClosed: false,
|
|
|
|
|
+ dailyStartTime: '08:00',
|
|
|
|
|
+ dailyEndTime: '18:00'
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+const formData = reactive(defaultFormData())
|
|
|
|
|
+
|
|
|
|
|
+const openTotalLimitDialog = () => {
|
|
|
|
|
+ if (availableTotalTypes.value.length === 0) return
|
|
|
|
|
+ totalLimitForm.type = availableTotalTypes.value[0].value
|
|
|
|
|
+ totalLimitForm.count = 1
|
|
|
|
|
+ totalLimitDialogVisible.value = true
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const confirmTotalLimit = () => {
|
|
|
|
|
+ if (!totalLimitForm.type || !totalLimitForm.count) {
|
|
|
|
|
+ message.warning('请填写完整信息')
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ totalLimits.value.push({ type: totalLimitForm.type, count: totalLimitForm.count })
|
|
|
|
|
+ totalLimitDialogVisible.value = false
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const removeTotalLimit = (index: number) => {
|
|
|
|
|
+ totalLimits.value.splice(index, 1)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const openPersonLimitDialog = () => {
|
|
|
|
|
+ if (availablePersonTypes.value.length === 0) return
|
|
|
|
|
+ personLimitForm.type = availablePersonTypes.value[0].value
|
|
|
|
|
+ personLimitForm.count = 1
|
|
|
|
|
+ personLimitDialogVisible.value = true
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const confirmPersonLimit = () => {
|
|
|
|
|
+ if (!personLimitForm.type || !personLimitForm.count) {
|
|
|
|
|
+ message.warning('请填写完整信息')
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ personLimits.value.push({ type: personLimitForm.type, count: personLimitForm.count })
|
|
|
|
|
+ personLimitDialogVisible.value = false
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const removePersonLimit = (index: number) => {
|
|
|
|
|
+ personLimits.value.splice(index, 1)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const dailyDuration = computed(() => {
|
|
|
|
|
+ if (!formData.dailyStartTime || !formData.dailyEndTime) return '--'
|
|
|
|
|
+ const [sh, sm] = formData.dailyStartTime.split(':').map(Number)
|
|
|
|
|
+ const [eh, em] = formData.dailyEndTime.split(':').map(Number)
|
|
|
|
|
+ const startMin = sh * 60 + sm
|
|
|
|
|
+ const endMin = eh * 60 + em
|
|
|
|
|
+ const diff = endMin - startMin
|
|
|
|
|
+ if (diff <= 0) return '--'
|
|
|
|
|
+ const hours = Math.floor(diff / 60)
|
|
|
|
|
+ const mins = diff % 60
|
|
|
|
|
+ if (hours > 0 && mins > 0) return `${hours}小时${mins}分钟`
|
|
|
|
|
+ if (hours > 0) return `${hours}小时`
|
|
|
|
|
+ return `${mins}分钟`
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+const validateEndDate = (rule: any, value: any, callback: any) => {
|
|
|
|
|
+ if (formData.ruleType === 2 && formData.releaseStartDate && value) {
|
|
|
|
|
+ if (value <= formData.releaseStartDate) {
|
|
|
|
|
+ callback(new Error('放号结束日期必须大于开始日期'))
|
|
|
|
|
+ } else {
|
|
|
|
|
+ callback()
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ callback()
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const validateLimitEndDate = (rule: any, value: any, callback: any) => {
|
|
|
|
|
+ if (formData.dateLimitEnabled && formData.limitStartDate && value) {
|
|
|
|
|
+ if (value <= formData.limitStartDate) {
|
|
|
|
|
+ callback(new Error('关闭日期必须大于开始日期'))
|
|
|
|
|
+ } else {
|
|
|
|
|
+ callback()
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ callback()
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const validateDailyEndTime = (rule: any, value: any, callback: any) => {
|
|
|
|
|
+ if (formData.dailyStartTime && value) {
|
|
|
|
|
+ if (value <= formData.dailyStartTime) {
|
|
|
|
|
+ callback(new Error('结束预约时间必须晚于开始时间'))
|
|
|
|
|
+ } else {
|
|
|
|
|
+ callback()
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ callback()
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const formRules: FormRules = reactive({
|
|
|
|
|
+ ruleType: [{ required: true, message: '请选择放号规则', trigger: 'change' }],
|
|
|
|
|
+ releaseStartDate: [{ required: true, message: '请选择放号开始日期', trigger: 'change' }],
|
|
|
|
|
+ releaseEndDate: [
|
|
|
|
|
+ { required: true, message: '请选择放号结束日期', trigger: 'change' },
|
|
|
|
|
+ { validator: validateEndDate, trigger: 'change' }
|
|
|
|
|
+ ],
|
|
|
|
|
+ limitStartDate: [{ required: true, message: '请选择限制开始日期', trigger: 'change' }],
|
|
|
|
|
+ limitEndDate: [
|
|
|
|
|
+ { required: true, message: '请选择限制关闭日期', trigger: 'change' },
|
|
|
|
|
+ { validator: validateLimitEndDate, trigger: 'change' }
|
|
|
|
|
+ ],
|
|
|
|
|
+ dailyStartTime: [{ required: true, message: '请选择开始时间', trigger: 'change' }],
|
|
|
|
|
+ dailyEndTime: [
|
|
|
|
|
+ { required: true, message: '请选择结束时间', trigger: 'change' },
|
|
|
|
|
+ { validator: validateDailyEndTime, trigger: 'change' }
|
|
|
|
|
+ ]
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+const changeTen = (i: any[]) => {
|
|
|
|
|
+ if (i.length > 1) {
|
|
|
|
|
+ message.error('只能选择一个机构!')
|
|
|
|
|
+ if (i[0].length > 1) {
|
|
|
|
|
+ tenantIds.value = [i[0][1]]
|
|
|
|
|
+ } else {
|
|
|
|
|
+ tenantIds.value = []
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (tenantIds.value.length > 0) {
|
|
|
|
|
+ organizationId = tenantIds.value[0]
|
|
|
|
|
+ getSetting()
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const getSetting = async () => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await fetchHttp.get('/admin/appointment/getTimeslotSetting', {
|
|
|
|
|
+ organizationId
|
|
|
|
|
+ })
|
|
|
|
|
+ if (res) {
|
|
|
|
|
+ Object.assign(formData, {
|
|
|
|
|
+ ruleType: res.ruleType || 1,
|
|
|
|
|
+ startDay: res.startDay ?? 0,
|
|
|
|
|
+ advanceDays: res.advanceDays || 6,
|
|
|
|
|
+ releaseStartDate: res.releaseStartDate || '',
|
|
|
|
|
+ releaseEndDate: res.releaseEndDate || '',
|
|
|
|
|
+ dateLimitEnabled: !!res.dateLimitEnabled,
|
|
|
|
|
+ limitStartDate: res.limitStartDate || '',
|
|
|
|
|
+ limitEndDate: res.limitEndDate || '',
|
|
|
|
|
+ holidayClosed: !!res.holidayClosed,
|
|
|
|
|
+ dailyStartTime: res.dailyStartTime || '08:00',
|
|
|
|
|
+ dailyEndTime: res.dailyEndTime || '18:00'
|
|
|
|
|
+ })
|
|
|
|
|
+ totalLimits.value = res.totalLimits || []
|
|
|
|
|
+ personLimits.value = res.personLimits || []
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.log('获取设置失败', e)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const handleSubmit = async () => {
|
|
|
|
|
+ if (!formRef.value) return
|
|
|
|
|
+ if (!organizationId) {
|
|
|
|
|
+ message.error('请选择机构!')
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ try {
|
|
|
|
|
+ await formRef.value.validate()
|
|
|
|
|
+ submitting.value = true
|
|
|
|
|
+ const params = {
|
|
|
|
|
+ organizationId,
|
|
|
|
|
+ ...formData,
|
|
|
|
|
+ totalLimits: totalLimits.value,
|
|
|
|
|
+ personLimits: personLimits.value
|
|
|
|
|
+ }
|
|
|
|
|
+ const res = await fetchHttp.post('/admin/appointment/setTimeslotSetting', params)
|
|
|
|
|
+ if (res) {
|
|
|
|
|
+ message.success('设置成功!')
|
|
|
|
|
+ getSetting()
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error: any) {
|
|
|
|
|
+ if (error?.message) {
|
|
|
|
|
+ message.error(error.message)
|
|
|
|
|
+ }
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ submitting.value = false
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const handleReset = () => {
|
|
|
|
|
+ formRef.value?.resetFields()
|
|
|
|
|
+ Object.assign(formData, defaultFormData())
|
|
|
|
|
+ totalLimits.value = []
|
|
|
|
|
+ personLimits.value = []
|
|
|
|
|
+ if (organizationId) {
|
|
|
|
|
+ getSetting()
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+onMounted(() => {
|
|
|
|
|
+ if (userStore.orgTenantId.length > 0) {
|
|
|
|
|
+ tenantIds.value = [userStore.orgTenantId[0]] as number[]
|
|
|
|
|
+ organizationId = tenantIds.value[0]
|
|
|
|
|
+ getSetting()
|
|
|
|
|
+ }
|
|
|
|
|
+})
|
|
|
|
|
+</script>
|
|
|
|
|
+
|
|
|
|
|
+<style scoped lang="scss">
|
|
|
|
|
+.setting-form {
|
|
|
|
|
+ .main-row {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: stretch;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .col-left,
|
|
|
|
|
+ .col-right {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ gap: 20px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .card {
|
|
|
|
|
+ background-color: #fff;
|
|
|
|
|
+ border-radius: 8px;
|
|
|
|
|
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+
|
|
|
|
|
+ &-top-left,
|
|
|
|
|
+ &-top-right,
|
|
|
|
|
+ &-mid-right {
|
|
|
|
|
+ flex: 0 0 auto;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ &-bottom-left,
|
|
|
|
|
+ &-bottom-right {
|
|
|
|
|
+ flex: 1 1 auto;
|
|
|
|
|
+ min-height: 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .card-header {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ padding: 16px 20px;
|
|
|
|
|
+ border-bottom: 1px solid #f0f0f0;
|
|
|
|
|
+ background: linear-gradient(90deg, #fafbfc 0%, #ffffff 100%);
|
|
|
|
|
+
|
|
|
|
|
+ .card-icon {
|
|
|
|
|
+ width: 28px;
|
|
|
|
|
+ height: 28px;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ border-radius: 6px;
|
|
|
|
|
+ margin-right: 10px;
|
|
|
|
|
+ color: #fff;
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+
|
|
|
|
|
+ &.icon-blue {
|
|
|
|
|
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
|
|
|
+ }
|
|
|
|
|
+ &.icon-green {
|
|
|
|
|
+ background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
|
|
|
|
|
+ }
|
|
|
|
|
+ &.icon-orange {
|
|
|
|
|
+ background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
|
|
|
|
+ }
|
|
|
|
|
+ &.icon-purple {
|
|
|
|
|
+ background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .card-title {
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ color: #303133;
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .add-btn {
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .card-body {
|
|
|
|
|
+ padding: 20px;
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+
|
|
|
|
|
+ &-scroll {
|
|
|
|
|
+ overflow-y: auto;
|
|
|
|
|
+ max-height: 360px;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .form-suffix {
|
|
|
|
|
+ margin-left: 8px;
|
|
|
|
|
+ color: #909399;
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .tip-row {
|
|
|
|
|
+ margin-top: 4px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .time-preview {
|
|
|
|
|
+ margin-top: 8px;
|
|
|
|
|
+ padding: 12px 16px;
|
|
|
|
|
+ background: linear-gradient(135deg, #f5f7fa 0%, #e4e7eb 100%);
|
|
|
|
|
+ border-radius: 6px;
|
|
|
|
|
+
|
|
|
|
|
+ .preview-item {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+
|
|
|
|
|
+ .preview-label {
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ color: #606266;
|
|
|
|
|
+ }
|
|
|
|
|
+ .preview-value {
|
|
|
|
|
+ font-size: 15px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ color: #409eff;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .empty-limits {
|
|
|
|
|
+ padding: 20px 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .card-body-limit {
|
|
|
|
|
+ padding: 16px 20px;
|
|
|
|
|
+ min-height: 120px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .empty-limits-inline {
|
|
|
|
|
+ padding: 10px 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .limit-list-inline {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ gap: 10px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .limit-item-inline {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ padding: 10px 14px;
|
|
|
|
|
+ background-color: #f8f9fa;
|
|
|
|
|
+ border-radius: 6px;
|
|
|
|
|
+ border: 1px solid #e9ecef;
|
|
|
|
|
+
|
|
|
|
|
+ .limit-type-badge {
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ color: #fff;
|
|
|
|
|
+ padding: 3px 10px;
|
|
|
|
|
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
|
|
|
+ border-radius: 4px;
|
|
|
|
|
+ margin-right: 16px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .limit-count-num {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ color: #f56c6c;
|
|
|
|
|
+
|
|
|
|
|
+ em {
|
|
|
|
|
+ font-style: normal;
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ font-weight: 400;
|
|
|
|
|
+ color: #909399;
|
|
|
|
|
+ margin-left: 2px;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .form-footer {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ gap: 16px;
|
|
|
|
|
+ margin-top: 24px;
|
|
|
|
|
+ padding-top: 20px;
|
|
|
|
|
+ border-top: 1px solid #f0f0f0;
|
|
|
|
|
+
|
|
|
|
|
+ .el-button {
|
|
|
|
|
+ min-width: 140px;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+:deep(.el-form-item) {
|
|
|
|
|
+ margin-bottom: 18px;
|
|
|
|
|
+
|
|
|
|
|
+ &:last-child {
|
|
|
|
|
+ margin-bottom: 0;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+:deep(.el-form-item__content) {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+}
|
|
|
|
|
+</style>
|