基于vue2實現(xiàn)一個日歷組件
更新時間:2022年12月29日 10:20:59 作者:華菱下水道二組_歷飛宇
最近在做一個類似課程表的需求,需要自制一個日歷來支持功能及展現(xiàn),就順便研究一下應該怎么開發(fā)日歷組件,下面這篇文章主要給大家介紹了關于如何基于vue2實現(xiàn)一個日歷組件的相關資料,需要的朋友可以參考下
不用任何第三方庫,只基于vue2實現(xiàn)一個日歷組件,末尾附上我的代碼,單文件,代碼沒有抽取,有點長。哪位大佬批評指正一下,末尾有效果圖
- 樣式是類似于window10日歷
- 支持控制周一還是周日在第一列
- 支持鼠標滑動切換
- 支持單選,拖動鼠標多選,范圍選擇
- 支持年月日選擇切換
- 支持傳入選中數(shù)據
- 支持隱藏非本月日期
QCalendar.scss
.Q-calendar-change-enter-active, .Q-calendar-change-leave-active { transition: opacity 0.5s; } .Q-calendar-change-enter, .Q-calendar-change-leave-to { opacity: 0; } .Q-calendar { width: 100%; height: 100%; margin: 0 auto; overflow: hidden; background-color: #ffffff; color: #000; user-select: none; border: 1px solid #4152b3; .Q-calendar-title { height: 50px; width: 100%; box-sizing: border-box; display: flex; justify-content: space-between; div { align-self: center; } .Q-calendar-button, .top { align-self: center; } .Q-calendar-title-box { width: calc((100% / 7) * 2); display: flex; justify-content: space-around; cursor: default; .Q-calendar-title-box-text { width: 50%; text-align: center; align-self: center; } .Q-calendar-title-box-text:hover { color: #4152b3; font-weight: 700; } } .Q-calendar-title-box-padding{ padding-left: 18px; } .Q-calendar-title-box-center{ margin: 0 auto; font-weight: 700 } } .Q-calendar-day { height: calc(100% - 50px); /* 周末 */ .Q-calendar-week { display: flex; justify-content: inherit; cursor: default; p { display: flex; justify-content: center; width: calc(100% / 7); box-sizing: border-box; } } /* 日歷內容 */ .Q-calendar-box { display: flex; justify-content: inherit; flex-wrap: wrap; width: 100%; height: 80%; div:hover { color: yellowgreen; font-weight: 700; } .Q-calendar-current-month { box-sizing: border-box; cursor: default; } .Q-calendar-current-month:hover { color: #4152b3; font-weight: 700; font-size: 20px; } div { display: flex; justify-content: center; width: calc(100% / 7); height: calc(100% / 7); span { margin: auto; } } p { display: flex; justify-content: center; width: calc(100% / 7); } } } .Q-calendar-years { height: calc(100% - 50px); .Q-calendar-years-box { // border: 1px solid pink; display: flex; justify-content: inherit; flex-wrap: wrap; height: 98%; div { display: flex; box-sizing: border-box; justify-content: center; width: calc(100% / 4); height: calc(100% / 4); span { margin: auto; } } div:hover { font-weight: 700; color: yellowgreen; } } } } /* //非本月時間內,或非本年內 */ .Q-calendar-surplus { color: #898989; } .nowCss { // border:1px solid pink; background: #f1f3f4; color: #40b8ff; } .Q-calendar-checked { span { color: var(--Q-calendar-color); background-color: var(--Q-calendar-background-color); border-radius: 10px; width: 50%; height: 50%; text-align: center; line-height: 24px; } }
getRangeDay.js
import { parseTime } from './formatTime' export function getRangeDay(startDate, endDate) { const result = []; const db = new Date(); db.setUTCFullYear(startDate.year, startDate.month - 1, startDate.day); const de = new Date(); de.setUTCFullYear(endDate.year, endDate.month - 1, endDate.day); let smallDate let bigDate if (db.getTime() > de.getTime()) { smallDate = de.getTime() bigDate = db.getTime() } else { smallDate = db.getTime() bigDate = de.getTime() } for (let k = smallDate; k <= bigDate;) { result.push({ year: parseTime(k, "{y}"), month: parseTime(k, "{m}").length===1?('0'+parseTime(k, "{m}")):parseTime(k, "{m}"), day: parseTime(k, "vvxyksv9kd").length===1?('0'+parseTime(k, "vvxyksv9kd")):parseTime(k, "vvxyksv9kd"), checked: true }); k = k + 24 * 60 * 60 * 1000; } return result; }
formatTime.js
export function parseTime(time, pattern) { if (arguments.length === 0 || !time) return null; const format = pattern || '{y}-{m}-vvxyksv9kd {h}:{i}:{s}'; let date; if (typeof time === 'object') { date = time; } else { if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) { time = parseInt(time); } else if (typeof time === 'string') { time = time.replace(new RegExp(/-/gm), '/'); } if ((typeof time === 'number') && (time.toString().length === 10)) { time = time * 1000; } date = new Date(time); } const formatObj = { y: date.getFullYear(), m: date.getMonth() + 1, d: date.getDate(), h: date.getHours(), i: date.getMinutes(), s: date.getSeconds(), a: date.getDay(), }; return format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => { // @ts-ignore let value = formatObj[key]; // Note: getDay() returns 0 on Sundayday if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value]; } if (result.length > 0 && value < 10) { value = '0' + value; } return value || 0; }); }
QCalendar.vue
<template> <!-- 外部 --> <div class="Q-calendar" @mouseup.stop="onMouseUp" @mouseleave.stop="onMouseleave"> <!-- 頭部 --> <div class="Q-calendar-title" v-if="hideTitle"> <div class="Q-calendar-title-box Q-calendar-title-box-padding"> <div @click="onClickYears" class="Q-calendar-title-box-text"> {{ this.currentYear }}年 </div> <div @click="onClickMonth" class="Q-calendar-title-box-text"> {{ this.currentMonth }}月 </div> <!-- <div>{{this.currentDay}}號</div> --> </div> <slot name="mkCalendarHeaderSlot"></slot> <div class="Q-calendar-title-box" v-if="isSwitch"> <div class="Q-calendar-title-box-text" @click="onClickUp">上</div> <div class="Q-calendar-title-box-text" @click="onClickDown">下</div> </div> </div> <div v-else class="Q-calendar-title"> <div class="Q-calendar-title-box Q-calendar-title-box-center"> <div class="Q-calendar-title-box-text">{{ this.currentYear }}年</div> <div class="Q-calendar-title-box-text">{{ this.currentMonth }}月</div> </div> </div> <!-- 日歷 --> <transition name="Q-calendar-change"> <div class="Q-calendar-day" v-if="hide === 1" @mousewheel="onMousewheel"> <!-- 周一到周日 --> <div class="Q-calendar-week"> <p v-for="item in isMonday ? weekSort.Monday : weekSort.Sunday" :key="item">{{ item }}</p> </div> <div class="Q-calendar-box"> <!-- 上個月剩余天數(shù) --> <div class="Q-calendar-surplus" v-for="item in lastMonthDays" :key="'dayLast' + item"> <span v-show="isOtherDate"> {{ item }}</span> </div> <!-- 當前月份天數(shù) --> <div v-for="item in currentMonthDays" :key="'dayCur' + item.day" class="Q-calendar-current-month" :style="cssProps" @click="onChangeDay(item)" :class="{ 'Q-calendar-checked': item.checked, nowCss: new Date().getFullYear()+'' === item.year && (new Date().getMonth() + 1)+'' === item.month && new Date().getDate()+'' === item.day, }" @mouseover="dragDay(item)" @mousedown="onMouseDown(item)"> <span> {{ item.day}}</span> </div> <!-- 下月余出 --> <div class="Q-calendar-surplus" v-for="item in this.nextMonth()" :key="'dayNext' + item"> <span v-show="isOtherDate">{{ item }}</span> </div> </div> </div> </transition> <!-- 月 --> <transition name="Q-calendar-change"> <div class="Q-calendar-years" v-if="hide === 2" @mousewheel="onMousewheel"> <div class="Q-calendar-years-box"> <div v-for="item in clickMonth" :key="'monthCur' + item.value" @click="onChangeMonth(item)" :class="{ nowCss: isNowYear && new Date().getMonth() + 1 === item.value, }"> <span>{{ item.key }}</span> </div> <div class="Q-calendar-surplus" v-for="item in lastMonth" :key="'monthNext' + item"> <span>{{ item }}</span> </div> </div> </div> </transition> <!-- 年 --> <transition name="Q-calendar-change"> <div class="Q-calendar-years Q-calendar-year" v-if="hide === 3" @mousewheel="onMousewheel"> <div class="Q-calendar-years-box"> <div class="Q-calendar-surplus" v-for="item in lastYear" :key="'yearLast' + item"> <span>{{ item }}</span> </div> <div v-for="item in thisYear" :key="'yearCur' + item" @click="onChangeYear" :class="{ nowCss: new Date().getFullYear() === item }"> <span>{{ item }}</span> </div> </div> </div> </transition> </div> </template> <script> /** * 日歷組件 * @description * @property {Boolean} isOtherDate false 是否展示非本月份的日期 * @property {Boolean} hideTitle true 是否展示title * @property {Boolean} multiSelect false 是否開啟摁下鼠標進行多選 * @property {String} SelectedBackgroundColor "#4152b3" 選中的背景色 * @property {String} SelectedTextColor "#ffffff" 選中的文字色 * @property {Boolean} isMonday true 是否從周一在開頭 * @property {Number} selectType 1 單選1,多選2,范圍選3 * @property {Array} selectList [] 選中數(shù)據的數(shù)組 * @property {Boolean} isSwitch true 需要切換按鈕傳入true * */ // import { parseTime } from "./utils/formatTime"; import { getRangeDay } from "./utils/getRangeDay"; export default { name: "QCalendar", props: { isOtherDate: { type: Boolean, default: false, }, hideTitle: { type: Boolean, default: true, }, multiSelect: { type: Boolean, default: false, }, SelectedBackgroundColor: { type: String, default: "#4152b3", }, SelectedTextColor: { type: String, default: "#ffffff", }, isMonday: { type: Boolean, default: true, }, selectType: { type: Number, default: 1, }, selectList: { type: Array, default: () => [] }, isSwitch: { type: Boolean, default: true, } }, data() { return { isMouseDown: false, arr: this.selectList, isNowYear: true, isNowMOnth: true, hide: 1, weekSort:{ Sunday: ["日", "一", "二", "三", "四", "五", "六"], Monday: ["一", "二", "三", "四", "五", "六", "日"], }, clickMonth: [ { key: "一月", value: 1 }, { key: "二月", value: 2 }, { key: "三月", value: 3 }, { key: "四月", value: 4 }, { key: "五月", value: 5 }, { key: "六月", value: 6 }, { key: "七月", value: 7 }, { key: "八月", value: 8 }, { key: "九月", value: 9 }, { key: "十月", value: 10 }, { key: "十一月", value: 11 }, { key: "十二月", value: 12 }, ], lastMonth: ["一月", "二月", "三月", "四月"], lastYear: [], thisYear: [], // 當前日 currentDay: new Date().getDate(), // 當前月 currentMonth: new Date().getMonth() + 1, // 當前年 currentYear: new Date().getFullYear(), }; }, created() { this.initParameter(); }, computed: { cssProps() { return { "--Q-calendar-background-color": this.SelectedBackgroundColor, "--Q-calendar-color": this.SelectedTextColor, }; }, // 當前月的天數(shù) currentMonthDays() { let dayLength = new Date( this.currentYear, this.currentMonth, 0 ).getDate(); let arr = []; for (let h = 0; h < dayLength; h++) { let dataObj = { year: this.currentYear + "", month: (this.currentMonth>0&&this.currentMonth<10)?'0'+ this.currentMonth :this.currentMonth+'', day: (h+1>0&&h+1<10)? '0'+ (h+1) : (h+1) + '', checked: false, }; arr[h] = dataObj; } for (let p = 0; p < this.arr.length; p++) { for (let k = 0; k < arr.length; k++) { if ((this.arr[p].year === arr[k].year + "") && (this.arr[p].month === arr[k].month + "") && (this.arr[p].day === arr[k].day + "") && this.arr[p].checked) { arr[k].checked = true } } } return arr; }, // 獲取上個月的剩余多少天 lastMonthDays() { const lastLength = new Date( this.currentYear, this.currentMonth - 1, 0 ).getDate(); let cutLength; if (this.isMonday) { cutLength = new Date( this.currentYear, this.currentMonth - 1, 0 ).getDay(); } else { cutLength = new Date( this.currentYear, this.currentMonth - 1, 1 ).getDay(); } let arr = []; for (let h = lastLength - cutLength + 1; h <= lastLength; h++) { arr.push(h); } return arr; }, }, methods: { onMousewheel(e) { let evt = e || window.event; //考慮兼容性 evt.preventDefault(); if (evt.deltaY > 0) { this.onClickDown(); } else { this.onClickUp(); } //檢查事件 // console.log(evt.type, evt.deltaX, evt.deltaY, evt.deltaZ); }, dragDay(dayObj) { if (!this.multiSelect) { return; } else { if (!this.isMouseDown) { return; } else { this.onChangeDay(dayObj); } } }, onMouseDown(dayObj) { if (!this.multiSelect) { return; } else { if (this.isMouseDown) this.onChangeDay(dayObj); this.isMouseDown = true; } }, onMouseUp() { this.isMouseDown = false; }, onMouseleave() { if (this.isMouseDown) { this.isMouseDown = false; } }, // 點擊多選 onChangeDay(val) { // 判斷單選,多選,還是范圍選,對應值1.2.3. if (this.selectType === 1) { if (this.arr.length === 0) { val.checked = true; this.arr=[val] } else if (this.arr.length === 1) { if ((this.arr[0].year === val.year) && (this.arr[0].month === val.month) && (this.arr[0].day === val.day)) { this.arr = [] } else { this.arr = [] val.checked = true; this.arr.push(val); } } else { return } } else if (this.selectType === 2) { if (val.checked) { // 剔除 val.checked = false; this.arr = this.arr.filter((ele) => { return !( ele.year === val.year && ele.month === val.month && ele.day === val.day ); }); } else { // 添加 val.checked = true; this.arr.push(val); } } else if (this.selectType === 3) { // 范圍選擇, if (this.arr.length === 0) { val.checked = true; this.arr.push(val); } else if (this.arr.length === 1) { val.checked = true; this.arr.push(val); const arrS = getRangeDay(this.arr[0],this.arr[1]) this.arr = [] this.arr = arrS } else { this.arr = [] val.checked = true; this.arr.push(val); } } this.$emit("selectedData", this.arr); }, initParameter() { let currentYear = this.currentYear - 1; for (let p = 3; p >= 0; p--) { this.lastYear[p] = currentYear--; } currentYear = this.currentYear; for (let l = 0; l < 12; l++) { this.thisYear[l] = currentYear++; } }, onChangeYear(val) { this.hide = 2; let currentYear = new Date().getFullYear(); this.currentYear = val.srcElement.innerText; this.isNowYear = val.srcElement.innerText + "" === currentYear+'' }, onChangeMonth(val, ) { this.hide = 1; let currentMonth = new Date().getMonth() + 1; this.currentMonth = val.value; this.isNowMOnth=val.value + "" === currentMonth + "" }, // 點擊年 onClickYears() { let currentYear = this.currentYear - 1; for (let p = 3; p >= 0; p--) { this.lastYear[p] = currentYear--; } this.hide = 3; }, // 點擊月 onClickMonth() { this.hide = 2; }, // 獲取上個月的剩余多少天 nextMonth() { const ac = 42 - this.currentMonthDays.length - this.lastMonthDays.length; return ac; }, // 上 onClickUp() { let currentYear = new Date().getFullYear(); if (this.hide === 1) { if (this.currentMonth === 1) { this.currentYear--, (this.currentMonth = 13); } this.currentMonth--; } else if (this.hide === 2) { this.currentYear--; this.isNowYear = this.currentYear+'' === currentYear+'' } else { this.switchingYear(1); } }, // 下 onClickDown() { let currentYear = new Date().getFullYear(); if (this.hide === 1) { // 日 if (this.currentMonth === 12) { this.currentYear++, (this.currentMonth = 0); } this.currentMonth++; } else if (this.hide === 2) { // 月默認切換年 this.currentYear++; this.isNowYear = this.currentYear+'' === currentYear+'' } else { // 切換年的選擇 this.switchingYear(2); } }, switchingYear(type) { // 1上,2下 if (type === 1) { // last最后一個為this的最后一個 let thisAnchor = this.lastYear[3] - 11; let lastAnchor = this.lastYear[3] - 15; this.thisYear = []; for (let p = 0; p < 12; p++) { this.thisYear[p] = thisAnchor++; } this.lastYear = []; for (let l = 0; l < 4; l++) { this.lastYear[l] = lastAnchor++; } } else if (type === 2) { let anchor = this.thisYear[11] + 1; this.lastYear = []; for (let p = 3; p >= 0; p--) { this.lastYear[p] = this.thisYear[11]--; } this.thisYear = []; for (let l = 0; l < 12; l++) { this.thisYear[l] = anchor++; } } }, }, }; </script> <style scoped lang="scss"> @import './utils/QCalendar.scss'; </style>
日歷組件效果圖
2022-12-27日補充
日
月
年
tips
支持滑動切換年月日,具體功能請移步組件文檔
總結
到此這篇關于基于vue2實現(xiàn)一個日歷組件的文章就介紹到這了,更多相關vue日歷組件內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
vue中this.$refs.name.offsetHeight獲取不到值問題
這篇文章主要介紹了vue中this.$refs.name.offsetHeight獲取不到值問題及解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-05-05vue與bootstrap實現(xiàn)時間選擇器的示例代碼
本篇文章主要介紹了vue與bootstrap實現(xiàn)時間選擇器的示例代碼,非常具有實用價值,需要的朋友可以參考下2017-08-08vue?demi支持sfc方式的vue2vue3通用庫開發(fā)詳解
這篇文章主要為大家介紹了vue?demi支持sfc方式的vue2vue3通用庫開發(fā)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-08-08