#HarmonyOS NEXT体验官# 体验HarmonyOS开发流程,开发一个记步app(二) 原创

RandomBoolean
发布于 2024-8-21 17:55
浏览
0收藏

接上一篇文章,本文将继续补充剩余部分。

首页源码已经贴过,下面介绍其他组件的封装。

TargetInformation

展示一言内容的组件,源码如下。

/*
 * Copyright (c) 2023 Huawei Device Co., Ltd.
 * Licensed under the Apache License,Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { CommonConstants } from '../common/constant/CommonConstant';
import { httpRequestGet } from '../common/utils/HttpUtil';
import ResponseResult from '../viewmodel/ResponseResult';

@Component
export default struct TargetInformation {
  @Prop latestUpdateDate: string = '';
  @Prop totalTasksNumber: number = 0;
  @Prop completedTasksNumber: number = 0;
  @State hitokoto: string  = ''
  @State hitokoto_from: string = ''

  aboutToAppear(): void {
    httpRequestGet(`https://v1.hitokoto.cn?c=d`).then((data: ESObject) => {
      // this.dataArray = data.data || []
      this.hitokoto = data.hitokoto
      this.hitokoto_from = data.from
    })

  }

  build() {
    Column() {
      this.TargetItem()
      // this.OverallProgress()
    }
    .padding($r('app.float.target_padding'))
    .width(CommonConstants.MAIN_BOARD_WIDTH)
    .backgroundColor(Color.White)
    .borderRadius(CommonConstants.TARGET_BORDER_RADIUS)
  }

  @Builder
  TargetItem() {
    Column() {
      Text(this.hitokoto)
        .fontSize($r('app.float.target_name_font'))
        .fontWeight(CommonConstants.FONT_WEIGHT_LARGE)
        .width(CommonConstants.TITLE_WIDTH)
      Text(`@${this.hitokoto_from}`)
        .opacityTextStyle()
        .fontSize($r('app.float.target_desc_font'))
        .margin({ top: $r('app.float.title_margin') })
    }
    .margin({ left: CommonConstants.TARGET_MARGIN_LEFT })
    .alignItems(HorizontalAlign.Start)
  }

  @Builder
  OverallProgress() {
    Row() {
      Column() {
        Text($r('app.string.overall_progress'))
          .fontSize($r('app.float.button_font'))
          .fontColor($r('app.color.title_black_color'))
          .fontWeight(CommonConstants.FONT_WEIGHT)
        Row() {
          Text($r('app.string.latest_updateTime'))
            .opacityTextStyle()
          Text(this.latestUpdateDate)
            .opacityTextStyle()
        }
        .margin({ top: $r('app.float.text_margin') })
      }
      .alignItems(HorizontalAlign.Start)

      Blank()
      Stack() {
        Row() {
          Text(this.completedTasksNumber.toString())
            .fontSize($r('app.float.progress_font'))
            .fontWeight(CommonConstants.FONT_WEIGHT)
            .fontColor($r('app.color.main_blue'))
          Text(`/${this.totalTasksNumber}`)
            .fontSize($r('app.float.progress_font'))
            .fontWeight(CommonConstants.FONT_WEIGHT)
        }

        Progress({
          value: this.completedTasksNumber,
          total: this.totalTasksNumber,
          type: ProgressType.Ring
        })
          .color($r('app.color.main_blue'))
          .style({
            strokeWidth: CommonConstants.STROKE_WIDTH
          })
          .width($r('app.float.progress_length'))
          .height($r('app.float.progress_length'))
      }
    }
    .width(CommonConstants.FULL_WIDTH)
    .height($r('app.float.progress_length'))
    .margin({ top: $r('app.float.progress_margin_top') })
  }
}

/**
 * Custom Transparent Text Styles
 */
@Extend(Text) function opacityTextStyle() {
  .fontSize($r('app.float.text_font'))
  .fontColor($r('app.color.title_black_color'))
  .opacity(CommonConstants.OPACITY)
  .fontWeight(CommonConstants.FONT_WEIGHT)
}

AddTargetDialog

添加目标的弹窗

源码如下,主要是一些表单的输入。

import { CommonConstants } from '../common/constant/CommonConstant';

@CustomDialog
export default struct AddTargetDialog {
  @State subtaskName: string = '';
  @State runNum: number = 0
  private controller?: CustomDialogController;
  onClickOk?: (value: string, runNum: number) => void;

  build() {
    Column() {
      Text('添加目标🎯')
        .width(CommonConstants.FULL_WIDTH)
        .fontSize($r('app.float.secondary_title'))
        .fontWeight(CommonConstants.FONT_WEIGHT)
        .fontColor($r('app.color.title_black_color'))
        .textAlign(TextAlign.Start)
      TextInput({ placeholder: '请输入名称'})
        .placeholderColor(Color.Grey)
        .placeholderFont({ size: $r('app.float.list_font')})
        .caretColor(Color.Blue)
        .backgroundColor($r('app.color.input_background'))
        .width(CommonConstants.FULL_WIDTH)
        .height('60vp')
        .margin({ top: CommonConstants.DIALOG_INPUT_MARGIN })
        .fontSize($r('app.float.list_font'))
        .fontColor($r('app.color.title_black_color'))
        .onChange((value: string) => {
          this.subtaskName = value;
        })
      TextInput({ placeholder: '请输入步数'})
        .type(InputType.Number)
        .placeholderColor(Color.Grey)
        .placeholderFont({ size: $r('app.float.list_font')})
        .caretColor(Color.Blue)
        .backgroundColor($r('app.color.input_background'))
        .width(CommonConstants.FULL_WIDTH)
        .height('60vp')
        .margin({ top: CommonConstants.DIALOG_INPUT_MARGIN })
        .fontSize($r('app.float.list_font'))
        .fontColor($r('app.color.title_black_color'))
        .onChange((value: string) => {
          this.runNum = Number(value);
        })
      Blank()
      Row() {
        Button($r('app.string.cancel_button'))
          .dialogButtonStyle()
          .onClick(() => {
            this.controller?.close();
          })
        Divider()
          .vertical(true)
        Button($r('app.string.confirm_button'))
          .dialogButtonStyle()
          .onClick(() => {
            if (this.onClickOk !== undefined) {
              this.onClickOk(this.subtaskName, this.runNum);
            }
          })
      }
      .width(CommonConstants.DIALOG_OPERATION_WIDTH)
      .height(CommonConstants.DIALOG_OPERATION_HEIGHT)
      .justifyContent(FlexAlign.SpaceBetween)
    }
    .padding($r('app.float.dialog_padding'))
    .height('300vp')
    .width(CommonConstants.DIALOG_WIDTH)
    .borderRadius(CommonConstants.DIALOG_BORDER_RADIUS)
    .backgroundColor(Color.White)
  }
}

/**
 * Custom button style.
 */
@Extend(Button) function dialogButtonStyle() {
  .fontSize($r('app.float.button_font'))
  .height($r('app.float.dialog_btn_height'))
  .width($r('app.float.dialog_btn_width'))
  .backgroundColor(Color.White)
  .fontColor($r('app.color.main_blue'))
}

TargetList

我的目标列表,源码如下,主要通过封装的getTasks方法,获取db数据。展示内容。


import TaskItemModel from '../viewmodel/TaskItemModel';
import TargetListItem from './TargetListItem';
import { CommonConstants } from '../common/constant/CommonConstant';
import DataModel from '../viewmodel/DataModel';
import { Task } from '../model/Task';
import { relationalStore } from '@kit.ArkData';
import { showToast } from '../utils/ToastUtils';
import { taskModel } from '../db/TaskModel';
import { emitter } from '@kit.BasicServicesKit';

@Component
export default struct TargetList {
  @Consume overAllProgressChanged: boolean;
  @State isEditMode: boolean = false;
  @State selectArray: Array<boolean> = [];
  @State clickIndex: number = CommonConstants.DEFAULT_CLICK_INDEX;
  @State selectAll: boolean = false;
  @Link targetData: Array<TaskItemModel>;
  onAddClick?: () => void;
  @State tasks: Array<Task> = new Array()
  @Consume store: relationalStore.RdbStore
  @State limit: number = 20
  @State skip: number = 0

  getTasks() {
    taskModel.getTasks(this.store, (res: ESObject) => {
      if (res && res.length > 0) {
        this.tasks = this.tasks.concat(res)
      }

    }, (err: string) => {
      showToast(err)
    }, this.limit, this.skip)
  }

  aboutToAppear(): void {
    setTimeout(() => {
      this.getTasks()
    }, 1000)
    emitter.on("addTaskSuccess", () => {
      this.tasks.length = 0
      this.skip = 0
      this.getTasks()
    })

    emitter.on('refresh', () => {
      this.tasks.length = 0
      this.skip = 0
      this.getTasks()
    })
  }



  build() {
    Column() {
      Row() {
        Text('我的目标')
          .fontSize($r('app.float.secondary_title'))
          .fontWeight(CommonConstants.FONT_WEIGHT_LARGE)
          .fontColor($r('app.color.title_black_color'))
        Blank()
        if (this.targetData.length > 0) {
          if (this.isEditMode) {
            Text($r('app.string.cancel_button'))
              .operateTextStyle($r('app.color.main_blue'))
              .margin({ left: $r('app.float.operate_button_margin') })
              .onClick(() => {
                this.selectAll = false;
                this.isEditMode = false;
                this.selectAllOrCancel(false);
              })
            Text($r('app.string.select_all_button'))
              .operateTextStyle($r('app.color.main_blue'))
              .margin({
                left: $r('app.float.operate_button_margin')
              })
            Checkbox()
              .select(this.isSelectAll())
              .selectedColor($r('app.color.main_blue'))
              .width(CommonConstants.CHECKBOX_WIDTH)
              .onClick(() => {
                this.selectAll = !this.selectAll;
                this.selectAllOrCancel(this.selectAll);
              })
          } else {
            Text($r('app.string.edit_button'))
              .operateTextStyle($r('app.color.main_blue'))
              .onClick(() => {
                this.isEditMode = true;
                this.selectAllOrCancel(false);
              })
          }
        }
      }
      .width(CommonConstants.FULL_WIDTH)
      .height($r('app.float.history_line_height'))
      .padding({
        left: $r('app.float.list_padding'),
        right: $r('app.float.list_padding_right')
      })

      List({ space: CommonConstants.LIST_SPACE }) {
        ForEach(this.tasks, (item: Task, index: number | undefined) => {
          ListItem() {
            TargetListItem({
              taskItem: item,
              index: index,
              selectArr: $selectArray,
              isEditMode: this.isEditMode,
              clickIndex: $clickIndex
            })
          }
        })
      }
      .edgeEffect(EdgeEffect.None)
      .margin({ top: $r('app.float.list_margin_top') })
      .width(CommonConstants.FULL_WIDTH)
      .height(CommonConstants.LIST_HEIGHT)

      Blank()
      if (this.isEditMode) {
        Button($r('app.string.delete_button'))
          .opacity(this.isSelectRows() ? CommonConstants.NO_OPACITY : CommonConstants.OPACITY)
          .enabled(this.isSelectRows() ? true : false)
          .operateButtonStyle($r('app.color.main_red'))
          .onClick(() => {
            this.deleteSelected();
            this.selectAllOrCancel(false);
            this.selectAll = false;
          })
      } else {
        Button('添加目标')
          .operateButtonStyle($r('app.color.main_blue'))
          .onClick(() => {
            if (this.onAddClick !== undefined) {
              this.onAddClick()
            }
          })
      }
    }
    .width(CommonConstants.MAIN_BOARD_WIDTH)
    .height(CommonConstants.FULL_HEIGHT)
    .padding({ top: $r('app.float.operate_row_margin') })
  }

  /**
   * Delete the selected item and exit the editing mode.
   */
  deleteSelected() {
    DataModel.deleteData(this.selectArray);
    this.targetData = DataModel.getData();
    this.overAllProgressChanged = !this.overAllProgressChanged;
    this.isEditMode = false;
  }

  /**
   * Select or deselect all.
   *
   * @param selectStatus true: Select all. Otherwise, deselect all.
   */
  selectAllOrCancel(selectStatus: boolean) {
    let newSelectArray: Array<boolean> = [];
    this.targetData.forEach(() => {
      newSelectArray.push(selectStatus);
    });
    this.selectArray = newSelectArray;
  }

