在这里插入图片描述

目录

  1. 概述
  2. 工具功能
  3. 核心实现
  4. Kotlin 源代码
  5. JavaScript 编译代码
  6. ArkTS 调用代码
  7. 实战案例
  8. 最佳实践

概述

本文档介绍如何在 Kotlin Multiplatform (KMP) 鸿蒙跨端开发中实现一个完整的烹饪体积转换工具,特别是食谱单位转换助手。烹饪涉及多种体积单位的转换,包括杯、毫升、升、液体盎司、汤匙和茶匙等。这个案例展示了如何使用 Kotlin 的数据处理和烹饪知识来创建一个功能丰富的烹饪辅助工具。

食谱单位转换助手是烹饪爱好者的必备工具,它能够根据食材和体积单位,自动进行转换,并提供食材信息、密度数据和烹饪建议。无论是使用美国食谱、英国食谱还是欧洲食谱,这个工具都能帮助用户准确地测量食材。通过 KMP,这个工具可以无缝编译到 JavaScript,在 OpenHarmony 应用中运行,为烹饪爱好者提供专业的烹饪指导。

工具的特点

  • 多食材支持:支持面粉、糖、黄油、牛奶、油、水、盐、鸡蛋、蜂蜜、香草等常见食材
  • 体积单位转换:支持杯、毫升、升、液体盎司、汤匙、茶匙等多种单位
  • 食材密度数据:提供每种食材的密度信息,支持体积到重量的转换
  • 烹饪建议:根据食材提供专业的烹饪技巧和建议
  • 地区食谱标准:支持美国、英国、欧洲等不同地区的食谱标准
  • 跨端兼容:一份 Kotlin 代码可同时服务多个平台

工具功能

1. 支持的食材

系统支持 10 种常见的烹饪食材,每种食材都有详细的信息和烹饪建议:

面粉 (Flour)
  • 密度:0.52 g/ml
  • 用途:烘焙基础材料,影响蛋糕、饼干的结构
  • 烹饪建议
    • 过筛面粉可以增加空气
    • 不要过度混合面粉
    • 使用高筋面粉做面包,低筋面粉做蛋糕
糖 (Sugar)
  • 密度:0.80 g/ml
  • 用途:甜味剂,影响甜度和质地
  • 烹饪建议
    • 糖有助于保持湿度
    • 过量糖会导致过甜
    • 可用蜂蜜或糖浆替代
黄油 (Butter)
  • 密度:0.91 g/ml
  • 用途:脂肪来源,提供风味和质地
  • 烹饪建议
    • 使用室温黄油混合更均匀
    • 冷黄油适合做酥皮
    • 不要过热黄油
牛奶 (Milk)
  • 密度:1.03 g/ml
  • 用途:液体成分,提供湿度和营养
  • 烹饪建议
    • 室温牛奶混合更好
    • 可用酸奶或豆浆替代
    • 不要煮沸牛奶
油 (Oil)
  • 密度:0.92 g/ml
  • 用途:脂肪来源,提供湿度和风味
  • 烹饪建议
    • 中性油适合烘焙
    • 橄榄油适合咸味烘焙
    • 不要用太多油
水 (Water)
  • 密度:1.00 g/ml
  • 用途:基础液体,调节面团湿度
  • 烹饪建议
    • 冷水适合冷饮
    • 温水适合激活酵母
    • 不要用热水
盐 (Salt)
  • 密度:1.20 g/ml
  • 用途:调味料,增强风味
  • 烹饪建议
    • 盐增强风味
    • 不要过量
    • 海盐比食盐更细致
鸡蛋 (Egg)
  • 密度:1.03 g/ml
  • 用途:粘合剂,提供结构和营养
  • 烹饪建议
    • 室温鸡蛋混合更好
    • 鸡蛋提供结构和湿度
    • 不要过度搅打
蜂蜜 (Honey)
  • 密度:1.42 g/ml
  • 用途:天然甜味剂,增加湿度
  • 烹饪建议
    • 蜂蜜增加湿度和风味
    • 可替代部分糖
    • 会加速褐变
香草 (Vanilla)
  • 密度:0.88 g/ml
  • 用途:调味料,增加香气
  • 烹饪建议
    • 使用真正的香草精
    • 少量即可
    • 不要加热过度

2. 体积单位转换

