| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234 | <template>	<view class="page adffc">		<div class="week adf">			<div class="week-pre adfacjc" v-for="(w, i) in weeks" :class="{ active: i === 0 || i === 6 }" :key="i">{{ w }}</div>		</div>		<div class="list">			<div class="list-item" v-for="(c,i) in calendarsCopy" :key="i">				<div class="title">{{c.monthTitle}}</div>				<div class="days">					<div class="days-pre" 					:class="{'active':d.selected&&d.isCurrentMonth,'in':d.inrange&&d.isCurrentMonth}" 					v-for="(d,di) in c.days" :key="di">						<div class="today" v-if="d.isToday">今天</div>						<div class="day adfacjc" :class="{'hui':d.isPast}">{{d.isCurrentMonth?d.day:''}}</div>						<div class="se" v-if="d.start&&d.isCurrentMonth">开始</div>						<div class="se" v-if="d.end&&d.isCurrentMonth">结束</div>					</div>				</div>			</div>		</div>	</view></template><script setup name="ActivityCalendar">import { ref, onMounted } from 'vue';const weeks = ['日', '一', '二', '三', '四', '五', '六'];const calendars = ref([]);const calendarsCopy = ref([]);const createCalendar = (months) => {	return new Promise((resolve,reject)=>{		const calendarList = [];		const today = new Date();		today.setHours(0, 0, 0, 0); // 标准化日期				for (let i = 0; i < months; i++) {			// --- 1. 计算目标月份 ---			const targetDate = new Date();			targetDate.setHours(0, 0, 0, 0);			targetDate.setMonth(targetDate.getMonth() + i, 1);					const year = targetDate.getFullYear();			const month = targetDate.getMonth(); // 0-11					const monthData = {				monthTitle: `${year}/${String(month + 1).padStart(2, '0')}`,				days: []			};					// --- 2. 计算日历网格的起始和结束日期 ---			let startDate;			const firstDayOfMonth = new Date(year, month, 1);			const lastDayOfMonth = new Date(year, month + 1, 0);					// **核心逻辑:根据是否为第一个月,决定网格的起始日期**			if (i === 0) {				// **第一个月(当前月):** 网格从今天所在周的周日开始				startDate = new Date(today);				startDate.setDate(today.getDate() - today.getDay());			} else {				// **后续月份:** 网格从该月第一天所在周的周日开始,保证月份的完整性				startDate = new Date(firstDayOfMonth);				startDate.setDate(firstDayOfMonth.getDate() - firstDayOfMonth.getDay());			}					// **网格的结束日期总是该月最后一天所在周的周六**			const endDate = new Date(lastDayOfMonth);			endDate.setDate(lastDayOfMonth.getDate() + (6 - lastDayOfMonth.getDay()));					// --- 3. 循环填充日历网格中的每一天 ---			for (let d = new Date(startDate); d <= endDate; d.setDate(d.getDate() + 1)) {				const currentYear = d.getFullYear();				const currentMonth = d.getMonth() + 1;				const currentDay = d.getDate();						monthData.days.push({					day: currentDay,					dateStr: `${currentYear}-${String(currentMonth).padStart(2, '0')}-${String(currentDay).padStart(2, '0')}`,					isToday: d.getTime() === today.getTime(),					// 标识是否属于当前正在生成的月份 (e.g., 在7月数据中,8月1日这项为 false)					isCurrentMonth: d.getMonth() === month,					// 标识是否为过去日期 (仅在第一个月生效)					isPast: i === 0 && d.getTime() < today.getTime()				});			}					calendarList.push(monthData);		}		resolve(calendarList)	})};const setStartEndDay = type => {//type 0:全部时间 1:一周内 2:一月内 3:本周末	let d = new Date();	let s = new Date().Format('yyyy-MM-dd');	let e = null;	switch(type){		case 0:			s = e = '';			break;		case 1:			e = new Date(d.setDate(d.getDate(s)+6)).Format('yyyy-MM-dd');			break;		case 2:			e = new Date(d.setDate(d.getDate(s)+30)).Format('yyyy-MM-dd');			break;		case 3:			let sw = new Date(s).getDay();			if(sw===0){//当天是周日				s = new Date(d.setDate(d.getDate(s)-1)).Format('yyyy-MM-dd');				e = new Date(s).Format('yyyy-MM-dd');			}else{				s = new Date(d.setDate(d.getDate(s)+(6-sw))).Format('yyyy-MM-dd');				e = new Date(d.setDate(d.getDate(s)+1)).Format('yyyy-MM-dd');			}			break;	}	calendarsCopy.value = JSON.parse(JSON.stringify(calendars.value));	if(!s||!e) return	calendarsCopy.value.forEach(c=>{		c?.days.forEach(d=>{			if(d.dateStr===s||d.dateStr===e){				d.selected = true;				d.start = d.dateStr===s;				d.end = d.dateStr===e;			}			if(Date.parse(new Date(d.dateStr))>Date.parse(new Date(s))&&Date.parse(new Date(d.dateStr))<Date.parse(new Date(e))){				d.inrange = true;			}		})	})}onMounted(async () => {	calendars.value = await createCalendar(2);	calendarsCopy.value = JSON.parse(JSON.stringify(calendars.value));});defineExpose({	setStartEndDay})</script><style scoped lang="scss">.page {	width: 100%;	height: 100%;	background: #f7f7f7;	.week {		width: 100%;		height: 81rpx;		background: #ffffff;		&-pre {			width: calc(100% / 7);			height: 100%;			font-family: PingFangSC, PingFang SC;			font-weight: 400;			font-size: 24rpx;			color: #6b7280;			line-height: 33rpx;			&.active {				font-weight: bold;				color: #70cf52;			}		}	}	.list {		flex: 1;		overflow-y: auto;		&-item{			.title{				font-family: PingFang-SC, PingFang-SC;				font-weight: bold;				font-size: 30rpx;				color: #252525;				line-height: 30rpx;				text-align: center;				margin-top: 30rpx;			}			.days{				display: flex;				flex-wrap: wrap;				margin-top: 40rpx;				&-pre{					width: calc(100% / 7);					height: 120rpx;					position: relative;					&.active{						background: #B7F358;					}					&.in{						background: rgba(183, 243,88, .4);					}					.day{						width: 100%;						height: 100%;						font-family: PingFang-SC, PingFang-SC;						font-weight: bold;						font-size: 30rpx;						color: #252525;						line-height: 42rpx;						&.hui{							color: #989998;						}					}					.today{						width: 100%;						font-family: PingFangSC, PingFang SC;						font-weight: 400;						font-size: 20rpx;						color: #989998;						line-height: 28rpx;						text-align: center;						position: absolute;						top: 10rpx;					}					.se{						width: 100%;						font-family: PingFangSC, PingFang SC;						font-weight: bold;						font-size: 20rpx;						color: #989998;						line-height: 28rpx;						text-align: center;						position: absolute;						bottom: 15rpx;					}				}			}		}	}}</style>
 |