OpenHarmony开发者论坛

标题: HarmonyOS 汇率转换应用Demo练习 [打印本页]

作者: Zeekrom    时间: 7 天前
标题: HarmonyOS 汇率转换应用Demo练习
[md]<!-- more -->

### **简介**

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

![1111.png](http://reeshiram.site/static/img ... c96591218.1111.webp)

![22222.png](http://reeshiram.site/static/img ... 6b6f87f6.22222.webp)

### **开发环境**

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

### **1. 项目结构**

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

```js
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)

```js
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的修改:**

- **用户输入验证**:添加防抖机制,避免用户连续输入触发过多请求:

```js
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)

```js
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 返回值的类型检查和错误捕获,防止意外崩溃。

```js
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)

```js
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)

```js
{
  "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 数据解析。
- 组织和管理代码文件。
[/md]




欢迎光临 OpenHarmony开发者论坛 (https://forums.openharmony.cn/) Powered by Discuz! X3.5