HarmonyOS 应用开发基础案例(十三):完成任务项设置

17 阅读9分钟

案例效果

资源文件与初始化

string.json

{
  "string": [
    {
      "name": "entry_desc",
      "value": "description"
    },
    {
      "name": "entryAbility_desc",
      "value": "description"
    },
    {
      "name": "entryAbility_label",
      "value": "List_HDC"
    },
    {
      "name": "task_morning",
      "value": "早起"
    },
    {
      "name": "task_water",
      "value": "喝水"
    },
    {
      "name": "task_apple",
      "value": "吃苹果"
    },
    {
      "name": "task_smile",
      "value": "每日微笑"
    },
    {
      "name": "task_brush",
      "value": "每日刷牙"
    },
    {
      "name": "task_night",
      "value": "早睡"
    },
    {
      "name": "already_open",
      "value": "已开启"
    },
    {
      "name": "complete",
      "value": "完成"
    },
    {
      "name": "frequency",
      "value": "频率"
    },
    {
      "name": "remind_time",
      "value": "提醒时间"
    },
    {
      "name": "open_reminder",
      "value": "开启提醒"
    },
    {
      "name": "target_setting",
      "value": "目标设置"
    },
    {
      "name": "cancel",
      "value": "取消"
    },
    {
      "name": "confirm",
      "value": "确认"
    },
    {
      "name": "set_your_frequency",
      "value": "请设置您的频率"
    }
  ]
}

color.json

{
  "color": [
    {
      "name": "white",
      "value": "#FFFFFF"
    },
    {
      "name": "primaryBgColor",
      "value": "#F1F3F5"
    },
    {
      "name": "titleColor",
      "value": "#182431"
    },
    {
      "name": "btnBgColor",
      "value": "#F2F2F2"
    },
    {
      "name": "statusTipColor",
      "value": "#989A9C"
    },
    {
      "name": "blueColor",
      "value": "#007DFF"
    },
    {
      "name": "black",
      "value": "#000000"
    },
    {
      "name": "primaryRed",
      "value": "#E92F4F"
    },
    {
      "name": "tabTitleColor",
      "value": "#999"
    },
    {
      "name": "signatureColor",
      "value": "#66686a"
    },
    {
      "name": "leveColor",
      "value": "#c99411"
    },
    {
      "name": "leveBgColor",
      "value": "#d4e6f1"
    },
    {
      "name": "borderColor",
      "value": "#cccccc"
    },
    {
      "name": "mineBgColor",
      "value": "#edf2f5"
    },
    {
      "name": "launcherBlueColor",
      "value": "#4694C2"
    },
    {
      "name": "disabledColor",
      "value": "#dddadc"
    }
  ]
}

CommonConstant

// ets/common/contants/CommonConstant.ets

export const THOUSANDTH_80: string = '8%'
export const THOUSANDTH_100: string = '10%'
export const THOUSANDTH_400: string = '40%'
export const THOUSANDTH_500: string = '50%'
export const THOUSANDTH_560: string = '56%'
export const THOUSANDTH_800: string = '80%'
export const THOUSANDTH_900: string = '90%'
export const THOUSANDTH_940: string = '94%'
export const THOUSANDTH_1000: string = '100%'

export const DEFAULT_2: number = 2
export const DEFAULT_8: number = 8
export const DEFAULT_12: number = 12
export const DEFAULT_10: number = 10
export const DEFAULT_16: number = 16
export const DEFAULT_18: number = 18
export const DEFAULT_20: number = 20
export const DEFAULT_24: number = 24
export const DEFAULT_28: number = 28
export const DEFAULT_32: number = 32
export const DEFAULT_48: number = 48
export const DEFAULT_56: number = 56
export const DEFAULT_60: number = 60

export const LIST_ITEM_SPACE: number = 2

export const ADD_TASK_TITLE: string = '添加任务'
export const EDIT_TASK_TITLE: string = '编辑任务'

export const SETTING_FINISHED_MESSAGE = '设置完成!!!'
export const CHOOSE_TIME_OUT_RANGE: string = '选择时间超出范围'

export const TODAY: string = new Date().toDateString()

export const DEFAULT_TIME: string = '08:00'
export const GET_UP_TIME_RANGE: string = '(06:00 - 09:00)'
export const SLEEP_TIME_RANGE: string = '(20:00 - 23:00)'
export const GET_UP_EARLY_TIME: string = '06:00'
export const GET_UP_LATE_TIME: string = '09:00'
export const SLEEP_EARLY_TIME: string = '20:00'
export const SLEEP_LATE_TIME: string = '23:00'
export const DEFAULT_SELECTED_TIME: Date = new Date(`${TODAY} 8:00:00`)

export const EVERYDAY: string = '每天'
export const NO_LENGTH: number = 0
export const INIT_WEEK_IDS: string = '1, 2, 3, 4, 5, 6, 7'

export const PER_DAY: string = '/ 天'

export const ZERO: number = 0
export const MINUS_20: number = -20
export const HAS_NO_INDEX: number = -1

export const DRINK_STEP: number = 25
export const DRINK_MAX_RANGE: number = 500
export const TIMES_100: number = 100
export const EAT_APPLE_RANGE: number = 100
export const DEFAULT_TEXT: string = '0.25'
export const DEFAULT_APPLE: string = '1'

添加任务

首页组件

// ets/pages/Index.ets

import TaskList from '../view/TaskList'
import { TaskListItem, TaskInitList } from '../model/TaskInitList'
import { THOUSANDTH_1000, ADD_TASK_TITLE } from '../common/constants/CommonConstant'

@Entry
@Component
struct Index {
  @Provide taskList: TaskListItem[] = TaskInitList

  build() {
    Row() {
      Navigation() {
        Column() {
          TaskList()
        }
        .width(THOUSANDTH_1000)
        .justifyContent(FlexAlign.Center)
      }
      .size({ width: THOUSANDTH_1000, height: THOUSANDTH_1000 })
      .title(ADD_TASK_TITLE)
      .titleMode(NavigationTitleMode.Mini)
    }
    .backgroundColor($r('app.color.primaryBgColor'))
    .height(THOUSANDTH_1000)
  }
}

任务列表初始化

// ets/model/TaskInitList.ets

export interface TaskListItem {
  taskID: number
  taskName: Resource
  isOpen: boolean
  unit: string
  icon: Resource
  targetValue: string
  isAlarm: boolean
  startTime: string
  frequency: string
}

export interface FrequencyContentType {
  id: number,
  label: string,
  isChecked: boolean
}

export const TaskInitList: TaskListItem[] = [
  { // Get up early.
    taskID: 1,
    taskName: $r('app.string.task_morning'),
    icon: $r('app.media.morning'),
    targetValue: '08: 00',
    isOpen: true,
    unit: '',
    isAlarm: false,
    startTime: '08: 00',
    frequency: '1, 2, 3, 4, 5, 6, 7'
  },
  { // Drink water.
    taskID: 2,
    taskName: $r('app.string.task_water'),
    icon: $r('app.media.water'),
    targetValue: '0.25',
    isOpen: true,
    unit: 'L',
    isAlarm: false,
    startTime: '08: 00',
    frequency: '1, 2, 3, 4, 5, 6, 7'
  },
  { // Eat apples.
    taskID: 3,
    taskName: $r('app.string.task_apple'),
    icon: $r('app.media.apple'),
    targetValue: '1',
    isOpen: true,
    unit: '个',
    isAlarm: false,
    startTime: '08: 00',
    frequency: '1, 2, 3, 4, 5, 6, 7'
  },
  { // Smile every day.
    taskID: 4,
    taskName: $r('app.string.task_smile'),
    icon: $r('app.media.smile'),
    targetValue: '1',
    isOpen: false,
    unit: '次',
    isAlarm: false,
    startTime: '08: 00',
    frequency: '1, 2, 3, 4, 5, 6, 7'
  },
  { // Clean one’s teeth.
    taskID: 5,
    taskName: $r('app.string.task_brush'),
    icon: $r('app.media.brush'),
    targetValue: '1',
    isOpen: false,
    unit: '次',
    isAlarm: false,
    startTime: '08: 00',
    frequency: '1, 2, 3, 4, 5, 6, 7'
  },
  { // Go to bed early.
    taskID: 6,
    taskName: $r('app.string.task_night'),
    icon: $r('app.media.night'),
    targetValue: '20: 00',
    isOpen: false,
    unit: '',
    isAlarm: false,
    startTime: '08: 00',
    frequency: '1, 2, 3, 4, 5, 6, 7'
  }
]

