自己封装的目的:

使用场景:打卡,日期计划,日期选择,特别日期标志

根据自己的需求可以定制化何样式

不依赖任何第三方插件或者组件

效果图:

1、日历组件封装

<template>
	<div class="calendar">
		<div class="year-month">
			<div class="arrow up-page" @click="upPage"></div>
			<span>{{year}}年{{month}}月</span>
			<div class="arrow next-page"  @click="nextPage"></div>
		</div>
		<div class="week">
			<div class="item" v-for="item of weeks">{{item}}</div>
		</div>
		<template v-for="line of dates">
			<div class="week">
				<div v-for="item of line" @click="onClick(item)" :class="['item' ,
				{'current-month':month==item.month},
				{'today':today==`${item.month}-${item.date}`},
				{'clickFlag':clickFlag===item.timeStamp},
				{'select':selectDates.find(t=> t>item.timeStamp&&t<item.timeStamp+oneDayTimeStamp)}]">{{item.date}}</div>
			</div>
		</template>
	</div>
</template>

<script setup>
	import { watchEffect, ref, onBeforeMount } from "vue"
	const today = `${new Date().getMonth()+1}-${new Date().getDate()}`
	const weeks = ["日", '一', '二', '三', '四', '五', '六']
	const oneDayTimeStamp = 86400 * 1000
	const dates = ref([])
	const month = ref("")
	const year = ref("")
	const clickFlag = ref("")
	const emit = defineEmits(["onSelect"])
	const props = defineProps({
		defaultDate: {
			type: [String, Number],
			default: Date.now()
		},
		selectDates: {
			type: Array,
			default () {
				const now = Date.now();
				const oneDayTimeStamp = 86400 * 1000
				return [now - oneDayTimeStamp, now, now + oneDayTimeStamp, now + oneDayTimeStamp * 3]
			}
		},
	})
	onBeforeMount(() => {
		let firstTimestamp = getCurrentPageTimestampOf1(props.defaultDate)
		getList(firstTimestamp)
	})

	function upPage() {
		let firstTimestamp = getFirstTimestampByUp()
		getList(firstTimestamp)
	}

	function nextPage() {
		let firstTimestamp = getFirstTimestampByNext()
		getList(firstTimestamp)
	}

	// 获取当前页日期列表
	function getList(timestamp) {
		const temp = []
		const list = []
		// 包含一个足月的最小周期为42天
		const pageT = 42
		for (let i = 0; i < pageT; i++) {
			temp.push(coverToDate(timestamp, i))
		}

		while (temp.length > 0) {
			list.push(temp.splice(0, 7))
		}
		removeLine(list)
		month.value = list[1][1].month
		year.value = list[1][1].year
		dates.value = list
	}

	// 获取上一页的第一个元素的时间戳
	function getFirstTimestampByUp() {
		const firstObj = dates.value[0][0]
		let timestamp = 0
		//  第一行第一个元素是1号,返回firstObj前一天的时间戳 ,
		//  否则直接返回firstObj的时间戳
		if (firstObj.date === 1) {
			timestamp = firstObj.timeStamp - oneDayTimeStamp
		} else {
			timestamp = firstObj.timeStamp
		}

		return getCurrentPageTimestampOf1(timestamp)
	}

	// 获取下一页的第一个元素的时间戳
	function getFirstTimestampByNext() {
		let len = dates.value.length
		const lastLine = dates.value[len - 1]
		// 最后一行包含1号,直接返回第一个元素的时间戳,
		// 否则返回最后一个元素的时间戳 + 一天的时间戳
		const includeDateOf1 = lastLine.find(item => item.date === 1)
		if (includeDateOf1) {
			return lastLine[0].timeStamp
		} else {
			return lastLine[6].timeStamp + oneDayTimeStamp
		}
	}

	// 删除掉不是属于当前月的行
	function removeLine(list) {
		if (list.length > 5) {
			let index = list.slice(1).findIndex(line => line.some(item => item.date == 1))
			if (index != -1) {
				const startIndex = index + 1
				// 最后一行的第一天
				const lastLineOf1 = list[startIndex][0].date
				if (lastLineOf1 === 1) {
					list.splice(startIndex)
				} else {
					list.splice(startIndex + 1)
				}
			}
		}
	}

	// 获取当页的第一个日期的时间戳
	function getCurrentPageTimestampOf1(timestamp) {
		const time = new Date(timestamp)
		const year = time.getFullYear()
		const month = time.getMonth() + 1
		// 当月的第一天
		const dateOf1 = new Date(`${year}/${month}/1`)
		// 当月的第一天,是星期几
		const weekOf1 = dateOf1.getDay()
		// 当页的第一天时间戳
		const firstTimestampOfPage = dateOf1.getTime() - weekOf1 * oneDayTimeStamp
		return firstTimestampOfPage
	}

	// 时间戳转成日期对象
	function coverToDate(firstTimestampOfPage, index) {
		const timeStamp = firstTimestampOfPage + index * oneDayTimeStamp
		let obj = new Date(timeStamp)
		let date = obj.getDate()
		const week = obj.getDay()
		const month = obj.getMonth() + 1
		const year = obj.getFullYear()
		return { year, month, date, week, timeStamp }
	}

	function onClick(e) {
		clickFlag.value = e.timeStamp
		emit("onSelect", e)
	}
