功能:

1.日历可以周视图、月视图切换;

2.点击月视图中日期可以切换到对应周视图;

3.点击周视图查看当日对应数据;

4.周、月视图状态下,点击前后按钮,分别切换对应上下的周、月;

5.点击回到今天,立即切回到周、月视图下对应的当日;

引用dayjs处理日期,结合el-calendar完美实现。

要注意的是,日历显示周的话,传的日期范围要按照计算所在星期,比如我们的需求是周日为每周起始日,那么就要给周日的日期和周六日期为起始日,月视图我不想再去计算日期范围了,就直接用了:value,注意用的不是v-model而是value,因为value是单向的,v-model是双向数据绑定了。

// 此处省略组件、接口引入

import dayjs from 'dayjs';

var weekplugin = require('dayjs/plugin/weekday');

dayjs.extend(weekplugin)

// 此处省略,直接放核心代码

data(){

return{

activePlan:'tabApplyEndPlan',

monthYear:'周', // 周、月视图切换,默认显示周

monthDate:'', // 传后端参数 YYYY-MM,查视图上需要显示点的日期

dayDate:'', // 传后端参数 YYYY-MM-DD,查视图下面对应的当日数据列表

dateRange:[], // 周日历,传入周日历视图日期范围

dateList:[], // 存放月数据,视图中需要显示点的日期

}

},

watch:{

// 比较简单,直接省略代码了,记录下逻辑

// 监听 monthDate、dayDate 值的改变,调用对应接口

},

methods:{

showPrev() {

// 上个月

if (this.monthYear === '月') {

this.monthDate = dayjs(this.monthDate).add(-1, 'month').format('YYYY-MM');

// 需要判断当前选中日期是否属于当前月份

let _dayDate = dayjs(this.dayDate).format('YYYY-MM');

if (_dayDate === this.monthDate) {

// 计算本周第一天

let day1 = dayjs(this.dayDate).startOf('week').format('YYYY-MM-DD');

// 计算本周最后一天

let day2 = dayjs(this.dayDate).endOf('week').format('YYYY-MM-DD');

this.dateRange = [day1, day2];

} else {

let day1 = dayjs(this.monthDate).startOf('month').startOf('week').format('YYYY-MM-DD');

let day2 = dayjs(this.monthDate).startOf('month').endOf('week').format('YYYY-MM-DD');

this.dateRange = [day1, day2]

}

}

// 上星期

if (this.monthYear === '周') {

// 获取当前周视图

let day1 = dayjs(this.dateRange[0]).add(-1, 'week').startOf('week').format('YYYY-MM-DD');

let day2 = dayjs(this.dateRange[1]).add(-1, 'week').endOf('week').format('YYYY-MM-DD');

this.monthDate = dayjs(this.dateRange[0]).add(-1, 'week').startOf('week').format('YYYY-MM');

this.dateRange = [day1, day2]

}

},

showNext() {

// 下个月

if (this.monthYear === '月') {

this.monthDate = dayjs(this.monthDate).add(1, 'month').format('YYYY-MM');

// 需要判断当前选中日期是否属于当前月份

let _dayDate = dayjs(this.dayDate).format('YYYY-MM');

if (_dayDate === this.monthDate) {

// 计算本周第一天

let day1 = dayjs(this.dayDate).startOf('week').format('YYYY-MM-DD');

// 计算本周最后一天

let day2 = dayjs(this.dayDate).endOf('week').format('YYYY-MM-DD');

this.dateRange = [day1, day2];

} else {

let day1 = dayjs(this.monthDate).endOf('month').startOf('week').format('YYYY-MM-DD');

let day2 = dayjs(this.monthDate).endOf('month').endOf('week').format('YYYY-MM-DD');

this.dateRange = [day1, day2]

}

}

// 下星期

if (this.monthYear === '周') {

// 获取当前周视图

let day1 = dayjs(this.dateRange[0]).add(1, 'week').startOf('week').format('YYYY-MM-DD');

let day2 = dayjs(this.dateRange[1]).add(1, 'week').endOf('week').format('YYYY-MM-DD');

this.monthDate = dayjs(this.dateRange[0]).add(1, 'week').startOf('week').format('YYYY-MM');

this.dateRange = [day1, day2]

}

},

// 返回今日

nowCalendar() {

this.monthDate = dayjs(new Date()).format('YYYY-MM');

this.dayDate = dayjs(new Date()).format('YYYY-MM-DD');

let day1 = dayjs(new Date()).startOf('week').format('YYYY-MM-DD');

let day2 = dayjs(new Date()).endOf('week').format('YYYY-MM-DD');

this.dateRange = [day1, day2];

this.activePlan = 'tabApplyEndPlan'

},

// 周、月视图日期被点击处理方法

getPlanList(date) {

// console.log(this.monthYear)

// console.log(date)

this.dayDate = date.day;

// 点击上、下月/周日期,不涉及视图的切换

if (this.monthYear === '月') {

if (date.type === 'next-month') {

this.showNext()

}

if (date.type === 'prev-month') {

this.showPrev()

}

}

if (this.monthYear === '周') {

let _month = dayjs(date.day).format('YYYY-MM');

if (date.type === 'next-month') {

if (_month !== this.monthDate) {

this.monthDate = dayjs(date.day).format('YYYY-MM');

}

}

if (date.type === 'prev-month') {

if (_month !== this.monthDate) {

this.monthDate = dayjs(date.day).format('YYYY-MM');

}

}

}

if (date.type === 'current-month') {

this.monthYear = '周';

// 计算本周第一天

let day1 = dayjs(this.dayDate).startOf('week').format('YYYY-MM-DD');

// 计算本周最后一天

let day2 = dayjs(this.dayDate).endOf('week').format('YYYY-MM-DD');

// 计算点击日期所在周第一天所属月

this.monthDate = dayjs(day1).startOf('week').format('YYYY-MM');

this.dateRange = [day1, day2];

}

},

}