任务列表视图

// ets/view/TaskList.ets

import * as commonConst from '../common/constants/CommonConstant'
import { TaskListItem } from '../model/TaskInitList'

@Component
export default struct TaskList {
  @Consume taskList: TaskListItem[]

  build() {
    List({ space: commonConst.LIST_ITEM_SPACE }) {
      ForEach(this.taskList, (item: TaskListItem) => {
        ListItem() {
          Row() {
            Row() {
              Image(item?.icon)
                .width(commonConst.DEFAULT_24)
                .height(commonConst.DEFAULT_24)
                .margin({ right: commonConst.DEFAULT_8 })
              Text(item?.taskName)
                .fontSize(commonConst.DEFAULT_20)
                .fontColor($r('app.color.titleColor'))
            }
            .width(commonConst.THOUSANDTH_500)

            Blank()
              .layoutWeight(1)

            if (item?.isOpen) {
              Text($r('app.string.already_open'))
                .fontSize(commonConst.DEFAULT_16)
                .flexGrow(1)
                .align(Alignment.End)
                .margin({ right: commonConst.DEFAULT_8 })
                .fontColor($r('app.color.titleColor'))
            }

            Image($r('app.media.right_grey'))
              .width(commonConst.DEFAULT_8)
              .height(commonConst.DEFAULT_16)
          }
          .width(commonConst.THOUSANDTH_1000)
          .justifyContent(FlexAlign.SpaceBetween)
          .padding({ left: commonConst.DEFAULT_12, right: commonConst.DEFAULT_12 })
        }
        .height(commonConst.THOUSANDTH_80)
        .borderRadius(commonConst.DEFAULT_12)
        .backgroundColor($r('app.color.white'))
      })
    }
    .height(commonConst.THOUSANDTH_1000)
    .width(commonConst.THOUSANDTH_940)
  }
}

任务编辑页

添加跳转

// ets/view/TaskList.ets

import * as commonConst from '../common/constants/CommonConstant'
import { TaskListItem } from '../model/TaskInitList'
import { router } from '@kit.ArkUI'
import { formatParams } from '../viewModel/TaskTargetSetting'

@Component
export default struct TaskList {
  @Consume taskList: TaskListItem[]

  build() {
    List({ space: commonConst.LIST_ITEM_SPACE }) {
      ForEach(this.taskList, (item: TaskListItem) => {
        ListItem() {
          Row() {
            Row() {
              Image(item?.icon)
                .width(commonConst.DEFAULT_24)
                .height(commonConst.DEFAULT_24)
                .margin({ right: commonConst.DEFAULT_8 })
              Text(item?.taskName)
                .fontSize(commonConst.DEFAULT_20)
                .fontColor($r('app.color.titleColor'))
            }
            .width(commonConst.THOUSANDTH_500)

            Blank()
              .layoutWeight(1)

            if (item?.isOpen) {
              Text($r('app.string.already_open'))
                .fontSize(commonConst.DEFAULT_16)
                .flexGrow(1)
                .align(Alignment.End)
                .margin({ right: commonConst.DEFAULT_8 })
                .fontColor($r('app.color.titleColor'))
            }

            Image($r('app.media.right_grey'))
              .width(commonConst.DEFAULT_8)
              .height(commonConst.DEFAULT_16)
          }
          .width(commonConst.THOUSANDTH_1000)
          .justifyContent(FlexAlign.SpaceBetween)
          .padding({ left: commonConst.DEFAULT_12, right: commonConst.DEFAULT_12 })
        }
        .height(commonConst.THOUSANDTH_80)
        .borderRadius(commonConst.DEFAULT_12)
        .backgroundColor($r('app.color.white'))
        
        // 1. 添加链接
        .onClick(() => {
          router.pushUrl({
            url: 'pages/TaskEditPage',
            params: {
              params: formatParams(item)
            }
          })
          
        })
      })
    }
    .height(commonConst.THOUSANDTH_1000)
    .width(commonConst.THOUSANDTH_940)
  }
}

任务目标设置模型(formatParams)

// ets/viewModel/TaskTargetSetting

import { TaskListItem } from '../model/TaskInitList'
export const formatParams = (params: TaskListItem) => {
  return JSON.stringify(params)
}

编辑页面

// ets/pages/TaskEditPage.ets

import { THOUSANDTH_1000, EDIT_TASK_TITLE } from '../common/constants/CommonConstant'
import TaskDetail from '../view/TaskDetail'

@Entry
@Component
struct TaskEdit {
  build() {
    Row() {
      Navigation() {
        Column() {
          TaskDetail()
        }
        .width(THOUSANDTH_1000)
        .height(THOUSANDTH_1000)
      }
      .size({ width: THOUSANDTH_1000, height: THOUSANDTH_1000 })
      .title(EDIT_TASK_TITLE)
      .titleMode(NavigationTitleMode.Mini)
    }
    .height(THOUSANDTH_1000)
    .backgroundColor($r('app.color.primaryBgColor'))
  }
}

详情页

// ets/view/TaskDetail.ets

import * as commonConst from '../common/constants/CommonConstant'
import { TaskListItem } from '../model/TaskInitList'
import {
  TaskChooseItem
} from './TaskEditListItem'
import { router } from '@kit.ArkUI'

@Styles
function listItemStyle() {
  .backgroundColor($r('app.color.white'))
  .height(commonConst.DEFAULT_56)
  .borderRadius(commonConst.DEFAULT_10)
  .padding({ left: commonConst.DEFAULT_12, right: commonConst.DEFAULT_12 })
}

@Component
export default struct TaskDetail {
  @Provide settingParams: TaskListItem = this.parseRouterParams()

  parseRouterParams() {
    let params = router.getParams() as Record<string, Object>
    const routerParams: TaskListItem = JSON.parse(params.params as string)
    return routerParams
  }

  build() {
    Column() {
      List({ space: commonConst.LIST_ITEM_SPACE }) {
        ListItem() {
          TaskChooseItem()
        }
        .listItemStyle()
      }
    }
    .width(commonConst.THOUSANDTH_1000)
  }
}

任务编辑列表项

// ets/view/TaskEditListItem.ets

import { TaskListItem } from '../model/TaskInitList'
import {
  DEFAULT_20,
  DEFAULT_32,
  DEFAULT_56,
  THOUSANDTH_1000,
} from '../common/constants/CommonConstant'

@Component
export struct TaskChooseItem {
  @Consume settingParams: TaskListItem

  build() {
    Row() {
      Text(this.settingParams.taskName).fontSize(DEFAULT_20).fontWeight(FontWeight.Medium)
      Toggle({ type: ToggleType.Switch, isOn: this.settingParams.isOpen })
        .width(DEFAULT_56)
        .height(DEFAULT_32)
        .selectedColor($r('app.color.blueColor'))
        .onChange((isOn: boolean) => {
          this.settingParams.isOpen = isOn;
        })
    }
    .width(THOUSANDTH_1000)
    .justifyContent(FlexAlign.SpaceBetween)
  }
}

目标设置展示

引入目标设置

// ets/view/TaskDetail.ets

import * as commonConst from '../common/constants/CommonConstant'
import { TaskListItem } from '../model/TaskInitList'
import {
  TaskChooseItem,
  // 2. 引入TargetSetItem
  TargetSetItem
} from './TaskEditListItem'
import { router } from '@kit.ArkUI'
import { taskType } from '../model/TaskInfo'

@Styles
function listItemStyle() {
  .backgroundColor($r('app.color.white'))
  .height(commonConst.DEFAULT_56)
  .borderRadius(commonConst.DEFAULT_10)
  .padding({ left: commonConst.DEFAULT_12, right: commonConst.DEFAULT_12 })
}

@Component
export default struct TaskDetail {
  @Provide settingParams: TaskListItem = this.parseRouterParams()

