1、演示
2、代码
<template> <div class="box" v-if="startMonth.year"> <div class="left"> <div class="top"> <span class="iconfont" @click="changeMonth(-1)">左</span> <span>{{ startMonth.year }}年{{ startMonth.month }}月</span> <span></span> </div> <div class="calendarMain"> <div class="weeks"> <span>日</span> <span>一</span> <span>二</span> <span>三</span> <span>四</span> <span>五</span> <span>六</span> </div> <div class="days"> <div class="day" v-for="item in startMonth.dates" :class="monthDaysClass(item)" :style="[{ '--ml': item.week }]" @mouseenter="dayMouseMove(item)" @click="dayMouseClick(item)" > {{ item.day }} </div> </div> </div> </div> <div class="right"> <div class="top"> <span></span> <span>{{ endMonth.year }}年{{ endMonth.month }}月</span> <span class="iconfont" @click="changeMonth(1)">右</span> </div> <div class="calendarMain"> <div class="weeks"> <span>日</span> <span>一</span> <span>二</span> <span>三</span> <span>四</span> <span>五</span> <span>六</span> </div> <div class="days"> <div class="day" v-for="item in endMonth.dates" :class="monthDaysClass(item)" :style="[{ '--ml': item.week }]" @mouseenter="dayMouseMove(item)" @click="dayMouseClick(item)" > {{ item.day }} </div> </div> </div> </div> </div> </template> <script setup> import { ref, reactive, onMounted } from 'vue' const currentDateIndex = ref(0) const startMonth = ref({}) const endMonth = ref({}) const selectDate = ref([]) const isMove = ref(false) onMounted(() => { initCalendar() }) const initCalendar = () => { getCalendarData() const startIndex = startMonth.value.dates.findIndex(item => !item.isTodayBefore) if (startIndex == startMonth.value.dates.length - 1) { selectDate.value[0] = startMonth.value.dates[startIndex].yymmdd selectDate.value[1] = endMonth.value.dates[0].yymmdd } else { selectDate.value[0] = startMonth.value.dates[startIndex].yymmdd selectDate.value[1] = startMonth.value.dates[startIndex + 1].yymmdd } } const getCalendarData = () => { startMonth.value = getMonthDates(currentDateIndex.value) endMonth.value = getMonthDates(currentDateIndex.value + 1) } const changeMonth = num => { currentDateIndex.value += num getCalendarData() } const monthDaysClass = item => { if (item.isTodayBefore) return 'disabled' if (item.yymmdd == selectDate.value[0]) return 'active' if (item.yymmdd == selectDate.value[1]) return 'active' if (getDatesBetween(selectDate.value[0], selectDate.value[1]).includes(item.yymmdd)) return 'middle' } const getDatesBetween = (date1, date2) => { let dates = [] let currentDate = new Date(date1) let endDate = new Date(date2) while (currentDate < endDate) { let dateString = currentDate.toISOString().substr(0, 10) dates.push(dateString) currentDate.setDate(currentDate.getDate() + 1) } if (dates.length > 0) { dates.shift() } return dates } const dayMouseClick = item => { if (item.isTodayBefore) return let arr = [...selectDate.value] if (!isMove.value) { if (arr.length == 1) { arr[1] = item.yymmdd } else { arr = [] arr[0] = item.yymmdd } isMove.value = true } else { isMove.value = false } if (arr[0] > arr[1]) { selectDate.value = arr.reverse() } else { selectDate.value = arr } console.log(selectDate.value) } const dayMouseMove = item => { if (item.isTodayBefore) return if (!isMove.value) return selectDate.value[1] = item.yymmdd } function getMonthDates(monthOffset) { const today = new Date() const targetDate = new Date(today.getFullYear(), today.getMonth() + monthOffset, 1) const year = targetDate.getFullYear() let month = targetDate.getMonth() + 1 // 月份是从0开始的,所以要加1 month = month >= 10 ? month : '0' + month const firstDay = new Date(year, targetDate.getMonth(), 1) const lastDay = new Date(year, targetDate.getMonth() + 1, 0) const monthDates = [] for (let d = firstDay; d <= lastDay; d.setDate(d.getDate() + 1)) { const day = d.getDate() const dayOfWeek = d.getDay() // 返回0到6,0代表星期日 const isTodayBefore = d.getTime() < today.setHours(0, 0, 0, 0) // 判断是否是今天之前的日期 monthDates.push({ day, week: dayOfWeek, isTodayBefore, yymmdd: `${year}-${month}-${day >= 10 ? day : '0' + day}`, }) } return { year, month, dates: monthDates } } </script> <style scoped lang="scss"> .box { width: 793px; height: 436px; box-shadow: 2px 2px 6px #0003; display: flex; justify-content: space-between; padding: 30px 15px; .left, .right { width: 46%; height: 100%; .top { display: flex; justify-content: space-between; font-weight: bold; .iconfont { cursor: pointer; user-select: none; } } .calendarMain { .weeks { font-weight: bold; margin-top: 20px; display: flex; justify-content: space-between; & > span { display: inline-block; width: 50px; height: 50px; line-height: 50px; text-align: center; } } .days { display: flex; flex-wrap: wrap; cursor: pointer; .day { width: 50px; height: 50px; height: 50px; text-align: center; line-height: 50px; color: #111; font-size: 14px; } .day:nth-child(1) { margin-left: calc(var(--ml) * 50px); } .disabled { color: #ccc; cursor: not-allowed; } .active { background-color: #266fff; color: #fff; } .middle { background-color: rgba(38, 111, 255, 0.3); color: #fff; } } } } } </style>
3、代码解释
import { ref, reactive, onMounted } from 'vue' import { useDatesBetween } from '@/hooks/time.js'
这里引入了 Vue 中的
ref
和reactive
函数,以及onMounted
钩子函数,以及从time.js
模块中引入了useDatesBetween
函数。const currentDateIndex = ref(0) const startMonth = ref({}) const endMonth = ref({}) const selectDate = ref([]) const getDatesBetween = useDatesBetween() const isMove = ref(false)
创建了一些响应式变量,包括当前日期索引
currentDateIndex
,起始月份startMonth
和结束月份endMonth
的引用,以及选择的日期范围selectDate
,useDatesBetween
函数的引用getDatesBetween
,以及一个标志isMove
,用于标记是否在移动状态。onMounted(() => { initCalendar() })
使用
onMounted
钩子,在组件挂载后执行initCalendar
函数。const initCalendar = () => { getCalendarData() const startIndex = startMonth.value.dates.findIndex(item => !item.isTodayBefore) if (startIndex == startMonth.value.dates.length - 1) { selectDate.value[0] = startMonth.value.dates[startIndex].yymmdd selectDate.value[1] = endMonth.value.dates[0].yymmdd } else { selectDate.value[0] = startMonth.value.dates[startIndex].yymmdd selectDate.value[1] = startMonth.value.dates[startIndex + 1].yymmdd } }
initCalendar
函数初始化日历,获取日历数据并设置选择的日期范围。const getCalendarData = () => { startMonth.value = getMonthDates(currentDateIndex.value) endMonth.value = getMonthDates(currentDateIndex.value + 1) }
getCalendarData
函数获取当前月份和下个月份的日历数据。const changeMonth = num => { currentDateIndex.value += num getCalendarData() }
changeMonth
函数根据给定的数字改变当前月份索引,并重新获取日历数据。const monthDaysClass = item => { if (item.isTodayBefore) return 'disabled' if (item.yymmdd == selectDate.value[0]) return 'active' if (item.yymmdd == selectDate.value[1]) return 'active' if (getDatesBetween(selectDate.value[0], selectDate.value[1]).includes(item.yymmdd)) return 'middle' }
monthDaysClass
函数根据日期项目返回对应的 CSS 类名,用于渲染日历中的日期。const dayMouseClick = item => { if (item.isTodayBefore) return let arr = [...selectDate.value] if (!isMove.value) { if (arr.length == 1) { arr[1] = item.yymmdd } else { arr = [] arr[0] = item.yymmdd } isMove.value = true } else { isMove.value = false } if (arr[0] > arr[1]) { selectDate.value = arr.reverse() } else { selectDate.value = arr } console.log(selectDate.value) }
dayMouseClick
函数处理日期的点击事件,根据点击选择日期范围。const dayMouseMove = item => { if (item.isTodayBefore) return if (!isMove.value) return selectDate.value[1] = item.yymmdd }
dayMouseMove
函数处理鼠标移动事件,根据鼠标移动选择日期范围。function getMonthDates(monthOffset) { const today = new Date() const targetDate = new Date(today.getFullYear(), today.getMonth() + monthOffset, 1) const year = targetDate.getFullYear() let month = targetDate.getMonth() + 1 month = month >= 10 ? month : '0' + month const firstDay = new Date(year, targetDate.getMonth(), 1) const lastDay = new Date(year, targetDate.getMonth() + 1, 0) const monthDates = [] for (let d = firstDay; d <= lastDay; d.setDate(d.getDate() + 1)) { const day = d.getDate() const dayOfWeek = d.getDay() const isTodayBefore = d.getTime() < today.setHours(0, 0, 0, 0) monthDates.push({ day, week: dayOfWeek, isTodayBefore, yymmdd: `${year}-${month}-${day >= 10 ? day : '0' + day}`, }) } return { year, month, dates: monthDates } }
getMonthDates
函数根据月份偏移量获取该月的日期数据,包括年份、月份以及日期数组。