在这里插入图片描述

使用 Kotlin Multiplatform 编译为 JavaScript,在 OpenHarmony ArkTS 页面中调用,实现一个简单直观的跨端汇率转换小工具。

1. 案例简介

这个小案例的目标,是在现有 KMP + OpenHarmony 工程中,新增一个汇率转换计算器

  • 在 Kotlin 侧实现 currencyConverter 函数,负责解析输入、套用汇率、计算手续费、生成详细文本结果;
  • 通过 Kotlin/JS IR 后端编译成 hellokjs.mjshellokjs.d.ts,再复制为 ArkTS 可用的 hellokjs.js
  • 在 ArkTS 页面 Index.ets 中通过 ES 模块导入 currencyConverter,绑定到输入框和按钮,实现一键汇率转换体验;
  • 最终形成一套可复用的跨端汇率逻辑,无论是 Web 还是 鸿蒙端,都可以共享同一份 Kotlin 代码。

与工程中其它案例(例如“每日时间分配与效率分析工具”“任务优先级排序工具”“睡眠质量评分与建议工具”等)相比,本案例更偏向金融工具类,主题是金额和货币之间的换算,非常适合用来演示:“一段业务逻辑如何在不同 UI 之上反复复用”。


2. Kotlin 侧实现:currencyConverter

Kotlin 源码位于 src/jsMain/kotlin/App.kt 中,通过 @JsExport 将函数导出到 JS。

输入采用一行字符串的简洁格式:

  • 金额:正数,例如 100250.5
  • 源货币代码:USDCNYEURGBPJPYKRW 等常见三位大写代码;
  • 目标货币代码:同样是三位代码;

示例输入:

  • 100 USD CNY:表示把 100 美元按当前模拟汇率换算为人民币;
  • 200 CNY USD:表示把 200 人民币换算为美元;
  • 50 EUR JPY:表示把 50 欧元换算为日元。

Kotlin 核心实现节选如下(只展示关键逻辑):

@OptIn(ExperimentalJsExport::class)
@JsExport
fun currencyConverter(inputText: String = "100 USD CNY"): String {
    val parts = inputText.trim().split(" ").filter { it.isNotEmpty() }
    if (parts.size < 3) {
        return "❌ 错误: 请按 '金额 源货币 目标货币' 的格式输入,例如: 100 USD CNY"
    }

    val amount = parts[0].toDoubleOrNull()
    val fromCurrency = parts[1].uppercase()
    val toCurrency = parts[2].uppercase()
    if (amount == null || amount <= 0) {
        return "❌ 错误: 金额无效\n请输入正数,例如: 100 USD CNY"
    }

    val exchangeRates = mapOf(
        "USD" to mapOf("CNY" to 7.2, "EUR" to 0.85, "GBP" to 0.73, "JPY" to 110.0, "KRW" to 1180.0),
        "CNY" to mapOf("USD" to 0.139, "EUR" to 0.118, "GBP" to 0.101, "JPY" to 15.3, "KRW" to 164.0),
        // 其他货币略
    )

    val fromRates = exchangeRates[fromCurrency]
        ?: return "❌ 错误: 不支持的源货币 '$fromCurrency'..."

    val rate = fromRates[toCurrency]
        ?: return "❌ 错误: 不支持的目标货币 '$toCurrency'..."

    val convertedAmount = amount * rate
    val convertedRounded = (convertedAmount * 100).toInt() / 100.0

    val feeRate = 0.015
    val fee = convertedAmount * feeRate
    val feeRounded = (fee * 100).toInt() / 100.0
    val finalAmount = convertedAmount - fee
    val finalRounded = (finalAmount * 100).toInt() / 100.0

    val currencyInfo = mapOf("USD" to "美元", "CNY" to "人民币", /* ... */ )
    val fromName = currencyInfo[fromCurrency] ?: fromCurrency
    val toName = currencyInfo[toCurrency] ?: toCurrency

    return "💱 汇率转换计算器..." // 实际返回为多行详细中文说明
}

这是汇率转换计算器的核心实现函数。函数使用 @OptIn(ExperimentalJsExport::class)@JsExport 注解,使其可以被编译成 JavaScript 并在 ArkTS 中调用。函数首先进行输入解析,使用 split(" ") 将输入字符串分割为三部分(金额、源货币、目标货币),使用 filter { it.isNotEmpty() } 过滤空字符串。然后验证输入格式,检查是否包含三个部分。接着使用 toDoubleOrNull() 安全转换金额为浮点数,使用 uppercase() 将货币代码转换为大写,并验证金额是否为正数。然后定义汇率数据,使用嵌套 Map 存储不同货币对之间的汇率。使用 Elvis 操作符 ?: 检查源货币和目标货币是否支持,如果不支持则返回错误信息。计算转换后的金额,并使用整数运算进行四舍五入到两位小数。计算 1.5% 的手续费和最终到账金额。定义货币名称映射,将货币代码转换为中文名称。最后返回格式化的多行中文说明文本,包含汇率、手续费和最终到账金额等信息。

