Vue encapsulates a calendar component

First put the effect diagram, the style is more casual, we understand the article ideas, you can change.

This is used in the company’s internal SASS system

This is the component of Element-UI

Copy the code

Writing a calendar, first of all to find his data typesetting rules, like a pupil looking for rules.

  • In order to keep the overall calendar height from changing with the month, the height is generally designed to be 6 lines, including the days of the last month and the next month (generally set to gray).
  • Find out what day the first day of the month is, as shown in the picture above,2020-11-01On Sunday,getDay = 0I’ll show you last month’s total7 minus 0 is 7Number of days. The tail number is42 - Total number of days beforeUsually you don’t need to be able to calculate tail days

Some of their own encapsulation of the simple time function, in order to prevent the main program to write too much code

function getYearMonthDay (value) {
  const date = new Date(value)
  const year = date.getFullYear()
  const month = date.getMonth()
  const day = date.getDate()
  return { year, month, day }
}

function getDate (year, month, day = 1) {
  return new Date(year, month, day)
}

export default {
  getYearMonthDay,
  getDate
}
Copy the code

The visibleDays shown in the calendar are ok in the template as long as the for loop loops through visibleDays

computed: {
    visibleDays() {
        // Get the first day of the month
        const currentFirstDay = utils.getDate(this.timer.year, this.timer.month, 1)
        // Get the day of the week to move forward a few days
        let week = currentFirstDay.getDay()
        // The beginning of the calendar month is moved forward 7 days if it falls on a Sunday
        week = week === 0 ? 7 : week
        const startDay = currentFirstDay - week * 60 * 60 * 1000 * 24
        // Loop 42 days, 6 rows and 7 columns
        const arr = []
        for (let i = 0; i < 42; i++) {
            arr.push(new Date(startDay + i * 60 * 60 * 1000 * 24))}return arr
    }
}
Copy the code

How to present the visibleDays data in the template, which is also a question of finding patterns

 <div v-for="i in 6" :key="i">
     <span 
         v-for="j in 7" 
         :key="j"
         class="cell"
         :class="[
           { notCurrentMonth: !isCurrentMonth(visibleDays[(i -1) * 7 + (j - 1)]) },
           { today: isToday(visibleDays[(i -1) * 7 + (j - 1)]) }
         ]"
         @click="selectDay(visibleDays[(i -1) * 7 + (j - 1)])">
         <span :class="{ selected: isSelect(visibleDays[(i -1) * 7 + (j - 1)]) }">{{ visibleDays[(i -1) * 7 + (j - 1)].getDate() }}</span>
     </span>
</div>
Copy the code

Complete component code

<template> <div v-click-outsize> <input :value="formDate" type="text" /> <div v-if="isVisible" class="panel"> <div class="panel-nav"> <span @click="pervMonth"> &lt; &lt; </span> <span>{{timer. Month + 1}} month </span> <span @click="nextMonth"> > </span> <span>{{timer. &gt; </span> </div> <div class="panel-content"> <p class="panel-week"> <span v-for="w in week" :key="'-' + w">{{ w }}</span> </p> <! - similar to the 9 * 9 times table -- -- > < div v - for = "I in 6:" key = "I" > < span v - for = "j in 7:" key = "j" class = "cell:" class = "[{ notCurrentMonth: !isCurrentMonth(visibleDays[(i -1) * 7 + (j - 1)]) }, { today: isToday(visibleDays[(i -1) * 7 + (j - 1)]) } ]" @click="selectDay(visibleDays[(i -1) * 7 + (j - 1)])"> <span :class="{ selected: isSelect(visibleDays[(i -1) * 7 + (j - 1)]) }">{{ visibleDays[(i -1) * 7 + (j - 1)].getDate() }}</span> </span> </div> </div> <div class="panel-footer"></div> </div> </template> <script> import utils from '.. /utils/index.js' export default { name: 'DatePicker', directives: { clickOutsize: { bind(el, binding, vnode) { const handler = (e) => { if (el.contains(e.target)) { if (! vnode.context.isVisible) { vnode.context.focus() } } else { if (vnode.context.isVisible) { vnode.context.blur() } } } el.handler = handler document.addEventListener('click', handler) }, unbind(el) { document.removeEventListener('ckick', el.handler) } } }, props: { value: { type: [Date, String] } }, computed: { formDate() { const { year, month, day } = utils.getYearMonthDay(this.value) return `${year}-${month + 1}-${day}` }, VisibleDays () {const currentFirstDay = utils.getDate(this.timer. Year, this.timer. Month, this.timer. 1) // Let week = currentFirstDay.getDay() // If the calendar month is Sunday, move 7 days forward week = week === 0? 7: week const startDay = currentFirstday-week * 60 * 60 * 1000 * 24 const arr = [] for (let I = 0; i < 42; i++) { arr.push(new Date(startDay + i * 60 * 60 * 1000 * 24)) } return arr } }, data() { return { isVisible: false, timer: { year: '', month: '' }, week: }}, mounted() {const {year, month, day } = utils.getYearMonthDay(this.value) this.timer.year = year this.timer.month = month }, methods: { focus() { this.isVisible = true }, blur() { this.isVisible = false }, isCurrentMonth(date) { const { year, month } = utils.getYearMonthDay(utils.getDate(this.timer.year, this.timer.month, 1)) const { year: y, month: m } = utils.getYearMonthDay(date) return year === y && month === m }, isToday(date) { const { year, month, day } = utils.getYearMonthDay(new Date) const { year: y, month: m, day: d } = utils.getYearMonthDay(date) return year === y && month === m && day === d }, selectDay(date) { this.timer = utils.getYearMonthDay(date) this.$emit('input', date) this.blur() }, isSelect(date) { const { year, month, day } = utils.getYearMonthDay(this.value) const { year: y, month: m, day: d } = utils.getYearMonthDay(date) return year === y && month === m && day === d }, pervMonth() { const d = utils.getDate(this.timer.year, this.timer.month, 1) d.setMonth(--this.timer.month) this.timer = utils.getYearMonthDay(d) }, nextMonth() { const d = utils.getDate(this.timer.year, this.timer.month, 1) d.setMonth(++this.timer.month) this.timer = utils.getYearMonthDay(d) } } } </script> <style lang="scss" scoped> * { padding: 0; margin: 0; } .panel { position: absolute; background-color: #fff; Box-shadow: 0px 0px 10px 0px Rgba (153, 153, 153, 0.5); .panel-nav { text-align: center; span:first-child, span:last-child { cursor: pointer; } } .panel-content { .panel-week { span { display: inline-block; width: 32px; height: 32px; text-align: center; line-height: 32px; font-weight: bold; font-size: 14px; } } .cell { display: inline-flex; justify-content: center; align-items: center; width: 32px; height: 32px; text-align: center; box-sizing: border-box; font-weight: bold; font-size: 14px; box-sizing: border-box; cursor: pointer; span { display: inline-block; width: 20px; height: 20px; } &:hover { opacity: .3; } } .notCurrentMonth { color: gray; } .today { color: #1890FF; } .selected { background-color: #1890FF; color: #fff; border-radius: 50%; } } } </style>Copy the code

Post a GitHub address: If you need to paste and copy, the MySimpleCalendar. Vue component is responsive. Github.com/HuDaDa0/cal…