一、组件功能分析
核心功能需求:
- 支持多行周视图布局(6行×7列)
- 日期与状态混合展示
- 不同状态差异化样式:
- 未填写(红色背景)
- 已锁定(绿色背景)
- 跨月日期显示处理
- 表头星期展示
二、核心实现代码
1. 数据结构定义
// CalendarData.ets
export interface CalendarData {
date: number
isCurrentMonth: boolean
status: 'none' | 'pending' | 'completed'
hours: number
}
2. 组件UI实现
@Builder
CalendarHeader() {
Row() {
Text(`${this.currentDate.getFullYear()}年${this.currentDate.getMonth() + 1}月`)
.fontSize(24)
.fontWeight(FontWeight.Bold)
Blank()
Row() {
Text('今天')
.border({
width: 1,
color: '#ccc',
})
.margin({ right: 8 })
.padding(10)
Image($r('app.media.ic_prev'))
.width(24)
.height(24)
.margin({ right: 8 })
.onClick(() => {
this.currentDate = new Date(this.currentDate.getFullYear(), this.currentDate.getMonth() - 1)
this.generateCalendarData()
})
Image($r('app.media.ic_next'))
.width(24)
.height(24)
.onClick(() => {
this.currentDate = new Date(this.currentDate.getFullYear(), this.currentDate.getMonth() + 1)
this.generateCalendarData()
})
}
}
.width('100%')
.padding(16)
}
@Builder
WeekHeader() {
Row() {
ForEach(['日', '一', '二', '三', '四', '五', '六'], (day: string) => {
Text(day)
.fontSize(14)
.width('14.28%')
.textAlign(TextAlign.Center)
})
}
.width('100%')
.padding({ left: 16, right: 16 })
}
build() {
Column() {
this.CalendarHeader()
this.WeekHeader()
Grid() {
ForEach(this.calendarData, (item: CalendarData) => {
GridItem() {
Column() {
Text(`${item.date}`)
.fontSize(16)
.fontColor(item.isCurrentMonth ? '#000000' : '#CCCCCC')
if (item.status !== 'none') {
Column() {
Row() {
Image(item.status === 'completed' ?
$r('app.media.ic_completed') :
$r('app.media.ic_pending'))
.width(16)
.height(16)
// Text(item.status === 'completed' ? '已锁定' : '未填写')
// .fontSize(12)
// .margin({ left: 4 })
// .fontColor(item.status === 'completed' ? '#36B365' : '#E84026')
}
if (item.hours > 0) {
Text(`${item.hours}h`)
.fontSize(12)
.margin({ top: 4 })
}
}
.backgroundColor(item.status === 'completed' ? '#E8F5ED' : '#FFEFED')
.padding(8)
.borderRadius(4)
.width('100%')
}
}
.width('100%')
.height('100%')
.padding(8)
.justifyContent(FlexAlign.SpaceBetween)
}
})
}
.columnsTemplate('1fr 1fr 1fr 1fr 1fr 1fr 1fr')
.rowsTemplate('1fr 1fr 1fr 1fr 1fr 1fr')
.width('100%')
.height('100%')
.padding({ left: 16, right: 16 })
}
}
3. js交互数据初始化
@State currentDate: Date = new Date()
@State calendarData: CalendarData[] = []
aboutToAppear() {
this.generateCalendarData()
}
generateCalendarData() {
const year = this.currentDate.getFullYear()
const month = this.currentDate.getMonth()
// 获取当月第一天是星期几
const firstDay = new Date(year, month, 1).getDay()
// 获取当月天数
const daysInMonth = new Date(year, month + 1, 0).getDate()
// 获取上月天数
const daysInLastMonth = new Date(year, month, 0).getDate()
let days: CalendarData[] = []
// 添加上月末尾几天
for (let i = firstDay - 1; i >= 0; i--) {
days.push({
date: daysInLastMonth - i,
isCurrentMonth: false,
status: 'none',
hours: 0
})
}
// 添加当月天数
for (let i = 1; i <= daysInMonth; i++) {
days.push({
date: i,
isCurrentMonth: true,
status: i <= 12 ? (i === 3 || i === 4 ? 'completed' : 'pending') : 'none',
hours: i < 0 ? 8 : 0
})
}
// 补充下月开始几天
const remainingDays = 42 - days.length
for (let i = 1; i <= remainingDays; i++) {
days.push({
date: i,
isCurrentMonth: false,
status: 'none',
hours: 0
})
}
this.calendarData = days
}
运行截图: