Vue简单日历实现
Vue简单日历实现
Vue简单日历实现
先看一下实现的效果:
说明
- 布局使用了flex布局,里面日期元素排布,会随着容器的大小自动缩放。
- 鼠标在日期上的悬浮和移出(点击没有实现,可自行添加),会触发父组件的对应方法(enter、leave),会将鼠标事件和日期信息传过去。
- 切换年份或月份,会触发父组件的changeMonth方法,会将选择的月份的开始和结束时间传过去。
- 考虑到可能要对每个日期上,自定义一些显示效果,在日历中每个日期,都对应有一个插槽,插槽名格式:年-月-日,例如:2021-5-21、2021-10-5。日期的div设置了相对定位,插值中的元素,可使用绝对定位,设置位置。
实现代码
父调用组件
父组件上,可设置初始的时间参数:
- date,日历初始化的时间(Date类型)
事件有:
- enter(event, dateInfo), dateInfo.date 为悬浮的日期对象(Date类型)
- eave(event, dateInfo)
- changeMonth(startDate, endDate),startDate, endDate 为选择的月份的开始和结束时间(Date类型)
插槽名格式:年-月-日,例如:2021-5-21、2021-10-5
<template><div class="home"><Calendar:date="new Date()"@enter="mouseenter"@leave="mouseleave"@changeMonth="changeMonth"style="width:400px;height:400px"><!-- 可使用动态插槽名设置,v-slot:[slotName] --><template v-slot:2021-5-21><div style="position:absolute;bottom:-20px">今天</div></template></Calendar></div>
</template><script>
import Calendar from '@/components/Calendar.vue'export default {name: 'Home',components: {Calendar},methods: {mouseenter (event, dateInfo) {console.log("悬浮进入", event, dateInfo)},mouseleave (event, dateInfo) {console.log("悬浮离开", event, dateInfo)},changeMonth (start, end) {console.log("开始:", start)console.log("结束:", end)}}
}
</script>
日历组件
使用了flex布局,上面的点击按钮,用svg直接写的,不依赖与其他图片或者icon。
<template><div class="cal_con"><div class="cal_header"><div class="cal_h_left"><divclass="cal_h_btn"@click="preYear"><svg class="cal_h_l_icon"><polylinepoints="6,0 2,4 6,8"style="fill:none;stroke:#909399;stroke-width:1"/><polylinepoints="10,0 6,4 10,8"style="fill:none;stroke:#909399;stroke-width:1"/></svg></div><divclass="cal_h_btn"@click="preMonth"><svg class="cal_h_l_icon"><polylinepoints="6,0 2,4 6,8"style="fill:none;stroke:#909399;stroke-width:1"/></svg></div></div><div><span class="cal_h_time">{{ year }} 年 </span><span class="cal_h_time">{{ month }} 月</span></div><div class="cal_h_left"><divclass="cal_h_btn"@click="nextMonth"><svg class="cal_h_l_icon"><polylinepoints="2,0 8,4 2,8"style="fill:none;stroke:#909399;stroke-width:1"/></svg></div><divclass="cal_h_btn"@click="nextYear"><svg class="cal_h_l_icon"><polylinepoints="2,0 8,4 2,8"style="fill:none;stroke:#909399;stroke-width:1"/><polylinepoints="6,0 12,4 6,8"style="fill:none;stroke:#909399;stroke-width:1"/></svg></div></div></div><div class="cal_month"><div class="cal_m_weeks"><spanv-for="w in weeks":key="w"class="cal_m_day_cell">{{w}}</span></div><div class="cal_m_days"><divv-for="(ds, index) in monthData":key="index"class="cal_m_day_line"><divv-for="d in ds":key="d.day"class="cal_m_day_cell":style="{color: getCellColor(d)}"@mouseenter="mouseenter(d, $event)"@mouseleave="mouseleave(d, $event)">{{ d.day }}<slot :name="d.fullYear + '-' + d.month + '-' + d.day"></slot></div></div></div></div></div>
</template><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()}},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.nowlet 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.nowlet 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.nowlet 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.nowlet 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 = datethis.dateChange()},generateMonth (date) {date.setDate(1)// 星期 0 - 6, 星期天 - 星期6let weekStart = date.getDay()let endDate = new Date(date.getFullYear(), date.getMonth() + 1, 0)let dayEnd = endDate.getDate()// 星期 0 - 6, 星期天 - 星期6let weeEnd = endDate.getDay()let milsStart = date.getTime()let dayMils = 24 * 60 * 60 * 1000let milsEnd = endDate.getTime() + dayMilslet 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) {if (d.fullYear == this.currentYear && d.month == this.currentMonth && d.day == this.currentDay) {return "#409eff"}let color = d.type == -1 ? '#c0c4cc' : (d.type == 1 ? '#c0c4cc ' : '')return color;},mouseenter (d, event) {this.$emit("enter", event, d)},mouseleave (d, event) {this.$emit("leave", event, 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><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 {height: 24px;padding: 5px;font-size: 14px;line-height: 24px;display: flex;justify-content: space-between;justify-items: center;.cal_h_time {cursor: pointer;}.cal_h_time:hover {color: #409eff;}.cal_h_left {height: 100%;display: flex;.cal_h_btn {height: 100%;width: 24px;cursor: pointer;}.cal_h_btn:hover {background-color: #ebeef5;}.cal_h_l_icon {height: 8px;width: 12px;margin: auto;}}}.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;}.cal_m_day_cell:hover {color: #409eff;}.cal_m_weeks {height: 16px;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>