  parseRouterParams() {
    let params = router.getParams() as Record<string, Object>
    const routerParams: TaskListItem = JSON.parse(params.params as string)
    return routerParams
  }

  build() {
    Column() {
      List({ space: commonConst.LIST_ITEM_SPACE }) {
        ListItem() {
          TaskChooseItem()
        }
        .listItemStyle()

        // 1. 目标设置入口
        ListItem() {
          TargetSetItem()
        }
        .listItemStyle()
        .enabled(
          this.settingParams?.isOpen &&
            (this.settingParams?.taskID !== taskType.smile) &&
            (this.settingParams?.taskID !== taskType.brushTeeth)
        )
      }
      .width(commonConst.THOUSANDTH_940)
    }
    .width(commonConst.THOUSANDTH_1000)
  }
}

目标设置展示实现

// ets/view/TaskEditListItem.ets

import { TaskListItem } from '../model/TaskInitList'
import {
  DEFAULT_16,
  DEFAULT_20,
  DEFAULT_32,
  DEFAULT_56,
  DEFAULT_8,
  PER_DAY,
  THOUSANDTH_1000,
} from '../common/constants/CommonConstant'
import { taskType } from '../model/TaskInfo'

// 2.定义公共样式targetSetCommon
@Extend(Text)
function targetSetCommon() {
  .fontSize(DEFAULT_16)
  .flexGrow(1)
  .margin({ right: DEFAULT_8 })
  .align(Alignment.End)
}

// 2.定义公共样式targetSettingStyle
@Extend(Text)
function targetSettingStyle(isOpen: boolean, taskID: number) {
  .fontColor(isOpen && taskID !== taskType.smile && taskID !== taskType.brushTeeth ?
  $r('app.color.titleColor') :
  $r('app.color.disabledColor'))
}

@Component
export struct TaskChooseItem {
  @Consume settingParams: TaskListItem

  build() {
    Row() {
      Text(this.settingParams.taskName).fontSize(DEFAULT_20).fontWeight(FontWeight.Medium)
      Toggle({ type: ToggleType.Switch, isOn: this.settingParams.isOpen })
        .width(DEFAULT_56)
        .height(DEFAULT_32)
        .selectedColor($r('app.color.blueColor'))
        .onChange((isOn: boolean) => {
          this.settingParams.isOpen = isOn;
        })
    }
    .width(THOUSANDTH_1000)
    .justifyContent(FlexAlign.SpaceBetween)
  }
}

// 1. 定义TargetSetItem组件
@Component
export struct TargetSetItem {
  @Consume settingParams: TaskListItem;

  build() {
    Row() {
      Text($r('app.string.target_setting'))
        .fontSize(DEFAULT_20)
        .fontWeight(FontWeight.Medium)

      Blank()
        .layoutWeight(1)

      if (this.settingParams?.unit === '') {
        Text(`${this.settingParams?.targetValue}`)
          .targetSetCommon()
          .targetSettingStyle(this.settingParams?.isOpen, this.settingParams?.taskID)
      } else {
        Text(`${this.settingParams?.targetValue} ${this.settingParams?.unit} ${PER_DAY}`)
          .targetSetCommon()
          .targetSettingStyle(this.settingParams?.isOpen, this.settingParams?.taskID)
      }
      Image($r('app.media.right_grey')).width(DEFAULT_8).height(DEFAULT_16);
    }
    .width(THOUSANDTH_1000)
    .justifyContent(FlexAlign.SpaceBetween)
  }
}

TaskInfo 枚举模型设置

// ets/model/TaskInfo.ets

export enum taskType {
  'getup' = 1,
  'drinkWater',
  'eatApple',
  'smile',
  'brushTeeth',
  'sleepEarly'
}

弹窗构造逻辑

定义单击事件

import * as commonConst from '../common/constants/CommonConstant'
import { TaskListItem } from '../model/TaskInitList'
import {
  TaskChooseItem,
  TargetSetItem
} from './TaskEditListItem'
import { router } from '@kit.ArkUI'
import { taskType } from '../model/TaskInfo'

// 4. 引入BroadCast(先去创建)
import { BroadCast, BroadCastType } from '../common/utils/BroadCast'

import { CustomDialogView } from './CustomDialogView'

@Styles
function listItemStyle() {
  .backgroundColor($r('app.color.white'))
  .height(commonConst.DEFAULT_56)
  .borderRadius(commonConst.DEFAULT_10)
  .padding({ left: commonConst.DEFAULT_12, right: commonConst.DEFAULT_12 })
}

@Component
export default struct TaskDetail {
  @Provide settingParams: TaskListItem = this.parseRouterParams()

  // 5. 提供 broadCast
  @Provide broadCast: BroadCast = new BroadCast()

  parseRouterParams() {
    let params = router.getParams() as Record<string, Object>
    const routerParams: TaskListItem = JSON.parse(params.params as string)
    return routerParams
  }

  // 7. 先去定义弹窗和builder,注册(on)完,这里解绑
  aboutToAppear() {
    this.broadCast.off()
  }

  build() {
    Column() {
      List({ space: commonConst.LIST_ITEM_SPACE }) {
        ListItem() {
          TaskChooseItem()
        }
        .listItemStyle()

        ListItem() {
          TargetSetItem()
        }
        .listItemStyle()
          
        // 3. 设置 smile & brushTeeth 不可单击
        .enabled(
          this.settingParams?.isOpen &&
            (this.settingParams?.taskID !== taskType.smile) &&
            (this.settingParams?.taskID !== taskType.brushTeeth)
        )
        // 1. 定义单击事件
        .onClick(() => {
          // 2. 测试单击,目的是引出第 3 步
          // console.log('test')

          // 8. 最后再触发
          this.broadCast.emit(
            BroadCastType.SHOW_TARGET_SETTING_DIALOG)
        })

        // 9. 测试提醒时间设置
        ListItem() {
          Text('提醒时间')
        }
        .listItemStyle()
        .enabled(this.settingParams?.isOpen && this.settingParams?.isAlarm)
        .onClick(() => {
          this.broadCast.emit(
            BroadCastType.SHOW_REMIND_TIME_DIALOG
          )
        })

        // 9. 测试频率设置
        ListItem() {
          Text('频率')
        }
        .listItemStyle()
        .enabled(this.settingParams?.isOpen)
        .onClick(() => {
          this.broadCast.emit(
            BroadCastType.SHOW_FREQUENCY_DIALOG
          )
        })
      }

      // 6.定义弹框视图(先去创建)
      CustomDialogView()
    }
    .width(commonConst.THOUSANDTH_1000)
  }
}

定义 BroadCast

// ets/common/util/BroadCast.ets

export class BroadCast {
  private callBackArray = []

  public on(event: string, callback: Function) {
    (this.callBackArray[event] || (this.callBackArray[event] = [])).push(callback)
  }

  public off() {
    this.callBackArray = []
  }

  public emit(event: string) {
    let _self = this
    if (!this.callBackArray[event]) {
      return
    }
    let cbs: Function[] = this.callBackArray[event]
    if (cbs) {
      let len = cbs.length;
      for (let i = 0; i < len; i++) {
        try {
          cbs[i](_self)
        } catch (e) {
          new Error(e)
        }
      }
    }
  }
}

export enum BroadCastType {
  SHOW_TARGET_SETTING_DIALOG = 'showTargetSettingDialog',
  SHOW_REMIND_TIME_DIALOG = 'showRemindTimeDialog',
  SHOW_FREQUENCY_DIALOG = 'showFrequencyDialog'
}

定义弹窗视图

// ets/view/CustomDialogView.ets

import { TargetSettingDialog, RemindTimeDialog, FrequencyDialog } from './TaskSettingDialog'
import { BroadCast, BroadCastType } from '../common/utils/BroadCast'
import { ZERO, MINUS_20 } from '../common/constants/CommonConstant'

@Component
export struct CustomDialogView {
  @State isShow: boolean = false
  @Provide achievementLevel: number = 3
  @Consume broadCast: BroadCast

  targetSettingDialogController: CustomDialogController = new CustomDialogController({
    builder: TargetSettingDialog(),
    autoCancel: true,
    alignment: DialogAlignment.Bottom,
    offset: { dx: ZERO, dy: MINUS_20 }
  })