系统支持 8 种常见的体积单位,适用于不同地区的食谱:

  • 杯 (cup):美国食谱中最常用的单位,1 杯 = 236.588 毫升
  • 毫升 (ml):公制系统中的基本单位,用于精确测量
  • 升 (l):公制系统中的大单位,1 升 = 1000 毫升
  • 液体盎司 (fl oz):英制系统中的单位,1 液体盎司 = 29.5735 毫升
  • 汤匙 (tbsp):烹饪中常用的单位,1 汤匙 = 14.7868 毫升
  • 茶匙 (tsp):烹饪中常用的小单位,1 茶匙 = 4.92892 毫升
  • 品脱 (pint):英制系统中的单位,1 品脱 = 473.176 毫升
  • 夸脱 (quart):英制系统中的大单位,1 夸脱 = 946.353 毫升

3. 食材密度信息

系统提供每种食材的密度信息,可以将体积转换为重量:

  • 面粉:0.52 g/ml(较轻,因为面粉是粉末)
  • :0.80 g/ml(中等密度)
  • 黄油:0.91 g/ml(接近水的密度)
  • 牛奶:1.03 g/ml(略高于水)
  • :0.92 g/ml(略低于水)
  • :1.00 g/ml(参考标准)
  • :1.20 g/ml(较重)
  • 鸡蛋:1.03 g/ml(略高于水)
  • 蜂蜜:1.42 g/ml(最重)
  • 香草:0.88 g/ml(较轻)

4. 烹饪建议

系统根据食材提供专业的烹饪建议,包括:

  • 食材特性:每种食材在烹饪中的作用和特点
  • 使用技巧:如何正确使用食材以获得最佳效果
  • 替代方案:如果没有某种食材,可以用什么替代
  • 注意事项:使用食材时需要注意的事项

5. 地区食谱标准

系统支持不同地区的食谱标准:

  • 美国食谱:使用杯 (cup) 和汤匙 (tbsp)
  • 英国食谱:使用毫升 (ml) 和液体盎司 (fl oz)
  • 欧洲食谱:使用毫升 (ml) 和克 (g)

核心实现

算法原理

烹饪体积转换工具的核心算法包括:

  1. 输入解析:解析用户输入的食材、数值和单位
  2. 中间单位转换:将源单位转换为毫升
  3. 目标单位转换:将毫升转换为目标单位
  4. 重量计算:根据食材密度计算重量
  5. 建议生成:根据食材生成烹饪建议

食材密度

食材密度是体积到重量转换的关键。不同的食材有不同的密度:

  • 低密度食材(< 0.9 g/ml):面粉、香草
  • 中等密度食材(0.9-1.1 g/ml):黄油、油、牛奶、鸡蛋、水
  • 高密度食材(> 1.1 g/ml):盐、蜂蜜

转换系数

系统使用国际标准的转换系数:

体积单位转换系数(以毫升为基准):

  • 1 杯 = 236.588 ml
  • 1 汤匙 = 14.7868 ml
  • 1 茶匙 = 4.92892 ml
  • 1 液体盎司 = 29.5735 ml
  • 1 品脱 = 473.176 ml
  • 1 夸脱 = 946.353 ml

Kotlin 源代码