</script>

<style lang="scss" scoped>
	.calendar {
		height: 642rpx;
		margin: 20rpx;
		display: flex;
		flex-direction: column;
		justify-content: space-around;
		border-radius: 10rpx;
		background-color: #FFF;
		
		.year-month{
			height: 88rpx;
			display: flex;
			justify-content: space-between;
			align-items: center;
			border-bottom: 2rpx solid #d7d7d7;
			.arrow{
				height: 30rpx;
				width: 30rpx;
				border: 2rpx solid #666;
				border-bottom: none;
				border-right: none;
			}
			.up-page{
				margin-left: 20rpx;
				transform: rotate(-45deg);
			}
			.next-page{
				margin-right: 20rpx;
				transform: rotate(135deg);
			}
		}

		.week {
			height: 70rpx;
			display: flex;
			color: #CCCCCC;
			font-size: 28rpx;
			justify-content: space-around;
			align-items: center;

			.item {
				position: relative;
				width: 70rpx;
				height: 70rpx;
				line-height: 70rpx;
				text-align: center;
				box-sizing: border-box;

				&.current-month {
					color: #666;
				}

				&.today::before {
					position: absolute;
					bottom: 2rpx;
					left: 29rpx;
					content: "";
					display: block;
					width: 12rpx;
					height: 12rpx;
					border-radius: 50%;
					background: #6C93FF;
				}

				&.clickFlag {
					color: #6C93FF ;
					font-weight: 600;
					border: 2rpx solid #6C93FF;
					border-radius: 50%;
				}

				&.select {
					color: #FFF;
					background: #6C93FF;
				}
			}
		}
	}
</style>

2、使用日历组件

<template>
	<div>
		<Calendar @onSelect="onSelect"></Calendar>
		<div>日历选择:{{selectDate}}</div>
	</div>
</template>

<script setup>
	import { ref } from 'vue'
	import Calendar from "@/components/calendar.vue"
	const selectDate = ref("")

	function onSelect(e) {
		selectDate.value = e		
	}
</script>

<style>
</style>
GitHub 加速计划 / vu / vue
207.52 K
33.66 K
下载
vuejs/vue: 是一个用于构建用户界面的 JavaScript 框架,具有简洁的语法和丰富的组件库,可以用于开发单页面应用程序和多页面应用程序。
最近提交(Master分支:1 个月前 )
73486cb5 * chore: fix link broken Signed-off-by: snoppy <michaleli@foxmail.com> * Update packages/template-compiler/README.md [skip ci] --------- Signed-off-by: snoppy <michaleli@foxmail.com> Co-authored-by: Eduardo San Martin Morote <posva@users.noreply.github.com> 3 个月前
e428d891 Updated Browser Compatibility reference. The previous currently returns HTTP 404. 3 个月前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