  RemindTimeDialogController: CustomDialogController = new CustomDialogController({
    builder: RemindTimeDialog(),
    autoCancel: true,
    alignment: DialogAlignment.Bottom,
    offset: { dx: ZERO, dy: MINUS_20 }
  });

  FrequencyDialogController: CustomDialogController = new CustomDialogController({
    builder: FrequencyDialog(),
    autoCancel: true,
    alignment: DialogAlignment.Bottom,
    offset: { dx: ZERO, dy: MINUS_20 }
  })

  aboutToAppear() {
    let self = this

    this.broadCast.on(
      BroadCastType.SHOW_TARGET_SETTING_DIALOG,
      () => {
        self.targetSettingDialogController.open()
      })

    this.broadCast.on(
      BroadCastType.SHOW_REMIND_TIME_DIALOG,
      () => {
        self.RemindTimeDialogController.open()
      })

    this.broadCast.on(
      BroadCastType.SHOW_FREQUENCY_DIALOG,
      () => {
        self.FrequencyDialogController.open()
      })
  }

  build() {
  }
}

定义弹窗 builder 组件

// ets/view/TaskSettingDialog.ets

import * as commonConst from '../common/constants/CommonConstant'

@CustomDialog
export struct TargetSettingDialog {
  controller: CustomDialogController = new CustomDialogController({
    builder: TargetSettingDialog()
  })

  build() {
    Column() {
      Text('target setting dialog')
    }
    .justifyContent(FlexAlign.Center)
    .height(commonConst.THOUSANDTH_560)
    .padding(commonConst.DEFAULT_12)
  }
}

@CustomDialog
export struct RemindTimeDialog {
  controller: CustomDialogController = new CustomDialogController({
    builder: RemindTimeDialog()
  })

  build() {
    Column() {
      Text('remind time dialog')
    }
    .justifyContent(FlexAlign.Center)
    .height(commonConst.THOUSANDTH_560)
    .padding(commonConst.DEFAULT_12)
  }
}

@CustomDialog
export struct FrequencyDialog {
  controller: CustomDialogController = new CustomDialogController({
    builder: FrequencyDialog()
  })

  build() {
    Column() {
      Text('frequency dialog')
    }
    .justifyContent(FlexAlign.Center)
    .height(commonConst.THOUSANDTH_560)
    .padding(commonConst.DEFAULT_12)
  }
}

目标设置弹窗实现

目标设置窗口逻辑

// ets/view/TaskSettingDialog.ets

import * as commonConst from '../common/constants/CommonConstant'
import { taskType } from '../model/TaskInfo'
import { TaskListItem } from '../model/TaskInitList'
import { createAppleRange, createDrinkRange, formatTime, returnTimeStamp } from '../viewModel/TaskTargetSetting'
import { promptAction } from '@kit.ArkUI'

@CustomDialog
export struct TargetSettingDialog {
  controller: CustomDialogController = new CustomDialogController({
    builder: TargetSettingDialog()
  })

  @Consume settingParams: TaskListItem

  currentTime: string = commonConst.DEFAULT_TIME
  currentValue: string = this.settingParams.taskID === taskType.drinkWater ? commonConst.DEFAULT_TEXT :
  commonConst.DEFAULT_APPLE

  drinkRange: string[] = createDrinkRange()
  appleRange: string[] = createAppleRange()

  createSubTitle() {
    if (this.settingParams.taskID === taskType.getup) {
      return commonConst.GET_UP_TIME_RANGE
    }
    if (this.settingParams.taskID === taskType.sleepEarly) {
      return commonConst.SLEEP_TIME_RANGE
    }
    return ''
  }

  setTargetValue() {
    if (this.settingParams.taskID === taskType.getup) {
      if (!this.compareTime(commonConst.GET_UP_EARLY_TIME, commonConst.GET_UP_LATE_TIME)) {
        return
      }
      this.settingParams.targetValue = this.currentTime
      return
    }

    if (this.settingParams.taskID === taskType.sleepEarly) {
      if (!this.compareTime(commonConst.SLEEP_EARLY_TIME, commonConst.SLEEP_LATE_TIME)) {
        return
      }
      this.settingParams.targetValue = this.currentTime
      return
    }
    this.settingParams.targetValue = this.currentValue
  }

  compareTime(startTime: string, endTime: string) {
    if (returnTimeStamp(this.currentTime) < returnTimeStamp(startTime) ||
      returnTimeStamp(this.currentTime) > returnTimeStamp(endTime)) {

      promptAction.showToast({
        message: commonConst.CHOOSE_TIME_OUT_RANGE
      })
      return false
    }
    return true
  }

  build() {
    Column() {
      Row() {
        Text($r('app.string.target_setting')).fontSize(commonConst.DEFAULT_20).margin({ right: commonConst.DEFAULT_12 })
        Text(this.createSubTitle())
          .fontSize(commonConst.DEFAULT_16)
      }
      .width(commonConst.THOUSANDTH_1000)
      .justifyContent(FlexAlign.Start)

      if ([taskType.getup, taskType.sleepEarly].indexOf(this.settingParams.taskID) > commonConst.HAS_NO_INDEX) {
        TimePicker({
          selected: commonConst.DEFAULT_SELECTED_TIME
        })
          .height(commonConst.THOUSANDTH_800)
          .useMilitaryTime(true)
          .onChange((value: TimePickerResult) => {
            this.currentTime = formatTime(value)
          })
      } else {
        TextPicker({ range: this.settingParams.taskID === taskType.drinkWater ? this.drinkRange : this.appleRange,
          value: this.settingParams.targetValue })
          .width(commonConst.THOUSANDTH_900)
          .height(commonConst.THOUSANDTH_800)
          .onChange((value: string | string[]) => {
            this.currentValue = (value as string)?.split(' ')[0]
          })
      }

      Row() {
        Text($r('app.string.cancel')).fontSize(commonConst.DEFAULT_20).fontColor($r('app.color.blueColor'))
          .onClick(() => {
            this.currentTime = commonConst.DEFAULT_TIME
            this.currentValue = commonConst.DEFAULT_TEXT
            this.controller.close()
          })

        Text($r('app.string.confirm')).fontSize(commonConst.DEFAULT_20).fontColor($r('app.color.blueColor'))
          .onClick(() => {
            this.setTargetValue()
            this.controller.close()
          })
      }
      .justifyContent(FlexAlign.SpaceAround)
      .width(commonConst.THOUSANDTH_1000)
      .height(commonConst.DEFAULT_28)
      .margin({ bottom: commonConst.DEFAULT_20 })
    }
    .justifyContent(FlexAlign.Center)
    .height(commonConst.THOUSANDTH_560)
    .padding(commonConst.DEFAULT_12)
  }
}

@CustomDialog
export struct RemindTimeDialog {
  controller: CustomDialogController = new CustomDialogController({
    builder: RemindTimeDialog()
  })

  build() {
    Column() {
      Text('remind time dialog')
    }
    .justifyContent(FlexAlign.Center)
    .height(commonConst.THOUSANDTH_560)
    .padding(commonConst.DEFAULT_12)
  }
}

@CustomDialog
export struct FrequencyDialog {
  controller: CustomDialogController = new CustomDialogController({
    builder: FrequencyDialog()
  })

  build() {
    Column() {
      Text('frequency dialog')
    }
    .justifyContent(FlexAlign.Center)
    .height(commonConst.THOUSANDTH_560)
    .padding(commonConst.DEFAULT_12)
  }
}

任务目标设置视图模型

// ets/TaskTargetSetting.ets

import { DRINK_MAX_RANGE, DRINK_STEP, EAT_APPLE_RANGE, TIMES_100, TODAY } from '../common/constants/CommonConstant'
import { TaskListItem } from '../model/TaskInitList'
import { padTo2Digits } from './FrequencySetting'

export const formatParams = (params: TaskListItem) => {
  return JSON.stringify(params)
}

export const formatTime = (value: TimePickerResult) => {
  let hour: number = 0
  let minute: number = 0
  if (value.hour !== undefined && value.minute !== undefined) {
    hour = value.hour
    minute = value.minute
  }
  return `${padTo2Digits(hour)}:${padTo2Digits(minute)}`
}