// 案例 35: 烹饪中的体积转换 - 食谱单位转换助手
@OptIn(ExperimentalJsExport::class)
@JsExport
fun cookingVolumeConverter(inputData: String = "flour 2 cup ml"): String {
    if (inputData.isEmpty()) {
        return "❌ 错误: 输入不能为空\n请输入: 食材 数值 源单位 目标单位"
    }
    
    val parts = inputData.trim().split(" ")
    if (parts.size != 4) {
        return "❌ 错误: 输入格式不正确\n请输入: 食材 数值 源单位 目标单位"
    }
    
    val ingredient = parts[0].lowercase()
    val value = parts[1].toDoubleOrNull()
    val fromUnit = parts[2].lowercase()
    val toUnit = parts[3].lowercase()
    
    if (value == null) {
        return "❌ 错误: 数值输入无效"
    }
    
    if (value <= 0) {
        return "❌ 错误: 数值必须大于 0"
    }
    
    // 支持的食材列表
    val supportedIngredients = listOf("flour", "sugar", "butter", "milk", "oil", "water", "salt", "egg", "honey", "vanilla")
    if (ingredient !in supportedIngredients) {
        return "❌ 错误: 不支持的食材 ($ingredient)\n支持的食材: flour, sugar, butter, milk, oil, water, salt, egg, honey, vanilla"
    }
    
    // 1. 转换为毫升
    val valueInMl = when (fromUnit) {
        "cup" -> value * 236.588
        "ml" -> value
        "l" -> value * 1000
        "fl oz" -> value * 29.5735
        "tbsp" -> value * 14.7868
        "tsp" -> value * 4.92892
        "pint" -> value * 473.176
        "quart" -> value * 946.353
        else -> return "❌ 错误: 不支持的体积单位 ($fromUnit)"
    }
    
    // 2. 转换为目标单位
    val result = when (toUnit) {
        "cup" -> valueInMl / 236.588
        "ml" -> valueInMl
        "l" -> valueInMl / 1000
        "fl oz" -> valueInMl / 29.5735
        "tbsp" -> valueInMl / 14.7868
        "tsp" -> valueInMl / 4.92892
        "pint" -> valueInMl / 473.176
        "quart" -> valueInMl / 946.353
        else -> return "❌ 错误: 不支持的体积单位 ($toUnit)"
    }
    
    // 3. 获取食材信息
    val ingredientInfo = when (ingredient) {
        "flour" -> "面粉 (Flour)" to "烘焙基础材料,影响蛋糕、饼干的结构"
        "sugar" -> "糖 (Sugar)" to "甜味剂,影响甜度和质地"
        "butter" -> "黄油 (Butter)" to "脂肪来源,提供风味和质地"
        "milk" -> "牛奶 (Milk)" to "液体成分,提供湿度和营养"
        "oil" -> "油 (Oil)" to "脂肪来源,提供湿度和风味"
        "water" -> "水 (Water)" to "基础液体,调节面团湿度"
        "salt" -> "盐 (Salt)" to "调味料,增强风味"
        "egg" -> "鸡蛋 (Egg)" to "粘合剂,提供结构和营养"
        "honey" -> "蜂蜜 (Honey)" to "天然甜味剂,增加湿度"
        "vanilla" -> "香草 (Vanilla)" to "调味料,增加香气"
        else -> "未知食材" to "无信息"
    }
    
    // 4. 获取食材密度信息(克/毫升)
    val density = when (ingredient) {
        "flour" -> 0.52
        "sugar" -> 0.80
        "butter" -> 0.91
        "milk" -> 1.03
        "oil" -> 0.92
        "water" -> 1.00
        "salt" -> 1.20
        "egg" -> 1.03
        "honey" -> 1.42
        "vanilla" -> 0.88
        else -> 1.0
    }
    
    // 5. 计算克数
    val grams = valueInMl * density
    
    // 6. 获取烹饪建议
    val cookingTips = when (ingredient) {
        "flour" -> "• 过筛面粉可以增加空气\n• 不要过度混合面粉\n• 使用高筋面粉做面包,低筋面粉做蛋糕"
        "sugar" -> "• 糖有助于保持湿度\n• 过量糖会导致过甜\n• 可用蜂蜜或糖浆替代"
        "butter" -> "• 使用室温黄油混合更均匀\n• 冷黄油适合做酥皮\n• 不要过热黄油"
        "milk" -> "• 室温牛奶混合更好\n• 可用酸奶或豆浆替代\n• 不要煮沸牛奶"
        "oil" -> "• 中性油适合烘焙\n• 橄榄油适合咸味烘焙\n• 不要用太多油"
        "water" -> "• 冷水适合冷饮\n• 温水适合激活酵母\n• 不要用热水"
        "salt" -> "• 盐增强风味\n• 不要过量\n• 海盐比食盐更细致"
        "egg" -> "• 室温鸡蛋混合更好\n• 鸡蛋提供结构和湿度\n• 不要过度搅打"
        "honey" -> "• 蜂蜜增加湿度和风味\n• 可替代部分糖\n• 会加速褐变"
        "vanilla" -> "• 使用真正的香草精\n• 少量即可\n• 不要加热过度"
        else -> "• 按照食谱指示使用\n• 测量要准确\n• 使用量勺而不是普通勺"
    }
    
    return "👨‍🍳 烹饪体积转换助手\n" +
           "━━━━━━━━━━━━━━━━━━━━━\n" +
           "🥘 食材: ${ingredientInfo.first}\n" +
           "说明: ${ingredientInfo.second}\n\n" +
           "📏 体积转换\n" +
           "输入: $value $fromUnit\n" +
           "输出: ${(result * 100).toInt() / 100.0} $toUnit\n\n" +
           "⚖️ 重量信息\n" +
           "毫升数: ${(valueInMl * 100).toInt() / 100.0} ml\n" +
           "克数: ${(grams * 100).toInt() / 100.0} g\n" +
           "密度: $density g/ml\n\n" +
           "📚 单位参考\n" +
           "• 1 杯 (cup) = 236.588 ml\n" +
           "• 1 汤匙 (tbsp) = 14.7868 ml\n" +
           "• 1 茶匙 (tsp) = 4.92892 ml\n" +
           "• 1 液体盎司 (fl oz) = 29.5735 ml\n" +
           "• 1 品脱 (pint) = 473.176 ml\n" +
           "• 1 夸脱 (quart) = 946.353 ml\n\n" +
           "👨‍🍳 烹饪建议\n" +
           cookingTips + "\n\n" +
           "🌍 地区食谱标准\n" +
           "• 美国食谱: 使用杯 (cup) 和汤匙 (tbsp)\n" +
           "• 英国食谱: 使用毫升 (ml) 和液体盎司 (fl oz)\n" +
           "• 欧洲食谱: 使用毫升 (ml) 和克 (g)\n\n" +
           "━━━━━━━━━━━━━━━━━━━━━\n" +
           "✅ 转换完成!"
}

Kotlin 代码详解

这个 Kotlin 函数实现了一个完整的烹饪体积转换工具。函数接收一个字符串参数,包含食材名称、数值、源单位和目标单位。

首先,函数进行严格的输入验证。它检查输入是否为空,是否包含正确数量的字段,以及数值是否有效。

接下来,函数检查食材是否在支持的列表中。如果食材不支持,返回错误信息。

然后,函数进行两步转换:

  1. 将源单位转换为毫升(中间单位)
  2. 将毫升转换为目标单位

同时,函数获取食材的信息、密度和烹饪建议。最后,函数计算食材的重量(克数)并返回一个格式化的结果字符串。


JavaScript 编译代码

// 编译后的 JavaScript 代码(部分示例)
function cookingVolumeConverter(inputData) {
    if (inputData === undefined) {
        inputData = "flour 2 cup ml";
    }
    
    if (inputData.length === 0) {
        return "❌ 错误: 输入不能为空\n请输入: 食材 数值 源单位 目标单位";
    }
    
    var parts = inputData.trim().split(" ");
    if (parts.length !== 4) {
        return "❌ 错误: 输入格式不正确\n请输入: 食材 数值 源单位 目标单位";
    }
    
    var ingredient = parts[0].toLowerCase();
    var value = parseFloat(parts[1]);
    var fromUnit = parts[2].toLowerCase();
    var toUnit = parts[3].toLowerCase();
    
    if (isNaN(value)) {
        return "❌ 错误: 数值输入无效";
    }
    
    if (value <= 0) {
        return "❌ 错误: 数值必须大于 0";
    }
    
    var supportedIngredients = ["flour", "sugar", "butter", "milk", "oil", "water", "salt", "egg", "honey", "vanilla"];
    if (supportedIngredients.indexOf(ingredient) === -1) {
        return "❌ 错误: 不支持的食材 (" + ingredient + ")\n支持的食材: flour, sugar, butter, milk, oil, water, salt, egg, honey, vanilla";
    }
    
    // 转换为毫升
    var valueInMl;
    switch (fromUnit) {
        case "cup": valueInMl = value * 236.588; break;
        case "ml": valueInMl = value; break;
        case "l": valueInMl = value * 1000; break;
        case "fl oz": valueInMl = value * 29.5735; break;
        case "tbsp": valueInMl = value * 14.7868; break;
        case "tsp": valueInMl = value * 4.92892; break;
        case "pint": valueInMl = value * 473.176; break;
        case "quart": valueInMl = value * 946.353; break;
        default: return "❌ 错误: 不支持的体积单位 (" + fromUnit + ")";
    }
    
    // 转换为目标单位
    var result;
    switch (toUnit) {
        case "cup": result = valueInMl / 236.588; break;
        case "ml": result = valueInMl; break;
        case "l": result = valueInMl / 1000; break;
        case "fl oz": result = valueInMl / 29.5735; break;
        case "tbsp": result = valueInMl / 14.7868; break;
        case "tsp": result = valueInMl / 4.92892; break;
        case "pint": result = valueInMl / 473.176; break;
        case "quart": result = valueInMl / 946.353; break;
        default: return "❌ 错误: 不支持的体积单位 (" + toUnit + ")";
    }
    
    // 获取食材信息
    var ingredientInfo;
    switch (ingredient) {
        case "flour": ingredientInfo = ["面粉 (Flour)", "烘焙基础材料,影响蛋糕、饼干的结构"]; break;
        case "sugar": ingredientInfo = ["糖 (Sugar)", "甜味剂,影响甜度和质地"]; break;
        case "butter": ingredientInfo = ["黄油 (Butter)", "脂肪来源,提供风味和质地"]; break;
        case "milk": ingredientInfo = ["牛奶 (Milk)", "液体成分,提供湿度和营养"]; break;
        case "oil": ingredientInfo = ["油 (Oil)", "脂肪来源,提供湿度和风味"]; break;
        case "water": ingredientInfo = ["水 (Water)", "基础液体,调节面团湿度"]; break;
        case "salt": ingredientInfo = ["盐 (Salt)", "调味料,增强风味"]; break;
        case "egg": ingredientInfo = ["鸡蛋 (Egg)", "粘合剂,提供结构和营养"]; break;
        case "honey": ingredientInfo = ["蜂蜜 (Honey)", "天然甜味剂,增加湿度"]; break;
        case "vanilla": ingredientInfo = ["香草 (Vanilla)", "调味料,增加香气"]; break;
        default: ingredientInfo = ["未知食材", "无信息"];
    }
    
    // 获取食材密度
    var density;
    switch (ingredient) {
        case "flour": density = 0.52; break;
        case "sugar": density = 0.80; break;
        case "butter": density = 0.91; break;
        case "milk": density = 1.03; break;
        case "oil": density = 0.92; break;
        case "water": density = 1.00; break;
        case "salt": density = 1.20; break;
        case "egg": density = 1.03; break;
        case "honey": density = 1.42; break;
        case "vanilla": density = 0.88; break;
        default: density = 1.0;
    }
    
    // 计算克数
    var grams = valueInMl * density;
    
    // 获取烹饪建议
    var cookingTips;
    switch (ingredient) {
        case "flour": cookingTips = "• 过筛面粉可以增加空气\n• 不要过度混合面粉\n• 使用高筋面粉做面包,低筋面粉做蛋糕"; break;
        case "sugar": cookingTips = "• 糖有助于保持湿度\n• 过量糖会导致过甜\n• 可用蜂蜜或糖浆替代"; break;
        case "butter": cookingTips = "• 使用室温黄油混合更均匀\n• 冷黄油适合做酥皮\n• 不要过热黄油"; break;
        case "milk": cookingTips = "• 室温牛奶混合更好\n• 可用酸奶或豆浆替代\n• 不要煮沸牛奶"; break;
        case "oil": cookingTips = "• 中性油适合烘焙\n• 橄榄油适合咸味烘焙\n• 不要用太多油"; break;
        case "water": cookingTips = "• 冷水适合冷饮\n• 温水适合激活酵母\n• 不要用热水"; break;
        case "salt": cookingTips = "• 盐增强风味\n• 不要过量\n• 海盐比食盐更细致"; break;
        case "egg": cookingTips = "• 室温鸡蛋混合更好\n• 鸡蛋提供结构和湿度\n• 不要过度搅打"; break;
        case "honey": cookingTips = "• 蜂蜜增加湿度和风味\n• 可替代部分糖\n• 会加速褐变"; break;
        case "vanilla": cookingTips = "• 使用真正的香草精\n• 少量即可\n• 不要加热过度"; break;
        default: cookingTips = "• 按照食谱指示使用\n• 测量要准确\n• 使用量勺而不是普通勺";
    }
    
    return "👨‍🍳 烹饪体积转换助手\n" +
           "━━━━━━━━━━━━━━━━━━━━━\n" +
           "🥘 食材: " + ingredientInfo[0] + "\n" +
           "说明: " + ingredientInfo[1] + "\n\n" +
           "📏 体积转换\n" +
           "输入: " + value + " " + fromUnit + "\n" +
           "输出: " + (Math.floor(result * 100) / 100) + " " + toUnit + "\n\n" +
           "⚖️ 重量信息\n" +
           "毫升数: " + (Math.floor(valueInMl * 100) / 100) + " ml\n" +
           "克数: " + (Math.floor(grams * 100) / 100) + " g\n" +
           "密度: " + density + " g/ml\n\n" +
           "📚 单位参考\n" +
           "• 1 杯 (cup) = 236.588 ml\n" +
           "• 1 汤匙 (tbsp) = 14.7868 ml\n" +
           "• 1 茶匙 (tsp) = 4.92892 ml\n" +
           "• 1 液体盎司 (fl oz) = 29.5735 ml\n" +
           "• 1 品脱 (pint) = 473.176 ml\n" +
           "• 1 夸脱 (quart) = 946.353 ml\n\n" +
           "👨‍🍳 烹饪建议\n" +
           cookingTips + "\n\n" +
           "🌍 地区食谱标准\n" +
           "• 美国食谱: 使用杯 (cup) 和汤匙 (tbsp)\n" +
           "• 英国食谱: 使用毫升 (ml) 和液体盎司 (fl oz)\n" +
           "• 欧洲食谱: 使用毫升 (ml) 和克 (g)\n\n" +
           "━━━━━━━━━━━━━━━━━━━━━\n" +
           "✅ 转换完成!";
}

JavaScript 代码详解

Kotlin 代码编译到 JavaScript 后,保留了原有的逻辑结构,但使用 JavaScript 的语法和 API。主要的转换包括:

  1. 类型转换:Kotlin 的 toDoubleOrNull() 转换为 JavaScript 的 parseFloat()
  2. 条件语句:Kotlin 的 when 表达式转换为 JavaScript 的 switch 语句
  3. 集合操作:Kotlin 的 in 操作符转换为 JavaScript 的 indexOf()
  4. 字符串处理:Kotlin 的字符串模板转换为 JavaScript 的字符串拼接

ArkTS 调用代码

import { cookingVolumeConverter } from './hellokjs';

@Entry
@Component
struct Index {
  @State message: string = '准备就绪';
  @State ingredient: string = 'flour';
  @State value: string = '2';
  @State fromUnit: string = 'cup';
  @State toUnit: string = 'ml';
  @State resultText: string = '';
  @State isLoading: boolean = false;

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

  generateNewCase(): void {
    this.resultText = '';
    this.message = '准备就绪';
    
    const samples = [
      { ing: 'flour', v: '2', from: 'cup', to: 'ml' },
      { ing: 'sugar', v: '1', from: 'cup', to: 'g' },
      { ing: 'butter', v: '4', from: 'tbsp', to: 'g' },
      { ing: 'milk', v: '250', from: 'ml', to: 'cup' },
      { ing: 'honey', v: '1', from: 'cup', to: 'g' }
    ];
    
    const random = samples[Math.floor(Math.random() * samples.length)];
    this.ingredient = random.ing;
    this.value = random.v;
    this.fromUnit = random.from;
    this.toUnit = random.to;
  }