这段代码体现了几个要点:

  • 输入解析清晰:先整体 split,再针对金额和货币代码进行校验,错误信息直接以中文返回;
  • 汇率数据内嵌:为了示例简单,汇率通过 mapOf 模拟,实际项目可以替换为网络请求或配置文件;
  • 手续费和实际到账分离:用户不仅看到理论转换金额,也能看到扣除 1.5% 手续费后的到帐金额,体验更接近真实业务;
  • 返回文本直观友好:返回值并非纯数字,而是带 Emoji 和分段标题的多行说明文本,非常适合直接在 UI 文本区域展示。

3. JS/ES Module 导出与使用示意

在构建阶段,Kotlin/JS IR 会生成一个名为 hellokjs.mjs 的 ES Module 文件以及对应的 hellokjs.d.ts 类型声明。脚本 build-and-copy.bat 会把它们复制到 ArkTS 页面的 pages 目录,并将 .mjs 重命名为 .js

在纯 JavaScript 环境下,使用方式和普通 ES 模块几乎没有区别。例如,在 Node.js 或 Web 项目中可以像这样调用(示意):

import { currencyConverter } from './hellokjs.mjs';

const input = '100 USD CNY';
const result = currencyConverter(input);
console.log(result);

这段代码展示了如何在 JavaScript 环境中调用编译后的 Kotlin 函数。首先使用 import 语句从编译后的 JavaScript 模块 hellokjs.mjs 中导入 currencyConverter 函数。然后定义输入字符串 '100 USD CNY',表示将 100 美元转换为人民币。调用 currencyConverter(input) 函数,传入输入字符串,获取转换结果。最后使用 console.log() 输出结果。这展示了 KMP 的跨端能力,同一份 Kotlin 代码可以在 JavaScript 环境中无缝调用,无需任何修改。

虽然实际项目中你不需要手写这个 JS 入口文件,但这个示例可以帮助理解:Kotlin 导出的函数,就是一个普通的 JS 函数,可以被任何支持 ES Module 的环境导入和使用。

hellokjs.d.ts 则为 TypeScript/ArkTS 提供了类型信息,例如:

export declare function currencyConverter(inputText?: string): string;

有了这份声明,ArkTS 在编译和写代码时就能获得自动补全和类型检查,避免传错参数类型。


4. ArkTS 页面:在 Index.ets 中调用汇率转换

OpenHarmony 端的 UI 页面位于:

  • kmp_ceshiapp/entry/src/main/ets/pages/Index.ets

在这个文件中,我们通过 ES Module 语法导入编译后的 JS 函数,然后将其绑定到输入框与按钮的交互逻辑上:

import { currencyConverter } from './hellokjs';

@Entry
@Component
struct Index {
  @State message: string = '请输入金额与货币,例如: 100 USD CNY';
  @State inputText: string = '100 USD CNY';
  @State resultText: string = '';

  aboutToAppear(): void {
    this.convertCurrency();
  }

  convertCurrency(): void {
    try {
      const input: string = this.inputText;
      const result: string = currencyConverter(input);
      this.resultText = result;
      this.message = '✓ 转换完成';
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error);
      this.message = `✗ 错误: ${errorMessage}`;
    }
  }

  build() {
    Column() {
      // 头部标题栏 + 输入区 + 结果区(完整代码见项目)
    }
  }
}

这段 ArkTS 代码是鸿蒙应用的用户界面实现,通过 import 语句导入之前编译的 Kotlin 函数。使用 @State 装饰器定义三个响应式状态变量:message 用于显示状态信息,inputText 存储用户输入的转换指令(默认为 “100 USD CNY”),resultText 存储转换结果。aboutToAppear() 生命周期函数在组件加载时自动调用 convertCurrency() 进行初始转换。convertCurrency() 方法调用 Kotlin 编译的 JavaScript 函数 currencyConverter(),将用户输入的文本传入,获取转换结果,然后更新 resultTextmessage。使用 try-catch 块捕获异常,如果发生错误则显示错误信息。build() 方法构建整个 UI 布局,包含标题栏、输入区和结果显示区。这个结构实现了清晰的前后端职责划分:ArkTS 只负责收集输入和展示结果,所有汇率计算逻辑都集中在 Kotlin 中。

在 UI 层面,页面提供了:

  • 一个多行 TextInput,用于输入类似 100 USD CNY 的指令;
  • 一个“开始汇率转换”按钮,点击时调用 this.convertCurrency(),进而调用 Kotlin 导出的 currencyConverter
  • 一个滚动区域,用来显示 resultText 中的多行中文说明,包括汇率、手续费和注意事项等。

依托这一结构,ArkTS 页面只负责收集输入、展示结果,所有汇率计算逻辑都集中在 Kotlin 中,达到了非常清晰的前后端职责划分。


