| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455 |
- <template>
- <Dialog
- scroll
- @close="dialogVisible = false"
- style="max-width: 100vw; min-width: 800px"
- v-model="dialogVisible"
- :title="dialogTitle"
- class="OutwardRegistrationBak"
- noPaddingEL="OutwardRegistrationBak"
- >
- <el-form ref="formRef" :model="dataForm" :label-width="labelWidth">
- <div style="display: flex; flex-direction: column; margin-top: 20px">
- <text>当前规则:</text>
- <el-radio-group v-model="dataForm.type" class="ml-4">
- <el-radio v-if="!isTodayAdd" :value="1" size="large">每天</el-radio>
- <el-radio v-if="isTodayAdd" :value="2" size="large">单次</el-radio>
- </el-radio-group>
- </div>
- <div style="display: flex; align-items: center; flex-direction: row" v-if="!isEdit">
- <el-button v-if="!isEdit" @click="addTimes" type="primary" style="margin-left: 10px">
- 新增时间段
- </el-button>
- <template v-if="formMode === 'dailyAdd'">
- <el-text style="margin-left: 8vw">可提前</el-text>
- <el-input
- :maxlength="2"
- style="max-width: 66px; margin-left: 8px"
- v-model="dataForm.advancedDays"
- />
- <el-text style="margin-left: 8px">天预约</el-text>
- </template>
- </div>
- <div class="info-wrap">
- <el-row
- v-for="(item, index) in dataForm.slots"
- :key="index"
- style="
- position: relative;
- padding: 10px 0;
- margin-bottom: 10px;
- border: #e0e0e0 solid 1px;
- border-radius: 6px;
- "
- >
- <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
- <div style="flex-direction: row; display: flex; align-items: center; width: 100%">
- <text
- style="
- display: flex;
- width: 20px;
- height: 20px;
- margin-left: 10px;
- border: #ddd solid 1px;
- border-radius: 50%;
- align-items: center;
- justify-content: center;
- "
- >{{ index + 1 }}</text
- >
- <div style="display: flex; flex-direction: column; margin-left: -12px">
- <el-form-item label="开始时间">
- <el-select
- v-model="item.hourS"
- class="m-2"
- placeholder="Select"
- style="width: 64px"
- >
- <el-option
- v-for="items in optionsH"
- :key="items.value"
- :label="items.label"
- :value="items.value"
- />
- </el-select>
- 点
- <el-select
- v-model="item.minuteS"
- class="m-2"
- placeholder="Select"
- style="width: 64px"
- >
- <el-option
- v-for="items in optionsM"
- :key="items.value"
- :label="items.label"
- :value="items.value"
- />
- </el-select>
- 分
- </el-form-item>
- <el-form-item label="结束时间">
- <el-select
- v-model="item.hourE"
- class="m-2"
- placeholder="Select"
- style="width: 64px"
- >
- <el-option
- v-for="items in optionsH"
- :key="items.value"
- :label="items.label"
- :value="items.value"
- />
- </el-select>
- 点
- <el-select
- v-model="item.minuteE"
- class="m-2"
- placeholder="Select"
- style="width: 64px"
- >
- <el-option
- v-for="items in optionsM"
- :key="items.value"
- :label="items.label"
- :value="items.value"
- />
- </el-select>
- 分
- </el-form-item>
- </div>
- <div style="align-items: center; display: flex; flex-direction: row">
- <text style="margin-left: 2vw">此时间段可预约次数:</text>
- <el-input
- :maxlength="5"
- placeholder="输入次数"
- style="width: 78px; margin-left: 3px"
- v-model="item.totalQuota"
- />
- </div>
- <el-icon
- v-if="!isEdit"
- @click="deleteItem(index)"
- color="red"
- size="large"
- style="
- position: absolute;
- top: 0;
- right: 0;
- width: 30px;
- height: 30px;
- margin-left: auto;
- cursor: pointer;
- "
- ><Close
- /></el-icon>
- </div>
- </el-col>
- </el-row>
- </div>
- </el-form>
- <template #footer>
- <el-button @click="handleClosed">关闭</el-button>
- <el-button v-loading="formLoading" type="primary" @click="submitForm">确定</el-button>
- </template>
- </Dialog>
- </template>
- <script lang="ts" setup>
- import { computed, ref } from 'vue'
- import { useMediaQuery } from '@vueuse/core'
- import { Close } from '@element-plus/icons-vue'
- import fetchHttp from '@/config/axios/fetchHttp'
- import { compareTimeStrings, isValidTimeFormat } from '@/utils/dateUtil'
- import { useUserStore } from '@/store/modules/user'
- type FormMode = 'dailyAdd' | 'dailyEdit' | 'todayAdd'
- type TimeslotEditItem = {
- id?: number
- timeslot: string
- hourS: string
- minuteS: string
- hourE: string
- minuteE: string
- totalQuota: string | number
- }
- type DailyEditApiItem = {
- id: number
- timeQuantum: string
- totalQuota: number
- }
- const { t } = useI18n()
- const message = useMessage()
- const userStore = useUserStore()
- const formRef = ref()
- const formLoading = ref(false)
- const dialogVisible = ref(false)
- const formMode = ref<FormMode>('dailyAdd')
- const currentWidth = useMediaQuery('(max-width: 800px)')
- const labelWidth = computed(() => (currentWidth.value ? '110px' : '120px'))
- const isEdit = computed(() => formMode.value === 'dailyEdit')
- const isTodayAdd = computed(() => formMode.value === 'todayAdd')
- const isDailyAdd = computed(() => formMode.value === 'dailyAdd')
- const dialogTitle = computed(() => {
- if (formMode.value === 'dailyEdit') return '编辑预约时间段'
- if (formMode.value === 'todayAdd') return '新增单日时间段'
- return '新增预约时间段'
- })
- const dataForm = ref({
- organizationId: undefined as any,
- date: '',
- advancedDays: '',
- type: 1,
- slots: [] as TimeslotEditItem[]
- })
- const optionsH = ref(
- Array.from({ length: 24 }, (_, i) => {
- const v = String(i).padStart(2, '0')
- return { value: v, label: v }
- })
- )
- const optionsM = ref([
- { value: '00', label: '00' },
- { value: '30', label: '30' }
- ])
- const makeEmptySlot = (): TimeslotEditItem => ({
- timeslot: '',
- hourS: '00',
- minuteS: '00',
- hourE: '00',
- minuteE: '00',
- totalQuota: '1'
- })
- const buildTimeslot = (slot: TimeslotEditItem) => {
- const sTime = `${slot.hourS}:${slot.minuteS}`
- const eTime = `${slot.hourE}:${slot.minuteE}`
- if (!isValidTimeFormat(sTime) || !isValidTimeFormat(eTime)) {
- throw new Error('时间格式不正确,请使用HH:mm格式')
- }
- const result = compareTimeStrings(sTime, eTime)
- if (result !== -1) {
- throw new Error('开始时间不能大于等于结束时间!')
- }
- return `${sTime}-${eTime}`
- }
- const normalizeAndValidateSlots = () => {
- for (const slot of dataForm.value.slots) {
- if (slot.totalQuota === '' || slot.totalQuota === undefined || slot.totalQuota === null) {
- throw new Error('请选择时间和输入次数')
- }
- const total = Number.parseInt(String(slot.totalQuota), 10)
- if (Number.isNaN(total)) {
- throw new Error('请输入数字!')
- }
- slot.totalQuota = total
- slot.timeslot = buildTimeslot(slot) || ''
- }
- }
- const addTimes = () => {
- dataForm.value.slots.push(makeEmptySlot())
- }
- const deleteItem = (index: number) => {
- dataForm.value.slots.splice(index, 1)
- }
- const resetForm = () => {
- dataForm.value = {
- organizationId: undefined,
- date: '',
- advancedDays: '',
- type: 1,
- slots: []
- }
- formRef.value?.resetFields()
- }
- const open = async (payload:{date: string, organizationId: number}) => {
- formMode.value = 'dailyAdd'
- resetForm()
- dialogVisible.value = true
- dataForm.value.date = payload.date
- dataForm.value.type = 1
- dataForm.value.organizationId = payload.organizationId
- }
- const openEdit = async (payload: { date: string; organizationId: number; list: DailyEditApiItem[] }) => {
- formMode.value = 'dailyEdit'
- resetForm()
- dialogVisible.value = true
- dataForm.value.date = payload.date
- dataForm.value.organizationId = payload.organizationId
- dataForm.value.type = 1
- dataForm.value.slots = (payload.list || []).map((it) => {
- const [start, end] = (it.timeQuantum || '').split('-')
- const [hourS, minuteS] = (start || '').split(':')
- const [hourE, minuteE] = (end || '').split(':')
- return {
- id: it.id,
- timeslot: it.timeQuantum,
- hourS: hourS || '00',
- minuteS: minuteS || '00',
- hourE: hourE || '00',
- minuteE: minuteE || '00',
- totalQuota: String(it.totalQuota ?? 1)
- }
- })
- }
- const openToday = async (payload: { date: string; organizationId: number }) => {
- formMode.value = 'todayAdd'
- resetForm()
- dialogVisible.value = true
- dataForm.value.date = payload.date
- dataForm.value.organizationId = payload.organizationId
- dataForm.value.type = 2
- if (dataForm.value.slots.length === 0) addTimes()
- }
- defineExpose({ open, openEdit, openToday })
- const emit = defineEmits(['success'])
- const submitForm = async () => {
- if (formLoading.value) return
- formLoading.value = true
- try {
- try {
- if (!formRef.value) return
- const valid = await formRef.value.validate()
- if (!valid) return
- if (isDailyAdd.value) {
- const num = Number.parseInt(String(dataForm.value.advancedDays), 10)
- if (Number.isNaN(num)) {
- message.error('天数请输入数字!')
- return
- }
- }
- } catch {
- if (isDailyAdd.value) {
- message.error('天数请输入数字!')
- return
- }
- }
- try {
- normalizeAndValidateSlots()
- } catch (e: any) {
- message.error(e?.message || '提交失败')
- return
- }
- if (formMode.value === 'dailyEdit') {
- const submitSlots = dataForm.value.slots.map((s) => ({
- slotId: s.id,
- timeslot: s.timeslot,
- totalQuota: Number(s.totalQuota)
- }))
- const res = await fetchHttp.post('/admin/appointment/updateTimeslotInstance', {
- slots: submitSlots
- })
- if (res) {
- dialogVisible.value = false
- emit('success')
- }
- return
- }
- if (formMode.value === 'todayAdd') {
- const submitSlots = dataForm.value.slots.map((s) => ({
- timeslot: s.timeslot,
- totalQuota: Number(s.totalQuota)
- }))
- if(!submitSlots.length) {
- message.success('请添加时间段!')
- return
- }
- const res = await fetchHttp.post('/admin/appointment/addTimeslotForOneDay', {
- organizationId: dataForm.value.organizationId,
- date: dataForm.value.date,
- slots: submitSlots
- })
- if (res) {
- dialogVisible.value = false
- emit('success')
- }
- return
- }
- let params = {
- ...dataForm.value,
- advancedDays: Number(dataForm.value.advancedDays),
- slots: dataForm.value.slots.map((s) => ({
- timeslot: s.timeslot,
- totalQuota: Number(s.totalQuota)
- }))
- }
- const res = await fetchHttp.post('/admin/appointment/setTimeslot', params)
- if (res) {
- dialogVisible.value = false
- emit('success')
- }
- } finally {
- setTimeout(() => {
- formLoading.value = false
- }, 500)
- }
- }
- const handleClosed = () => {
- dialogVisible.value = false
- formMode.value = 'dailyAdd'
- resetForm()
- }
- </script>
- <style lang="scss" scoped>
- .OutwardRegistrationBak {
- @media (width <= 1200) {
- :deep(.el-form-item--default .el-form-item__label) {
- line-height: 0 !important;
- }
- }
- .el-form {
- padding: 15px !important;
- }
- .info-title {
- max-width: 180px;
- min-width: 75px;
- padding: 15px;
- font-weight: bolder;
- background-color: #f3f6fb;
- border-top-right-radius: 50px;
- border-bottom-right-radius: 50px;
- }
- .info-wrap {
- margin: 15px;
- }
- }
- :deep(.el-form-item--default) {
- display: flex;
- flex-direction: row;
- align-items: center;
- }
- </style>
|