  convert(): void {
    this.isLoading = true;
    try {
      const input: string = `${this.ingredient} ${this.value} ${this.fromUnit} ${this.toUnit}`;
      const result: string = cookingVolumeConverter(input);
      this.resultText = result;
      console.log(result);
      this.message = '✓ 转换完成';
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error);
      this.message = `✗ 错误: ${errorMessage}`;
    } finally {
      this.isLoading = false;
    }
  }

  build() {
    Column() {
      // ===== 顶部标题栏 - 烹饪橙色主题 =====
      Column() {
        Text('👨‍🍳 Cooking Volume Converter')
          .fontSize(28)
          .fontWeight(FontWeight.Bold)
          .fontColor(Color.White)
          .margin({ bottom: 8 })
        
        Text('Recipe Unit Conversion Assistant')
          .fontSize(13)
          .fontColor('#FFE0B2')
      }
      .width('100%')
      .padding(24)
      .backgroundColor('#E65100')
      .alignItems(HorizontalAlign.Start)

      // ===== 主内容区域 =====
      Scroll() {
        Column() {
          // 输入卡片
          Column() {
            Row() {
              Text('🍳 Conversion')
                .fontSize(16)
                .fontWeight(FontWeight.Bold)
                .fontColor('#FFFFFF')
              
              Blank()
              
              Text('🎲 Random')
                .fontSize(12)
                .fontColor(Color.White)
                .padding({ left: 10, right: 10, top: 6, bottom: 6 })
                .backgroundColor('#BF360C')
                .borderRadius(12)
                .onClick(() => {
                  this.generateNewCase();
                })
            }
            .width('100%')
            .margin({ bottom: 18 })

            // 食材选择
            TextInput({ placeholder: 'Ingredient (flour/sugar/butter/milk/oil/water/salt/egg/honey/vanilla)', text: this.ingredient })
              .width('100%')
              .height(50)
              .padding(14)
              .fontSize(15)
              .fontColor('#000000')
              .placeholderColor('#999999')
              .border({ width: 2, color: '#E65100' })
              .borderRadius(12)
              .backgroundColor('#FFF3E0')
              .onChange((value: string) => {
                this.ingredient = value;
              })
              .margin({ bottom: 14 })

            // 数值输入
            TextInput({ placeholder: 'Value', text: this.value })
              .width('100%')
              .height(50)
              .padding(14)
              .fontSize(15)
              .fontColor('#000000')
              .placeholderColor('#999999')
              .border({ width: 2, color: '#E65100' })
              .borderRadius(12)
              .backgroundColor('#FFF3E0')
              .onChange((value: string) => {
                this.value = value;
              })
              .margin({ bottom: 14 })

            // 源单位
            TextInput({ placeholder: 'From Unit (cup/ml/l/fl oz/tbsp/tsp/pint/quart)', text: this.fromUnit })
              .width('100%')
              .height(50)
              .padding(14)
              .fontSize(15)
              .fontColor('#000000')
              .placeholderColor('#999999')
              .border({ width: 2, color: '#E65100' })
              .borderRadius(12)
              .backgroundColor('#FFF3E0')
              .onChange((value: string) => {
                this.fromUnit = value;
              })
              .margin({ bottom: 14 })

            // 目标单位
            TextInput({ placeholder: 'To Unit (cup/ml/l/fl oz/tbsp/tsp/pint/quart/g)', text: this.toUnit })
              .width('100%')
              .height(50)
              .padding(14)
              .fontSize(15)
              .fontColor('#000000')
              .placeholderColor('#999999')
              .border({ width: 2, color: '#E65100' })
              .borderRadius(12)
              .backgroundColor('#FFF3E0')
              .onChange((value: string) => {
                this.toUnit = value;
              })
              .margin({ bottom: 20 })

            // 转换按钮
            Button(this.isLoading ? 'Converting...' : 'Convert')
              .width('100%')
              .height(54)
              .fontSize(16)
              .fontWeight(FontWeight.Bold)
              .fontColor(Color.White)
              .backgroundColor(this.isLoading ? '#BF360C' : '#E65100')
              .borderRadius(14)
              .onClick(() => {
                if (!this.isLoading) {
                  this.convert();
                }
              })
          }
          .width('90%')
          .padding(20)
          .backgroundColor('#FFFFFF')
          .borderRadius(16)
          .margin({ top: 20, bottom: 20, left: 'auto', right: 'auto' })
          .border({ width: 2, color: '#E65100' })

          // 状态提示卡片
          if (this.message !== '准备就绪') {
            Row() {
              Text(this.message.startsWith('✓') ? '✅' : '⚠️')
                .fontSize(20)
                .margin({ right: 12 })

              Text(this.message)
                .fontSize(14)
                .fontColor(this.message.startsWith('✓') ? '#4ECDC4' : '#FF6B6B')
                .fontWeight(FontWeight.Bold)
                .layoutWeight(1)
            }
            .width('90%')
            .padding(16)
            .backgroundColor(this.message.startsWith('✓') ? '#1a3a3a' : '#3a1a1a')
            .border({ width: 2, color: this.message.startsWith('✓') ? '#4ECDC4' : '#FF6B6B' })
            .borderRadius(14)
            .margin({ bottom: 20, left: 'auto', right: 'auto' })
            .alignItems(VerticalAlign.Center)
          }

          // 结果卡片
          if (this.resultText) {
            Column() {
              Row() {
                Text('📊 Conversion Result')
                  .fontSize(14)
                  .fontWeight(FontWeight.Bold)
                  .fontColor('#E65100')
              }
              .width('100%')
              .padding(14)
              .backgroundColor('#FFF8F0')
              .borderRadius(12)
              .margin({ bottom: 14 })
              .border({ width: 1, color: '#E65100' })

              Text(this.resultText)
                .fontSize(12)
                .fontFamily('monospace')
                .fontColor('#BF360C')
                .width('100%')
                .padding(14)
                .backgroundColor('#FFF8F0')
                .borderRadius(12)
                .border({ width: 1, color: '#FF6F00' })
            }
            .width('90%')
            .padding(16)
            .backgroundColor('#FFF3E0')
            .borderRadius(16)
            .margin({ bottom: 20, left: 'auto', right: 'auto' })
            .border({ width: 2, color: '#E65100' })
          }

          // 底部提示
          Column() {
            Row() {
              Text('💡')
                .fontSize(18)
                .margin({ right: 10 })
              
              Text('👨‍🍳 Cooking Tips')
                .fontSize(14)
                .fontWeight(FontWeight.Bold)
                .fontColor('#E65100')
            }
            .width('100%')
            .margin({ bottom: 12 })

            Text('• Select ingredient\n• Enter volume and units\n• Get instant conversion\n• Get cooking advice')
              .fontSize(12)
              .fontColor('#BF360C')
              .lineHeight(1.6)
          }
          .width('90%')
          .padding(16)
          .backgroundColor('#FFF3E0')
          .borderRadius(16)
          .margin({ bottom: 20, left: 'auto', right: 'auto' })
          .border({ width: 2, color: '#E65100' })

          Blank()
            .height(20)
        }
        .width('100%')
      }
      .layoutWeight(1)
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#FFF3E0')
  }
}

