移动端组件,预期效果如图
#############################################
思路: 日历分两部分,上半部功能区和下半部展示区。
功能区展示年月,可以左右切换年月。
下半部展示日历,点击即为选中。默认日期有底色,选中日期为另一底色,默认为6行7列
第一种,计算每个月第一天和最后一天
第二种:时间戳
知识点:
// 获取某月最后一天
var date= new Date(year, month, 0)
初版:
<template>
<div class="calendar">
<div class="box">
<!-- 头部 -->
<div class="cHeader">
<div class="toolBar">
<div class="arr yearPrev" @click="yearPrev"><<</div>
<div class="arr monPrev" @click="monPrev"><</div>
<div class="subTitle">{{yearAndMon}}</div>
<div class="arr monNext" @click="monNext">></div>
<div class="arr yearNext" @click="yearNext">>></div>
</div>
<ul class="weekends">
<li class="weekend ">日</li>
<li class="weekend">一</li>
<li class="weekend">二</li>
<li class="weekend">三</li>
<li class="weekend">四</li>
<li class="weekend">五</li>
<li class="weekend">六</li>
</ul>
</div>
<!-- 主体 -->
<div class="">
<ul class="cBody">
<li
class="item"
v-for="(item,index) in list"
:key="index"
:class="{'selected':item.fullDate===selectedDate.fullDate, 'current':item.tag==='current'}"
@click="selectDate(item)"
>
{{item.date}}
</li>
</ul>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'calendar',
data() {
return {
list: [],
// 选中日期
selectedDate: {
year: '',
month: '',
date: ''
},
showDate: {
year: '',
month: '',
date: ''
}
}
},
computed: {
yearAndMon: function() {
return this.showDate.year + '年' + this.showDate.month + '月'
}
},
created() {
// 获取当前日期
var date = new Date()
this.selectedDate = this.showDate = this.date2Obj(date)
// console.log(this.selectedDate, this.showDate)
this.list = this.getMonthList(this.selectedDate)
},
methods: {
// 日期对象转普通对象
date2Obj(date) {
// console.log(date.toLocaleString())
let temp = {}
temp.year = date.getFullYear()
temp.month = date.getMonth() + 1
temp.date = date.getDate()
temp.day = date.getDay()
temp.fullDate = temp.year + '-' + temp.month + '-' + temp.date
return temp
},
/*
生成日历
每一页有6行, 共42个数字
*/
getMonthList(date) {
// console.log(date)
// 获取本月第一天
var firstDate = new Date(date.year, date.month-1)
// console.log('获取本月第一天', firstDate.toLocaleString())
// 获取本月第一天是周几
var startDay = firstDate.getDay()
// console.log('获取本月第一天是周几', startDay)
// 获取本月最后一天 (获取本月天数)
var lastDate = new Date(date.year, date.month, 0).getDate()
// 判断当天是否在页面中
var tag = false
if(this.selectedDate.year == date.year && this.selectedDate.month == date.month) {
tag = true
}
// console.log(lastDate.toLocaleString())
var dateList = Array.from(new Array(lastDate),(item,index)=>{
let temp = {}
temp.year = date.year
temp.month = date.month
temp.date = index + 1
temp.fullDate = temp.year + '-' + temp.month + '-' + temp.date
temp.tag = 'current'
// if(tag && this.selectedDate.date == index + 1) {
// temp.tag = 'selected'
// } else {
// temp.tag = 'current'
// }
return temp
})
// console.log(dateList)
if (startDay>0) {
// 需要上个月日期补全开头
// 获取上个月最后一天
var PMLastDate = new Date(date.year, date.month-1, 0).getDate()
// console.log('上个月最后一天',PMLastDate)
for(let i = 0; i < startDay; i++) {
let temp = {}
temp.year = date.month > 1 ? date.year : date.year -1
temp.month = date.month > 1 ? date.month - 1 : 12
temp.date = PMLastDate
temp.fullDate = temp.year + '-' + temp.month + '-' + temp.date
temp.tag = 'prev'
dateList.unshift(temp)
PMLastDate--
}
// 下个月需要补的数
var NMDate = 42 - startDay - lastDate
for(let j = 1; j <= NMDate; j++) {
let temp = {}
temp.year = date.month < 12 ? date.year : date.year + 1
temp.month = date.month <12 ? date.month + 1 : 1
temp.date = j
temp.fullDate = temp.year + '-' + temp.month + '-' + temp.date
temp.tag = 'next'
dateList.push(temp)
}
} else {
// 获取上个月最后一天
var PMLastDate = new Date(date.year, date.month-1, 0).getDate()
console.log('上个月最后一天',PMLastDate)
for(let i = 0; i < 7; i++) {
let temp = {}
temp.year = date.month > 1 ? date.year : date.year -1
temp.month = date.month > 1 ? date.month - 1 : 12
temp.date = PMLastDate
temp.fullDate = temp.year + '-' + temp.month + '-' + temp.date
temp.tag = 'prev'
dateList.unshift(temp)
PMLastDate--
}
var NMDate = 42 - lastDate - 7
for(let j = 1; j <= NMDate; j++) {
let temp = {}
temp.year = date.year
temp.month = date.month <12 ? date.month + 1 : 1
temp.date = j
temp.fullDate = temp.year + '-' + temp.month + '-' + temp.date
temp.tag = 'next'
dateList.push(temp)
}
}
// console.log(dateList)
return dateList
},
selectDate(item) {
// console.log(item)
if(item.tag === 'current') {
this.selectedDate = item
} else {
this.selectedDate = item
this.showDate = item
this.list = this.getMonthList(this.selectedDate)
}
},
// 上一年
yearPrev() {
var temp ={}
temp.year = this.showDate.year-1
temp.month = this.showDate.month
// console.log(temp)
this.showDate = temp
this.list = this.getMonthList(this.showDate)
},
// 上一月
monPrev() {
var temp ={}
if(this.showDate.month>1) {
temp.year = this.showDate.year
temp.month = this.showDate.month - 1
} else {
temp.year = this.showDate.year-1
temp.month = 12
}
// console.log(temp)
this.showDate = temp
this.list = this.getMonthList(this.showDate)
},
// 下一月
monNext() {
var temp ={}
if(this.showDate.month<12) {
temp.year = this.showDate.year
temp.month = this.showDate.month + 1
} else {
temp.year = this.showDate.year+1
temp.month = 1
}
// console.log(temp)
this.showDate = temp
this.list = this.getMonthList(this.showDate)
},
// 下一年
yearNext() {
var temp ={}
temp.year = this.showDate.year+1
temp.month = this.showDate.month
// console.log(temp)
this.showDate = temp
this.list = this.getMonthList(this.showDate)
}
}
}
</script>
<style>
.box {
width: 100%;
height: 100vh;
background: #fba;
}
/*日历头部样式*/
.cHeader {
text-align: center;
background: #bfa;
}
.toolBar {
display: flex;
width: 100%;
line-height: 30px;
padding: 0 20px;
background: #baf;
box-sizing: border-box;
}
.subTitle {
flex: 1 1 auto;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.arr {
flex: 0 0 30px;
color: #00f;
}
.weekends {
display: flex;
}
.weekend {
flex: 1 1 auto;
line-height: 30px;
}
/*日历主体样式*/
.cBody {
display: flex;
flex-wrap: wrap;
width: 100%;
padding: 0 1%;
overflow: hidden;
box-sizing: border-box;
}
.cBody .item {
flex: 1 1 14%;
height: 40px;
line-height: 40px;
color: #666;
text-align: center;
}
.cBody .item.current {
color: #333;
}
.cBody .item.selected {
color: #fff;
background: #f95;
border-radius: 4px;
}
</style>
二版
<template>
<!-- <Calendar
v-model="selectedDate"
:defaultDate="selectedDate"
@selectedDate="thisSelecteddate"
showBtn
> -->
<div class="calendar">
<div class="navHeader">
<span class="arr prevYear" @touchstart="prevYear"><<</span>
<span class="arr prevMonth" @touchstart="prevMonth"><</span>
<span class="title">{{title}}</span>
<span class="arr nextMonth" @touchstart="nextMonth">></span>
<span class="arr nextYear" @touchstart="nextYear">>></span>
</div>
<div class="calendarHeader">
<ul>
<li class="item" v-for="item in weekends" :key="item">{{item}}</li>
</ul>
</div>
<div class="calendarCont">
<ul class="calendarWrap">
<li
class="item"
:class="{current:item.current,selected: item.date.toLocaleString()===selected.toLocaleString()}"
@touchstart.stop="select(item.date)"
v-for="(item,index) in list"
:key="index"
>
{{(new Date(item.date)).getDate()}}
</li>
</ul>
</div>
<div class="btnWrap" v-if="showBtn">
<div class="calendarBtn reset">重置</div>
<div class="calendarBtn sure">确定</div>
</div>
</div>
</template>
<script>
export default {
props: {
defaultDate: {
default: ()=>{}
},
showBtn: {
type: Boolean,
default: false
}
},
model: {
prop: 'selectedDate',
event: 'selectedDateEvent'
},
data() {
return {
weekends: ['日','一','二','三','四','五','六'],
year: '',
month: '',
date: {}, // 今天
list: []
}
},
computed: {
title: function() {
return `${this.year} 年 ${this.month + 1} 月`
}
},
mounted() {
this.date = new Date();
var temp = this.isDate(this.defaultDate);
this.selected = temp?
new Date(new Date(this.defaultDate).getFullYear(),new Date(this.defaultDate).getMonth(),new Date(this.defaultDate).getDate())
:new Date(this.date.getFullYear(),this.date.getMonth(),this.date.getDate())
this.setDate(this.selected)
this.getList(this.selected)
console.log(`%c${this.selected.toLocaleString()}`,'color:purple')
},
methods: {
isDate(data) {
if(typeof data === 'object' && data instanceof Date) {
console.log(`%c对象格式`,'color:purple')
return true
}else if(typeof data === 'string') {
if(new Date(data).getFullYear()) {
console.log(`%c正确字符串形式`,'color:purple')
return true
} else {
console.log(`%c错误字符串形式`,'color:purple')
return false
}
}else if (typeof data === 'number') {
if(new Date(data).getFullYear()) {
console.log(`%c正确number形式`,'color:purple')
return true
} else {
console.log(`%c错误number形式`,'color:purple')
return false
}
}else {
console.log(`%c啥也不是`,'color:purple')
return false
}
},
select(data) {
this.selected = data
this.$emit('selectedDate',data)
if(new Date(data).getMonth != this.month) {
this.setDate(data)
this.getList(data)
}
},
reset(date) {
this.selected = new Date(this.date.getFullYear(),this.date.getMonth(),this.date.getDate())
this.$emit('selectedDate', this.selected)
this.setDate(this.date)
this.getList(this.date)
},
sure() {
this.$emit('selectedDate',this.selected)
},
prevYear() {
var temp = new Date(this.year - 1, this.month, 1)
this.setDate(temp)
this.getList(temp)
},
prevMonth() {
var temp = new Date(this.monthBegin - 24 * 60 * 60 * 1000)
this.setDate(temp)
this.getList(temp)
},
nextMonth() {
var last = new Date(this.year, this.month - (-1), 0)
var temp = new Date(last - (-24 * 60 * 60 * 1000))
this.setDate(temp)
this.getList(temp)
},
nextYear() {
var temp = new Date(this.year + 1, this.month, 1)
this.setDate(temp)
this.getList(temp)
},
setDate(date) {
this.year = date.getFullYear()
this.month = date.getMonth()
// 本月开始
this.monthBegin = new Date(this.year, this.month, 1)
},
getList(date) {
this.list = [];
var beginDate;
var monthBegin = new Date(this.year, this.month, 1)
var day = monthBegin.getDay()
if(day > 0) {
beginDate = new Date(monthBegin - day * 24 * 60 * 60 * 1000)
}else {
beginDate = date
}
for(let i = 0; i < 42; i++) {
var date = new Date(beginDate - (-1000 * 60 * 60 * 24 * i))
var current = (date.getMonth() === this.month)
this.list.push({
date,
current
})
}
// console.log(this.list)
}
}
}
</script>
<style>
.calendar {
width: 100%;
overflow: hidden;
background: #fff;
}
.navHeader {
display: flex;
height: 44px;
line-height: 44px;
padding: 0 10px;
}
.navHeader .arr {
flex: 0 0 20px;
width: 20px;
height: 20px;
color: #1677ff;
}
.navHeader .arr.prevMonth,
.navHeader .arr.nextMonth {
margin: 0 12px;
}
.navHeader .title {
flex: 1 1 auto;
text-align: center;
}
.calendarHeader {
width: 100%;
height: 50px;
}
.calendarHeader ul {
display: flex;
}
.calendarHeader .item {
flex: 1 1 auto;
line-height: 50px;
font-size: 20px;
color: #333;
text-align: center;
}
.calendarWrap {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.calendarWrap .item {
color: #ccc;
width: calc(100%/7);
height: 50px;
line-height: 50px;
text-align: center;
}
.calendarWrap .item.current {
color: #333;
}
.calendarWrap .item.selected {
color: #fff;
border-radius: 8px;
background: #00b28a;
}
.btnWrap {
display: flex;
align-items: center;
justify-content: space-around;
height: 120px;
}
.btnWrap .calendarBtn {
width: 260px;
height: 90px;
font-size: 32px;
line-height: 90px;
border-radius: 48px;
text-align: center;
}
.btnWrap .calendarBtn.reset {
color: #333;
}
.btnWrap .calendarBtn.sure {
color: #fff;
background: #00b28a;
}
</style>