前言:日历组件在我们日常开发中非常常见,用于查看指定日期的数据,例如:人员排班等
一、周日历(显示星期和日期)
------ 根据指定的日期,获取当前日期所在周以及切换前、后一周的日期
代码实现:
公用工具函数 utils/index.js
js
export function parseTime(time, cFormat, noSameYearOrDay = false) {
if (arguments.length === 0) {
return null;
}
let format = cFormat || 'y-m-d h:i:s';
let date;
if (time && typeof time == 'object') {
date = time;
} else {
if (('' + time).length === 10) time = parseInt(time) * 1000;
date = new Date(time);
}
let today = new Date();
let time_str = '';
if (today.getFullYear() == date.getFullYear() &&
today.getMonth() == date.getMonth() &&
today.getDate() == date.getDate() &&
!noSameYearOrDay
) {
time_str = '今天';
} else if (today.getFullYear() == date.getFullYear() && !noSameYearOrDay) {
format = 'm-d';
const formatObj = {
m: date.getMonth() + 1,
d: date.getDate(),
};
time_str = format.replace(/(y|m|d|h|i|s|a)+/g, (result, key) => {
let value = formatObj[key];
if (result.length > 0 && value < 10) {
value = '0' + value;
}
return value || 0;
});
} else {
const formatObj = {
y: date.getFullYear(),
m: date.getMonth() + 1,
d: date.getDate(),
h: date.getHours(),
i: date.getMinutes(),
s: date.getSeconds(),
a: date.getDay(),
};
time_str = format.replace(/(y|m|d|h|i|s|a)+/g, (result, key) => {
let value = formatObj[key];
if (result.length > 0 && value < 10) {
value = '0' + value;
}
return value || 0;
});
}
return time_str;
}
/**
* 获取指定日期的上一个月
* @param date
* @return {Date}
*/
export function prevMonth(date) {
const year = date.getFullYear();
const month = date.getMonth();
return month === 0 ?
changeYearMonthAndClampDate(date, year - 1, 11) :
changeYearMonthAndClampDate(date, year, month - 1);
};
/**
* 获取指定日期的下一个月
* @param date
* @return {Date}
*/
export function nextMonth(date) {
const year = date.getFullYear();
const month = date.getMonth();
return month === 11 ?
changeYearMonthAndClampDate(date, year + 1, 0) :
changeYearMonthAndClampDate(date, year, month + 1);
};
/**
* 获取指定月份的天数
* @param year - 年份
* @param month - 月份
* @return {number} 当月天数
*/
export function getDayCountOfMonth(year, month) {
if (month === 3 || month === 5 || month === 8 || month === 10) {
return 30;
}
if (month === 1) {
if (year % 4 === 0 && year % 100 !== 0 || year % 400 === 0) {
return 29;
}
return 28;
}
return 31;
};
export function changeYearMonthAndClampDate(date, year, month) {
// clamp date to the number of days in `year`, `month`
// eg: (2010-1-31, 2010, 2) => 2010-2-28
const monthDate = Math.min(date.getDate(), getDayCountOfMonth(year, month));
return modifyDate(date, year, month, monthDate);
};
export function modifyDate(date, y, m, d) {
return new Date(y, m, d, date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds());
};
week-calendar.vue
JS
<template>
<div class="week-calendar-wrapper">
<h3>周日历</h3>
<div class="content-wrapper">
<div class="prev-week">
<el-button
icon="el-icon-arrow-left"
:disabled="prevDisabled"
@click="handlePrev"
></el-button>
</div>
<div class="current-week">
<div class="week-wrapper">
<div class="col week-item" v-for="item in weekList" :key="item">{{ item }}</div>
</div>
<div class="date-wrapper">
<div
:class="['col', 'data-item', {'today': item.isToday}]"
v-for="item in dateList"
:key="item.date"
>{{ item.date }}</div>
</div>
</div>
<div class="next-week">
<el-button icon="el-icon-arrow-right" @click="handleNext"></el-button>
</div>
</div>
</div>
</template>
<script>
import { parseTime } from '@/utils/index';
export default {
name: 'WeekCalendar',
data() {
return {
weekList: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
startDate: '',
today: new Date(),
}
},
computed: {
weekStartDate() {
const date = this.startDate ? new Date(this.startDate) : this.today;
const nowYear = date.getFullYear(); //当前年
const nowMonth = date.getMonth(); //当前月
const nowDay = date.getDate(); //当前日
const nowDayOfWeek = date.getDay(); // 今天本周的第几天
if (nowDayOfWeek) {
return new Date(nowYear, nowMonth, nowDay - nowDayOfWeek + 1);
}
return new Date(nowYear, nowMonth, nowDay - 6);
},
weekEndDate() {
const year = this.weekStartDate.getFullYear();
const month = this.weekStartDate.getMonth();
const day = this.weekStartDate.getDate();
return new Date(year, month, day + 6);
},
prevDisabled() {
const now = Date.now();
return this.weekStartDate.getTime() <= now && this.weekEndDate.getTime() >= now;
},
dateList() {
const arr = [];
const year = this.weekStartDate.getFullYear();
const month = this.weekStartDate.getMonth();
const day = this.weekStartDate.getDate();
for (let i = 0; i < 7; i++) {
const time = new Date(year, month, day + i);
const isToday = parseTime(time, 'y-m-d', true) === parseTime(this.today, 'y-m-d', true);
arr.push({
date: isToday ? '今天' : parseTime(time, 'm-d', true),
isToday
});
}
return arr;
},
},
methods: {
parseTime,
handlePrev() {
const startDate = this.weekStartDate.getTime() - 24 * 60 * 60 * 1000 * 7;
this.setStartDate(startDate)
},
handleNext() {
const startDate = this.weekStartDate.getTime() + 24 * 60 * 60 * 1000 * 7;
this.setStartDate(startDate)
},
setStartDate(startDate) {
const date = new Date(startDate);
this.startDate = parseTime(date, 'y-m-d', true);
}
}
}
</script>
<style scoped lang="scss">
.week-calendar-wrapper {
display: flex;
justify-content: center;
flex-direction: column;
.content-wrapper {
display: flex;
align-items: center;
.current-week {
display: flex;
flex-direction: column;
margin: 0 20px;
.week-wrapper,
.date-wrapper {
display: flex;
width: 100%;
.col {
display: flex;
justify-content: center;
width: 40px;
& + .col {
margin-left: 10px;
}
&.week-item {
color: #aab4bf;
}
&.data-item {
font-size: 14px;
color: #7a8794;
}
&.today {
color: #459eff
}
}
}
}
}
}
</style>
二、月日历(显示日期数字)
------ 根据指定的日期,获取当前日期所在月以及切换前、后一月的日期
只展示当月日期:
代码实现:
month-calendar.vue
JS
<template>
<div class="month-calendar-wrapper">
<h3>月日历</h3>
<div class="content-wrapper">
<div class="btn-group">
<div class="pre-month">
<el-button
size="small"
icon="el-icon-arrow-left"
@click="handlePrev"
></el-button>
</div>
<div class="today">
<el-button size="small" @click="handleToday">今天</el-button>
</div>
<div class="next-month">
<el-button size="small" icon="el-icon-arrow-right" @click="handleNext"></el-button>
</div>
<div class="current-date">【 {{ currentDate }} 】</div>
</div>
<div class="month-wrapper">
<div class="week-wrapper">
<div class="col week-item" v-for="item in weekList" :key="item">{{ item }}</div>
</div>
<div class="date-list-wrapper">
<div class="date-wrapper" v-for="(row, index) in showDateList" :key="index">
<template v-for="item in row">
<div
v-if="item.type === 'current'"
:key="item.date"
:class="['col', 'data-item',{
'today': item.isToday,
'disabled': item.type !== 'current',
'selected': item.date === selectedDate,
}]"
@click="handleSelectedDate(item)"
>
{{ item.text }}
</div>
<div
v-else
:key="item.date"
:class="['col', 'data-item',{
'today': item.isToday,
'disabled': item.type !== 'current',
'selected': item.date === selectedDate,
}]"
>
</div>
</template>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import {nextMonth, parseTime, prevMonth} from "@/utils/index";
export default {
name: 'MonthCalendar',
data() {
return {
weekList: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
date: new Date(),
today: parseTime(new Date(), 'y-m-d', true),
selectedDate: parseTime(new Date(), 'y-m-d', true)
}
},
computed: {
currentDate() {
return parseTime(this.date, 'y-m', true)
},
dateList() {
const dates = [];
// 获取当前第一天是周几
let firstDay = this.getFirstDayOfMonth(this.date);
firstDay = firstDay === 0 ? 7 : firstDay;
// 获取当前最后一天是周几
let lastDay = this.getLastDayOfMonth(this.date);
lastDay = lastDay === 0 ? 7 : lastDay;
// 获取上个月后(firstDay - 1)的日期
const prevMonthDays = this.getPrevMonthDays(this.date, firstDay - 1);
const currentMonthDays = this.getMonthDays(this.date);
const nextMonthDays = this.getNextMonthDays(this.date, 7 - lastDay);
dates.push(...prevMonthDays, ...currentMonthDays, ...nextMonthDays);
return dates
},
showDateList() {
return this.dateList.reduce((arr, item, index) => {
if (index % 7 === 0) {
arr.push([]);
}
arr[arr.length - 1].push(item)
return arr;
}, [])
}
},
methods: {
getFirstDayOfMonth(date) {
// 获取当前第一天是周几
const temp = new Date(date.getFullYear(), date.getMonth(), 1);
return temp.getDay();
},
getLastDayOfMonth(date) {
// 获取当前最后一天是周几
const temp = new Date(date.getFullYear(), date.getMonth() + 1, 0);
return temp.getDay();
},
getPrevMonthDays(date, amount) {
if (amount <= 0) return []
// 获取上个月份的日期
const temp = new Date(date.getFullYear(), date.getMonth(), 0);
const prefix = parseTime(temp, 'y-m', true);
const days = temp.getDate();
return new Array(amount).fill(0).map((item, index) => {
const text = days - amount + 1 + index;
return {
date: `${prefix}-` + `${text}`.padStart(2, '0'),
text,
isToday: false,
type: 'prev'
}
})
},
getMonthDays(date) {
// 获取当前月份的天数
const temp = new Date(date.getFullYear(), date.getMonth() + 1, 0);
const prefix = parseTime(temp, 'y-m', true);
const days = temp.getDate();
return new Array(days).fill(0).map((item, index) => {
const text = index + 1;
const date = `${prefix}-` + `${text}`.padStart(2, '0')
return {
date,
text: date === this.today ? '今': text,
isToday: date === this.today,
type: 'current'
};
})
},
getNextMonthDays(date, amount) {
if (amount <= 0) return []
// 获取下个月份的日期
const temp = new Date(date.getFullYear(), date.getMonth() + 2, 0);
const prefix = parseTime(temp, 'y-m', true);
return new Array(amount).fill(0).map((item, index) => {
const text = index + 1;
return {
date: `${prefix}-` + `${text}`.padStart(2, '0'),
text: index + 1,
isToday: false,
type: 'next'
}
})
},
handlePrev() {
this.date = prevMonth(this.date);
},
handleToday() {
this.selectedDate = parseTime(new Date(), 'y-m-d', true);
this.date = new Date();
},
handleNext() {
this.date = nextMonth(this.date);
},
handleSelectedDate(item) {
this.selectedDate = item.date;
}
}
}
</script>
<style scoped lang="scss">
.month-calendar-wrapper {
.content-wrapper {
.btn-group {
display: flex;
align-items: center;
gap: 10px;
}
.month-wrapper {
margin-top: 12px;
width: 420px;
padding: 10px;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
.week-wrapper,
.date-wrapper {
width: 100%;
display: grid;
grid-template-columns: repeat(7, 40px);
gap: 10px 20px;
cursor: pointer;
.col {
display: flex;
justify-content: center;
align-items: center;
width: 40px;
&.week-item {
color: #aab4bf;
line-height: 30px;
}
&.data-item {
height: 40px;
box-sizing: border-box;
&:hover:not(.disabled):not(.selected) {
background-color: #c6e2ff;
border: 3px solid #f9fafc;
border-radius: 50%;
};
}
&.today {
color: #459eff;
}
&.disabled {
color: #ccc;
}
&.selected {
color: #fff;
background-color: #459eff;
border-radius: 50%;
border: 3px solid #ebf4ff;
}
}
}
}
}
}
</style>