需求背景:
最近做项目遇到一个需求,需要制作一个定制化的日历组件,但市面上的组件或者插件要样式不太美观,要功能不全,要自定义不支持;
需求要求:
可选择年月切换,选中年时展示12个月份;选中月时展示当前选中月份日历;
默认选中当前年、月、日;
可对年、月进行修改,左减右加;
在点击某月或者某日时监听事件,进行其他操作;
按照颜色实现日期区分:当前月份排班信息正常颜色,今天显示深色,其他月份显示浅色。
实现效果:图一
<template>
<div class="CalendarMonth">
<!-- 每年月份具体内容 -->
<div class="year">
<van-icon name="arrow-left" @click="reduceYear()" />
<span>{{time.year}}年</span>
<van-icon name="arrow" @click="addYear()" />
</div>
<div class="monthList">
<div :class="time.month == index ? ['currentMonth', 'month'] : 'month'" v-for="(item, index) in monthList" :key="index">
<div class="content" @click="clickMonth(index)">
<div class="contentMonth">{{ item }}月</div>
<div class="contentDes">描述内容</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
},
data() {
return {
monthList: ['一', '二', '三', '四', '五', '六','七','八','九','十','十一','十二'],
time: {
//获取完整的年份(4位)
year: new Date().getFullYear(),
//获取当前月份(0-11,0代表1月)
month: new Date().getMonth()
}
}
},
created() {
},
methods: {
// 获取
reduceYear () {
this.time.year = this.time.year - 1
this.$emit('changeYear', this.time)
},
addYear () {
this.time.year = this.time.year + 1
this.$emit('changeYear', this.time)
},
clickMonth (index) {
console.log('选择' + index + '月')
}
},
computed: {
}
}
</script>
<style lang="less" scoped>
.CalendarMonth {
.year {
font-weight: 700;
font-size: 18px;
}
.monthList {
display: flex;
flex-wrap: wrap;
align-items: center;
.month {
display: flex;
align-items: center;
width: 50px;
height: 50px;
margin: 6px;
text-align: center;
background-color: rgb(220, 245, 253);
}
.currentMonth {
background-color: rgb(169, 225, 243);
}
}
}
</style>
效果如图二:
<template>
<div class="date-page">
<div class="date-header">
<div class="year">
<van-icon name="arrow-left" @click="preMonthClick()" />
<span>{{year}} 年 {{month}} 月</span>
<van-icon name="arrow" @click="nextMonthClick()" />
</div>
</div>
<div class="date-content">
<div class="weekList">
<div class="week" v-for="item in weekList" :key="item">{{ item }}</div>
</div>
<div class="dataList">
<div
:class="isActive.data == showDay.data && isActive.year == showDay.year && isActive.month == showDay.month? 'dateActive data': 'data'"
v-for="(showDay, index) in showData"
:key="index"
@click="detailClick(showDay,index)"
>
<div :class="item.thisMonth == 'thisMonthNot' ? 'unShowContent' : ''" @click="clickDay(item.day)">
<div class="contentDay">{{ showDay.data }}</div>
<div class="contentDes">描述内容描述内容</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
year: "",
month: "",
today: "",
showData: [],
isActive: {},
show: false,
weekList: ['日','一', '二', '三', '四', '五', '六']
};
},
created() {
this.getToday()
},
methods: {
// 获取当前日期
getToday() {
let date = new Date();
this.year = date.getFullYear();
this.month = date.getMonth() + 1;
this.today = date.getDate();//获取这是周几
//初始化数据
this.getMonthDays(this.year, this.month - 1);
//得到当月今天的位置index
this.isActive = {
data : this.today,
year: this.year,
month: this.month
}
},
// 上一月
preMonthClick() {
this.show = true;
if (this.month === 1) {
this.month = 12;
this.year = this.year - 1;
} else {
this.month = this.month - 1;
}
this.getMonthDays(this.year, this.month - 1);
},
// 下一月
nextMonthClick() {
let date = new Date();
let year = date.getFullYear();
let month = date.getMonth() + 1;
if (this.year == year) {
this.month = this.month + 1;
if (this.month < month) {
this.show = true;
} else {
this.show = false;
this.getMonthDays(this.year, this.month - 1);
}
} else if (this.year < year) {
this.show = true;
if (this.month === 12) {
this.month = 1;
this.year = this.year + 1;
} else {
this.month = this.month + 1;
}
}
this.getMonthDays(this.year, this.month - 1);
},
// 获取日期数组
getMonthDays(year, month) {
// 定义每个月的天数,如果是闰年第二月改为29天
let daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
if ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0) {
daysInMonth[1] = 29;
}
// 当月第一天为周几
let targetDay = new Date(year, month, 1).getDay();
let calendarDateList = [];
let preNum = targetDay;
let nextNum = 0;
if (targetDay > 0) {
// 当前月份1号前的自然周剩余日期,置空
for (let i = 0; i < preNum; i++) {
calendarDateList.push({data: "", year: this.year, month: this.month});
}
}
for (let i = 0; i < daysInMonth[month]; i++) {
// 正常显示的日期
calendarDateList.push({data: i + 1, year: this.year, month: this.month})
}
// 当前月份最后一天的自然周剩余日期,置空
nextNum = 6 - new Date(year, month + 1, 0).getDay();
for (let i = 0; i < nextNum; i++) {
calendarDateList.push({data: "", year: this.year, month: this.month});
}
this.showData = calendarDateList;
},
// 当前点击日期
detailClick (showData, index) {
this.isActive = {...showData}
}
}
}
</script>
<style lang="less" scoped>
.year {
font-weight: 700;
font-size: 18px;
}
.weekList {
display: flex;
align-items: center;
.week {
width: 14.285%;
background-color: rgb(242, 242, 242);
padding: 10px 0;
margin: 5px;
text-align: center;
}
}
.dataList {
display: flex;
flex-wrap: wrap;
.date {
width: 14.285%;
text-align: center;
padding: 5px;
.contentDes {
padding: 10px 0;
background-color: rgba(220, 245, 253, 0.3);
}
}
.dateActive {
background-color: rgb(169, 225, 243);
}
}
</style>
<template>
<div>
<!-- 操作栏 -->
<div class="typeBox">
<div v-for="(item, index) in typeList" :key="index" :class="currentType == index ? ['currentType','type'] : 'type'" @click="clickType(index)">
{{item.text}}
</div>
</div>
<div class="dataBox" v-if='currentType'>
<!-- 月日历栏 -->
<calendarData></calendarData>
</div>
<div class="monthBox" v-else>
<!-- 年月份栏 -->
<calendarMonth @changeYear="changeYear"></calendarMonth>
</div>
</div>
</template>
<script>
// 日历组件
import CalendarData from '@/components/CalendarData/index.vue'
// 月份组件
import CalendarMonth from '@/components/CalendarMonth/index.vue'
export default {
components: {
calendarData: CalendarData,
calendarMonth: CalendarMonth
},
data() {
return {
typeList: [
{text: '年'},
{text: '月'}
],
currentType: 0,
}
},
created() {},
methods: {
// 修改年
changeYear (value) {
console.log('value', value)
},
clickType (index) {
this.currentType = index
}
}
}
</script>
<style lang="less" scoped>
.typeBox {
display: flex;
align-items: center;
margin: 15px 0;
.type {
width: 40px;
height: 30px;
line-height: 30px;
font-size: 16px;
margin-right: 5px;
border-radius: 5px;
text-align: center;
background-color: rgb(220, 245, 253);
}
.currentType {
background-color: rgb(169, 225, 243);
}
}
.dataBox, .monthBox{
margin-top: 20px;
}
</style>