
子组件
<template>
<div class="cal_con">
<div class="cal_header">
<div class="cal_h_left">
<!-- 上一年按钮 -->
<div class="cal_h_btn" @click="preYear">
<svg-icon icon-class="el-icon-d-arrow-left" class-name="cal_h_l_icon"></svg-icon>
</div>
<!-- 上个月按钮 -->
<div class="cal_h_btn" @click="preMonth">
<svg-icon icon-class="el-icon-arrow-left" class-name="cal_h_l_icon"></svg-icon>
</div>
</div>
<div>
<span class="cal_h_time"><span class="cal_h_time_text">{{ year }}</span> 年 </span>
<span class="cal_h_time"><span class="cal_h_time_text">{{ month }}</span> 月</span>
</div>
<div class="cal_h_left">
<!-- 下个月按钮 -->
<div class="cal_h_btn" @click="nextMonth">
<svg-icon icon-class="el-icon-arrow-right" class-name="cal_h_l_icon"></svg-icon>
</div>
<!-- 下一年按钮 -->
<div class="cal_h_btn" @click="nextYear">
<svg-icon icon-class="el-icon-d-arrow-right" class-name="cal_h_l_icon"></svg-icon>
</div>
</div>
</div>
<div class="cal_month">
<!-- 星期 -->
<div class="cal_m_weeks">
<span class="cal_m_day_cell" v-for="(wItem, wIndex) in weeks" :key="wIndex">{{ wItem }}</span>
</div>
<div class="cal_m_days">
<div class="cal_m_day_line" v-for="(ds, index) in monthData" :key="index">
<div
v-for="d in ds"
:key="d.day"
:class="['cal_m_day_cell',{
'current_date': d.fullYear == currentYear && d.month == currentMonth && d.day == currentDay,
'active_cell': selectObj.fullYear == d.fullYear && selectObj.month == d.month && selectObj.day == d.day
}]"
:style="getCellColor(d)"
@mouseenter="mouseenter(d, $event)"
@mouseleave="mouseleave(d, $event)"
@click="handleSelected(d)"
>
{{ d.day }}
<!-- <slot :name="d.fullYear + '-' + d.month + '-' + d.day"></slot> -->
</div>
</div>
</div>
</div>
</div>
</template>
js
<script>
export default {
name: 'Calendar',
props: {
date: {
type: Date,
default: () => new Date()
}
},
data () {
return {
now: this.date,
year: 0,
month: 0,
weeks: ["日", "一", "二", "三", "四", "五", "六"],
monthData: [],
currentYear: new Date().getFullYear(), // 当前年
currentMonth: new Date().getMonth() + 1, // 当前月
currentDay: new Date().getDate(), // 当前日
// 选中的数据
selectObj: {
fullYear: new Date().getFullYear(),
month: new Date().getMonth() + 1,
day: new Date().getDate()
},
}
},
computed: {
},
mounted () {
this.setYearMonth(this.now)
this.generateMonth(this.now)
},
methods: {
// 设置年月
setYearMonth (now) {
this.year = now.getFullYear()
this.month = now.getMonth() + 1
},
// 上一年按钮
preYear () {
let n = this.now
let date = new Date(n.getFullYear() - 1, n.getMonth(), n.getDate(), n.getHours(), n.getMinutes(), n.getSeconds(), n.getMilliseconds());
this.setYearMonthInfos(date)
},
// 上个月按钮
preMonth () {
let n = this.now
let date = new Date(n.getFullYear(), n.getMonth() - 1, n.getDate(), n.getHours(), n.getMinutes(), n.getSeconds(), n.getMilliseconds());
this.setYearMonthInfos(date)
},
// 下一年按钮
nextYear () {
let n = this.now
let date = new Date(n.getFullYear() + 1, n.getMonth(), n.getDate(), n.getHours(), n.getMinutes(), n.getSeconds(), n.getMilliseconds());
this.setYearMonthInfos(date)
},
// 下个月按钮
nextMonth () {
let n = this.now
let date = new Date(n.getFullYear(), n.getMonth() + 1, n.getDate(), n.getHours(), n.getMinutes(), n.getSeconds(), n.getMilliseconds());
this.setYearMonthInfos(date)
},
// 设置年月的详细信息
setYearMonthInfos (date) {
this.setYearMonth(date)
this.generateMonth(date)
this.now = date
this.dateChange()
},
// 生成月份
generateMonth (date) {
date.setDate(1)
// 星期 0 - 6, 星期天 - 星期6
let weekStart = date.getDay()
let endDate = new Date(date.getFullYear(), date.getMonth() + 1, 0)
let dayEnd = endDate.getDate()
// 星期 0 - 6, 星期天 - 星期6
let weeEnd = endDate.getDay()
let milsStart = date.getTime()
let dayMils = 24 * 60 * 60 * 1000
let milsEnd = endDate.getTime() + dayMils
let monthDatas = []
let current;
// 上个月的几天
for (let i = 0; i < weekStart; i++) {
current = new Date(milsStart - (weekStart - i) * dayMils)
monthDatas.push({
type: -1,
date: current,
fullYear: current.getFullYear(),
month: current.getMonth() + 1,
day: current.getDate()
})
}
// 当前月
for (let i = 0; i < dayEnd; i++) {
current = new Date(milsStart + i * dayMils)
monthDatas.push({
type: 0,
date: current,
fullYear: current.getFullYear(),
month: current.getMonth() + 1,
day: current.getDate()
})
}
// 下个月的几天
for (let i = 0; i < (6 - weeEnd); i++) {
current = new Date(milsEnd + i * dayMils)
monthDatas.push({
type: 1,
date: current,
fullYear: current.getFullYear(),
month: current.getMonth() + 1,
day: current.getDate()
})
}
this.monthData = []
for (let i = 0; i < monthDatas.length; i++) {
let mi = i % 7;
if (mi == 0) {
this.monthData.push([])
}
this.monthData[Math.floor(i / 7)].push(monthDatas[i])
}
// 少于6行,补足6行
if (this.monthData.length <= 5) {
milsStart = current.getTime()
let lastLine = []
for (let i = 1; i <= 7; i++) {
current = new Date(milsStart + i * dayMils)
lastLine.push({
type: 1,
date: current,
fullYear: current.getFullYear(),
month: current.getMonth() + 1,
day: current.getDate()
})
}
this.monthData.push(lastLine)
}
},
// 当前月份中不存在的日期
getCellColor (d) {
let color = d.type == -1 ? 'color: #c0c4cc;' : (d.type == 1 ? 'color: #c0c4cc;' : '')
return color;
},
// 鼠标移入事件
mouseenter (d, event) {
this.$emit("enter", event, d)
},
// 鼠标移出事件
mouseleave (d, event) {
this.$emit("leave", event, d)
},
// 选中的日期
handleSelected(d){
if(d.month < this.selectObj.month){
this.preMonth()
}else if(d.month > this.selectObj.month){
this.nextMonth()
}
this.selectObj = d
this.$emit("selected", d)
},
// 数据改变
dateChange () {
let fullYear = this.now.getFullYear()
let month = this.now.getMonth()
let startDay = new Date(fullYear, month, 1)
let endDay = new Date(fullYear, month + 1, 0, 23, 59, 59)
this.$emit("changeMonth", startDay, endDay)
}
}
}
</script>
css
<style scoped lang="scss">
.cal_con {
width: 100%;
height: 100%;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
color: #606266;
// border: 1px solid #e4e7ed;
// box-shadow: 0 2px 12px 0 #0000006e;
background: #fff;
border-radius: 4px;
margin: auto;
.cal_header {
margin: 12px;
text-align: center;
font-size: 16px;
display: flex;
justify-content: space-between;
justify-items: center;
.cal_h_time {
cursor: pointer;
.cal_h_time_text{
color: #02C2B0;
}
}
.cal_h_left {
height: 100%;
display: flex;
.cal_h_btn {
height: 100%;
width: 24px;
cursor: pointer;
}
.cal_h_l_icon {
/* height: 8px;
width: 12px;*/
height: 24px;
width: 24px;
margin: auto;
&:hover{
color: #02C2B0;
}
}
}
}
.cal_month {
font-size: 12px;
text-align: center;
height: calc(100% - 34px);
.cal_m_day_cell {
width: 24px;
height: 24px;
line-height: 24px;
cursor: pointer;
position: relative;
&:hover {
color: #02C2B0;
border: 1px solid #409eff;
}
}
.active_cell{
color: #02C2B0;
border: 1px solid #02C2B0;
}
.current_date{
color: #02C2B0;
}
.cal_m_weeks {
padding: 8px;
display: flex;
justify-content: space-around;
justify-items: center;
border-bottom: 1px solid #e4e7ed;
}
.cal_m_days {
height: calc(100% - 49px);
padding: 8px;
display: flex;
justify-content: space-around;
justify-items: center;
flex-wrap: wrap;
.cal_m_day_line {
width: 100%;
display: flex;
justify-content: space-around;
justify-items: center;
}
}
}
}
</style>
父组件
<template>
<div class="calendar-box">
<Calendar :date="new Date()" @selected="selected"/>
</div>
</template>
js
<script>
import Calendar from './components/calendar.vue'
export default{
components: {
Calendar
},
data(){
return{
}
},
methods: {
selected(dateInfo){
console.log('选中的日期', dateInfo)
},
}
}
</script>
css
<style lang="scss" scoped>
.calendar-box{
width: 300px;
height: 300px;
}
</style>