  /**
   * Whether to select all.
   */
  isSelectAll(): boolean {
    if (this.selectArray.length === 0) {
      return false;
    }
    let deSelectCount: Length = this.selectArray.filter((selected: boolean) => selected === false).length;
    if (deSelectCount === 0) {
      this.selectAll = true;
      return true;
    }
    this.selectAll = false;
    return false;
  }

  /**
   * Check whether there are selected rows.
   */
  isSelectRows(): boolean {
    return this.selectArray.filter((selected: boolean) => selected === true).length !== 0;
  }
}

/**
 * Custom text button style.
 */
@Extend(Text)
function operateTextStyle(color: Resource) {
  .fontSize($r('app.float.text_button_font'))
  .fontColor(color)
  .lineHeight($r('app.float.text_line_height'))
  .fontWeight(CommonConstants.FONT_WEIGHT)
}

/**
 * Custom button style.
 */
@Extend(Button)
function operateButtonStyle(color: Resource) {
  .width($r('app.float.button_width'))
  .height($r('app.float.button_height'))
  .fontSize($r('app.float.button_font'))
  .fontWeight(CommonConstants.FONT_WEIGHT)
  .fontColor(color)
  .backgroundColor($r('app.color.button_background'))
}

接口的封装

在如图目录下创建文件
#HarmonyOS NEXT体验官# 体验HarmonyOS开发流程,开发一个记步app(二)-鸿蒙开发者社区

简单封装了一个接口请求,包含get和post。使用方式可以参考一言组件。

import { http } from '@kit.NetworkKit';
import ResponseResult from '../../viewmodel/ResponseResult';
import { promptAction } from '@kit.ArkUI';


const HTTP_READ_TIMEOUT = 10000
const HTTP_CODE_200 = 200
const SERVER_CODE_ERROR = 500

/**
 * Initiates an HTTP request to a given URL.
 *
 * @param url URL for initiating an HTTP request.
 * @param params Params for initiating an HTTP request.
 */
export function httpRequestGet(url: string): Promise<ResponseResult> {
  let httpRequest = http.createHttp();
  let responseResult = httpRequest.request(url, {
    method: http.RequestMethod.GET,
    readTimeout: HTTP_READ_TIMEOUT,
    header: {
      'Content-Type': 'application/json'
    },
    connectTimeout: HTTP_READ_TIMEOUT,
    extraData: {}
  });
  let serverData: ResponseResult = new ResponseResult();
  // Processes the data and returns.
  return responseResult.then((value: http.HttpResponse) => {
    if (value.responseCode === HTTP_CODE_200) {
      // Obtains the returned data.
      let result = `${value.result}`;
      let resultJson: ResponseResult = JSON.parse(result);
      return resultJson
    } else {
      serverData.msg = `网络请求失败,请稍后尝试!${value.responseCode}`;
    }
    return serverData;
  }).catch(() => {
    serverData.msg = '网络请求失败,请稍后尝试!';
      return serverData;
  })
}

export function httpRequestPost(url: string, data?: ESObject): Promise<ResponseResult> {
  let httpRequest = http.createHttp();
  let responseResult = httpRequest.request(url, {
    method: http.RequestMethod.POST,
    readTimeout: HTTP_READ_TIMEOUT,
    header: {
      'Content-Type': 'application/json'
    },
    connectTimeout: HTTP_READ_TIMEOUT,
    extraData: data
  });
  let serverData: ResponseResult = new ResponseResult();
  // Processes the data and returns.
  return responseResult.then((value: http.HttpResponse) => {
    if (value.responseCode === HTTP_CODE_200) {
      // Obtains the returned data.
      let result = `${value.result}`;
      let resultJson: ResponseResult = JSON.parse(result);
      serverData.data = resultJson.data;
      serverData.code = resultJson.code;
      serverData.msg = resultJson.msg;

      if (resultJson.code === SERVER_CODE_ERROR) {
        promptAction.showToast({
          message: serverData.msg
        })
      }
    } else {
      serverData.msg = `网络请求失败,请稍后尝试!${value.responseCode}`;
      promptAction.showToast({
        message: serverData.msg
      })
    }
    return serverData;
  }).catch(() => {
    serverData.msg = '网络请求失败,请稍后尝试!';
    promptAction.showToast({
      message: serverData.msg
    })
    return serverData;
  })
}

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
标签
收藏
回复
举报
回复
    相关推荐