export const createDrinkRange = () => {
  const drinkRangeArr: string[] = []
  for (let i = DRINK_STEP; i <= DRINK_MAX_RANGE; i += DRINK_STEP) {
    drinkRangeArr.push(`${i / TIMES_100} L`)
  }
  return drinkRangeArr
}

export const createAppleRange = () => {
  const appleRangeArr: string[] = []
  for (let i = 1; i <= EAT_APPLE_RANGE; i++) {
    appleRangeArr.push(`${i} 个`)
  }
  return appleRangeArr
}

export const returnTimeStamp = (currentTime: string) => {
  const timeString = `${TODAY} ${currentTime}`
  return new Date(timeString).getTime()
}

频率设置视图模型

// ets/viewModel/FrequencySetting.ets

export function padTo2Digits(num: number) {
  return num.toString().padStart(2, '0')
}

时间提醒弹窗实现

更新TaskDetail

// ets/view/TaskDetail.ets

import * as commonConst from '../common/constants/CommonConstant'
import { TaskListItem } from '../model/TaskInitList'
import {
  TaskChooseItem,
  TargetSetItem,
  // 3. 导入模块
  OpenRemindItem, 
  RemindTimeItem
} from './TaskEditListItem'
import { router } from '@kit.ArkUI'
import { taskType } from '../model/TaskInfo'
import { BroadCast, BroadCastType } from '../common/utils/BroadCast'
import { CustomDialogView } from './CustomDialogView'

@Styles
function listItemStyle() {
  .backgroundColor($r('app.color.white'))
  .height(commonConst.DEFAULT_56)
  .borderRadius(commonConst.DEFAULT_10)
  .padding({ left: commonConst.DEFAULT_12, right: commonConst.DEFAULT_12 })
}

@Component
export default struct TaskDetail {
  @Provide settingParams: TaskListItem = this.parseRouterParams()
  @Provide broadCast: BroadCast = new BroadCast()

  parseRouterParams() {
    let params = router.getParams() as Record<string, Object>
    const routerParams: TaskListItem = JSON.parse(params.params as string)
    return routerParams
  }

  aboutToAppear() {
    this.broadCast.off()
  }

  build() {
    Column() {
      List({ space: commonConst.LIST_ITEM_SPACE }) {
        ListItem() {
          TaskChooseItem()
        }
        .listItemStyle()

        ListItem() {
          TargetSetItem()
        }
        .listItemStyle()
        .enabled(
          this.settingParams?.isOpen &&
            (this.settingParams?.taskID !== taskType.smile) &&
            (this.settingParams?.taskID !== taskType.brushTeeth)
        )
        .onClick(() => {
          this.broadCast.emit(
            BroadCastType.SHOW_TARGET_SETTING_DIALOG)
        })

        // 1.构造编辑列表相应内容
        ListItem() {
          OpenRemindItem()
        }
        .listItemStyle()
        .enabled(this.settingParams.isOpen)

        
        ListItem() {
          // 2.构造编辑列表相应内容
          RemindTimeItem()
        }
        .listItemStyle()
        .onClick(() => {
          this.broadCast.emit(
            BroadCastType.SHOW_REMIND_TIME_DIALOG
          )
        })

        ListItem() {
          Text('频率')
        }
        .listItemStyle()
        .onClick(() => {
          this.broadCast.emit(
            BroadCastType.SHOW_FREQUENCY_DIALOG
          )
        })
      }

      CustomDialogView()
    }
    .width(commonConst.THOUSANDTH_1000)
  }
}

实现编辑任务列表的开启提醒与提醒时间

import { TaskListItem } from '../model/TaskInitList'
import {
  DEFAULT_16,
  DEFAULT_20,
  DEFAULT_32,
  DEFAULT_56,
  DEFAULT_8,
  PER_DAY,
  THOUSANDTH_1000,
} from '../common/constants/CommonConstant'
import { taskType } from '../model/TaskInfo'

@Extend(Text)
function targetSetCommon() {
  .fontSize(DEFAULT_16)
  .flexGrow(1)
  .margin({ right: DEFAULT_8 })
  .align(Alignment.End)
}

@Extend(Text)
function targetSettingStyle(isOpen: boolean, taskID: number) {
  .fontColor(isOpen && taskID !== taskType.smile && taskID !== taskType.brushTeeth ?
  $r('app.color.titleColor') :
  $r('app.color.disabledColor'))
}

@Extend(Text)
function remindTimeStyle(isOpen: boolean, isAlarm: boolean) {
  .fontColor(isOpen && isAlarm ? $r('app.color.titleColor') : $r('app.color.disabledColor'))
}

@Component
export struct TaskChooseItem {
  @Consume settingParams: TaskListItem

  build() {
    Row() {
      Text(this.settingParams.taskName).fontSize(DEFAULT_20).fontWeight(FontWeight.Medium)
      Toggle({ type: ToggleType.Switch, isOn: this.settingParams.isOpen })
        .width(DEFAULT_56)
        .height(DEFAULT_32)
        .selectedColor($r('app.color.blueColor'))
        .onChange((isOn: boolean) => {
          this.settingParams.isOpen = isOn;
        })
    }
    .width(THOUSANDTH_1000)
    .justifyContent(FlexAlign.SpaceBetween)
  }
}

@Component
export struct TargetSetItem {
  @Consume settingParams: TaskListItem

  build() {
    Row() {
      Text($r('app.string.target_setting'))
        .fontSize(DEFAULT_20)
        .fontWeight(FontWeight.Medium)

      Blank()
        .layoutWeight(1)

      if (this.settingParams?.unit === '') {
        Text(`${this.settingParams?.targetValue}`)
          .targetSetCommon()
          .targetSettingStyle(this.settingParams?.isOpen, this.settingParams?.taskID)
      } else {
        Text(`${this.settingParams?.targetValue} ${this.settingParams?.unit} ${PER_DAY}`)
          .targetSetCommon()
          .targetSettingStyle(this.settingParams?.isOpen, this.settingParams?.taskID)
      }
      Image($r('app.media.right_grey')).width(DEFAULT_8).height(DEFAULT_16);
    }
    .width(THOUSANDTH_1000)
    .justifyContent(FlexAlign.SpaceBetween)
  }
}


// 1.实现开启提醒
@Component
export struct OpenRemindItem {
  @Consume settingParams: TaskListItem

  build() {
    Row() {
      Text($r('app.string.open_reminder'))
        .fontSize(DEFAULT_20)
        .fontWeight(FontWeight.Medium)

      Blank()
        .layoutWeight(1)

      Toggle({ type: ToggleType.Switch, isOn: this.settingParams?.isAlarm })
        .width(DEFAULT_56)
        .height(DEFAULT_32)
        .selectedColor($r('app.color.blueColor'))
        .onChange((isOn: boolean) => {
          this.settingParams.isAlarm = isOn
        })
    }
    .width(THOUSANDTH_1000)
    .justifyContent(FlexAlign.SpaceBetween)
  }
}

// 2.实现提醒时间
@Component
export struct RemindTimeItem {
  @Consume settingParams: TaskListItem

  build() {
    Row() {
      Text($r('app.string.remind_time')).fontSize(DEFAULT_20).fontWeight(FontWeight.Medium)
      Blank()
        .layoutWeight(1)
      Text(this.settingParams?.startTime)
        .targetSetCommon()
        .remindTimeStyle(this.settingParams.isOpen, this.settingParams.isAlarm)
      Image($r('app.media.right_grey')).width(DEFAULT_8).height(DEFAULT_16)
    }
    .width(THOUSANDTH_1000)
    .justifyContent(FlexAlign.SpaceBetween)
  }
}

实现时间提醒弹窗

import * as commonConst from '../common/constants/CommonConstant'
import { taskType } from '../model/TaskInfo'
import { TaskListItem } from '../model/TaskInitList'
import { createAppleRange, createDrinkRange, formatTime, returnTimeStamp } from '../viewModel/TaskTargetSetting'
import { promptAction } from '@kit.ArkUI'

@CustomDialog
export struct TargetSettingDialog {
  controller: CustomDialogController = new CustomDialogController({
    builder: TargetSettingDialog()
  })