5. 生成与替换 hellokjs.d.ts / hellokjs.js 的自动化流程

项目根目录下已经准备好了自动脚本:

  • build-and-copy.bat
  • build-and-copy.ps1

以 Windows 批处理为例,整个流程只需要一条命令:

cd /d d:\flutter_Obj\kmp_openharmony
build-and-copy.bat

这段批处理脚本命令用于自动化编译和复制流程。首先使用 cd /d 命令切换到项目根目录 d:\flutter_Obj\kmp_openharmony/d 参数允许跨驱动器切换)。然后执行 build-and-copy.bat 脚本,该脚本会自动执行以下操作:调用 gradlew build 编译 Kotlin/JS 工程,生成 hellokjs.mjshellokjs.d.ts 文件;检查输出文件是否存在于 build/js/packages/hellokjs/kotlin/ 目录;将这两个文件复制到 ArkTS 页面的 pages 目录,并将 .mjs 重命名为 .js。这个自动化流程确保 ArkTS 端始终使用最新的 Kotlin 逻辑和类型声明,无需手动复制文件。

脚本内部主要做了三件事:

  1. 编译 Kotlin/JS
    调用 gradlew build,触发 Kotlin Multiplatform 的 JS 目标构建,生成 hellokjs.mjshellokjs.d.ts

  2. 检查输出文件
    确认 build/js/packages/hellokjs/kotlin/ 目录下确实存在最新的 hellokjs.mjshellokjs.d.ts,如果缺失则提示错误。

  3. 复制与重命名
    将上述两个文件复制到 kmp_ceshiapp/entry/src/main/ets/pages/ 目录下:

    • .d.ts 直接复制为 hellokjs.d.ts
    • .mjs 复制后重命名为 hellokjs.js,方便 ArkTS 使用 ./hellokjs 的模块名导入。

由此,整个从 Kotlin 代码改动到 ArkTS 实际调用的链路变得非常顺滑:只要跑一遍脚本,ArkTS 就能拿到最新的逻辑实现和类型声明。


6. 设计思路与可扩展方向

从教学和工程实践两个角度来看,这个“汇率转换计算器”案例有几个比较有代表性的特点:

  • 纯函数式业务逻辑currencyConverter 只依赖输入字符串和内部汇率表,不访问数据库、不调用网络,这使得函数非常容易被测试和复用。
  • 面向文本的返回结果:返回的不是结构化 JSON,而是一段面向终端用户的中文说明文本,其中包含 Emoji、分节标题和多行内容。ArkTS 页面只需要把它塞进 Text 即可渲染,非常省事。
  • 支持多币种扩展:汇率数据集中维护在一个 Map<String, Map<String, Double>> 中,日后要新增货币只需在这张表里添加一行即可,不会影响调用方 API。
  • 可以无缝扩展到 Web/H5:由于底层是标准 ES Module,未来如果再做一个浏览器端页面,只需复用同一个 hellokjs.mjs 模块即可调用同一套逻辑,无需重新实现。

如果希望进一步增强这个案例,可以考虑:

  1. 接入真实汇率 API
    将当前硬编码的汇率替换为从网络接口获取的实时汇率,Kotlin 侧可以通过协程和 JS interop 调用浏览器或 Node.js 提供的 fetch,然后在 ArkTS 侧展示“更新时间”等信息。

  2. 增加多步骤交互
    比如为用户提供常用的货币对快捷按钮(USD→CNY、CNY→USD 等),或者增加一个简单的汇率趋势说明,让工具不仅给出瞬时结果,还给出趋势提示。

  3. 组合其他案例
    将“个人收支预算分析工具”和“汇率转换计算器”组合起来,实现一个同时支持本币和外币的个人资产小面板;或者和“任务优先级排序工具”结合,用来规划投资/理财任务的优先级。

通过这些扩展,可以进一步展示 KMP 在复杂业务场景下的复用能力:无论逻辑多复杂,只要封装在 Kotlin 模块里,就可以一处维护,多端调用。


7. 小结

在本案例中,我们围绕一个简单实用的“汇率转换计算器”构建了完整链路:

  1. 在 Kotlin 中实现并导出 currencyConverter,负责解析输入、套用汇率、计算手续费,以及返回易读的说明文本;
  2. 使用 Kotlin/JS IR 编译为 hellokjs.mjshellokjs.d.ts,再通过脚本复制并重命名为 ArkTS 侧使用的 hellokjs.js
  3. Index.ets 中导入 currencyConverter,将其绑定到输入框和按钮的交互上,实现一键汇率转换体验;
  4. 通过统一的 README 文档,总结了 Kotlin 代码、JS 导出和 ArkTS 调用三者之间的关系。

如果你已经熟悉本工程中其他多个案例,那么这个“汇率转换计算器”可以作为一块金融工具类的小积木。未来无论是做多页面 Demo,还是构建一个更完整的跨端理财/记账应用,都可以在这个案例的基础上继续演化和扩展。

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