vue实现日历(仿钉钉考勤日历)

简易版效果图:

数据格式为:

js 复制代码
[ 
// time:日期 
// forenoon:上班打卡,1:正常 2:存在异常 3:提交过申请
// afternoon:上班打卡,1:正常 2:存在异常 3:提交过申请
{ 
time:'2025-04-21',
forenoon:1,
afternoon:1,
}...]

颜色校验格式为:

计算当前月的展示日期数据为:

js 复制代码
initCalendarData() {
				const firstDayOfMonth = new Date(this.currentYear, this.currentMonth - 1, 1).getDay();
				const lastDateOfMonth = new Date(this.currentYear, this.currentMonth, 0).getDate();
				const lastDateOfPrevMonth = new Date(this.currentYear, this.currentMonth - 1, 0).getDate();
				// 计算上个月剩余天数(补齐到当前月的第一天之前的空白)
				this.prevMonthDays = Array.from({
					// 如果 firstDayOfMonth 是 0(星期日),需要填充 6 天(从上个月最后一天往前数 6 天)
					// 否则填充 firstDayOfMonth - 1 天
					length: firstDayOfMonth === 0 ? 6 : firstDayOfMonth - 1
				}, (_, i) => {
					const date = lastDateOfPrevMonth - i;
					return {
						date,
						isCurrentMonth: false,
					};
				}).reverse();
				// 当前月天数
				this.currentMonthDays = Array.from({
						length: lastDateOfMonth
					},
					(_, i) => {
						const date = i + 1;
						return {
							date,
							isCurrentMonth: true,
							isFirstDayOfMonth: i + 1 === 1,
							isToday: i + 1 === this.currentDate &&
								this.currentMonth === new Date().getMonth() + 1 &&
								this.currentYear === new Date().getFullYear(),
							forenoon: Math.floor(Math.random() * 4) || 1,
							afternoon: Math.floor(Math.random() * 4) || 1,
						};
					}
				);

				// 计算下个月前几天(补齐到当前月的最后一天之后的空白)
				const lastDayOfMonth = new Date(this.currentYear, this.currentMonth - 1, lastDateOfMonth).getDay();
				const nextMonthDaysCount = 7 - lastDayOfMonth; // 下个月需要填充的天数
				this.nextMonthDays = Array.from({
						length: nextMonthDaysCount === 7 ? 0 : nextMonthDaysCount
					}, // 如果是整周则不需要填充
					(_, i) => {
						const date = i + 1; // 下个月从 1 开始
						return {
							date,
							isCurrentMonth: false,
						};
					}
				);
				if (!this.selectedDate) {
					this.handleDateClick(this.currentMonthDays[0]);
				}
			}

源代码:

js 复制代码
<template>
	<div class="calendar">
		<!-- 顶部星期标题 -->
		<div class="week-header">
			<span v-for="(day, index) in weekDays" :key="index">{{ day }}</span>
		</div>

		<!-- 日期网格 -->
		<div class="date-grid">
			<div v-for="(day, index) in calendarDays" :key="index" class="date-cell" :class="{
					'selected-first-day': day.isFirstDayOfMonth && day.isCurrentMonth,
					'selected-today': day.isToday && day.isCurrentMonth,
					'selected-date': selectedDate && day.date === selectedDate && day.isCurrentMonth,
				}" @click="handleDateClick(day)">
				<!-- 日期数字 -->
				<span :style="{ color: getDateColor(day) }">{{ day.date || '' }}</span>

				<!-- 小圆点 -->
				<div class="dots" v-if="day.isCurrentMonth">
					<span v-for="n in getDotCount(day.forenoon, day.afternoon)" :key="n" class="dot"
						:style="{ backgroundColor: getDotColor(day.forenoon, day.afternoon, n) }"></span>
				</div>
			</div>
		</div>

		<!-- 切换月份按钮 -->
		<div class="month-switch">
			<button @click="prevMonth">上一月</button>
			<span>{{ currentYear }}年{{ currentMonth }}月</span>
			<button @click="nextMonth">下一月</button>
		</div>
	</div>
</template>