ArkTS 代码详解

这个 ArkTS 组件实现了烹饪体积转换工具的用户界面。组件使用了 OpenHarmony 的 ArkTS 语言,提供了一个现代化的、烹饪橙色主题的用户界面。

组件定义了五个 @State 属性来存储转换信息:食材、数值、源单位和目标单位。这些属性与输入框绑定,当用户输入数据时,这些属性会自动更新。

generateNewCase() 方法生成随机的烹饪转换样本,帮助用户快速测试应用。用户可以点击"Random"按钮来生成随机数据。

convert() 方法调用 Kotlin 编译的 JavaScript 函数 cookingVolumeConverter(),并将转换信息作为参数传递。函数返回的转换结果会显示在结果卡片中。

用户界面包括四个输入框(食材、数值、源单位、目标单位)、一个转换按钮、一个状态提示卡片、一个结果卡片和一个提示卡片。所有元素都使用了烹饪橙色主题。


实战案例

案例 1:面粉转换

烹饪需要 2 杯面粉,需要转换为毫升。输入:flour 2 cup ml

系统转换结果:

  • 输入:2 cup
  • 输出:473.18 ml
  • 克数:246.06 g
  • 密度:0.52 g/ml
  • 烹饪建议:过筛面粉可以增加空气,不要过度混合面粉

