如何自定义日历
日历的组件
第一步:创建一个MyCalendar.vue页面
页面代码
<template>
<section class='m-calendar' :style="dateStyle">
<!-- 切换月份 -->
<header class='changeMonth'>
<div class ="Month">
<span class='prepMonth' @click="changeMonth(-1)"></span>
<h1>{{year}}年{{month}}月</h1>
<span class='nextMonth' @click="changeMonth(1)"></span>
</div>
</header>
<ul class='dates'>
<!-- 星期 -->
<li class='weeks' v-for="item in weeks" :key="item">{{item}}</li>
<!-- 日期 -->
<li
class='day'
v-for="(item, i) in dates" :key="i"
:class="{
isPrep: item.isPrep,
isNext: item.isNext,
hidden: (item.isNext || item.isPrep) && !showPrepNext,
isToday: item.date == today,
isSelected: item.date == selectedDate,
isMarked: markDates.includes(item.date),
isEndMarked:markendDates.includes(item.date)
}"
@click="clickDate(item)"
>{{ item.day}}</li>
</ul>
</section>
</template>
js代码
const D = new Date()
const ThisYear = D.getFullYear()
const ThisMonth = D.getMonth() + 1
const today = new Date().toLocaleDateString()
export default {
// props都为非必传
props: {
// 初始年月
startYearMonth: {
type: String,
default() {
return `${ThisYear}/${ThisMonth}`
}
},
// 需要标记的日期数组,第一组
startDates: {
type: Array,
default() {
return []
}
},
// 需要标记的日期数组,第二组
endDates: {
type: Array,
default() {
return []
}
},
// 选中的日期
checkedDate: {
type: String,
default() {
return ''
}
},
// 是否星期一开始,默认星期日开始
mondayStart: {
type: Boolean,
default() {
return true //如果希望从星期日开始改成false
}
},
// 是否显示上个月和下个月日期
showPrepNext: {
type: Boolean,
default() {
return true
}
},
// 日期字体颜色
fontColor: {
type: String,
default() {
return '#000'
}
},
// 标记点颜色
markColor: {
type: String,
default() {
return '#fff'
}
},
// 选中的日期字体颜色
activeColor: {
type: String,
default() {
return '#fff'
}
},
// 选中的日期背景颜色
activeBgColor: {
type: String,
default() {
return '#4B4C61'
}
}
},
data() {
return {
// 当前年
year: ThisYear,
// 当前月
month: ThisMonth,
// 今天
today,
// 日期数组
dates: [],
// 选中的日期
selectedDate: ''
}
},
computed: {
// 标记的日期
markDates() {
// 获得不带0的日期,如:2021/1/1 => 2021/1/1
return this.startDates.map(item => this.formatDate(item))
},
markendDates() {
// 获得不带0的日期,如:2021/1/1 => 2021/1/1
return this.endDates.map(item => this.formatDate(item))
},
// 星期
weeks() {
if (this.mondayStart) {
return ['一', '二', '三', '四', '五', '六', '日']
} else {
return ['日', '一', '二', '三', '四', '五', '六']
}
},
dateStyle() {
return {
'--font-color': this.fontColor,
'--mark-color': this.markColor,
'--active-color': this.activeColor,
'--active-bg-color': this.activeBgColor
}
}
},
created() {
this.year = new Date(this.startYearMonth).getFullYear()
this.month = new Date(this.startYearMonth).getMonth() + 1
// 选中的日期
if (this.checkedDate) {
// 获得不带0的日期,如:2021-1-1 => 2021/1/1
this.selectedDate = this.formatDate(this.checkedDate)
}
// 初始化日历
this.initCalendar()
},
methods: {
// 初始化日历
initCalendar() {
// ⚠️注意:new Date中的month要减1才是真正的本月月数,即本月:this.month - 1,下个月:this.month
// ⚠️注意:下个月的第0天即为本月的最后一天
// 上个月总天数(本月第0天日期)
const prepMonthDays = new Date(this.year, this.month - 1, 0).getDate()
// 上个月最后一天星期几(本月第0天星期数)
const prepMonthEndDayWeek = new Date(this.year, this.month - 1, 0).getDay()
// 当前月总天数(下个月第0天日期)
const thisMonthDays = new Date(this.year, this.month, 0).getDate()
// 当前月第一天是星期几
const firstDayWeek = new Date(this.year, this.month - 1, 1).getDay()
// 当前月最后一天是星期几(下个月第0天星期数)
const thisEndDayWeek = new Date(this.year, this.month, 0).getDay()
var dates = []
// 需要计算的总天数
var totalDays = firstDayWeek + thisMonthDays
// 从星期一开始index为1,从星期天开始index为0
var index = this.mondayStart ? 1 : 0
// 星期一开始且本月最后一天是星期天之后,需补满最后一行
if (this.mondayStart && thisEndDayWeek > 0) {
totalDays += 7 - thisEndDayWeek
} else if (!this.mondayStart && thisEndDayWeek < 6) {
// 星期天开始且本月最后一天是星期天之前,需补满最后一行
totalDays += 6 - thisEndDayWeek
}
for (index; index < totalDays; index++) {
// 上个月月底
if (index < firstDayWeek) {
// 上个月天数 - 上个月最后一天星期数 + 下标 (如:31 - 5 + 0)
const day = prepMonthDays - prepMonthEndDayWeek + index
const date = new Date(this.year, this.month - 2, day).toLocaleDateString()
dates.push({ isPrep: true, day, date })
} else if (index >= firstDayWeek + thisMonthDays) {
// 下个月月初
// 下标 - 当前月总天数 - 当前月第一天星期数 + 1 (如:30 - 31 + 1 + 1)
const day = index - thisMonthDays - firstDayWeek + 1
const date = new Date(this.year, this.month, day).toLocaleDateString()
dates.push({ isNext: true, day, date })
} else {
// 本月
// 下标 - 当前月第一天星期数 + 1 (如:5 - 5 + 1)
const day = index - firstDayWeek + 1
const date = new Date(this.year, this.month - 1, day).toLocaleDateString()
dates.push({ day, date })
}
}
this.dates = [...dates]
},
// 点击日期
clickDate({ date, isPrep, isNext }) {
if (isPrep || isNext) return
this.selectedDate = date
this.$emit('clickDate', date.replace(/\//g, '-'))
},
// 切换月份
changeMonth(month) {
this.month += month
if (this.month === 0) {
this.month = 12
this.year--
} else if (this.month === 13) {
this.month = 1
this.year++
}
this.initCalendar()
this.$emit('changeMonth', `${this.year}-${this.month}`)
},
// 格式化日期
formatDate(date) {
// 获得不带0,且分隔符为/的日期,如:2020-01-01 => 2021/1/1
return new Date(date).toLocaleDateString()
}
}
}
css代码(可以根据自己的需求进行调整)
$fontColor: var(--font-color);
$markColor: var(--mark-color);
$activeColor: var(--active-color);
$activeBgColor: var(--active-bg-color);
.m-calendar{
max-width: 400px;
max-height: 450px;
border: 1px solid #ECEEF1 ;
border-radius: 8px 8px 0 0;
padding-bottom: 20px;
header{
display: flex;
align-items: center;
justify-content: center;
border-bottom: 1px solid #ECEEF1 ;
padding: 3px 0;
margin-bottom: 5px;
p{
padding-left:10px;
font-size:14px;
}
.Month{
padding-left:15px;
display:flex;
align-items: center;
justify-content:center;
h1{
// margin: 0 20px;
color: #4B4C61;
font-size: 17px;
font-weight: bold;
width: 120px;
text-align: center;
}
span{
cursor: pointer;
padding: 4px 10px;
&::after{
display: inline-block;
content: '';
width: 10px;
height: 10px;
border-top: 2px solid $fontColor;
}
&.prepMonth::after{
border-left: 2px solid $fontColor;
transform: rotate(-45deg);
}
&.nextMonth::after{
border-right: 2px solid $fontColor;
transform: rotate(45deg);
}
&:hover::after{
border-color: $markColor;
}
}
}
}
ul{
display: flex;
flex-wrap: wrap;
margin: 0 auto;
padding: 0 12px;
li{
width: 40px;
height: 30px;
// margin: 4px 5px;
margin: 4px calc((100% / 7 - 42px) / 2);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
font-weight: bold;
position: relative;
transition: all ease .25s;
border-radius: 6px;
// 标记
&::after{
bottom: 0;
left: 50%;
transform: translateX(-50%);
position: absolute;
display: inline-block;
content: '';
width: 5px;
height: 5px;
border-radius: 50%;
}
// 星期
&.weeks{
font-size: 16px;
color: #4B4C61;
margin-bottom: 12px;
}
&.day{
cursor: pointer;
font-size: 14px;
color: $fontColor;
// 今天
&.isToday{
color: #187eff;
// background-color:$activeBgColor;
}
// 第一组的标记颜色
&.isMarked::after{
transition: all ease .25s;
background: #187EFF;
}
// 第二组的标记颜色
&.isEndMarked::after{
transition: all ease .25s;
background: #EA2727;
}
// 选中、hover
&:hover, &.isSelected{
background: $activeBgColor;
color: $activeColor;
&:after{
display: none;
}
}
// 上个月、下个月
&.isNext, &.isPrep{
cursor: default;
opacity: .3;
&:hover{
color: $fontColor;
opacity: .3;
background: transparent;
}
}
// hidden
&.hidden{
opacity: 0;
&:hover{
opacity: 0;
}
}
}
}
}
}
自定义的日历就完成了,接下来就是在父组件中进行调用
第二步 在父组件中调用
页面代码
<MyCalendar
showPrepNext
:startDates="startDates"
:endDates="endDates"
:checkedDate='checkedDate'
@clickDate="clickDate"
@changeMonth="changeMonth"
/>
js代码
import MyCalendar from './components/MyCalendar.vue' //引入你刚创建好的页面,注意文件夹的位置
export default {
components: {
MyCalendar //注册组件
},
data(){
return{
// 第一组标记时间
startDates: [ "2023-10-26", "2023-10-09", "2023-10-16", "2023-09-01","2023-10-01", "2023-10-20", "2023-10-25"],
// 第二组标记时间
endDates: [ "2024-02-23", "2023-10-28", "2023-10-30", "2023-10-09", "2023-12-31", "2023-11-03", "2023-10-21","2023-10-23", "2023-11-01", "2023-10-31", "2023-11-30"],
// 选择的某一天
checkedDate: '',
}
},
methods: {
// 点击日期
clickDate(date) {
console.log(date)
},
// 切换月份
changeMonth(date) {
// console.log(date)
},
}
最后的效果图