• Lv0
    粉丝0

积分2 / 贡献0

提问0答案被采纳0文章1

作者动态

    [经验分享] HarmonyOS 汇率转换应用Demo练习

    Zeekrom 显示全部楼层 发表于 2024-11-28 21:56:04

    <!-- more -->

    简介

    • 在本教程中,我对课堂上讲解的汇率转换Demo进行复现和代码微调。
    • 本Demo基于 HarmonyOS 和 TypeScript。应用程序通过一个在线汇率 API,实时查询美元(USD)兑换人民币(CNY)的汇率,用户可以输入金额并获得对应兑换值。

    1111.png

    22222.png

    开发环境

    • DevEco Studio 最新版
    • HarmonyOS 3.0+ 支持的设备或模拟器
    • 熟悉 TypeScript 基础

    1. 项目结构

    以下是项目的基本文件结构: 代码拆分为四个模块,分别为主界面逻辑(index.ets)、API 服务类(apiService.ets)、数据模型(exchange.ets)以及模块配置(module.json5)。

    Demo1125/
    ├── entry/
    │   ├── src/
    │   │   ├── main/
    │   │   │   ├── ets/
    │   │   │   │   ├── pages/
    │   │   │   │   │   ├── index.ets        # 主界面逻辑
    │   │   │   │   │   ├── apiService.ets   # API 服务类
    │   │   │   │   │   ├── exchange.ets     # 数据模型
    │   │   │   │   │
    │   │   │   │   ├── module.json5         # 模块配置
    │   │   │   ├── resources/
    │   │   │   │   ├── base/
    │   │   │   │   ├── en/
    │   │   │   │   ├── zh/
    ├── .hvigor/
    ├── .idea/
    ├── build/
    

    2. 功能描述

    • 主页面(index.ets): 用户输入美元金额后,点击或自动触发查询按钮,将调用在线汇率 API 查询并返回实时结果。
    • API 服务(apiService.ets): 提供与 API 后端交互的逻辑,负责发送请求并返回结果。
    • 数据模型(exchange.ets): 处理 API 返回的 JSON 数据,转化为可用的对象数据。
    • 模块配置(module.json5): 包含权限申请、页面路径配置等基础信息。

    3. 代码实现

    3.1 主页面代码(index.ets)

    import { ApiService } from './apiService';
    import { ExchangeResponse, ExchangeData } from './exchange';
    
    @Entry
    @Component
    export struct Index {
      @State value: number = 0; // 用户输入的美元金额
      @State exchangeRate: string = "等待输入..."; // 显示兑换结果
      @State updateTime: string = "暂无数据"; // 汇率更新时间
      @State errorMessage: string = ""; // 错误提示信息
    
      private apiService = new ApiService();
    
      // 获取汇率逻辑
      getExchangeRate(amount: number): void {
        this.apiService.getExchangeData("USD", "CNY", (response, error) => {
          if (response && response.code === 200 && response.data) {
            const exchangeData: ExchangeData = response.data as ExchangeData;
            this.exchangeRate = ((exchangeData.exchange ?? 0) * amount).toFixed(2);
            this.updateTime = exchangeData.update_time ?? "未知时间";
            this.errorMessage = "";
          } else {
            this.errorMessage = error || "API请求失败";
          }
        });
      }
    
      // 输入防抖逻辑
      handleInput(value: string): void {
        const inputValue = parseFloat(value);
        if (!isNaN(inputValue) && inputValue > 0) {
          this.value = inputValue;
          this.getExchangeRate(inputValue);
        } else {
          this.errorMessage = "请输入有效金额";
        }
      }
    
      build() {
        Column() {
          Text("汇率转换器")
            .fontSize(24)
            .fontWeight(FontWeight.Bold)
            .margin(20);
    
          TextInput({
            placeholder: "请输入兑换的美元金额",
            text: this.value.toString(),
          })
            .type(InputType.Number)
            .onChange((e: string) => {
              this.handleInput(e);
            })
            .padding({ left: 20, right: 20 })
            .backgroundColor("#f0f0f0")
            .borderRadius(8);
    
          if (this.errorMessage) {
            Text(this.errorMessage)
              .fontColor("#FF0000")
              .margin(10);
          } else {
            Text(`兑换金额: ${this.exchangeRate} 人民币`).margin(10);
            Text(`更新时间: ${this.updateTime}`).margin(10);
          }
        }
        .height("100%")
        .width("100%")
        .backgroundColor("#FFFFFF");
      }
    }

    对于index的修改:

    • 用户输入验证:添加防抖机制,避免用户连续输入触发过多请求:
    private debounceTimeout: number | null = null;
    
    handleInput(value: string): void {
      if (this.debounceTimeout) {
        clearTimeout(this.debounceTimeout);
      }
      this.debounceTimeout = setTimeout(() => {
        const inputValue = parseFloat(value);
        if (!isNaN(inputValue) && inputValue > 0) {
          this.value = inputValue;
          this.getExchangeRate(inputValue);
        } else {
          this.errorMessage = "请输入有效的金额";
        }
      }, 500); // 防抖延迟 500 毫秒
    }
    • 错误提示友好
      • 输入无效金额时提示:请输入有效金额
      • API 请求失败时提示:网络请求失败 或 API返回错误。
    • UI 样式优化
      • TextInput 添加圆角、背景色。
      • 错误信息采用红色字体,醒目且易区分。

    3.2 API 服务类(apiService.ets)

    import http from '@ohos.net.http';
    import { ExchangeResponse } from './exchange';
    
    export class ApiService {
      private readonly API_URL = "https://v2.alapi.cn/api/exchange";
      private readonly API_KEY = "4YxBbmkhGAWA9BcW";
    
      public getExchangeData(
        from: string,
        to: string,
        callback: (response: ExchangeResponse | null, error: string | null) => void
      ): void {
        const httpRequest = http.createHttp();
        httpRequest.request(
          this.API_URL,
          {
            method: http.RequestMethod.POST,
            header: { 'Content-Type': 'application/json' },
            extraData: {
              token: this.API_KEY,
              from: from,
              to: to,
              money: 1,
            },
            expectDataType: http.HttpDataType.STRING,
          },
          (err, data) => {
            if (!err) {
              try {
                const response: ExchangeResponse = JSON.parse(data.result.toString());
                callback(response, null);
              } catch (parseError) {
                callback(null, "数据解析错误");
              }
            } else {
              callback(null, "网络请求失败");
            }
            httpRequest.destroy();
          }
        );
      }
    }

    对于apiService.ets的修改:

    • 服务类封装:将网络请求逻辑封装到 apiService.ets 中,主页面只需要调用接口。
    • 数据解析安全性:增加了对 API 返回值的类型检查和错误捕获,防止意外崩溃。
    try {
      const response: ExchangeResponse = JSON.parse(data.result.toString());
      if (response.code === 200 && response.data) {
        // 确保数据是字符串或 JSON 对象
        if (typeof response.data === "string") {
          response.data = JSON.parse(response.data) as ExchangeData;
        }
        callback(response, null);
      } else {
        callback(null, response.msg || "API返回错误");
      }
    } catch (parseError) {
      callback(null, "数据解析错误");
    }

    3.3 数据模型(exchange.ets)

    export class ExchangeResponse {
      code: number = 0; // 状态码
      msg: string = ""; // 返回消息
      data?: string | ExchangeData = undefined; // 返回的数据
    }
    
    export class ExchangeData {
      exchange?: number = 0; // 汇率
      update_time?: string = ""; // 更新时间
    }
    

    3.4 模块配置(module.json5)

    {
      "module": {
        "name": "entry",
        "type": "entry",
        "description": "$string:module_desc",
        "mainElement": "EntryAbility",
        "deviceTypes": ["phone", "tablet", "2in1"],
        "requestPermissions": [
          { "name": "ohos.permission.INTERNET" }
        ],
        "pages": [
          { "srcEntry": "./ets/pages/index.ets", "name": "Index" }
        ]
      }
    }

    4. 项目总结

    • 构建一个简单的 HarmonyOS 应用。
    • 通过对一个现有Demo进行复现和微调,提升自身编码能力。
    • 使用网络请求和 API 数据解析。
    • 组织和管理代码文件。

    ©著作权归作者所有,转载或内容合作请联系作者

    您尚未登录,无法参与评论,登录后可以:
    参与开源共建问题交流
    认同或收藏高质量问答
    获取积分成为开源共建先驱

    Copyright   ©2023  OpenHarmony开发者论坛  京ICP备2020036654号-3 |技术支持 Discuz!

    返回顶部