vue 写一个日历组件

移动端组件,预期效果如图
在这里插入图片描述
#############################################
思路: 日历分两部分,上半部功能区和下半部展示区。
功能区展示年月,可以左右切换年月。
下半部展示日历,点击即为选中。默认日期有底色,选中日期为另一底色,默认为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">&lt;&lt;</span>
			<span class="arr prevMonth" @touchstart="prevMonth">&lt;</span>
			<span class="title">{{title}}</span>
			<span class="arr nextMonth" @touchstart="nextMonth">&gt;</span>
			<span class="arr nextYear" @touchstart="nextYear">&gt;&gt;</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>
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值