vue 实现日历组件
组件代码
<template>
<div class="air-calendar-box">
<table class="sn-calendar-table">
<thead>
<th class="fc-day-header">日</th>
<th class="fc-day-header">一</th>
<th class="fc-day-header">二</th>
<th class="fc-day-header">三</th>
<th class="fc-day-header">四</th>
<th class="fc-day-header">五</th>
<th class="fc-day-header">六</th>
</thead>
<tbody>
<tr>
<td
v-for="(item, idx) in calendarData"
:key="idx"
:style="{'height': height}"
:class="{
'include': item.include,
'active': activeData && item.date === activeData.date
}"
@click="clickDay(item)">
<slot name="day" :row="item">
<div class="day">
{{ item.day2 }}
</div>
</slot>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
export default {
name: 'AirCalendarBox',
props: {
type: {
// all 可选中显示的所有日期 portion 只能选中当前月份的数据
type: String,
default: 'portion'
},
// 默认值
defaultMonth: {
type: Date,
default () {
let year = new Date().getFullYear() // 当月年份
let month = new Date().getMonth() + 1 // 当月月份
return new Date(year, month - 1, 1)
}
},
height: {
type: String,
default: '52px'
}
},
watch: {
defaultMonth () {
this.setDefaultValue()
this.getCalendarDay()
}
},
data () {
return {
calendarData: [],
activeData: null
}
},
created () {
this.getCalendarDay()
},
methods: {
// 获取日历天数
getCalendarDay () {
let year = new Date(this.defaultMonth).getFullYear() // 当月年份
let month = new Date(this.defaultMonth).getMonth() + 1 // 当月月份
let days = new Date(year, month, 0).getDate() // 当月天数
let prevYear = month === 1 ? (year - 1) : year // 上一年年份
let prevMonth = month === 1 ? 12 : (month - 1) // 上月月份
let prevDays = new Date(prevYear, prevMonth, 0).getDate() // 上一月天数
let lastYear = month === 12 ? (year + 1) : year // 下一年年份
let lastMonth = month === 12 ? 1 : (month + 1) // 下月月份
// let lastDays = new Date(lastYear, lastMonth, 0).getDate() // 下一月天数
let prevMonthArr = [] // 上月数据
let nextMonthArr = [] // 下月数据
let currentMonthArr = [] // 当月数据
// 判断当月1号是星期几
let firstDay = new Date(year, month - 1, 1).getDay()
// 获取显示上月的数据
for (let i = 0; i < firstDay; i++) {
let day = prevDays - firstDay + (i + 1)
let day2 = day >= 10 ? day : ('0' + day)
let month2 = prevMonth >= 10 ? prevMonth : ('0' + prevMonth)
prevMonthArr.push({
year: prevYear,
month: prevMonth,
month2: month2,
day: day,
day2: day2,
date: `${prevYear}-${prevMonth}-${day}`,
date2: `${prevYear}-${month2}-${day2}`,
include: false
})
}
// 获取显示的下一月的数据
for (let i = 0; i < (42 - firstDay - days); i++) {
let day = i + 1
let day2 = day >= 10 ? day : ('0' + day)
let month2 = lastMonth >= 10 ? lastMonth : ('0' + lastMonth)
nextMonthArr.push({
year: lastYear,
month: lastMonth,
month2: month2,
day: day,
day2: day2,
date: `${lastYear}-${lastMonth}-${day}`,
date2: `${lastYear}-${month2}-${day2}`,
include: false
})
}
// 获取当月显示数据
for (let i = 1; i <= days; i++) {
let day2 = i >= 10 ? i : ('0' + i)
let month2 = month >= 10 ? month : ('0' + month)
currentMonthArr.push({
year: year,
month: month,
month2: month2,
day: i,
day2: day2,
date: `${year}-${month}-${i}`,
date2: `${year}-${month2}-${day2}`,
include: true
})
}
this.calendarData = [...prevMonthArr, ...currentMonthArr, ...nextMonthArr]
},
// 设置默认值
setDefaultValue (date) {
if (!date) {
this.activeData = null
return
}
let year = new Date(date).getFullYear() // 当月年份
let month = new Date(date).getMonth() + 1 // 当月月份
let month2 = month >= 10 ? month : ('0' + month)
let day = new Date(date).getDate()
let day2 = day >= 10 ? day : '0' + day
let row = {
year: year,
month: month,
month2: month2,
day: day,
day2: day2,
date: `${year}-${month}-${day}`,
date2: `${year}-${month2}-${day2}`,
include: true
}
this.clickDay(row)
},
clickDay (row) {
if (this.type === 'portion' && !row.include) {
this.$message({
type: 'info',
message: '只能选择当前月份的日期'
})
return
}
this.activeData = { ...row }
this.$emit('click', { ...row })
}
}
}
</script>
<style lang="less" scoped>
.air-calendar-box {
.sn-calendar-table {
width: 100%;
border-radius: 4px;
display: block;
overflow: hidden;
thead,
tbody {
display: flex;
width: 100%;
}
tr {
width: 100%;
}
th {
background:#D9F5F5;
height: 32px;
line-height: 32px;
text-align: center;
color: #3E597B;
box-sizing: border-box;
display: inline-block;
width: 14.28%;
}
td {
width: 14.28%;
display: inline-block;
position: relative;
font-size: 16px;
font-weight: 500;
box-sizing: border-box;
text-align: center;
cursor: pointer;
border: solid 2px transparent;
color: rgba(62, 89, 123, 0.3);
&.include {
color: #3E597B;
}
&.active {
border: 2px solid #00BBBB;
}
.day {
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
&:nth-child(1),
&:nth-child(7),
&:nth-child(8),
&:nth-child(14),
&:nth-child(15),
&:nth-child(21),
&:nth-child(22),
&:nth-child(28),
&:nth-child(29),
&:nth-child(35),
&:nth-child(36),
&:nth-child(42) {
background: #EBFAFA;
}
}
tbody {
.clickDay {
border: solid 2px #00BBBB;
}
}
}
}
</style>
// 页面效果