案例 2:糖转换

烹饪需要 1 杯糖,需要转换为克。输入:sugar 1 cup g

系统转换结果:

  • 输入:1 cup
  • 输出:236.59 ml
  • 克数:189.27 g
  • 密度:0.80 g/ml
  • 烹饪建议:糖有助于保持湿度,过量糖会导致过甜

案例 3:黄油转换

烹饪需要 4 汤匙黄油,需要转换为克。输入:butter 4 tbsp g

系统转换结果:

  • 输入:4 tbsp
  • 输出:59.15 ml
  • 克数:53.83 g
  • 密度:0.91 g/ml
  • 烹饪建议:使用室温黄油混合更均匀,冷黄油适合做酥皮

案例 4:牛奶转换

烹饪需要 250 毫升牛奶,需要转换为杯。输入:milk 250 ml cup

系统转换结果:

  • 输入:250 ml
  • 输出:1.06 cup
  • 克数:257.50 g
  • 密度:1.03 g/ml
  • 烹饪建议:室温牛奶混合更好,可用酸奶或豆浆替代

案例 5:蜂蜜转换

烹饪需要 1 杯蜂蜜,需要转换为克。输入:honey 1 cup g

系统转换结果:

  • 输入:1 cup
  • 输出:236.59 ml
  • 克数:335.95 g
  • 密度:1.42 g/ml
  • 烹饪建议:蜂蜜增加湿度和风味,可替代部分糖

最佳实践

1. 准确测量

烹饪中的准确测量非常重要:

  • 使用量勺:使用专业的烹饪量勺而不是普通勺子
  • 水平测量:确保量勺中的食材与勺子边缘齐平
  • 不要压实:除非食谱特别说明,否则不要压实食材
  • 使用电子秤:对于精确的烘焙,使用电子秤比体积测量更准确

2. 理解食材密度

不同的食材有不同的密度,这会影响转换结果:

  • 低密度食材:面粉、糖等粉末状食材
  • 中等密度食材:黄油、油、牛奶等
  • 高密度食材:蜂蜜、盐等
  • 液体食材:水、油等

3. 适应不同的食谱标准

不同地区的食谱使用不同的单位制:

  • 美国食谱:使用杯和汤匙
  • 英国食谱:使用毫升和液体盎司
  • 欧洲食谱:使用毫升和克

4. 食材替代

如果没有某种食材,可以使用替代品:

  • 黄油替代品:油、人造黄油
  • 牛奶替代品:酸奶、豆浆、椰奶
  • 糖替代品:蜂蜜、糖浆、人工甜味剂
  • 鸡蛋替代品:亚麻籽、香蕉、豆腐

5. 烹饪技巧

掌握基本的烹饪技巧可以提高成功率:

  • 室温食材:大多数烘焙食材应该在室温下使用
  • 正确混合:不同的食材需要不同的混合方式
  • 烤箱温度:确保烤箱温度准确
  • 烘焙时间:根据食谱调整烘焙时间

总结

烹饪中的体积转换 - 食谱单位转换助手展示了如何使用 Kotlin Multiplatform 技术创建一个功能丰富、跨端兼容的烹饪辅助应用。通过支持多种食材和单位制的转换,系统能够帮助烹饪爱好者准确地测量食材。

这个案例不仅展示了 KMP 的强大功能,还演示了如何将烹饪知识转化为应用功能。无论是初学者还是经验丰富的厨师,都可以使用这个工具来确保食材的准确测量,从而制作出更美味的食物。

烹饪是一门艺术和科学的结合。通过使用这个烹饪体积转换工具,用户可以更加自信地按照食谱进行烹饪,享受烹饪的乐趣,制作出令人满意的美食。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