<script>
	export default {
		data() {
			return {
				weekDays: ["一", "二", "三", "四", "五", "六", "日"],
				currentYear: new Date().getFullYear(),
				currentMonth: new Date().getMonth() + 1,
				currentDate: new Date().getDate(),
				currentMonthDays: [],
				prevMonthDays: [],
				nextMonthDays: [],
				selectedDate: null, // 默认选中的日期
			};
		},
		computed: {
			calendarDays() {
				return [...this.prevMonthDays, ...this.currentMonthDays, ...this.nextMonthDays];
			},
		},
		methods: {
			initCalendarData() {
				const firstDayOfMonth = new Date(this.currentYear, this.currentMonth - 1, 1).getDay();
				const lastDateOfMonth = new Date(this.currentYear, this.currentMonth, 0).getDate();
				const lastDateOfPrevMonth = new Date(this.currentYear, this.currentMonth - 1, 0).getDate();
				// 计算上个月剩余天数(补齐到当前月的第一天之前的空白)
				this.prevMonthDays = Array.from({
					// 如果 firstDayOfMonth 是 0(星期日),需要填充 6 天(从上个月最后一天往前数 6 天)
					// 否则填充 firstDayOfMonth - 1 天
					length: firstDayOfMonth === 0 ? 6 : firstDayOfMonth - 1
				}, (_, i) => {
					const date = lastDateOfPrevMonth - i;
					return {
						date,
						isCurrentMonth: false,
					};
				}).reverse();
				// 当前月天数
				this.currentMonthDays = Array.from({
						length: lastDateOfMonth
					},
					(_, i) => {
						const date = i + 1;
						return {
							date,
							isCurrentMonth: true,
							isFirstDayOfMonth: i + 1 === 1,
							isToday: i + 1 === this.currentDate &&
								this.currentMonth === new Date().getMonth() + 1 &&
								this.currentYear === new Date().getFullYear(),
							forenoon: Math.floor(Math.random() * 4) || 1,
							afternoon: Math.floor(Math.random() * 4) || 1,
						};
					}
				);

				// 计算下个月前几天(补齐到当前月的最后一天之后的空白)
				const lastDayOfMonth = new Date(this.currentYear, this.currentMonth - 1, lastDateOfMonth).getDay();
				const nextMonthDaysCount = 7 - lastDayOfMonth; // 下个月需要填充的天数
				this.nextMonthDays = Array.from({
						length: nextMonthDaysCount === 7 ? 0 : nextMonthDaysCount
					}, // 如果是整周则不需要填充
					(_, i) => {
						const date = i + 1; // 下个月从 1 开始
						return {
							date,
							isCurrentMonth: false,
						};
					}
				);
				if (!this.selectedDate) {
					this.handleDateClick(this.currentMonthDays[0]);
				}
			},
			getDotCount(forenoon, afternoon) {
				if (forenoon === 1 && afternoon === 1) return 1;
				if (forenoon === 2 && afternoon === 1) return 2;
				if (forenoon === 3 && afternoon === 1) return 2;
				return 0;
			},
			getDotColor(forenoon, afternoon, index) {
				const rules = [{
						forenoon: 1,
						afternoon: 1,
						colors: ["#B5C7FF"]
					},
					{
						forenoon: 2,
						afternoon: 1,
						colors: ["#FFB9B0", "#B5C7FF"]
					},
					{
						forenoon: 3,
						afternoon: 1,
						colors: ["#E37318", "#B5C7FF"]
					},
				];

				for (const rule of rules) {
					if (rule.forenoon === forenoon && rule.afternoon === afternoon) {
						return rule.colors[index - 1] || "gray";
					}
				}
				return "gray";
			},
			getDateColor(day) {
				if (!day.isCurrentMonth) return "#CCCCCC";
				if (day.isToday) return "#0052D9";
				if (this.selectedDate === day.date) return "#FFFFFF";
				return "#000000";
			},
			prevMonth() {
				this.currentMonth = this.currentMonth === 1 ? 12 : this.currentMonth - 1;
				this.currentYear = this.currentMonth === 12 ? this.currentYear - 1 : this.currentYear;
				this.initCalendarData();
			},
			nextMonth() {
				this.currentMonth = this.currentMonth === 12 ? 1 : this.currentMonth + 1;
				this.currentYear = this.currentMonth === 1 ? this.currentYear + 1 : this.currentYear;
				this.initCalendarData();
			},
			handleDateClick(day) {
				if (!day.isCurrentMonth) return;
				this.selectedDate = day.date;
			},
		},
		mounted() {
			this.initCalendarData();
		},
	};