  @Consume settingParams: TaskListItem

  currentTime: string = commonConst.DEFAULT_TIME
  currentValue: string = this.settingParams.taskID === taskType.drinkWater ? commonConst.DEFAULT_TEXT :
  commonConst.DEFAULT_APPLE

  drinkRange: string[] = createDrinkRange()
  appleRange: string[] = createAppleRange()

  createSubTitle() {
    if (this.settingParams.taskID === taskType.getup) {
      return commonConst.GET_UP_TIME_RANGE
    }
    if (this.settingParams.taskID === taskType.sleepEarly) {
      return commonConst.SLEEP_TIME_RANGE
    }
    return ''
  }

  setTargetValue() {
    if (this.settingParams.taskID === taskType.getup) {
      if (!this.compareTime(commonConst.GET_UP_EARLY_TIME, commonConst.GET_UP_LATE_TIME)) {
        return
      }
      this.settingParams.targetValue = this.currentTime
      return
    }

    if (this.settingParams?.taskID === taskType.sleepEarly) {
      if (!this.compareTime(commonConst.SLEEP_EARLY_TIME, commonConst.SLEEP_LATE_TIME)) {
        return
      }
      this.settingParams.targetValue = this.currentTime
      return
    }
    this.settingParams.targetValue = this.currentValue
  }

  compareTime(startTime: string, endTime: string) {
    if (returnTimeStamp(this.currentTime) < returnTimeStamp(startTime) ||
      returnTimeStamp(this.currentTime) > returnTimeStamp(endTime)) {

      promptAction.showToast({
        message: commonConst.CHOOSE_TIME_OUT_RANGE
      })
      return false
    }
    return true
  }

  build() {
    Column() {
      Row() {
        Text($r('app.string.target_setting')).fontSize(commonConst.DEFAULT_20).margin({ right: commonConst.DEFAULT_12 })
        Text(this.createSubTitle())
          .fontSize(commonConst.DEFAULT_16)
      }
      .width(commonConst.THOUSANDTH_1000)
      .justifyContent(FlexAlign.Start)

      if ([taskType.getup, taskType.sleepEarly].indexOf(this.settingParams.taskID) > commonConst.HAS_NO_INDEX) {
        TimePicker({
          selected: commonConst.DEFAULT_SELECTED_TIME
        })
          .height(commonConst.THOUSANDTH_800)
          .useMilitaryTime(true)
          .onChange((value: TimePickerResult) => {
            this.currentTime = formatTime(value)
          })
      } else {
        TextPicker({ range: this.settingParams?.taskID === taskType.drinkWater ? this.drinkRange : this.appleRange,
          value: this.settingParams.targetValue })
          .width(commonConst.THOUSANDTH_900)
          .height(commonConst.THOUSANDTH_800)
          .onChange((value: string | string[]) => {
            this.currentValue = (value as string)?.split(' ')[0]
          })
      }

      Row() {
        Text($r('app.string.cancel')).fontSize(commonConst.DEFAULT_20).fontColor($r('app.color.blueColor'))
          .onClick(() => {
            this.currentTime = commonConst.DEFAULT_TIME;
            this.currentValue = commonConst.DEFAULT_TEXT;
            this.controller.close()
          })

        Text($r('app.string.confirm')).fontSize(commonConst.DEFAULT_20).fontColor($r('app.color.blueColor'))
          .onClick(() => {
            this.setTargetValue()
            this.controller.close()
          })
      }
      .justifyContent(FlexAlign.SpaceAround)
      .width(commonConst.THOUSANDTH_1000)
      .height(commonConst.DEFAULT_28)
      .margin({ bottom: commonConst.DEFAULT_20 })
    }
    .justifyContent(FlexAlign.Center)
    .height(commonConst.THOUSANDTH_560)
    .padding(commonConst.DEFAULT_12)
  }
}

// 实现时间提醒弹窗
@CustomDialog
export struct RemindTimeDialog {
  controller: CustomDialogController = new CustomDialogController({
    builder: RemindTimeDialog()
  })

  currentTime: string = commonConst.DEFAULT_TIME

  @Consume settingParams: TaskListItem

  build() {
    Column() {
      Column() {
        Text($r('app.string.remind_time'))
          .fontSize(commonConst.DEFAULT_20)
          .margin({ top: commonConst.DEFAULT_10 })
          .width(commonConst.THOUSANDTH_1000)
          .textAlign(TextAlign.Start)
      }
      .width(commonConst.THOUSANDTH_900)

      TimePicker({
        selected: commonConst.DEFAULT_SELECTED_TIME
      })
        .height(commonConst.THOUSANDTH_800)
        .useMilitaryTime(true)
        .onChange((value: TimePickerResult) => {
          this.currentTime = formatTime(value)
        })

      Row() {
        Text($r('app.string.cancel')).fontSize(commonConst.DEFAULT_20).fontColor($r('app.color.blueColor'))
          .onClick(() => {
            this.currentTime = commonConst.DEFAULT_TIME
            this.controller.close()
          })

        Text($r('app.string.confirm')).fontSize(commonConst.DEFAULT_20).fontColor($r('app.color.blueColor'))
          .onClick(() => {
            this.settingParams.startTime = this.currentTime
            this.controller.close()
          })
      }
      .justifyContent(FlexAlign.SpaceAround)
      .width(commonConst.THOUSANDTH_1000)
      .height(commonConst.DEFAULT_28)
      .margin({ bottom: commonConst.DEFAULT_20 })
    }
    .justifyContent(FlexAlign.Center)
    .height(commonConst.THOUSANDTH_560)
    .padding(commonConst.DEFAULT_12)
  }
}

@CustomDialog
export struct FrequencyDialog {
  controller: CustomDialogController = new CustomDialogController({
    builder: FrequencyDialog()
  })

  build() {
    Column() {
      Text('frequency dialog')
    }
    .justifyContent(FlexAlign.Center)
    .height(commonConst.THOUSANDTH_560)
    .padding(commonConst.DEFAULT_12)
  }
}

频率弹窗和提交完成的实现

// ets/view/TaskDetail.ets

import * as commonConst from '../common/constants/CommonConstant'
import { TaskListItem } from '../model/TaskInitList'
import {
  TaskChooseItem,
  TargetSetItem,
  OpenRemindItem,
  RemindTimeItem,
  FrequencyItem
} from './TaskEditListItem'
import { promptAction, router } from '@kit.ArkUI'
import { taskType } from '../model/TaskInfo'
import { BroadCast, BroadCastType } from '../common/utils/BroadCast'
import { CustomDialogView } from './CustomDialogView'

@Styles
function listItemStyle() {
  .backgroundColor($r('app.color.white'))
  .height(commonConst.DEFAULT_56)
  .borderRadius(commonConst.DEFAULT_10)
  .padding({ left: commonConst.DEFAULT_12, right: commonConst.DEFAULT_12 })
}

@Component
export default struct TaskDetail {
  @Provide settingParams: TaskListItem = this.parseRouterParams()
  @Provide broadCast: BroadCast = new BroadCast()
  @Provide frequency: string = commonConst.EVERYDAY

  parseRouterParams() {
    let params = router.getParams() as Record<string, Object>
    const routerParams: TaskListItem = JSON.parse(params.params as string)
    return routerParams
  }

  aboutToAppear() {
    this.broadCast.off()
  }

  finishTaskEdit() {
    promptAction.showToast({
      message: commonConst.SETTING_FINISHED_MESSAGE
    })
  }

