以下是基于鸿蒙(HarmonyOS)ArkUI(ETS)实现打卡组件的示例代码,包含打卡功能和已打卡天数的展示:

1. 基础打卡组件实现(含数据持久化)

import { Calendar } from '@ohos.application.DataAbility';
import { Preferences } from '@ohos.data.preferences';

@Entry
@Component
struct ClockInComponent {
  // 打卡记录(存储格式:YYYY-MM-DD)
  @State clockInDays: string[] = [];
  // 连续打卡天数
  @State consecutiveDays: number = 0;
  
  // 初始化Preferences存储
  private prefs: Preferences = await Preferences.getPreferences(getContext(this), 'ClockInData');

  aboutToAppear() {
    // 从本地存储加载打卡记录
    this.prefs.get('clockInDays', []).then((data) => {
      if (data) {
        this.clockInDays = JSON.parse(data as string);
        this.calculateConsecutiveDays();
      }
    });
  }

  // 计算连续打卡天数
  private calculateConsecutiveDays() {
    let count = 0;
    const today = new Date();
    for (let i = this.clockInDays.length - 1; i >= 0; i--) {
      const date = new Date(this.clockInDays[i]);
      if (this.isConsecutiveDate(today, date)) {
        count++;
        today.setDate(today.getDate() - 1);
      } else {
        break;
      }
    }
    this.consecutiveDays = count;
  }

  // 判断两个日期是否连续
  private isConsecutiveDate(date1: Date, date2: Date): boolean {
    const diffTime = date1.getTime() - date2.getTime();
    const diffDays = diffTime / (1000 * 3600 * 24);
    return diffDays >= 1 && diffDays < 2;
  }

  // 执行打卡操作
  async onClockIn() {
    const today = new Date();
    const todayStr = today.toISOString().split('T')[0]; // YYYY-MM-DD
    
    if (!this.clockInDays.includes(todayStr)) {
      this.clockInDays = [...this.clockInDays, todayStr];
      await this.prefs.put('clockInDays', JSON.stringify(this.clockInDays));
      await this.prefs.flush();
      this.calculateConsecutiveDays();
    }
  }

  build() {
    Column() {
      // 显示连续打卡天数
      Text(`连续打卡 ${this.consecutiveDays}`)
        .fontSize(20)
        .margin(10)

      // 打卡按钮
      Button('点击打卡')
        .width(150)
        .height(40)
        .onClick(() => this.onClockIn())

      // 历史打卡日历
      List({ space: 5 }) {
        ForEach(this.clockInDays, (day: string) => {
          ListItem() {
            Text(day)
              .padding(10)
              .backgroundColor(Color.Green)
              .borderRadius(5)
          }
        })
      }
      .layoutWeight(1)
      .width('100%')
    }
    .padding(20)
    .width('100%')
    .height('100%')
  }
}

2. 关键功能说明

  1. 数据持久化

    • 使用 Preferences 存储打卡记录
    • aboutToAppear 生命周期加载数据
    • 打卡时更新并保存数据
  2. 连续天数计算

    • 倒序遍历打卡记录
    • 通过 isConsecutiveDate 方法判断日期连续性
  3. UI组件

    • 使用 Button 触发打卡
    • List 展示历史打卡日期
    • 动态显示连续打卡天数

3. 扩展功能建议

  1. 日历视图展示
// 在build方法中添加日历视图
Grid() {
  ForEach(this.getLast7Days(), (day: string) => {
    GridItem() {
      Column() {
        Text(day.split('-')[2]) // 显示日期
          .fontSize(16)
        Circle()
          .width(20)
          .height(20)
          .fill(this.clockInDays.includes(day) ? Color.Green : Color.Gray)
      }
    }
  })
}
.columnsTemplate('1fr 1fr 1fr 1fr 1fr 1fr 1fr')
.rowsGap(10)
.columnsGap(5)
.margin(10)
  1. 打卡动画
// 添加动画效果
@State isAnimating: boolean = false

Button('点击打卡')
  .scale(this.isAnimating ? 1.2 : 1)
  .animation({ duration: 500, curve: Curve.EaseInOut })
  .onClick(() => {
    this.isAnimating = true;
    this.onClockIn();
    setTimeout(() => this.isAnimating = false, 500);
  })

4. 注意事项

  1. 时区处理

    • 使用 new Date().toLocaleDateString('zh-CN') 获取本地日期
    • 确保设备时区设置正确
  2. 数据安全

    • clockInDays 数组操作时使用不可变数据(spread operator)
    • 添加异常捕获处理存储操作
  3. 性能优化

    • 当打卡记录较多时,使用虚拟列表(LazyForEach)
    • 对日期计算使用缓存机制

如果需要更复杂的日历组件,可以参考鸿蒙的 Calendar 组件 进行扩展。

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