</script>

<style>
	.calendar {
		display: flex;
		flex-direction: column;
	}

	.week-header {
		display: flex;
		justify-content: space-around;
	}

	.date-grid {
		display: grid;
		grid-template-columns: repeat(7, 1fr);
	}

	.date-cell {
		text-align: center;
		padding: 4rpx 20rpx;
		cursor: pointer;
		margin-top: 26rpx;
	}

	.date-cell span {
		display: flex;
		border-radius: 20rpx;
		padding: 0px 4rpx;
		justify-content: center;
		align-items: center;
		gap: 10px;
		align-self: stretch;
		font-size: 28rpx;
		font-family: "PingFang SC";
		font-style: normal;
		font-weight: 400;
	}

	.date-cell.selected-date span {
		background-color: #0052D9;
		color: #FFFFFF;
	}

	.date-cell.selected-today span {
		background-color: #F2F3FF;
	}

	.dots {
		margin-top: 5px;
		display: flex;
		gap: 5px;
		justify-content: center;
	}

	.dot {
		width: 12rpx;
		height: 12rpx;
		flex-shrink: 0;
		padding: 0 !important;
		border-radius: 50%;
	}

	.month-switch {
		text-align: center;
		margin: 10px 0;
	}
</style>

使用的ai:文心一言

操作步骤: 1:截图效果图,让它生成1.0版本代码

2:代码跑起来后找到跟效果图不一样的地方,让它再次修改(需要告知它关联上下文,不然它会重新生成)

3:重复2操作,直到页面展示没有问题

4:将代码全部复制,让它对代码进行修改添加想要的逻辑

5:修改最终的逻辑,ai生成的逻辑不一定是正确的

js 复制代码
		// 计算上个月剩余天数(补齐到当前月的第一天之前的空白)
		this.prevMonthDays = Array.from({ length: firstDayOfMonth === 0 ? 6 : firstDayOfMonth - 1 }, (_, i) => {
			// 如果 firstDayOfMonth 是 0(星期日),需要填充 6 天(从上个月最后一天往前数 6 天)
			// 否则填充 firstDayOfMonth - 1 天
			const date = lastDateOfPrevMonth - (firstDayOfMonth === 0 ? i + 1 : (firstDayOfMonth - 1 - i));
			return {
				date,
				isCurrentMonth: false,
			};
		}).reverse(); // 倒序排列,因为是从上个月最后一天往前数的

其中date的计算在当前月(4月)的1号位置有问题,上个月的最后天数有问题,最后只能自己修改

相关推荐
梦想CAD控件5 分钟前
(AI帮忙网页cad二次开发)MxCAD多行文本扩展
前端·javascript·vue.js
zayyo7 分钟前
企业级:多版本代码管理
前端
阿彪最稳健了9 分钟前
整理一些好用的TS写法
前端·typescript
周星星日记15 分钟前
13.vue3中异步组件defineAsyncComponent实现原理
前端·vue.js·面试
anyup15 分钟前
uni-app APP 高效热更新全攻略
前端·前端框架·uni-app
心走19 分钟前
WebRTC系列 WebGL 绘制YUV 画面
前端·音视频开发
方方洛20 分钟前
组件是怎样写的(1):虚拟列表-VirtualList
前端·vue.js·react.js
VillanelleS29 分钟前
前端工程化之自动化部署
运维·前端·自动化
stoneSkySpace37 分钟前
算法—冒泡排序—js(教学示例、小数据)
java·javascript·算法
moyu8438 分钟前
高效开发必备:手把手整合IconFont、 Vant与Element Plus
前端·javascript