HarmonyOS开发案例:【生活健康app之实现打卡功能】(2)

电子说

1.3w人已加入

描述

实现打卡功能

英国威廉希尔公司网站 会展示当前用户已经开启的任务列表,每条任务会显示对应的任务名称以及任务目标、当前任务完成情况。用户只可对当天任务进行打卡操作,用户可以根据需要对任务列表中相应的任务进行点击打卡。如果任务列表中的每个任务都在当天完成则为连续打卡一天,连续打卡多天会获得成就徽章。打卡效果如下图所示:

OpenHarmony

开发前请熟悉鸿蒙开发指导文档:[qr23.cn/FBD4cY]。

任务列表

使用List组件展示用户当前已经开启的任务,每条任务对应一个TaskCard组件,clickAction包装了点击和长按事件,用户点击任务卡时会触发弹起打卡弹窗,从而进行打卡操作;长按任务卡时会跳转至任务编辑界面,对相应的任务进行编辑处理。代码如下:

// HomeComponent.ets
// 任务列表
ForEach(this.homeStore.getTaskListOfDay(), (item: TaskInfo) = > {
  TaskCard({
    taskInfoStr: JSON.stringify(item),
    clickAction: (isClick: boolean) = > this.taskItemAction(item, isClick)
  })
  .margin({ bottom: Const.DEFAULT_12 })
  .height($r('app.float.default_64'))
}, (item: TaskInfo) = > JSON.stringify(item))
...
CustomDialogView() // 自定义弹窗中间件

自定义弹窗中间件CustomDialogView

在组件CustomDialogView的aboutToAppear生命周期中注册SHOW_TASK_DETAIL_DIALOG的事件回调方法 ,当通过emit触发此事件时即触发回调方法执行。代码如下:

// CustomDialogView.ets
export class CustomDialogCallback {
  confirmCallback: Function = () = > {};
  cancelCallback: Function = () = > {};
}

@Component
export struct CustomDialogView {
  @State isShow: boolean = false;
  @Provide achievementLevel: number = 0;
  @Consume broadCast: BroadCast;
  @Provide currentTask: TaskInfo = TaskItem;
  @Provide dialogCallBack: CustomDialogCallback = new CustomDialogCallback();

  // 成就对话框
  achievementDialog: CustomDialogController = new CustomDialogController({
    builder: AchievementDialog(),
    autoCancel: true,
    customStyle: true
  });

  // 任务时钟对话框
  taskDialog: CustomDialogController = new CustomDialogController({
    builder: TaskDetailDialog(),
    autoCancel: true,
    customStyle: true
  });

  aboutToAppear() {
    Logger.debug('CustomDialogView', 'aboutToAppear');
    // 成就对话框
    this.broadCast.on(BroadCastType.SHOW_ACHIEVEMENT_DIALOG, (achievementLevel: number) = > {
      Logger.debug('CustomDialogView', 'SHOW_ACHIEVEMENT_DIALOG');
      this.achievementLevel = achievementLevel;
      this.achievementDialog.open();
    });

    // 任务时钟对话框
    this.broadCast.on(BroadCastType.SHOW_TASK_DETAIL_DIALOG,
      (currentTask: TaskInfo, dialogCallBack: CustomDialogCallback) = > {
        Logger.debug('CustomDialogView', 'SHOW_TASK_DETAIL_DIALOG');
        this.currentTask = currentTask || TaskItem;
        this.dialogCallBack = dialogCallBack;
        this.taskDialog.open();
    });
  }

  aboutToDisappear() {
    Logger.debug('CustomDialogView', 'aboutToDisappear');
  }

  build() {
  }
}

点击任务卡片

点击任务卡片会emit触发 “SHOW_TASK_DETAIL_DIALOG” 事件,同时把当前任务,以及确认打卡回调方法传递下去。代码如下:

// HomeComponent.ets
// 任务卡片事件
taskItemAction(item: TaskInfo, isClick: boolean): void {
  ...
  if (isClick) {
    // 点击任务打卡
     let callback: CustomDialogCallback = { confirmCallback: (taskTemp: TaskInfo) = > {
        this.onConfirm(taskTemp)
     }, cancelCallback: () = > {
     } };
    // 触发弹出打卡弹窗事件  并透传当前任务参数(item) 以及确认打卡回调
     this.broadCast.emit(BroadCastType.SHOW_TASK_DETAIL_DIALOG, [item, callback]);
  } else {
    // 长按编辑任务
    ...
  }
}
// 确认打卡
onConfirm(task) {
  this.homeStore.taskClock(task).then((res: AchievementInfo) = > {
    // 打卡成功后 根据连续打卡情况判断是否 弹出成就勋章  以及成就勋章级别
    if (res.showAchievement) {
      // 触发弹出成就勋章SHOW_ACHIEVEMENT_DIALOG 事件, 并透传勋章类型级别
      let achievementLevel = res.achievementLevel;
      if (achievementLevel) {
        this.broadCast.emit(BroadCastType.SHOW_ACHIEVEMENT_DIALOG, achievementLevel);
      } else {
        this.broadCast.emit(BroadCastType.SHOW_ACHIEVEMENT_DIALOG);
      }
    }
  })
}

