HarmonyOs应用《重要日》开发第17篇 - 日历页多视图切换

📅 2026/7/18 1:42:29
HarmonyOs应用《重要日》开发第17篇 - 日历页多视图切换
本文解析 ImportantDays 项目中的日历页面CalendarPage包括年/月视图切换、日期导航前后月、今天、日期选择器、选中日期的重要日列表展示、以及日历与重要日数据的联动设计。一、页面定位CalendarPage是应用的第二个 Tab 页面提供日历浏览功能。用户可以在年视图和月视图之间切换左右滑动浏览不同月份/年份点击日期查看当天的重要日通过日期选择器快速跳转二、组件定义ComponentV2exportstruct CalendarPage{vm:CalendarVMCalendarVM.getInstance();manager:ImportantDayManagerImportantDayManager.getInstance();LocalcurrentTitle:string;LocalselectedDays:ImportantDay[][];LocalshowDatePicker:booleanfalse;}状态变量变量说明vm日历视图模型单例manager重要日管理器单例currentTitle当前显示的标题年/月selectedDays选中日期的重要日列表showDatePicker日期选择器弹窗状态三、生命周期aboutToAppear():void{this.updateTitle();this.updateSelectedDays();}页面即将出现时初始化标题和选中日期的重要日列表。四、build 方法整体结构build(){Column(){// 1. 标题栏导航 今天按钮Row(){...}// 2. 视图切换按钮Row(){...}// 3. 日历主体if(this.vm.viewTypeViewTypeEnum.YEAR){YearCalendar({...})}else{MonthWeekCalendar({...})}// 4. 选中日期的重要日列表if(this.selectedDays.length0){Divider()Text(当日重要日)Scroll(){Column(){ForEach(this.selectedDays,(day){this.importantDayCard(day)})}}}}.bindSheet($$this.showDatePicker,this.datePickerBuilder(),{...})}布局层次Column ├── Row (标题栏) ├── Row (视图切换) ├── YearCalendar / MonthWeekCalendar (日历主体) └── [条件] 当日重要日列表 ├── Divider ├── Text (标题) └── Scroll → ForEach → importantDayCard五、标题栏与导航Row(){// 上个月按钮Text().fontSize(20).fontColor(#666666).padding(8).onClick((){this.vm.selectDatethis.vm.selectDate.subtract(1,month);this.updateTitle();})// 当前月份标题Text(this.currentTitle).fontSize(20).fontWeight(FontWeight.Bold).fontColor(#333333).layoutWeight(1).textAlign(TextAlign.Center).onClick((){this.showDatePickertrue;})// 下个月按钮Text().fontSize(20).fontColor(#666666).padding(8).onClick((){this.vm.selectDatethis.vm.selectDate.add(1,month);this.updateTitle();})// 今天按钮Text(今天).fontSize(14).fontColor(#FF6B6B).padding({left:8,right:8}).onClick((){this.vm.goToToday();this.updateTitle();this.updateSelectedDays();})}导航功能按钮功能上一月标题打开日期选择器下一月今天跳转到今天标题点击点击标题文字打开日期选择器用户可以快速选择目标日期。六、视图切换Row(){ForEach(this.getViewTypes(),(item:ViewTypeItem){Text(item.name).fontSize(13).fontColor(this.vm.viewTypeitem.type?#FFFFFF:#666666).backgroundColor(this.vm.viewTypeitem.type?#FF6B6B:#F5F5F5).borderRadius(12).padding({left:12,right:12,top:4,bottom:4}).margin({right:8}).onClick((){this.vm.changeViewType(item.type);this.updateTitle();})})}privategetViewTypes():ViewTypeItem[]{return[{type:ViewTypeEnum.YEAR,name:年},{type:ViewTypeEnum.MONTH,name:月}];}视图切换按钮[年] [月]选中状态白字红底未选中灰字灰底切换逻辑点击按钮 →vm.changeViewType(type)→ VM 切换视图并重置数据 →updateTitle()更新标题。七、日历主体渲染if(this.vm.viewTypeViewTypeEnum.YEAR){YearCalendar({vm:this.vm,onSelectDate:(date:Date){this.vm.changeDate(dayjs(date));this.vm.changeViewType(ViewTypeEnum.MONTH);this.updateTitle();this.updateSelectedDays();}})}else{MonthWeekCalendar({vm:this.vm,onChangeDay:(date:Date){this.updateSelectedDays();},selectedColor:#FF6B6B,startWeekday:this.vm.startWeekday,isShowHeader:true})}年视图回调年视图中选择日期后更新 VM 选中日期自动切换到月视图更新标题和重要日列表月视图回调月视图中选择日期后更新选中日期的重要日列表下方区域展示当天重要日八、当日重要日列表if(this.selectedDays.length0){Divider().color(#EEEEEE)Text(当日重要日).fontSize(14).fontWeight(FontWeight.Medium).fontColor(#333333).padding({left:12,top:8,bottom:4})Scroll(){Column(){ForEach(this.selectedDays,(day:ImportantDay){this.importantDayCard(day)})}.padding({left:12,right:12,bottom:12})}.layoutWeight(1)}条件渲染只有选中日期有重要日时才显示此区域避免空列表占用空间。重要日卡片BuilderimportantDayCard(day:ImportantDay){Row(){// 颜色条Column().width(4).height(40).backgroundColor(day.color).borderRadius(2).margin({right:8})Column(){Text(day.title).fontSize(15).fontWeight(FontWeight.Medium).fontColor(#333333).maxLines(1)Text(${DateUtil.getFullDateStr(day.targetDate)}·${day.getDisplayText()}).fontSize(12).fontColor(#999999).margin({top:2})}.layoutWeight(1)// 倒数/正数标签Text(day.isCountdown?倒数:正数).fontSize(10).fontColor(day.isCountdown?#FF6B6B:#4ECDC4).backgroundColor(day.isCountdown?#FFF0F0:#E8FAF8).borderRadius(4).padding({left:6,right:6,top:2,bottom:2})}.padding({top:8,bottom:8}).borderRadius(8).backgroundColor(#FAFAFA).margin({bottom:4})}卡片设计┌─┬───────────────────────────────┬──────┐ │ │ 妈妈生日 │ 倒数 │ │ │ 2026年08月15日 · 还有 29 天 │ │ └─┴───────────────────────────────┴──────┘ 4vp layoutWeight(1) auto比列表页的卡片更简洁细色条4vp 宽代替宽色条无操作按钮添加倒数/正数标签九、日期选择器BuilderdatePickerBuilder(){Column(){Text(选择日期).fontSize(18).fontWeight(FontWeight.Medium).padding(16)DatePicker({start:newDate(2000-01-01),end:newDate(2099-12-31),selected:this.vm.selectDate.toDate()}).onChange((value:DatePickerResult){if(value.yearvalue.month!undefinedvalue.day){constdatedayjs().year(value.year).month(value.month).date(value.day);this.vm.changeDate(date);this.updateTitle();this.updateSelectedDays();}}).width(100%).height(200)Button(确定).width(80%).height(40).backgroundColor(#FF6B6B).margin({top:16}).onClick((){this.showDatePickerfalse;})}}DatePicker 配置配置值说明start2000-01-01可选最早日期end2099-12-31可选最晚日期selected当前选中日期初始显示日期选择流程用户滚动选择年/月/日onChange实时更新 VM 选中日期点击确定关闭弹窗标题和重要日列表自动更新十、更新方法privateupdateTitle():void{if(this.vm.viewTypeViewTypeEnum.YEAR){this.currentTitlethis.vm.getCurrentYearTitle();}else{this.currentTitlethis.vm.getCurrentMonthTitle();}}privateupdateSelectedDays():void{this.selectedDaysthis.vm.getSelectedDateDays();}updateTitle根据当前视图类型生成不同标题年视图2026年月视图2026年07月updateSelectedDays从 VM 获取选中日期的重要日列表更新到selectedDays。十一、数据联动日期选择 → 列表更新用户点击日期 ↓ MonthWeekCalendar.onDateClick ↓ vm.changeDate(date) ↓ onChangeDay 回调 ↓ CalendarPage.updateSelectedDays() ↓ vm.getSelectedDateDays() ↓ ImportantDayManager.getDaysByDate(dateStr) ↓ selectedDays 更新 ↓ UI 自动刷新Local 变化月份切换 → 标题更新用户滑动 Swiper ↓ handleSwiperChange ↓ vm.selectDate 更新到新月份 ↓ CalendarPage 检测到月份变化 ↓ updateTitle() ↓ 标题更新十二、总结CalendarPage的设计体现了以下原则多视图共存年视图和月视图通过条件渲染切换多维度导航前后按钮 今天按钮 日期选择器数据联动日期选择自动更新重要日列表条件渲染无重要日时不显示列表区域卡片差异化日历页卡片比列表页更简洁标题智能切换根据视图类型显示年或月标题日历页为用户提供了从宏观年视图到微观月视图当日重要日的完整日历浏览体验。