|
@@ -0,0 +1,117 @@
|
|
|
|
|
+import { Component, Input, Output, EventEmitter, OnChanges, SimpleChanges } from '@angular/core';
|
|
|
|
|
+import { CommonModule } from '@angular/common';
|
|
|
|
|
+
|
|
|
|
|
+export interface CalendarDayData {
|
|
|
|
|
+ date: Date;
|
|
|
|
|
+ hasData: boolean;
|
|
|
|
|
+ completed?: boolean;
|
|
|
|
|
+ primaryText?: string; // 主要显示文本(如运动类型、体重值、餐食类型)
|
|
|
|
|
+ secondaryText?: string; // 次要显示文本(如时长、体脂率、热量)
|
|
|
|
|
+ badge?: string; // 徽章(如 ✓)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+@Component({
|
|
|
|
|
+ selector: 'app-calendar-grid',
|
|
|
|
|
+ standalone: true,
|
|
|
|
|
+ imports: [CommonModule],
|
|
|
|
|
+ templateUrl: './calendar-grid.component.html',
|
|
|
|
|
+ styleUrl: './calendar-grid.component.scss'
|
|
|
|
|
+})
|
|
|
|
|
+export class CalendarGridComponent implements OnChanges {
|
|
|
|
|
+ @Input() currentDate: Date = new Date();
|
|
|
|
|
+ @Input() dayDataMap: Map<string, CalendarDayData> = new Map();
|
|
|
|
|
+ @Input() selectedDate: Date | null = null;
|
|
|
|
|
+ @Input() isManageMode: boolean = false;
|
|
|
|
|
+ @Input() selectedRecords: Set<string> = new Set();
|
|
|
|
|
+ @Input() themeColor: 'green' | 'purple' | 'blue' = 'green';
|
|
|
|
|
+
|
|
|
|
|
+ @Output() dateSelected = new EventEmitter<CalendarDayData | null>();
|
|
|
|
|
+ @Output() previousMonth = new EventEmitter<void>();
|
|
|
|
|
+ @Output() nextMonth = new EventEmitter<void>();
|
|
|
|
|
+ @Output() goToCurrentMonth = new EventEmitter<void>();
|
|
|
|
|
+
|
|
|
|
|
+ weekDays = ['一', '二', '三', '四', '五', '六', '日'];
|
|
|
|
|
+ calendarDays: (CalendarDayData | null)[] = [];
|
|
|
|
|
+
|
|
|
|
|
+ ngOnChanges(changes: SimpleChanges): void {
|
|
|
|
|
+ if (changes['currentDate'] || changes['dayDataMap']) {
|
|
|
|
|
+ this.buildCalendar();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ get currentYearMonth(): string {
|
|
|
|
|
+ return `${this.currentDate.getFullYear()}年${this.currentDate.getMonth() + 1}月`;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ buildCalendar(): void {
|
|
|
|
|
+ const year = this.currentDate.getFullYear();
|
|
|
|
|
+ const month = this.currentDate.getMonth();
|
|
|
|
|
+ const firstDay = new Date(year, month, 1);
|
|
|
|
|
+ const lastDay = new Date(year, month + 1, 0);
|
|
|
|
|
+ let startWeekDay = firstDay.getDay();
|
|
|
|
|
+ startWeekDay = startWeekDay === 0 ? 6 : startWeekDay - 1;
|
|
|
|
|
+
|
|
|
|
|
+ this.calendarDays = [];
|
|
|
|
|
+
|
|
|
|
|
+ // 填充月初空白
|
|
|
|
|
+ for (let i = 0; i < startWeekDay; i++) {
|
|
|
|
|
+ this.calendarDays.push(null);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 填充日期
|
|
|
|
|
+ for (let day = 1; day <= lastDay.getDate(); day++) {
|
|
|
|
|
+ const date = new Date(year, month, day);
|
|
|
|
|
+ const dateKey = this.formatDateKey(date);
|
|
|
|
|
+ const dayData = this.dayDataMap.get(dateKey);
|
|
|
|
|
+
|
|
|
|
|
+ this.calendarDays.push(dayData || {
|
|
|
|
|
+ date,
|
|
|
|
|
+ hasData: false,
|
|
|
|
|
+ primaryText: '',
|
|
|
|
|
+ secondaryText: '',
|
|
|
|
|
+ badge: ''
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ onDateClick(day: CalendarDayData | null): void {
|
|
|
|
|
+ if (!day) return;
|
|
|
|
|
+ this.dateSelected.emit(day);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ isToday(date: Date): boolean {
|
|
|
|
|
+ const today = new Date();
|
|
|
|
|
+ return date.getDate() === today.getDate() &&
|
|
|
|
|
+ date.getMonth() === today.getMonth() &&
|
|
|
|
|
+ date.getFullYear() === today.getFullYear();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ isSelected(date: Date): boolean {
|
|
|
|
|
+ if (this.isManageMode) {
|
|
|
|
|
+ return this.selectedRecords.has(this.formatDateKey(date));
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!this.selectedDate) return false;
|
|
|
|
|
+ return date.getDate() === this.selectedDate.getDate() &&
|
|
|
|
|
+ date.getMonth() === this.selectedDate.getMonth() &&
|
|
|
|
|
+ date.getFullYear() === this.selectedDate.getFullYear();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private formatDateKey(date: Date): string {
|
|
|
|
|
+ const year = date.getFullYear();
|
|
|
|
|
+ const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
|
|
|
+ const day = String(date.getDate()).padStart(2, '0');
|
|
|
|
|
+ return `${year}-${month}-${day}`;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ onPreviousMonth(): void {
|
|
|
|
|
+ this.previousMonth.emit();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ onNextMonth(): void {
|
|
|
|
|
+ this.nextMonth.emit();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ onGoToCurrentMonth(): void {
|
|
|
|
|
+ this.goToCurrentMonth.emit();
|
|
|
|
|
+ }
|
|
|
|
|
+}
|