打卡弹窗组件TaskDetailDialog

打卡弹窗组件根据当前任务的ID获取任务名称以及弹窗背景图片资源。

打卡弹窗组件由两个小组件构成,代码如下:

// TaskDetailDialog.ets
Column() {
  // 展示任务的基本信息
  TaskBaseInfo({
    taskName: TaskMapById[this.currentTask?.taskID - 1].taskName  // 根据当前任务ID获取任务名称
  });
  // 打卡功能组件 (任务打卡、关闭弹窗)
  TaskClock({
    confirm: () = > {
      this.dialogCallBack.confirmCallback(this.currentTask);
      this.controller.close();
    },
    cancel: () = > {
      this.controller.close();
    },
    showButton: this.showButton
  })
}
...

TaskBaseInfo组件代码如下:

// TaskDetailDialog.ets
@Component
struct TaskBaseInfo {
  taskName: string | Resource = '';

  build() {
    Column({ space: Const.DEFAULT_8 }) {
      Text(this.taskName)
        .fontSize($r('app.float.default_22'))
        .fontWeight(FontWeight.Bold)
        .fontFamily($r('app.string.HarmonyHeiTi_Bold'))
        .taskTextStyle()
        .margin({left: $r('app.float.default_12')})
    }
    .position({ y: $r('app.float.default_267') })
  }
}

TaskClock组件代码如下:

// TaskDetailDialog.ets
@Component
struct TaskClock {
  confirm: Function = () = > {};
  cancel: Function = () = > {};
  showButton: boolean = false;

  build() {
    Column({ space: Const.DEFAULT_12 }) {
      Button() {
        Text($r('app.string.clock_in'))
          .height($r('app.float.default_42'))
          .fontSize($r('app.float.default_20'))
          .fontWeight(FontWeight.Normal)
          .textStyle()
      }
      .width($r('app.float.default_220'))
      .borderRadius($r('app.float.default_24'))
      .backgroundColor('rgba(255,255,255,0.40)')
      .onClick(() = > {
        GlobalContext.getContext().setObject('taskListChange', true);
        this.confirm();
      })
      .visibility(!this.showButton ? Visibility.None : Visibility.Visible)
      Text($r('app.string.got_it'))
        .fontSize($r('app.float.default_14'))
        .fontWeight(FontWeight.Regular)
        .textStyle()
        .onClick(() = > {
          this.cancel();
        })
    }
  }
}

打卡接口调用

// HomeViewModel.ets
public async taskClock(taskInfo: TaskInfo) {
  let taskItem = await this.updateTask(taskInfo);
  let dateStr = this.selectedDayInfo?.dateStr;
  // 更新任务失败
  if (!taskItem) {
    return {
      achievementLevel: 0,
      showAchievement: false
    } as AchievementInfo;
  }
  // 更新当前时间的任务列表
  this.selectedDayInfo.taskList = this.selectedDayInfo.taskList.map((item) = > {
    return item.taskID === taskItem?.taskID ? taskItem : item;
  });
  let achievementLevel: number = 0;
  if(taskItem.isDone) {
    // 更新每日任务完成情况数据
    let dayInfo = await this.updateDayInfo();
    ... 
    // 当日任务完成数量等于总任务数量时 累计连续打卡一天
    // 更新成就勋章数据 判断是否弹出获得勋章弹出及勋章类型
    if (dayInfo && dayInfo?.finTaskNum === dayInfo?.targetTaskNum) {
      achievementLevel = await this.updateAchievement(this.selectedDayInfo.dayInfo);
    }
  }
  ...
  return {
    achievementLevel: achievementLevel,
    showAchievement: ACHIEVEMENT_LEVEL_LIST.includes(achievementLevel)
  } as AchievementInfo;
}

`HarmonyOS与OpenHarmony鸿蒙文档籽料:mau123789是v直接拿`

OpenHarmony

// HomeViewModel.ets
// 更新当天任务列表
updateTask(task: TaskInfo): Promise< TaskInfo > {
  return new Promise((resolve, reject) = > {
    let taskID = task.taskID;
    let targetValue = task.targetValue;
    let finValue = task.finValue;
    let updateTask = new TaskInfo(task.id, task.date, taskID, targetValue, task.isAlarm, task.startTime,
      task.endTime, task.frequency, task.isDone, finValue, task.isOpen);
    let step = TaskMapById[taskID - 1].step; // 任务步长
    let hasExceed = updateTask.isDone;
    if (step === 0) { // 任务步长为0 打卡一次即完成该任务
      updateTask.isDone = true; // 打卡一次即完成该任务
      updateTask.finValue = targetValue;
    } else {
      let value = Number(finValue) + step; // 任务步长非0 打卡一次 步长与上次打卡进度累加
      updateTask.isDone = updateTask.isDone || value >= Number(targetValue); // 判断任务是否完成
      updateTask.finValue = updateTask.isDone ? targetValue : `${value}`;
    }
    TaskInfoTableApi.updateDataByDate(updateTask, (res: number) = > { // 更新数据库
      if (!res || hasExceed) {
        Logger.error('taskClock-updateTask', JSON.stringify(res));
        reject(res);
      }
      resolve(updateTask);
    })
  })
}

审核编辑 黄宇

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分