| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306 |
- <!--
- * @Author: wanglj wanglijie@dashoo.cn
- * @Date: 2025-03-24 16:28:47
- * @LastEditors: wanglj wanglijie@dashoo.cn
- * @LastEditTime: 2025-03-28 11:47:37
- * @FilePath: \labsop_h5\src\view\instr\components\appoint-dialog.vue
- * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
- -->
- <template>
- <div class="calendar-container">
- <header>
- <p>
- <strong><van-icon name="calendar-o" :size="16" />预约时间:</strong>
- <span v-if="state.selected.length === 0">请选择开始时间段</span>
- <span v-else-if="state.selected.length === 1">{{ formatDate(new Date(state.selected[0].startStamp), 'mm-dd HH:MM') }}~{{ formatDate(new Date(state.selected[0].endStamp), 'mm-dd HH:MM') }}</span>
- <span v-else-if="state.selected.length === 2">{{ formatDate(new Date(state.selected[0].startStamp), 'mm-dd HH:MM') }}~{{ formatDate(new Date(state.selected[1].endStamp), 'mm-dd HH:MM') }}</span>
- </p>
- <p>
- <strong><van-icon name="clock-o" :size="16" />预约时长:</strong>
- <span>{{ getAppointTime() }}</span>
- </p>
- </header>
- <div class="main">
- <template v-for="(item, index) in state.calendar">
- <h4 v-if="item.startTime == '00:00'">{{ formatDate(new Date(item.startStamp), 'YYYY-mm-dd/WWW') }}</h4>
- <div class="tag" :class="{ disabled: item.disabled, selected: item.selected }" @click="onTagClick(item, index)">
- {{ item.startTime }}-{{ item.endTime }}
- </div>
- </template>
- </div>
- </div>
- <van-action-bar placeholder>
- <van-action-bar-button class="w100" type="primary" text="提交" @click="onClickButton" />
- </van-action-bar>
- </template>
- <script lang="ts" setup>
- import { onMounted, reactive, ref } from 'vue'
- import { formatDate } from '/@/utils/formatTime'
- import { showNotify } from 'vant'
- import { useRoute, useRouter } from 'vue-router'
- import moment from 'moment'
- import to from 'await-to-js'
- import { useInstrApi } from '/@/api/instr'
- const route = useRoute()
- const router = useRouter()
- const instId = ref(0)
- const instApi = useInstrApi()
- const state = reactive({
- instInfo: {} as any,
- intervalTime: 30,
- furtherLimit: '',
- instrBusinessTime: '',
- begin_at: '00:00:00',
- ent_at: '23:50:00',
- currentWeekAppointList: [],
- selected: [],
- calendar: []
- })
- // 获取系统设置时间间隔
- const getTimeSplit = async () => {
- const [err, res]: ToResponse = await to(
- instApi.getSettingDetail({
- instId: Number(instId.value),
- code: 'InstCfgAppoint'
- })
- )
- if (err) return
- if (res.code == 200) {
- state.intervalTime = res.data?.config?.timeSplit
- state.begin_at = res.data?.config?.timeRange?.[0].start
- state.ent_at = res.data?.config?.timeRange?.[0].end
- appointTimeInfo()
- }
- }
- const appointTimeInfo = async () => {
- const currentDate = formatDate(new Date(), 'YYYY-mm-dd')
- const nextWeek = formatDate(new Date(new Date().getTime() + 604800000), 'YYYY-mm-dd')
- let params = {
- instId: instId.value,
- date: currentDate,
- dateType: 'week'
- }
- await Promise.all([
- instApi.getAppointInfo({ ...params }),
- instApi.getAppointInfo({...params, date: nextWeek})
- ]).then(([now, next]) => {
- const { appoint, unavailable, furtherLimit } = now.data
- const { appoint: appointNext, unavailable: unavailableNext } = next.data
- let allDate = [...(appoint || []), ...(unavailable || []), ...(appointNext || []), ...(unavailableNext || [])].map((item) => ({
- ...item,
- start: item.startTime ? item.startTime : item.start,
- startStamp: new Date(item.startTime ? item.startTime : item.start).getTime(),
- end: item.endTime ? item.endTime : item.end,
- endStamp: new Date(item.endTime ? item.endTime : item.end).getTime(),
- }))
- state.currentWeekAppointList = allDate
- state.furtherLimit = furtherLimit ? nearFurtherLimit(furtherLimit, state.intervalTime) : ''
- // 设备的工作时间
- state.instrBusinessTime = `${state.begin_at}-${state.ent_at}`
- initCalendar()
- })
- }
- const nearFurtherLimit = (time, split) => {
- // 将目标时间转换为Moment对象
- const targetMoment = moment(time)
- // 计算距离目标时间最近的能够被时间间隔整除的时间
- const remainder = targetMoment.minute() % split
- const divisibleTime = targetMoment.clone().subtract(remainder, 'minutes')
- const nearestAvailableTime = divisibleTime.format('YYYY/MM/DD HH:mm') + ':00'
- return nearestAvailableTime
- }
- const initCalendar = () => {
- const split = state.intervalTime
- const now = new Date().getTime()
- const start = formatDate(new Date(now), 'YYYY-mm-dd 00:00:00')
- const end = formatDate(new Date(now + 1000 * 60 * 60 * 24 * 7), 'YYYY-mm-dd 23:59:59')
- let stamp = new Date(start).getTime()
- while (stamp <= new Date(end).getTime()) {
- const obj = {
- startStamp: stamp,
- endStamp: stamp + 1000 * split * 60,
- startTime: formatDate(new Date(stamp), 'HH:MM'),
- endTime: formatDate(new Date(stamp + 1000 * split * 60), 'HH:MM'),
- disabled: stamp < now,
- selected: false
- }
- // 禁用不可预约时间段和已预约时间段
- for(const item of state.currentWeekAppointList) {
- if(obj.endStamp <= item.endStamp) {
- obj.disabled = true
- continue
- }
- }
- // 最早预约时间
- if(state.furtherLimit && obj.startStamp >= new Date(state.furtherLimit).getTime()) {
- obj.disabled = true
- }
- state.calendar.push(obj)
- stamp += 1000 * split * 60
- }
- }
- const onTagClick = (item: any, idx: number) => {
- if (item.disabled) return
- if (item.selected && state.selected[0].startStamp === item.startStamp) {
- return
- }
- if (state.selected.length === 2) {
- state.selected = []
- for (const item of state.calendar) {
- item.selected = false
- }
- } else if (state.selected.length === 1) {
- // 验证选中时间段是否有占用的
- const selected = JSON.parse(JSON.stringify(state.selected))
- selected.push(item)
- selected.sort((a, b) => {
- return a.startStamp - b.startStamp
- })
- let flag = false
- for (const item of state.calendar) {
- if (item.startStamp >= selected[0].startStamp && item.endStamp <= selected[1].endStamp && item.disabled) {
- flag = true
- break
- }
- }
- if (flag) {
- showNotify({
- message: '选中时间段已被占用,请重新选择',
- type: 'warning'
- })
- return
- }
- }
- state.selected.push(item)
- item.selected = true
- state.selected.sort((a, b) => {
- return a.startStamp - b.startStamp
- })
- if (state.selected.length == 2) {
- for (const item of state.calendar) {
- if (item.startStamp >= state.selected[0].startStamp && item.endStamp <= state.selected[1].endStamp) {
- item.selected = true
- }
- }
- }
- }
- const getAppointTime = () => {
- let startDate: Date = new Date()
- let endDate: Date = new Date()
- if(state.selected.length === 1) {
- startDate = new Date(state.selected[0].startStamp)
- endDate = new Date(state.selected[0].endStamp)
- } else if(state.selected.length === 2) {
- startDate = new Date(state.selected[0].startStamp)
- endDate = new Date(state.selected[1].endStamp)
- } else {
- return '-'
- }
- // 计算两个日期之间的时间差(以毫秒为单位)
- const timeDifference = endDate.getTime() - startDate.getTime()
- // 计算天数
- const days = Math.floor(timeDifference / (1000 * 60 * 60 * 24))
- // 计算剩余的毫秒数
- const remainingMilliseconds = timeDifference % (1000 * 60 * 60 * 24)
- // 计算小时数
- const hours = Math.floor(remainingMilliseconds / (1000 * 60 * 60))
- // 计算剩余的毫秒数
- const remainingMillisecondsAfterHours = remainingMilliseconds % (1000 * 60 * 60)
- // 计算分钟数
- const minutes = Math.floor(remainingMillisecondsAfterHours / (1000 * 60))
- return `${days}天${hours}小时${minutes}分`
- }
- const onClickButton = () => {
- if(!state.selected.length) {
- showNotify({
- message: '请选择预约时间',
- type: 'warning'
- })
- return
- }
- let startTime = 0
- let endTime = 0
- if(state.selected.length === 1) {
- startTime = state.selected[0].startStamp
- endTime = state.selected[0].endStamp
- } else {
- startTime = state.selected[0].startStamp
- endTime = state.selected[1].endStamp
- }
- router.push({
- path: '/instr-appoint',
- query: {
- id: instId.value,
- startTime,
- endTime
- }
- })
- }
- onMounted(() => {
- instId.value = route.query.id ? +route.query.id : 0
- getTimeSplit()
- })
- </script>
- <style lang="scss" scoped>
- .calendar-container {
- flex: 1;
- overflow: hidden;
- display: flex;
- flex-direction: column;
- header {
- padding: 10px;
- border-bottom: 1px solid #dcdfe6;
- p {
- padding: 4px;
- i {
- margin-right: 4px;
- }
- }
- }
- .main {
- flex: 1;
- overflow-y: auto;
- overflow-x: hidden;
- padding: 10px 6px;
- display: flex;
- flex-wrap: wrap;
- color: #323233;
- h4 {
- flex: 0 0 100%;
- margin: 4px 0 0 4px;
- height: 18px;
- line-height: 18px;
- display: flex;
- &::before {
- display: inline-block;
- content: '';
- width: 3px;
- height: 18px;
- background-color: #1c9bfd;
- margin-right: 4px;
- vertical-align: middle;
- }
- }
- .tag {
- font-size: 12px;
- text-align: center;
- flex: 0 0 calc(25% - 14px);
- padding: 4px;
- border: 1px solid #dcdfe6;
- border-radius: 4px;
- margin-left: 4px;
- margin-top: 4px;
- &.disabled {
- background-color: #dcdfe6;
- }
- &.selected {
- background-color: #1989fa;
- border-color: #1989fa;
- color: #fff;
- }
- }
- }
- }
- </style>
|