自定义日历样式(没有用日历原先的头,全部自己重写的,还不错):

::v-deep .kalendar {

&-header {

text-align: center;

margin: 10px 16px 0 16px;

.current-monuth {

font-size: 16px;

letter-spacing: 0;

font-weight: 500;

margin-left: 15%;

color: #262626;

font-family: PingFangSC-Medium;

i {

cursor: pointer;

}

}

.el-radio-group {

float: right;

}

.el-radio-button__orig-radio:checked + .el-radio-button__inner {

background: #ffffff;

box-shadow: -1px 0 0 0 transparent;

border: 1px solid rgba(199, 0, 11, 1);

font-family: PingFangSC-Medium;

font-size: 12px;

color: #c7000b;

letter-spacing: -0.04px;

font-weight: 500;

}

.el-radio-button__inner:hover {

color: #c7000b;

}

}

.calender-dot-box {

width: 100%;

bottom: -8px;

position: absolute;

span {

width: 6px;

height: 6px;

margin-right: 3px;

border-radius: 50%;

display: inline-block;

&:last-of-type {

margin-right: 0;

}

}

.endPlan {

background-color: #d61212;

}

.applyEndPlan {

background-color: #ffd100;

}

}

.el-calendar {

&__body {

padding: 10px 16px;

}

.is-today {

.el-calendar-day {

.calender-date {

width: 34px;

height: 34px;

margin: 0 auto;

color: #ff534f;

border-radius: 10px;

background: #fff;

box-shadow: none;

}

}

}

&__header {

display: none;

}

.current {

.el-calendar-day {

color: #262626;

}

}

.prev,

.next {

color: #bfbfbf;

}

&-day {

padding: 0;

font-weight: 700;

font-size: 16px;

letter-spacing: 0;

text-align: center;

position: relative;

transition: color 0.3s;

font-family: DINAlternate-Bold;

}

&-table {

th {

font-family: PingFangSC-Regular;

font-size: 16px;

color: #262626;

letter-spacing: 0;

text-align: center;

line-height: 34px;

font-weight: 400;

padding: 0;

&:last-of-type,

&:first-of-type {

color: #ff564e;

}

}

td {

border: none;

&.is-selected {

background-color: transparent;

}

}

.el-calendar-day {

height: 34px;

line-height: 34px;

&:hover {

background-color: transparent;

}

.calendar-isSelected {

width: 34px;

height: 34px;

margin: 0 auto;

color: #fff;

border-radius: 10px;

background: #ff534f;

box-shadow: 0px 0px 2px 0px rgba(238, 88, 64, 1);

}

}

}

}

再看看子组件里面,先看月的:

周日历组件:

其实应该把日历组件二次封装一下,就不用单独再去写周、月日历子组件了,有空了可以试试。

不过不得不再吐槽一句,elementui的日历组件,给提供的API真心的少,功能很简单。。。

最终效果图:

月视图效果图:

月视图下,有时候会出现整整一行的灰色日期,看起来不是很美观,那么就需要操作dom,通过js判断,操作dom来处理。大概思路就是,先通过 document.querySelectorAll('.el-calendar-table__row') 获取到所有.el-calendar-table__row的元素节点lists,然后循环遍历这些节点,若其子元素class中含有.current,那么就说明是带有当月的日期,则不改变样式,若不含,则说明这整行都是前\后月的日期,那么就可以把该.el-calendar-table__row的css里面加上属性display:none。

周视图效果图: