如何在Angular Material日期选择器中实现整周选择、高亮及防重叠?
Angular Material日期选择器整周选择实现方案
1. 自定义DateAdapter适配周选择逻辑
首先扩展Angular Material的NativeDateAdapter,让日期选择器默认以周一作为周起始,同时将选中日期自动转换为当周周一:
import { NativeDateAdapter } from '@angular/material/core'; import { Injectable } from '@angular/core'; @Injectable() export class WeekDateAdapter extends NativeDateAdapter { // 设置周起始为周一 override getFirstDayOfWeek(): number { return 1; } // 获取当前日期所属周的周一 getWeekStartDate(date: Date): Date { const dayOfWeek = date.getDay(); const diff = date.getDate() - dayOfWeek + (dayOfWeek === 0 ? -6 : 1); return new Date(date.setDate(diff)); } // 格式化显示为周范围(可选) override format(date: Date, displayFormat: Object): string { const weekStart = this.getWeekStartDate(date); const weekEnd = new Date(weekStart); weekEnd.setDate(weekStart.getDate() + 6); return `${this.formatDate(weekStart)} - ${this.formatDate(weekEnd)}`; } private formatDate(date: Date): string { return `${date.getFullYear()}-${(date.getMonth()+1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`; } }
在模块中注册适配器:
import { NgModule } from '@angular/core'; import { DateAdapter } from '@angular/material/core'; import { WeekDateAdapter } from './week-date-adapter'; @NgModule({ providers: [ { provide: DateAdapter, useClass: WeekDateAdapter } ] }) export class AppModule { }
2. 自定义日期面板实现整周高亮
通过自定义mat-datepicker的面板模板,结合CSS实现整周高亮效果:
组件HTML模板
<mat-form-field> <input matInput [matDatepicker]="picker" [(ngModel)]="selectedWeekStart"> <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle> <mat-datepicker #picker panelClass="week-highlight-datepicker"> <mat-calendar [selected]="selectedWeekStart" (selectedChange)="onDateSelected($event)" (mouseenter)="onDateHover($event)" [dateClass]="getDateClass.bind(this)"> </mat-calendar> </mat-datepicker> </mat-form-field>
组件TS逻辑
import { Component } from '@angular/core'; import { WeekDateAdapter } from './week-date-adapter'; @Component({ selector: 'app-week-date-picker', templateUrl: './week-date-picker.component.html', styleUrls: ['./week-date-picker.component.css'] }) export class WeekDatePickerComponent { selectedWeekStart: Date | null = null; hoveredWeekDates: Date[] = []; selectedWeeks: { start: Date, end: Date }[] = []; constructor(private dateAdapter: WeekDateAdapter) {} // 处理日期点击,转换为周起始并检查重叠 onDateSelected(date: Date | null): void { if (!date) return; const weekStart = this.dateAdapter.getWeekStartDate(date); const weekEnd = new Date(weekStart); weekEnd.setDate(weekStart.getDate() + 6); // 判断是否与已选周重叠 const isOverlapping = this.selectedWeeks.some(week => { return !(weekEnd < week.start || weekStart > week.end); }); if (isOverlapping) { alert('该周已被选择或与已选周重叠'); return; } this.selectedWeekStart = weekStart; this.selectedWeeks.push({ start: weekStart, end: weekEnd }); } // 处理鼠标hover,获取当前日期所属周的所有日期 onDateHover(date: Date): void { const weekStart = this.dateAdapter.getWeekStartDate(date); this.hoveredWeekDates = []; for (let i = 0; i < 7; i++) { const day = new Date(weekStart); day.setDate(weekStart.getDate() + i); this.hoveredWeekDates.push(day); } } // 给周内日期添加高亮类 getDateClass(date: Date): string { const isHoveredWeek = this.hoveredWeekDates.some(d => this.isSameDay(d, date)); const isSelectedWeek = this.selectedWeeks.some(week => { return date >= week.start && date <= week.end; }); if (isSelectedWeek) return 'selected-week'; if (isHoveredWeek) return 'hovered-week'; return ''; } // 判断两个日期是否为同一天 private isSameDay(date1: Date, date2: Date): boolean { return date1.getFullYear() === date2.getFullYear() && date1.getMonth() === date2.getMonth() && date1.getDate() === date2.getDate(); } }
高亮样式(CSS)
.week-highlight-datepicker .hovered-week { background-color: rgba(0, 123, 255, 0.1); } .week-highlight-datepicker .selected-week { background-color: rgba(0, 123, 255, 0.2); font-weight: bold; } .week-highlight-datepicker .selected-week.mat-calendar-body-selected { background-color: #007bff; color: white; }
3. 关键逻辑说明
- 周起始转换:通过
getWeekStartDate方法,将任意选中日期转为当周周一,确保选中值统一为周起始。 - 整周高亮:监听鼠标hover和点击事件,计算对应周的所有日期,通过
dateClass动态添加CSS类实现高亮。 - 重叠限制:维护已选周的日期范围列表,每次选择新周时,通过日期范围对比判断是否重叠,若重叠则阻止选择。
内容的提问来源于stack exchange,提问作者adnan ali
相关产品推荐
相关产品推荐

