• 前端开发攻略---封装calendar日历组件,实现日期多选。可根据您的需求任意调整,可玩性强。


    1、演示

    2、简介

    1、该日历组件是纯手搓出来的,没依赖任何组件库,因此您可以随意又轻松的改变代码,以实现您的需求。

    2、代码清爽干净,逻辑精妙,您可以好好品尝。

    3、好戏开场。

    3、代码(Vue3写法)

    1、子组件

     您可以在components文件夹下创建一个干净的组件,直接将代码复制粘贴即可。

    src/components/py-calendar/index.vue

    1. <template>
    2. <div class="box">
    3. <div class="left">
    4. <div class="top">
    5. <div>
    6. <span class="iconfont" @click="changeMonth(-1)">span>
    7. div>
    8. <span>{{ startMonth.year }}年{{ startMonth.month }}月span>
    9. <span>span>
    10. div>
    11. <div class="calendarMain">
    12. <div class="weekDays">
    13. <span>span>
    14. <span>span>
    15. <span>span>
    16. <span>span>
    17. <span>span>
    18. <span>span>
    19. <span>span>
    20. div>
    21. <div class="days">
    22. <div
    23. class="day"
    24. v-for="item in startMonth.dates"
    25. :class="dayClass(item)"
    26. :style="dayStyle(item)"
    27. @mouseenter="dayMouseMove(item)"
    28. @click="dayMouseClick(item)"
    29. >
    30. {{ item.day }}
    31. <span v-if="item.yymmdd == selectDate[0]?.yymmdd">开始span>
    32. <span v-if="item.yymmdd == selectDate[1]?.yymmdd">结束span>
    33. div>
    34. div>
    35. div>
    36. div>
    37. <div class="right">
    38. <div class="top">
    39. <span>span>
    40. <span>{{ endMonth.year }}年{{ endMonth.month }}月span>
    41. <div>
    42. <span class="iconfont" @click="changeMonth(1)">span>
    43. div>
    44. div>
    45. <div class="calendarMain">
    46. <div class="weekDays">
    47. <span>span>
    48. <span>span>
    49. <span>span>
    50. <span>span>
    51. <span>span>
    52. <span>span>
    53. <span>span>
    54. div>
    55. <div class="days">
    56. <div
    57. class="day"
    58. v-for="item in endMonth.dates"
    59. :class="dayClass(item)"
    60. :style="dayStyle(item)"
    61. @mouseenter="dayMouseMove(item)"
    62. @click="dayMouseClick(item)"
    63. >
    64. {{ item.day }}
    65. <span v-if="item.yymmdd == selectDate[0]?.yymmdd">开始span>
    66. <span v-if="item.yymmdd == selectDate[1]?.yymmdd">结束span>
    67. div>
    68. div>
    69. div>
    70. div>
    71. div>
    72. template>
    73. <script setup>
    74. import { ref, reactive, onMounted, computed } from 'vue'
    75. import { getMonthDates } from './index.js'
    76. const currentMonthIndex = ref(0)
    77. const startMonth = ref({})
    78. const endMonth = ref({})
    79. const selectDate = ref([])
    80. const isMove = ref(false)
    81. const emits = defineEmits(['change'])
    82. onMounted(() => {
    83. initCalendar()
    84. })
    85. const initCalendar = () => {
    86. getCalendarData()
    87. const startIndex = startMonth.value.dates.findIndex(item => !item.isTodayBefore)
    88. if (startIndex == startMonth.value.dates.length - 1) {
    89. selectDate.value[0] = startMonth.value.dates[startIndex]
    90. selectDate.value[1] = endMonth.value.dates[0]
    91. } else {
    92. selectDate.value[0] = startMonth.value.dates[startIndex]
    93. selectDate.value[1] = startMonth.value.dates[startIndex + 1]
    94. }
    95. }
    96. const getCalendarData = () => {
    97. startMonth.value = getMonthDates(currentMonthIndex.value)
    98. endMonth.value = getMonthDates(currentMonthIndex.value + 1)
    99. }
    100. const changeMonth = num => {
    101. currentMonthIndex.value += num
    102. getCalendarData()
    103. }
    104. const dayClass = item => {
    105. if (item.isTodayBefore) return 'disabled'
    106. if (item.yymmdd == selectDate.value[0]?.yymmdd) return 'active'
    107. if (item.yymmdd == selectDate.value[1]?.yymmdd) return 'active'
    108. if (getDatesBetween(selectDate.value[0]?.yymmdd, selectDate.value[1]?.yymmdd).includes(item.yymmdd)) return 'middle'
    109. }
    110. const dayStyle = item => {
    111. if (item.day == 1) return { marginLeft: item.week * 50 + 'px' }
    112. if (!item.isTodayBefore && (item.week == 0 || item.week == 6)) return { color: '#266fff' }
    113. }
    114. const dayMouseClick = item => {
    115. if (item.isTodayBefore) return
    116. let arr = [...selectDate.value]
    117. if (arr[0] && arr[1] && !isMove.value) {
    118. arr = []
    119. arr[0] = item
    120. isMove.value = true
    121. } else if (arr[0] && arr[1] && isMove.value) {
    122. isMove.value = false
    123. arr[1] = item
    124. } else if (arr[0] && !arr[1] && !isMove.value) {
    125. arr[0] = item
    126. isMove.value = false
    127. } else if (arr[0] && !arr[1] && isMove.value) {
    128. arr[0] = item
    129. isMove.value = true
    130. }
    131. selectDate.value = arr
    132. if (arr[0] && arr[1]) {
    133. emits('change', getDatesBetween(arr[0].yymmdd, arr[1].yymmdd))
    134. }
    135. }
    136. const dayMouseMove = item => {
    137. if (item.isTodayBefore) return
    138. if (!isMove.value) return
    139. if (item.yymmdd <= selectDate.value[0]?.yymmdd) {
    140. selectDate.value[1] = ''
    141. } else {
    142. selectDate.value[1] = item
    143. }
    144. }
    145. const getDatesBetween = (date1, date2) => {
    146. let dates = []
    147. let currentDate = new Date(date1)
    148. let endDate = new Date(date2)
    149. while (currentDate <= endDate) {
    150. let dateString = currentDate.toISOString().substr(0, 10)
    151. dates.push(dateString)
    152. currentDate.setDate(currentDate.getDate() + 1)
    153. }
    154. return dates
    155. }
    156. script>
    157. <style scoped lang="scss">
    158. .box {
    159. width: 793px;
    160. height: 436px;
    161. box-shadow: 2px 2px 6px #0003;
    162. display: flex;
    163. justify-content: space-between;
    164. flex-wrap: wrap;
    165. padding: 30px 15px;
    166. .left,
    167. .right {
    168. width: 46%;
    169. height: 95%;
    170. .top {
    171. display: flex;
    172. justify-content: space-between;
    173. font-weight: bold;
    174. .iconfont {
    175. cursor: pointer;
    176. user-select: none;
    177. }
    178. }
    179. .calendarMain {
    180. .weekDays {
    181. font-weight: bold;
    182. margin-top: 20px;
    183. display: flex;
    184. justify-content: space-between;
    185. & > span {
    186. display: inline-block;
    187. width: 50px;
    188. height: 50px;
    189. line-height: 50px;
    190. text-align: center;
    191. }
    192. & > span:first-child,
    193. & > span:last-child {
    194. color: #266fff;
    195. }
    196. }
    197. .days {
    198. display: flex;
    199. flex-wrap: wrap;
    200. cursor: pointer;
    201. .day {
    202. width: 50px;
    203. height: 50px;
    204. height: 50px;
    205. text-align: center;
    206. line-height: 60px;
    207. color: #111;
    208. font-size: 14px;
    209. position: relative;
    210. & > span {
    211. position: absolute;
    212. left: 11px;
    213. top: -18px;
    214. }
    215. }
    216. .disabled {
    217. color: #ccc;
    218. cursor: not-allowed;
    219. }
    220. .active {
    221. background-color: #266fff;
    222. color: #fff !important;
    223. }
    224. .middle {
    225. background-color: rgba(38, 111, 255, 0.3);
    226. color: #fff !important;
    227. }
    228. }
    229. }
    230. }
    231. .bottom {
    232. width: 100%;
    233. text-align: center;
    234. color: #111;
    235. }
    236. }
    237. style>

    src/components/py-calendar/index.js

    1. export function getMonthDates(monthOffset) {
    2. const today = new Date()
    3. const targetDate = new Date(today.getFullYear(), today.getMonth() + monthOffset, 1)
    4. const year = targetDate.getFullYear()
    5. let month = targetDate.getMonth() + 1 // 月份是从0开始的,所以要加1
    6. month = month >= 10 ? month : '0' + month
    7. const firstDay = new Date(year, targetDate.getMonth(), 1)
    8. const lastDay = new Date(year, targetDate.getMonth() + 1, 0)
    9. const monthDates = []
    10. for (let d = firstDay; d <= lastDay; d.setDate(d.getDate() + 1)) {
    11. const day = d.getDate()
    12. const dayOfWeek = d.getDay() // 返回0到6,0代表星期日
    13. const isTodayBefore = d.getTime() < today.setHours(0, 0, 0, 0) // 判断是否是今天之前的日期
    14. monthDates.push({
    15. day,
    16. week: dayOfWeek,
    17. isTodayBefore,
    18. yymmdd: `${year}-${month}-${day >= 10 ? day : '0' + day}`,
    19. })
    20. }
    21. return { year, month, dates: monthDates }
    22. }

    2、父组件

    随便在一个文件夹下使用日历组件

    1. <template>
    2. <div>
    3. <PYCalendar @change="change">PYCalendar>
    4. div>
    5. template>
    6. <script setup>
    7. import { ref, reactive, computed } from 'vue'
    8. import PYCalendar from '@/components/py-calendar/index.vue'
    9. const change = dateArr => {
    10. console.log(dateArr, '父组件获取到的数据')
    11. }
    12. script>
    13. <style scoped lang="scss">style>

    4、代码(Vue2写法 )

    1、子组件

     您可以在components文件夹下创建一个干净的组件,直接将代码复制粘贴即可。

    src/components/py-calendar/index.vue

    1. <template>
    2. <div class="box">
    3. <div class="left">
    4. <div class="top">
    5. <div>
    6. <span class="iconfont" @click="changeMonth(-1)">span>
    7. div>
    8. <span>{{ startMonth.year }}年{{ startMonth.month }}月span>
    9. <span>span>
    10. div>
    11. <div class="calendarMain">
    12. <div class="weekDays">
    13. <span>span>
    14. <span>span>
    15. <span>span>
    16. <span>span>
    17. <span>span>
    18. <span>span>
    19. <span>span>
    20. div>
    21. <div class="days">
    22. <div
    23. class="day"
    24. v-for="item in startMonth.dates"
    25. :class="dayClass(item)"
    26. :style="dayStyle(item)"
    27. @mouseenter="dayMouseMove(item)"
    28. @click="dayMouseClick(item)"
    29. >
    30. {{ item.day }}
    31. <span v-if="item.yymmdd == selectDate[0]?.yymmdd">开始span>
    32. <span v-if="item.yymmdd == selectDate[1]?.yymmdd">结束span>
    33. div>
    34. div>
    35. div>
    36. div>
    37. <div class="right">
    38. <div class="top">
    39. <span>span>
    40. <span>{{ endMonth.year }}年{{ endMonth.month }}月span>
    41. <div>
    42. <span class="iconfont" @click="changeMonth(1)">span>
    43. div>
    44. div>
    45. <div class="calendarMain">
    46. <div class="weekDays">
    47. <span>span>
    48. <span>span>
    49. <span>span>
    50. <span>span>
    51. <span>span>
    52. <span>span>
    53. <span>span>
    54. div>
    55. <div class="days">
    56. <div
    57. class="day"
    58. v-for="item in endMonth.dates"
    59. :class="dayClass(item)"
    60. :style="dayStyle(item)"
    61. @mouseenter="dayMouseMove(item)"
    62. @click="dayMouseClick(item)"
    63. >
    64. {{ item.day }}
    65. <span v-if="item.yymmdd == selectDate[0]?.yymmdd">开始span>
    66. <span v-if="item.yymmdd == selectDate[1]?.yymmdd">结束span>
    67. div>
    68. div>
    69. div>
    70. div>
    71. div>
    72. template>
    73. <script>
    74. import { getMonthDates } from './index.js'
    75. export default {
    76. data() {
    77. return {
    78. currentMonthIndex: 0,
    79. startMonth: {},
    80. endMonth: {},
    81. selectDate: [],
    82. isMove: false,
    83. }
    84. },
    85. mounted() {
    86. this.initCalendar()
    87. },
    88. methods: {
    89. initCalendar() {
    90. this.getCalendarData()
    91. const startIndex = this.startMonth.dates.findIndex(item => !item.isTodayBefore)
    92. if (startIndex == this.startMonth.dates.length - 1) {
    93. this.selectDate[0] = this.startMonth.dates[startIndex]
    94. this.selectDate[1] = this.endMonth.dates[0]
    95. } else {
    96. this.selectDate[0] = this.startMonth.dates[startIndex]
    97. this.selectDate[1] = this.startMonth.dates[startIndex + 1]
    98. }
    99. },
    100. getCalendarData() {
    101. this.startMonth = getMonthDates(this.currentMonthIndex)
    102. this.endMonth = getMonthDates(this.currentMonthIndex + 1)
    103. },
    104. changeMonth(num) {
    105. this.currentMonthIndex += num
    106. this.getCalendarData()
    107. },
    108. dayClass(item) {
    109. if (item.isTodayBefore) return 'disabled'
    110. if (item.yymmdd == this.selectDate[0]?.yymmdd) return 'active'
    111. if (item.yymmdd == this.selectDate[1]?.yymmdd) return 'active'
    112. if (this.getDatesBetween(this.selectDate[0]?.yymmdd, this.selectDate[1]?.yymmdd).includes(item.yymmdd)) return 'middle'
    113. },
    114. dayStyle(item) {
    115. if (item.day == 1) return { marginLeft: item.week * 50 + 'px' }
    116. if (!item.isTodayBefore && (item.week == 0 || item.week == 6)) return { color: '#266fff' }
    117. },
    118. dayMouseClick(item) {
    119. if (item.isTodayBefore) return
    120. let arr = [...this.selectDate]
    121. if (arr[0] && arr[1] && !this.isMove) {
    122. arr = []
    123. arr[0] = item
    124. this.isMove = true
    125. } else if (arr[0] && arr[1] && this.isMove) {
    126. this.isMove = false
    127. arr[1] = item
    128. } else if (arr[0] && !arr[1] && !this.isMove) {
    129. arr[0] = item
    130. this.isMove = false
    131. } else if (arr[0] && !arr[1] && this.isMove) {
    132. arr[0] = item
    133. this.isMove = true
    134. }
    135. this.selectDate = arr
    136. if (arr[0] && arr[1]) {
    137. this.$emit('change', this.getDatesBetween(arr[0].yymmdd, arr[1].yymmdd))
    138. }
    139. },
    140. dayMouseMove(item) {
    141. if (item.isTodayBefore) return
    142. if (!this.isMove) return
    143. if (item.yymmdd <= this.selectDate[0]?.yymmdd) {
    144. this.selectDate[1] = ''
    145. } else {
    146. this.selectDate[1] = item
    147. }
    148. },
    149. getDatesBetween(date1, date2) {
    150. let dates = []
    151. let currentDate = new Date(date1)
    152. let endDate = new Date(date2)
    153. while (currentDate <= endDate) {
    154. let dateString = currentDate.toISOString().substr(0, 10)
    155. dates.push(dateString)
    156. currentDate.setDate(currentDate.getDate() + 1)
    157. }
    158. return dates
    159. },
    160. },
    161. }
    162. script>
    163. <style scoped lang="scss">
    164. .box {
    165. width: 793px;
    166. height: 436px;
    167. box-shadow: 2px 2px 6px #0003;
    168. display: flex;
    169. justify-content: space-between;
    170. flex-wrap: wrap;
    171. padding: 30px 15px;
    172. .left,
    173. .right {
    174. width: 46%;
    175. height: 95%;
    176. .top {
    177. display: flex;
    178. justify-content: space-between;
    179. font-weight: bold;
    180. .iconfont {
    181. cursor: pointer;
    182. user-select: none;
    183. }
    184. }
    185. .calendarMain {
    186. .weekDays {
    187. font-weight: bold;
    188. margin-top: 20px;
    189. display: flex;
    190. justify-content: space-between;
    191. & > span {
    192. display: inline-block;
    193. width: 50px;
    194. height: 50px;
    195. line-height: 50px;
    196. text-align: center;
    197. }
    198. & > span:first-child,
    199. & > span:last-child {
    200. color: #266fff;
    201. }
    202. }
    203. .days {
    204. display: flex;
    205. flex-wrap: wrap;
    206. cursor: pointer;
    207. .day {
    208. width: 50px;
    209. height: 50px;
    210. height: 50px;
    211. text-align: center;
    212. line-height: 60px;
    213. color: #111;
    214. font-size: 14px;
    215. position: relative;
    216. & > span {
    217. position: absolute;
    218. left: 11px;
    219. top: -18px;
    220. }
    221. }
    222. .disabled {
    223. color: #ccc;
    224. cursor: not-allowed;
    225. }
    226. .active {
    227. background-color: #266fff;
    228. color: #fff !important;
    229. }
    230. .middle {
    231. background-color: rgba(38, 111, 255, 0.3);
    232. color: #fff !important;
    233. }
    234. }
    235. }
    236. }
    237. .bottom {
    238. width: 100%;
    239. text-align: center;
    240. color: #111;
    241. }
    242. }
    243. style>

    src/components/py-calendar/index.js

    1. export function getMonthDates(monthOffset) {
    2. const today = new Date()
    3. const targetDate = new Date(today.getFullYear(), today.getMonth() + monthOffset, 1)
    4. const year = targetDate.getFullYear()
    5. let month = targetDate.getMonth() + 1 // 月份是从0开始的,所以要加1
    6. month = month >= 10 ? month : '0' + month
    7. const firstDay = new Date(year, targetDate.getMonth(), 1)
    8. const lastDay = new Date(year, targetDate.getMonth() + 1, 0)
    9. const monthDates = []
    10. for (let d = firstDay; d <= lastDay; d.setDate(d.getDate() + 1)) {
    11. const day = d.getDate()
    12. const dayOfWeek = d.getDay() // 返回0到6,0代表星期日
    13. const isTodayBefore = d.getTime() < today.setHours(0, 0, 0, 0) // 判断是否是今天之前的日期
    14. monthDates.push({
    15. day,
    16. week: dayOfWeek,
    17. isTodayBefore,
    18. yymmdd: `${year}-${month}-${day >= 10 ? day : '0' + day}`,
    19. })
    20. }
    21. return { year, month, dates: monthDates }
    22. }

    2、父组件

    随便在一个文件夹下使用日历组件

    1. <template>
    2. <div>
    3. <PYCalendar @change="change">PYCalendar>
    4. div>
    5. template>
    6. <script>
    7. import PYCalendar from '@/components/py-calendar/index.vue'
    8. export default {
    9. data() {
    10. return {}
    11. },
    12. methods: {
    13. change(e) {
    14. console.log(e, '父组件')
    15. },
    16. },
    17. }
    18. script>
    19. <style scoped lang="scss">style>

    5、代码解析

    代码解析(子组件中的函数):

    1. initCalendar: 初始化日历,通过调用 getCalendarData() 获取日历的初始数据。然后找到第一个不在过去的日期的索引(isTodayBefore),并根据该索引设置初始选定的日期范围(selectDate)。如果起始索引是起始月份中的最后一个日期,则将结束日期设置为下个月的第一个日期。

    2. getCalendarData: 通过调用 getMonthDates 获取当前月份和下个月份的日历数据,分别为 currentMonthIndex.value 和 currentMonthIndex.value + 1

    3. changeMonth: 通过给定的偏移量(num)改变当前月份,更新 currentMonthIndex,然后调用 getCalendarData() 刷新日历数据。

    4. dayClass: 确定给定日期(item)的 CSS 类。如果日期在过去,则返回 ‘disabled’;如果日期与选定日期之一匹配,则返回 ‘active’;如果日期在两个选定日期之间,则返回 ‘middle’。

    5. dayStyle: 确定给定日期(item)的内联样式。为每个月的第一天设置左边距,并将周末的文字颜色设置为蓝色。

    6. dayMouseClick: 处理日期(item)的鼠标单击事件。根据单击的日期和选择的位置更新选定的日期范围(selectDate),并根据是否同时选择了两个日期来触发 ‘change’ 事件。

    7. dayMouseMove: 处理日期(item)的鼠标移动事件。如果日期选择正在进行中(isMove.value 为 true),则更新选定范围的结束日期。

    代码解析(外部导入的函数):

    1. export function getMonthDates(monthOffset) {: 这是一个导出函数的声明,函数名为 getMonthDates,它接受一个参数 monthOffset,表示要获取的月份相对于当前月份的偏移量。

    2. const today = new Date(): 创建了一个当前日期的 Date 对象。

    3. const targetDate = new Date(today.getFullYear(), today.getMonth() + monthOffset, 1): 创建了一个目标日期的 Date 对象,该日期是当前日期加上指定的月份偏移量,月份从0开始计数,因此 today.getMonth() 返回的是当前月份的索引,例如1月是0,2月是1,以此类推。

    4. const year = targetDate.getFullYear(): 获取目标日期的年份。

    5. let month = targetDate.getMonth() + 1: 获取目标日期的月份,并加1,因为月份是从0开始计数的,需要进行修正。

    6. month = month >= 10 ? month : '0' + month: 如果月份小于10,就在前面补0,确保月份是两位数的字符串格式。

    7. const firstDay = new Date(year, targetDate.getMonth(), 1): 获取目标月份的第一天的日期对象。

    8. const lastDay = new Date(year, targetDate.getMonth() + 1, 0): 获取目标月份的最后一天的日期对象。这里将月份加1,然后日期设为0,相当于得到了目标月份的最后一天。

    9. const monthDates = []: 创建一个空数组,用于存储该月份的日期信息。

    10. for (let d = firstDay; d <= lastDay; d.setDate(d.getDate() + 1)) {: 使用 for 循环遍历从第一天到最后一天的日期。

    11. const day = d.getDate(): 获取当前日期的日份。

    12. const dayOfWeek = d.getDay(): 获取当前日期是星期几,返回值是一个从0到6的整数,0 代表星期日。

    13. const isTodayBefore = d.getTime() < today.setHours(0, 0, 0, 0): 判断当前日期是否在今天之前。getTime() 返回日期的毫秒数,通过比较毫秒数可以确定日期的先后顺序。

    14. monthDates.push({ day, week: dayOfWeek, isTodayBefore, yymmdd: ����−year−{month}-${day >= 10 ? day : ‘0’ + day} }): 将当前日期的信息以对象的形式添加到 monthDates 数组中,包括日期、星期几、是否在今天之前以及日期的格式化字符串(年-月-日)。

    15. return { year, month, dates: monthDates }: 返回包含年份、月份和该月份日期信息的对象。

  • 相关阅读:
    vue3使用element-plus 树组件(el-tree)数据回显
    智慧工地APP全套源码,智慧工地云平台
    oracle OCP OCM MySQL OCP认证难吗?
    杠杆炒股的尾盘买入法
    [源码解析] TensorFlow 分布式 DistributedStrategy 之基础篇
    神经网络滤镜安装教程图,ps神经网络滤镜安装包
    ABAP面向对象
    k8s 污点和容忍
    【虹科技术分享】如何测试 DNS 服务器:DNS 性能和响应时间测试
    编程语言常识
  • 原文地址:https://blog.csdn.net/nibabaoo/article/details/138158484