  build() {
    Column() {
      List({ space: commonConst.LIST_ITEM_SPACE }) {
        ListItem() {
          TaskChooseItem()
        }
        .listItemStyle()

        ListItem() {
          TargetSetItem()
        }
        .listItemStyle()
        .enabled(
          this.settingParams?.isOpen &&
            (this.settingParams?.taskID !== taskType.smile) &&
            (this.settingParams?.taskID !== taskType.brushTeeth)
        )
        .onClick(() => {
          this.broadCast.emit(
            BroadCastType.SHOW_TARGET_SETTING_DIALOG)
        })

        ListItem() {
          OpenRemindItem()
        }
        .listItemStyle()
        .enabled(this.settingParams?.isOpen)

        ListItem() {
          RemindTimeItem()
        }
        .listItemStyle()
        .onClick(() => {
          this.broadCast.emit(
            BroadCastType.SHOW_REMIND_TIME_DIALOG
          )
        })

        // 1. 引入FrequencyItem
        ListItem() {
          FrequencyItem()
        }
        .listItemStyle()
        .onClick(() => {
          this.broadCast.emit(
            BroadCastType.SHOW_FREQUENCY_DIALOG
          )
        })
      }
      .width(commonConst.THOUSANDTH_940)

      // x. 最后实现完成按钮提交
      Button() {
        Text($r('app.string.complete')).fontSize(commonConst.DEFAULT_20).fontColor($r('app.color.blueColor'))
      }
      .width(commonConst.THOUSANDTH_800)
      .height(commonConst.DEFAULT_48)
      .backgroundColor($r('app.color.borderColor'))
      .onClick(() => {
        this.finishTaskEdit()
      })
      .position({
        x: commonConst.THOUSANDTH_100,
        y: commonConst.THOUSANDTH_800
      })

      CustomDialogView()
    }
    .width(commonConst.THOUSANDTH_1000)
  }
}

实现频率任务项视图

// ets/view/TaskEditListItem.ets

import { TaskListItem } from '../model/TaskInitList'
import {
  DEFAULT_12,
  DEFAULT_16,
  DEFAULT_20,
  DEFAULT_32,
  DEFAULT_56,
  DEFAULT_8,
  PER_DAY,
  THOUSANDTH_1000,
} from '../common/constants/CommonConstant'
import { taskType } from '../model/TaskInfo'

@Extend(Text)
function targetSetCommon() {
  .fontSize(DEFAULT_16)
  .flexGrow(1)
  .margin({ right: DEFAULT_8 })
  .align(Alignment.End)
}

@Extend(Text)
function targetSettingStyle(isOpen: boolean, taskID: number) {
  .fontColor(isOpen && taskID !== taskType.smile && taskID !== taskType.brushTeeth ?
  $r('app.color.titleColor') :
  $r('app.color.disabledColor'))
}

@Extend(Text)
function remindTimeStyle(isOpen: boolean, isAlarm: boolean) {
  .fontColor(isOpen && isAlarm ? $r('app.color.titleColor') : $r('app.color.disabledColor'))
}

@Extend(Text)
function frequencyStyle(isOpen: boolean) {
  .fontSize(DEFAULT_12)
  .flexGrow(1)
  .margin({ right: DEFAULT_8 })
  .textAlign(TextAlign.End)
  .fontColor(isOpen ? $r('app.color.titleColor') : $r('app.color.disabledColor'))
}

@Component
export struct TaskChooseItem {
  @Consume settingParams: TaskListItem

  build() {
    Row() {
      Text(this.settingParams.taskName).fontSize(DEFAULT_20).fontWeight(FontWeight.Medium)
      Toggle({ type: ToggleType.Switch, isOn: this.settingParams.isOpen })
        .width(DEFAULT_56)
        .height(DEFAULT_32)
        .selectedColor($r('app.color.blueColor'))
        .onChange((isOn: boolean) => {
          this.settingParams.isOpen = isOn;
        })
    }
    .width(THOUSANDTH_1000)
    .justifyContent(FlexAlign.SpaceBetween)
  }
}

@Component
export struct TargetSetItem {
  @Consume settingParams: TaskListItem

  build() {
    Row() {
      Text($r('app.string.target_setting'))
        .fontSize(DEFAULT_20)
        .fontWeight(FontWeight.Medium)

      Blank()
        .layoutWeight(1)

      if (this.settingParams?.unit === '') {
        Text(`${this.settingParams?.targetValue}`)
          .targetSetCommon()
          .targetSettingStyle(this.settingParams?.isOpen, this.settingParams?.taskID)
      } else {
        Text(`${this.settingParams?.targetValue} ${this.settingParams?.unit} ${PER_DAY}`)
          .targetSetCommon()
          .targetSettingStyle(this.settingParams?.isOpen, this.settingParams?.taskID)
      }
      Image($r('app.media.right_grey')).width(DEFAULT_8).height(DEFAULT_16);
    }
    .width(THOUSANDTH_1000)
    .justifyContent(FlexAlign.SpaceBetween)
  }
}

@Component
export struct OpenRemindItem {
  @Consume settingParams: TaskListItem

  build() {
    Row() {
      Text($r('app.string.open_reminder'))
        .fontSize(DEFAULT_20)
        .fontWeight(FontWeight.Medium)

      Blank()
        .layoutWeight(1)

      Toggle({ type: ToggleType.Switch, isOn: this.settingParams?.isAlarm })
        .width(DEFAULT_56)
        .height(DEFAULT_32)
        .selectedColor($r('app.color.blueColor'))
        .onChange((isOn: boolean) => {
          this.settingParams.isAlarm = isOn
        })
    }
    .width(THOUSANDTH_1000)
    .justifyContent(FlexAlign.SpaceBetween)
  }
}

@Component
export struct RemindTimeItem {
  @Consume settingParams: TaskListItem

  build() {
    Row() {
      Text($r('app.string.remind_time')).fontSize(DEFAULT_20).fontWeight(FontWeight.Medium)
      Blank()
        .layoutWeight(1)
      Text(this.settingParams?.startTime)
        .targetSetCommon()
        .remindTimeStyle(this.settingParams.isOpen, this.settingParams.isAlarm)
      Image($r('app.media.right_grey')).width(DEFAULT_8).height(DEFAULT_16)
    }
    .width(THOUSANDTH_1000)
    .justifyContent(FlexAlign.SpaceBetween)
  }
}

// 1. 实现频率任务项视图
@Component
export struct FrequencyItem {
  @Consume settingParams: TaskListItem
  @Consume frequency: string

  build() {
    Row() {
      Text($r('app.string.frequency')).fontSize(DEFAULT_20).fontWeight(FontWeight.Medium)
      Text(this.frequency)
        .targetSetCommon()
        .frequencyStyle(this.settingParams.isOpen)
      Image($r('app.media.right_grey')).width(DEFAULT_8).height(DEFAULT_16)
    }
    .width(THOUSANDTH_1000)
    .justifyContent(FlexAlign.SpaceBetween)
  }
}

实现频率设置弹窗

// ets/view/TaskSettingDialog.ets

import * as commonConst from '../common/constants/CommonConstant'
import { taskType } from '../model/TaskInfo'
import { FrequencyContentType, TaskListItem } from '../model/TaskInitList'
import { createAppleRange, createDrinkRange, formatTime, returnTimeStamp } from '../viewModel/TaskTargetSetting'
import { promptAction } from '@kit.ArkUI'
import { frequencyRange } from '../viewModel/FrequencySetting'

@CustomDialog
export struct TargetSettingDialog {
  controller: CustomDialogController = new CustomDialogController({
    builder: TargetSettingDialog()
  })

  @Consume settingParams: TaskListItem

  currentTime: string = commonConst.DEFAULT_TIME
  currentValue: string = this.settingParams.taskID === taskType.drinkWater ? commonConst.DEFAULT_TEXT :
  commonConst.DEFAULT_APPLE

  drinkRange: string[] = createDrinkRange()
  appleRange: string[] = createAppleRange()

  createSubTitle() {
    if (this.settingParams.taskID === taskType.getup) {
      return commonConst.GET_UP_TIME_RANGE
    }
    if (this.settingParams.taskID === taskType.sleepEarly) {
      return commonConst.SLEEP_TIME_RANGE
    }
    return ''
  }

  setTargetValue() {
    if (this.settingParams.taskID === taskType.getup) {
      if (!this.compareTime(commonConst.GET_UP_EARLY_TIME, commonConst.GET_UP_LATE_TIME)) {
        return
      }
      this.settingParams.targetValue = this.currentTime
      return
    }

    if (this.settingParams?.taskID === taskType.sleepEarly) {
      if (!this.compareTime(commonConst.SLEEP_EARLY_TIME, commonConst.SLEEP_LATE_TIME)) {
        return
      }
      this.settingParams.targetValue = this.currentTime
      return
    }
    this.settingParams.targetValue = this.currentValue
  }

