|
@@ -0,0 +1,429 @@
|
|
|
+Component({
|
|
|
+ properties: {
|
|
|
+ // 活动数据
|
|
|
+ activityData: {
|
|
|
+ type: Object,
|
|
|
+ value: {},
|
|
|
+ }
|
|
|
+ },
|
|
|
+ observers: {
|
|
|
+ 'activityData': function (newactivityData) {
|
|
|
+ if (newactivityData && Object.keys(newactivityData).length > 0) {
|
|
|
+ console.log('activityData有变化', newactivityData);
|
|
|
+ // 当监听到 activityData有变化时执行
|
|
|
+ this.data.activityData = newactivityData;
|
|
|
+ this.adjustCalendarForActivity();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ data: {
|
|
|
+ weekdays: ['一', '二', '三', '四', '五', '六', '日'],
|
|
|
+ currentYear: new Date().getFullYear(),
|
|
|
+ currentMonth: new Date().getMonth() + 1,
|
|
|
+ selectedDate: null,
|
|
|
+ daysInMonth: [],
|
|
|
+ activityData: {}
|
|
|
+ },
|
|
|
+
|
|
|
+ lifetimes: {
|
|
|
+ attached() {
|
|
|
+ // 不在这里初始化日历,等待activityData数据后再初始化
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ computed: {
|
|
|
+ currentYearMonth() {
|
|
|
+ return `${this.data.currentYear}-${this.data.currentMonth.toString().padStart(2, '0')}`;
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ methods: {
|
|
|
+
|
|
|
+
|
|
|
+ // 初始化日历
|
|
|
+ initCalendar() {
|
|
|
+ const year = this.data.currentYear;
|
|
|
+ const month = this.data.currentMonth - 1;
|
|
|
+
|
|
|
+ // 获取当月第一天是星期几(调整为中文星期顺序:周一=0, 周二=1...周日=6)
|
|
|
+ const firstDayWeek = new Date(year, month, 1).getDay();
|
|
|
+ const firstDay = firstDayWeek === 0 ? 6 : firstDayWeek - 1; // 将周日(0)转为6,其他-1
|
|
|
+
|
|
|
+ // 获取当月的总天数
|
|
|
+ const totalDays = new Date(year, month + 1, 0).getDate();
|
|
|
+
|
|
|
+ const days = [];
|
|
|
+ const today = new Date();
|
|
|
+ today.setHours(0, 0, 0, 0);
|
|
|
+
|
|
|
+ // 计算开放日期范围
|
|
|
+ let openStart, openEnd;
|
|
|
+ if (this.data.activityData && this.data.activityData.startTime && this.data.activityData.endTime) {
|
|
|
+ // 开放范围从活动开始时间和今天中的较大值开始,到活动结束时间结束
|
|
|
+ const activityStart = new Date(this.data.activityData.startTime);
|
|
|
+ activityStart.setHours(0, 0, 0, 0);
|
|
|
+
|
|
|
+ openStart = activityStart >= today ? activityStart : today;
|
|
|
+ openEnd = new Date(this.data.activityData.endTime);
|
|
|
+ openEnd.setHours(23, 59, 59, 999);
|
|
|
+ } else {
|
|
|
+ // 没有活动数据时不显示任何可选日期
|
|
|
+ openStart = new Date(0);
|
|
|
+ openEnd = new Date(0);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 添加空白占位符,不显示上个月日期
|
|
|
+ for (let i = 0; i < firstDay; i++) {
|
|
|
+ days.push({
|
|
|
+ date: '',
|
|
|
+ fullDate: null,
|
|
|
+ isCurrentMonth: false,
|
|
|
+ isEmpty: true, // 标记为空白
|
|
|
+ isPast: true,
|
|
|
+ isToday: false,
|
|
|
+ isInOpenRange: false,
|
|
|
+ status: ''
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ // 添加当月的日期
|
|
|
+ for (let i = 1; i <= totalDays; i++) {
|
|
|
+ const date = new Date(year, month, i);
|
|
|
+ const isToday = date.getDate() === today.getDate() &&
|
|
|
+ date.getMonth() === today.getMonth() &&
|
|
|
+ date.getFullYear() === today.getFullYear();
|
|
|
+
|
|
|
+ const isPast = date < today;
|
|
|
+ const isInOpenRange = this.isDateInOpenRange(date, openStart, openEnd);
|
|
|
+
|
|
|
+ // 获取日期状态
|
|
|
+ const status = this.getDateStatus(date, isPast, isToday, isInOpenRange);
|
|
|
+
|
|
|
+ // 添加到数组
|
|
|
+ const dayData = {
|
|
|
+ date: i,
|
|
|
+ fullDate: date,
|
|
|
+ isCurrentMonth: true,
|
|
|
+ isEmpty: false,
|
|
|
+ isPast,
|
|
|
+ isToday,
|
|
|
+ isInOpenRange,
|
|
|
+ status
|
|
|
+ };
|
|
|
+
|
|
|
+ // 预计算CSS类名
|
|
|
+ dayData.cssClass = this.calculateDayClass(dayData);
|
|
|
+
|
|
|
+ days.push(dayData);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 添加空白占位符填充剩余位置
|
|
|
+ const totalCells = Math.ceil(days.length / 7) * 7; // 确保是7的倍数
|
|
|
+ const remainingCells = totalCells - days.length;
|
|
|
+ for (let i = 0; i < remainingCells; i++) {
|
|
|
+ // 添加空白占位符
|
|
|
+ const emptyDay = {
|
|
|
+ date: '',
|
|
|
+ fullDate: null,
|
|
|
+ isCurrentMonth: false,
|
|
|
+ isEmpty: true,
|
|
|
+ isPast: false,
|
|
|
+ isToday: false,
|
|
|
+ isInOpenRange: false,
|
|
|
+ status: ''
|
|
|
+ };
|
|
|
+ emptyDay.cssClass = 'empty-cell';
|
|
|
+ days.push(emptyDay);
|
|
|
+ }
|
|
|
+
|
|
|
+ this.setData({
|
|
|
+ daysInMonth: days,
|
|
|
+ currentYearMonth: this.data.currentYear + '-' + this.data.currentMonth.toString().padStart(2, '0')
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ // 判断日期是否在开放日期范围内
|
|
|
+ isDateInOpenRange(date, start, end) {
|
|
|
+ return date >= start && date <= end;
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ // 获取日期状态
|
|
|
+ getDateStatus(date, isPast, isToday, isInOpenRange) {
|
|
|
+ // 已结束的日期
|
|
|
+ if (isPast) {
|
|
|
+ return '已结束';
|
|
|
+ }
|
|
|
+
|
|
|
+ // 不在开放日期范围内显示未开放
|
|
|
+ if (!isInOpenRange) {
|
|
|
+ return '未开放';
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果有活动数据,检查是否在活动开始时间之前
|
|
|
+ if (this.data.activityData && this.data.activityData.startTime) {
|
|
|
+ const activityStartDate = new Date(this.data.activityData.startTime);
|
|
|
+ activityStartDate.setHours(0, 0, 0, 0);
|
|
|
+
|
|
|
+ // 在活动开始时间之前的日期显示为未开放
|
|
|
+ if (date < activityStartDate) {
|
|
|
+ return '未开放';
|
|
|
+ }
|
|
|
+
|
|
|
+ // 根据活动数据的personCount判断
|
|
|
+ const personCount = this.data.activityData.personCount || 0;
|
|
|
+ if (isToday) {
|
|
|
+ return personCount > 0 ? '今天' : '已约满';
|
|
|
+ } else {
|
|
|
+ return personCount > 0 ? '已开放' : '已约满';
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 没有活动数据时默认为未开放
|
|
|
+ return '未开放';
|
|
|
+ },
|
|
|
+
|
|
|
+ // 计算日期单元格的CSS类(用于数据预处理)
|
|
|
+ calculateDayClass(day) {
|
|
|
+ // 如果是空白单元格,直接返回空白样式
|
|
|
+ if (day.isEmpty) {
|
|
|
+ return 'empty-cell';
|
|
|
+ }
|
|
|
+
|
|
|
+ const selectedDate = this.data.selectedDate ? new Date(this.data.selectedDate) : null;
|
|
|
+ const isSelected = selectedDate && day.fullDate &&
|
|
|
+ day.fullDate.getDate() === selectedDate.getDate() &&
|
|
|
+ day.fullDate.getMonth() === selectedDate.getMonth() &&
|
|
|
+ day.fullDate.getFullYear() === selectedDate.getFullYear();
|
|
|
+
|
|
|
+ let classes = [];
|
|
|
+
|
|
|
+ // 基础状态类
|
|
|
+ if (!day.isCurrentMonth && !day.isEmpty) classes.push('other-month');
|
|
|
+ if (day.isToday) classes.push('today'); // 今天总是添加today类,不管是否开放
|
|
|
+ if (day.isPast) classes.push('past');
|
|
|
+
|
|
|
+ // 可选择状态
|
|
|
+ if (day.isInOpenRange && !day.isPast && day.status !== '已约满' && day.status !== '未开放') {
|
|
|
+ classes.push('selectable');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 选中状态 - 优先级最高
|
|
|
+ if (isSelected && day.status !== '未开放') {
|
|
|
+ classes.push('selected');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 状态相关的样式类
|
|
|
+ if (day.status === '已开放' || day.status === '今天') {
|
|
|
+ classes.push('available');
|
|
|
+ }
|
|
|
+ if (day.status === '未开放') {
|
|
|
+ classes.push('unavailable');
|
|
|
+ }
|
|
|
+ if (day.status === '已约满') {
|
|
|
+ classes.push('full');
|
|
|
+ }
|
|
|
+
|
|
|
+ return classes.join(' ');
|
|
|
+ },
|
|
|
+
|
|
|
+ // 更新所有日期的CSS类(当选中状态改变时调用)
|
|
|
+ updateDayClasses() {
|
|
|
+ const updatedDays = this.data.daysInMonth.map(day => {
|
|
|
+ return {
|
|
|
+ ...day,
|
|
|
+ cssClass: this.calculateDayClass(day)
|
|
|
+ };
|
|
|
+ });
|
|
|
+
|
|
|
+ this.setData({
|
|
|
+ daysInMonth: updatedDays
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ // 选择日期
|
|
|
+ selectDay(e) {
|
|
|
+ const dayIndex = e.currentTarget.dataset.index;
|
|
|
+ const day = this.data.daysInMonth[dayIndex];
|
|
|
+
|
|
|
+ // 空白单元格不可点击
|
|
|
+ if (day.isEmpty || !day.fullDate) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 只有开放日期范围内且未过期且未约满且不是未开放的日期才可选择
|
|
|
+ if (day.isInOpenRange && !day.isPast && day.status !== '已约满' && day.status !== '未开放') {
|
|
|
+ this.setData({
|
|
|
+ selectedDate: day.fullDate
|
|
|
+ });
|
|
|
+
|
|
|
+ // 更新所有日期的CSS类以反映新的选中状态
|
|
|
+ this.updateDayClasses();
|
|
|
+
|
|
|
+ // 触发自定义事件,向父组件传递选择的日期
|
|
|
+ this.triggerEvent('datechange', {
|
|
|
+ date: day.fullDate,
|
|
|
+ dateString: this.formatDate(day.fullDate)
|
|
|
+ });
|
|
|
+
|
|
|
+ console.log('选择的日期:', day.fullDate);
|
|
|
+ } else {
|
|
|
+ console.log('日期不可选择,状态:', day.status);
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 上一个月
|
|
|
+ prevMonth() {
|
|
|
+ if (this.data.currentMonth === 1) {
|
|
|
+ this.setData({
|
|
|
+ currentYear: this.data.currentYear - 1,
|
|
|
+ currentMonth: 12
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ this.setData({
|
|
|
+ currentMonth: this.data.currentMonth - 1
|
|
|
+ });
|
|
|
+ }
|
|
|
+ this.initCalendar();
|
|
|
+ },
|
|
|
+
|
|
|
+ // 下一个月
|
|
|
+ nextMonth() {
|
|
|
+ if (this.data.currentMonth === 12) {
|
|
|
+ this.setData({
|
|
|
+ currentYear: this.data.currentYear + 1,
|
|
|
+ currentMonth: 1
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ this.setData({
|
|
|
+ currentMonth: this.data.currentMonth + 1
|
|
|
+ });
|
|
|
+ }
|
|
|
+ this.initCalendar();
|
|
|
+ },
|
|
|
+
|
|
|
+ // 初始化默认日期
|
|
|
+ initDefaultDate() {
|
|
|
+ const today = new Date();
|
|
|
+ today.setHours(0, 0, 0, 0);
|
|
|
+
|
|
|
+ // 计算开放日期范围
|
|
|
+ let openStart, openEnd;
|
|
|
+ if (this.data.activityData && this.data.activityData.startTime && this.data.activityData.endTime) {
|
|
|
+ // 如果有活动数据,使用活动的时间范围
|
|
|
+ openStart = new Date(this.data.activityData.startTime);
|
|
|
+ openEnd = new Date(this.data.activityData.endTime);
|
|
|
+ openStart.setHours(0, 0, 0, 0);
|
|
|
+ openEnd.setHours(23, 59, 59, 999);
|
|
|
+ } else {
|
|
|
+ // 默认逻辑:从今天开始的七天
|
|
|
+ openStart = new Date(today);
|
|
|
+ openEnd = new Date(today);
|
|
|
+ openEnd.setDate(today.getDate() + 6);
|
|
|
+ openEnd.setHours(23, 59, 59, 999);
|
|
|
+ }
|
|
|
+
|
|
|
+ const isInOpenRange = this.isDateInOpenRange(today, openStart, openEnd);
|
|
|
+ const todayStatus = this.getDateStatus(today, false, true, isInOpenRange);
|
|
|
+
|
|
|
+ console.log('今天状态检查:', {
|
|
|
+ isInOpenRange,
|
|
|
+ todayStatus,
|
|
|
+ activityData: this.data.activityData
|
|
|
+ });
|
|
|
+
|
|
|
+ // 只有今天可以选择时才默认选中(今天状态为'今天'表示可约)
|
|
|
+ if (isInOpenRange && (todayStatus === '今天' || todayStatus === '已开放')) {
|
|
|
+ this.setData({
|
|
|
+ selectedDate: today
|
|
|
+ });
|
|
|
+
|
|
|
+ // 更新日历显示以反映选中状态
|
|
|
+ this.updateDayClasses();
|
|
|
+
|
|
|
+ // 触发自定义事件,向父组件传递默认选择的日期
|
|
|
+ this.triggerEvent('datechange', {
|
|
|
+ date: today,
|
|
|
+ dateString: this.formatDate(today)
|
|
|
+ });
|
|
|
+
|
|
|
+ console.log('默认选中今天:', today, '状态:', todayStatus);
|
|
|
+ } else {
|
|
|
+ // 今天不可选择时,如果有活动数据且活动开始时间在未来,选择活动开始时间
|
|
|
+ if (this.data.activityData && this.data.activityData.startTime) {
|
|
|
+ const activityStartDate = new Date(this.data.activityData.startTime);
|
|
|
+ activityStartDate.setHours(0, 0, 0, 0);
|
|
|
+
|
|
|
+ // 检查活动开始时间是否在未来且在活动时间范围内
|
|
|
+ if (activityStartDate >= today && this.isDateInOpenRange(activityStartDate, openStart, openEnd)) {
|
|
|
+ const startDateStatus = this.getDateStatus(activityStartDate, false, false, true);
|
|
|
+ if (startDateStatus === '已开放') {
|
|
|
+ this.setData({
|
|
|
+ selectedDate: activityStartDate
|
|
|
+ });
|
|
|
+
|
|
|
+ // 更新日历显示以反映选中状态
|
|
|
+ this.updateDayClasses();
|
|
|
+
|
|
|
+ // 触发自定义事件,向父组件传递默认选择的日期
|
|
|
+ this.triggerEvent('datechange', {
|
|
|
+ date: activityStartDate,
|
|
|
+ dateString: this.formatDate(activityStartDate)
|
|
|
+ });
|
|
|
+
|
|
|
+ console.log('默认选中活动开始时间:', activityStartDate);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 今天不可选择且没有合适的活动开始时间时,确保selectedDate为null
|
|
|
+ this.setData({
|
|
|
+ selectedDate: null
|
|
|
+ });
|
|
|
+ console.log('今天不可选择,不默认选中。状态:', todayStatus);
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 格式化日期
|
|
|
+ formatDate(date) {
|
|
|
+ const year = date.getFullYear();
|
|
|
+ const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
|
|
+ const day = date.getDate().toString().padStart(2, '0');
|
|
|
+ return `${year}-${month}-${day}`;
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ // 根据活动时间调整日历
|
|
|
+ adjustCalendarForActivity() {
|
|
|
+ if (!this.data.activityData) return;
|
|
|
+
|
|
|
+ const { startTime, endTime } = this.data.activityData;
|
|
|
+ if (!startTime || !endTime) return;
|
|
|
+
|
|
|
+ const startDate = new Date(startTime);
|
|
|
+ const endDate = new Date(endTime);
|
|
|
+
|
|
|
+ // 如果开始时间不在当前月份,跳转到开始时间所在月份
|
|
|
+ const currentYear = this.data.currentYear;
|
|
|
+ const currentMonth = this.data.currentMonth;
|
|
|
+ const startYear = startDate.getFullYear();
|
|
|
+ const startMonth = startDate.getMonth() + 1;
|
|
|
+
|
|
|
+ if (startYear !== currentYear || startMonth !== currentMonth) {
|
|
|
+ this.setData({
|
|
|
+ currentYear: startYear,
|
|
|
+ currentMonth: startMonth
|
|
|
+ }, () => {
|
|
|
+ this.initCalendar();
|
|
|
+ this.initDefaultDate();
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ this.initCalendar();
|
|
|
+ this.initDefaultDate();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+});
|