目录QCalendar.sCSSgetRangeDay.jsfORMatTime.jsQCalendar.Vue日历组件效果图日月年tips总结不用任何第三方库,只基于vue2实现一
不用任何第三方库,只基于vue2实现一个日历组件,末尾附上我的代码,单文件,代码没有抽取,有点长。哪位大佬批评指正一下,末尾有效果图
.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;
}
}
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, "{d}").length===1?('0'+parseTime(k, "{d}")):parseTime(k, "{d}"),
checked: true
});
k = k + 24 * 60 * 60 * 1000;
}
return result;
}
export function parseTime(time, pattern) {
if (arguments.length === 0 || !time) return null;
const format = pattern || '{y}-{m}-{d} {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;
});
}
<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">
<!-- 上个月剩余天数 -->
<div class="Q-calendar-surplus" v-for="item in lastMonthDays" :key="'dayLast' + item">
<span v-show="isOtherDate"> {{ item }}</span>
</div>
<!-- 当前月份天数 -->
<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>
// 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,
};
},
// 当前月的天数
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日补充
支持滑动切换年月日,具体功能请移步组件文档
到此这篇关于基于vue2实现一个日历组件的文章就介绍到这了,更多相关vue日历组件内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!
--结束END--
本文标题: 基于vue2实现一个日历组件
本文链接: https://lsjlt.com/news/176188.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
2024-01-12
2023-05-20
2023-05-20
2023-05-20
2023-05-20
2023-05-20
2023-05-20
2023-05-20
2023-05-20
2023-05-20
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0