  compareTime(startTime: string, endTime: string) {
    if (returnTimeStamp(this.currentTime) < returnTimeStamp(startTime) ||
      returnTimeStamp(this.currentTime) > returnTimeStamp(endTime)) {

      promptAction.showToast({
        message: commonConst.CHOOSE_TIME_OUT_RANGE
      })
      return false
    }
    return true
  }

  build() {
    Column() {
      Row() {
        Text($r('app.string.target_setting')).fontSize(commonConst.DEFAULT_20).margin({ right: commonConst.DEFAULT_12 })
        Text(this.createSubTitle())
          .fontSize(commonConst.DEFAULT_16)
      }
      .width(commonConst.THOUSANDTH_1000)
      .justifyContent(FlexAlign.Start)

      if ([taskType.getup, taskType.sleepEarly].indexOf(this.settingParams.taskID) > commonConst.HAS_NO_INDEX) {
        TimePicker({
          selected: commonConst.DEFAULT_SELECTED_TIME
        })
          .height(commonConst.THOUSANDTH_800)
          .useMilitaryTime(true)
          .onChange((value: TimePickerResult) => {
            this.currentTime = formatTime(value)
          })
      } else {
        TextPicker({ range: this.settingParams?.taskID === taskType.drinkWater ? this.drinkRange : this.appleRange,
          value: this.settingParams.targetValue })
          .width(commonConst.THOUSANDTH_900)
          .height(commonConst.THOUSANDTH_800)
          .onChange((value: string | string[]) => {
            this.currentValue = (value as string)?.split(' ')[0]
          })
      }

      Row() {
        Text($r('app.string.cancel')).fontSize(commonConst.DEFAULT_20).fontColor($r('app.color.blueColor'))
          .onClick(() => {
            this.currentTime = commonConst.DEFAULT_TIME;
            this.currentValue = commonConst.DEFAULT_TEXT;
            this.controller.close()
          })

        Text($r('app.string.confirm')).fontSize(commonConst.DEFAULT_20).fontColor($r('app.color.blueColor'))
          .onClick(() => {
            this.setTargetValue()
            this.controller.close()
          })
      }
      .justifyContent(FlexAlign.SpaceAround)
      .width(commonConst.THOUSANDTH_1000)
      .height(commonConst.DEFAULT_28)
      .margin({ bottom: commonConst.DEFAULT_20 })
    }
    .justifyContent(FlexAlign.Center)
    .height(commonConst.THOUSANDTH_560)
    .padding(commonConst.DEFAULT_12)
  }
}

@CustomDialog
export struct RemindTimeDialog {
  controller: CustomDialogController = new CustomDialogController({
    builder: RemindTimeDialog()
  })

  currentTime: string = commonConst.DEFAULT_TIME

  @Consume settingParams: TaskListItem

  build() {
    Column() {
      Column() {
        Text($r('app.string.remind_time'))
          .fontSize(commonConst.DEFAULT_20)
          .margin({ top: commonConst.DEFAULT_10 })
          .width(commonConst.THOUSANDTH_1000)
          .textAlign(TextAlign.Start)
      }
      .width(commonConst.THOUSANDTH_900)

      TimePicker({
        selected: commonConst.DEFAULT_SELECTED_TIME
      })
        .height(commonConst.THOUSANDTH_800)
        .useMilitaryTime(true)
        .onChange((value: TimePickerResult) => {
          this.currentTime = formatTime(value);
        })

      Row() {
        Text($r('app.string.cancel')).fontSize(commonConst.DEFAULT_20).fontColor($r('app.color.blueColor'))
          .onClick(() => {
            this.currentTime = commonConst.DEFAULT_TIME
            this.controller.close()
          })

        Text($r('app.string.confirm')).fontSize(commonConst.DEFAULT_20).fontColor($r('app.color.blueColor'))
          .onClick(() => {
            this.settingParams.startTime = this.currentTime
            this.controller.close()
          })
      }
      .justifyContent(FlexAlign.SpaceAround)
      .width(commonConst.THOUSANDTH_1000)
      .height(commonConst.DEFAULT_28)
      .margin({ bottom: commonConst.DEFAULT_20 })
    }
    .justifyContent(FlexAlign.Center)
    .height(commonConst.THOUSANDTH_560)
    .padding(commonConst.DEFAULT_12)
  }
}

// 1.实现频率设置弹窗
@CustomDialog
export struct FrequencyDialog {
  controller: CustomDialogController = new CustomDialogController({
    builder: FrequencyDialog()
  })

  private frequencyChooseRange: FrequencyContentType[] = frequencyRange()

  private currentFrequency: string = commonConst.EVERYDAY

  @Consume settingParams: TaskListItem

  @Consume frequency: string

  setFrequency() {
    const checkedArr = this.frequencyChooseRange.filter((item: FrequencyContentType) => item.isChecked)

    if (checkedArr.length === this.frequencyChooseRange.length || checkedArr.length === commonConst.NO_LENGTH) {
      this.currentFrequency = commonConst.EVERYDAY
      this.settingParams.frequency = commonConst.INIT_WEEK_IDS
      return
    }

    this.currentFrequency = checkedArr.reduce((sum: string, current: FrequencyContentType) => {
      return sum + ' ' + current.label
    }, '')

    this.settingParams.frequency = checkedArr.reduce((sum: string, current: FrequencyContentType) => {
      return sum === '' ? sum + current.id : sum + ',' + current.id
    }, '')
  }

  build() {
    Column() {
      Column() {
        Text($r('app.string.set_your_frequency'))
          .fontSize(commonConst.DEFAULT_20)
          .margin({ top: commonConst.DEFAULT_10 })
          .width(commonConst.THOUSANDTH_1000)
          .textAlign(TextAlign.Start)
      }
      .width(commonConst.THOUSANDTH_900)

      List() {
        ForEach(this.frequencyChooseRange, (item: FrequencyContentType) => {
          ListItem() {
            Row() {
              Text(item?.label).fontSize(commonConst.DEFAULT_20)
              Toggle({ type: ToggleType.Checkbox })
                .onChange((isOn: boolean) => {
                  item.isChecked = isOn
                })
            }
            .width(commonConst.THOUSANDTH_1000)
            .justifyContent(FlexAlign.SpaceBetween)
            .height(commonConst.DEFAULT_60)
          }
        })
      }
      .divider({
        strokeWidth: commonConst.DEFAULT_2,
        color: $r('app.color.btnBgColor')
      })
      .flexGrow(1)
      .padding(commonConst.DEFAULT_12)
      .width(commonConst.THOUSANDTH_1000)

      Row() {
        Text($r('app.string.cancel')).fontSize(commonConst.DEFAULT_20).fontColor($r('app.color.blueColor'))
          .onClick(() => {
            this.controller.close()
          })

        Text($r('app.string.confirm')).fontSize(commonConst.DEFAULT_20).fontColor($r('app.color.blueColor'))
          .onClick(() => {
            this.setFrequency()
            this.frequency = this.currentFrequency
            this.controller.close()
          })
      }
      .justifyContent(FlexAlign.SpaceAround)
      .width(commonConst.THOUSANDTH_900)
      .height(commonConst.DEFAULT_28)
      .margin({ bottom: commonConst.DEFAULT_16 })
    }
    .justifyContent(FlexAlign.Center)
    .height(commonConst.THOUSANDTH_940)
    .padding(commonConst.DEFAULT_12)
  }
}

定义频率设置视图模型

// ets/viewModel/FrequencySetting.ets

import { FrequencyContentType } from "../model/TaskInitList"

export function padTo2Digits(num: number) {
  return num.toString().padStart(2, '0')
}

const chineseNumOfWeek: string[] = ['一', '二', '三', '四', '五', '六', '日']
const WEEK: string = '星期'

export const frequencyRange = () => {
  const frequencyRangeArr: FrequencyContentType[] = []
  chineseNumOfWeek.forEach((item: string, index: number) => {
    frequencyRangeArr.push({
      id: (index + 1),
      label: `${WEEK}${item}`,
      isChecked: false
    })
  })
  return frequencyRangeArr
}

✋ 需要参加鸿蒙认证的请点击 鸿蒙认证链接