【推荐+1】HarmonyOS官方模板优秀案例 (第4期:餐饮行业 · 美食菜谱)

💡 鸿蒙生态为开发者提供海量的HarmonyOS模板/组件,助力开发效率原地起飞 💡

★ 一键直达生态市场组件&模板市场 , 快速应用DevEco Studio插件市场集成组件&模板

好用的菜谱APP能够帮助饮食管理事半功倍!

小编本期为大家介绍餐饮行业案例

👉 覆盖20+行业,本帖以汇总形式持续更新中,点击收藏!一键三连!常看常新!

【第4期】餐饮行业 · 美食菜谱

一、概述

1. 行业洞察

1)行业痛点:

  • 传统餐饮私域流量缺失,依赖第三方平台导流,佣金成本高且较难沉淀用户。
  • 网络菜谱步骤描述模糊,用料配比不精确,成功率低。
  • 个性化不足:缺乏根据用户口味偏好、饮食限制(素食/低糖/无麸质)的智能推荐。
  • 购物不便:菜谱食材无法一键购买,特殊调料难以配齐。
  • 数据价值未挖掘:用户烹饪行为数据未形成精准画像。
  • 互动体验差:用户作品分享渠道单一,缺乏专业厨师点评指导。

2)行业常用三方SDK

|--------|----------------------------------------------------------|---------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 分类 | 三方库名称 | 功能 | 支持情况 | SDK链接 |
| 媒体 | 阿里云视频播放器SDK | 音视频 | 已支持 | 支付宝SDK 微信支付SDK 银联SDK 腾讯QQ SDK 新浪微博SDK 极光PUSH SDK 友盟移动统计SDK 腾讯微信SDK 高德地图SDK 个推 Bugly ShareSDK 听云SDK |
| 登录认证 | 中国移动一键登录SDK/易盾一键登录SDK/创蓝闪验/极光安全认证/阿里云号码认证SDK/中国电信一键登录SDK | 登录 | 已支持 | 支付宝SDK 微信支付SDK 银联SDK 腾讯QQ SDK 新浪微博SDK 极光PUSH SDK 友盟移动统计SDK 腾讯微信SDK 高德地图SDK 个推 Bugly ShareSDK 听云SDK |
| 分享 | 友盟/ShareSDK/微信分享/QQ分享/新浪微博SDK/MobTech ShareSDK | 统计/推送/分享 | 已支持 | 支付宝SDK 微信支付SDK 银联SDK 腾讯QQ SDK 新浪微博SDK 极光PUSH SDK 友盟移动统计SDK 腾讯微信SDK 高德地图SDK 个推 Bugly ShareSDK 听云SDK |
| 支付 | 支付宝支付/微信支付/银联支付 | 支付 | 已支持 | 支付宝SDK 微信支付SDK 银联SDK 腾讯QQ SDK 新浪微博SDK 极光PUSH SDK 友盟移动统计SDK 腾讯微信SDK 高德地图SDK 个推 Bugly ShareSDK 听云SDK |
| 数据分析 | 友盟移动统计SD/神策数据SDK | 数据收集、处理、分析、运用 | 已支持 | 支付宝SDK 微信支付SDK 银联SDK 腾讯QQ SDK 新浪微博SDK 极光PUSH SDK 友盟移动统计SDK 腾讯微信SDK 高德地图SDK 个推 Bugly ShareSDK 听云SDK |
| 性能监控 | 腾讯Bugly SDK/听云SDK/岳鹰全景监控SDK | 异常上报和运营统计 | 已支持 | 支付宝SDK 微信支付SDK 银联SDK 腾讯QQ SDK 新浪微博SDK 极光PUSH SDK 友盟移动统计SDK 腾讯微信SDK 高德地图SDK 个推 Bugly ShareSDK 听云SDK |
| 地图 | 高德地图SDK | 地图 | 已支持 | 支付宝SDK 微信支付SDK 银联SDK 腾讯QQ SDK 新浪微博SDK 极光PUSH SDK 友盟移动统计SDK 腾讯微信SDK 高德地图SDK 个推 Bugly ShareSDK 听云SDK |
| 推送 | 个推/华为推送/极光PUSH/阿里推送SDK | 消息推送 | 已支持 | 支付宝SDK 微信支付SDK 银联SDK 腾讯QQ SDK 新浪微博SDK 极光PUSH SDK 友盟移动统计SDK 腾讯微信SDK 高德地图SDK 个推 Bugly ShareSDK 听云SDK |
| 媒体 | 阿里云视频播放器SDK | 音视频 | 已支持 | 支付宝SDK 微信支付SDK 银联SDK 腾讯QQ SDK 新浪微博SDK 极光PUSH SDK 友盟移动统计SDK 腾讯微信SDK 高德地图SDK 个推 Bugly ShareSDK 听云SDK |

说明:"以上三方库及链接仅为示例,三方库由三方开发者独立提供,以其官方内容为准"

2. 案例概览(下载模板

基于以上行业分析,本期将介绍鸿蒙生态市场餐饮行业模板------美食菜谱应用模板,为行业提供常用功能的开发案例,涵盖分类搜索、作品分享、菜谱用料、热量计算、饮食计划等多个实用功能。

  • Stage开发模型 + 声明式UI开发范式。
  • 分层架构设计 + 组件化拆分,支持开发者在开发时既可以选择完整使用模板,也可以根据需求单独选用其中的业务组件。
  • 集成华为账号、支付等服务,只需做少量配置和定制即可快速实现华为账号的登录、菜谱上传等功能。

本模板主要页面及核心功能如下所示:

二、应用架构设计

1. 分层模块化设计

  • 产品定制层: 专注于满足不同设备或使用场景的个性化需求,作为应用的入口,是用户直接互动的界面。
    • 本实践暂时只支持直板机,为单HAP包形式,包含路由根节点、底部导航栏等。
  • 基础特性层: 用于存放相对独立的功能UI和业务逻辑实现。
    • 本实践的基础特性层将应用底部导航栏的每个选项拆分成一个独立的业务功能模块,包含首页、分类、热量计算和我的。
    • 每个功能模块都具备高内聚、低耦合、可定制的特点,支持产品的灵活部署。
  • 公共能力层: 存放公共能力,包括公共UI组件、数据管理、外部交互和工具库等共享功能。
    • 本实践的公共能力层分为公共基础能力和可分可合组件,均打包为HAR包被上层业务组件引用。
    • 公共基础能力包含日志、文件处理等工具类,公共类型定义,网络库,以及弹窗、加载等公共组件。
    • 可分可合组件将包含行业特点、可完全自闭环的能力抽出独立的组件模块,支持开发者在开发中单独集成使用,详见业务组件设计章节。

2. 业务组件设计

为支持开发者单独获取特定场景的页面和功能,例如开发者已搭建了一个自己的美食菜谱元服务工程,只想单独取用本模板中的菜篮子或热量计算功能,本模板将功能完全自闭环的部分能力抽离出独立的行业组件模块,不依赖公共基础能力包,开发者可以单独集成,开箱即用,降低使用难度。

三、行业场景技术方案

1. 账号管理

1)场景说明

支持华为账号一键登录及其他方式(账号密码登录、微信登录)。

用户登录后展示昵称和头像,点击用户信息栏可进入用户主页,查看并编辑个人信息。

2)技术方案

  • 华为账号一键登录
  • 微信登录
    • 根据鸿蒙接入指南接入微信SDK,可通过跳转微信并获取微信用户授权进行登录。
  • 头像修改
    • 通过Scenario Fusion Kit提供的选择头像Button快速拉起头像选择页面,供用户完成华为账号头像或其他头像的选择与展示。

3)代码参考

  • 部分核心代码参见个人信息实现章节。

2. 分类搜索

1)场景说明

  • 支持按菜谱分类展示和搜索菜谱,点击菜谱可以查看菜谱详细信息。

2)技术方案

使用ListGrid组件实现分类列表的展示、连续滚动以及搜索结果展示。

3)代码参考

  • 部分核心代码参见分类搜索实现章节。

3. 饮食计划

1)场景说明

支持制定饮食计划的食材,也可以自定义食材并填写热量。

2)技术方案

4. 热量计算

1)场景说明

支持查看当日饮食热量摄入情况,或者查看一周热量摄入统计。

2)技术方案

3)代码参考

  • 部分核心代码参见热量计算实现章节。

四、模板代码

1. 工程结构(下载模板

详细代码结构如下所示:

复制代码
Recipes
  ├─commons/commonlib/src/main
  │  ├─ets
  │  │  ├─components
  │  │  │      BaseHeader.ets                 // 一级页面标题组件
  │  │  │      BuildTitleBar.ets              // 二级页面标题组件
  │  │  │      HeaderMenuBuilder.ets          // 标题菜单内容组件
  │  │  │      MenuItemBuilder.ets            // 下拉菜单选项
  │  │  ├─constants
  │  │  │      CommonContants.ets             // 公共常量
  │  │  │      CommonEnums.ets                // 公共枚举值
  │  │  ├─types
  │  │  │      Types.ets                      // 公共抽象类
  │  │  └─utils
  │  │         AccountUtil.ets                // 账号工具类
  │  │         DialogUtil.ets                 // 弹窗工具类
  │  │         FormatUtil.ets                 // 格式化工具类
  │  │         Logger.ets                     // 日志工具类
  │  │         PermissionUtil.ets             // 权限获取工具类
  │  │         PreferenceUtil.ets             // 数据持久化工具类
  │  │         RouterModule.ets               // 路由工具类
  │  │         WindowUtil.ets                 // 窗口管理工具类
  │  └─resources
  ├─commons/network/src/main
  │  ├─ets
  │  │  ├─apis
  │  │  │      APIList.ets                    // 网络请求API
  │  │  │      HttpRequest.ets                // 网络请求
  │  │  ├─mocks
  │  │  │  └─MockData
  │  │  │         Calories.ets                // 热量mock数据
  │  │  │         Mine.ets                    // 我的mock数据
  │  │  │         Notice.ets                  // 通知mock数据
  │  │  │         RecipeList.ets              // 菜谱mock数据
  │  │  │      AxiosMock.ets                  // mock请求
  │  │  │      RequestMock.ets                // mock API
  │  │  └─types
  │  │         Calories.ets                   // 热量抽象类
  │  │         Member.ets                     // 会员抽象类
  │  │         Notice.ets                     // 通知抽象类
  │  │         Recipe.ets                     // 菜谱抽象类
  │  └─resources
  │─components/aggregated_ads/src/main   
  │  ├─ets
  │  │  ├─common
  │  │  │      Constant.ets                   // 常量类
  │  │  ├─components
  │  │  │      AdServicePage.ets              // 广告服务组件
  │  │  │      HwAdService.ets                // 华为广告
  │  │  ├─util
  │  │  │      UIUtil.ets                     // UI工具类
  │  │  └─viewmodel
  │  │         AggreagetedAdVM.ets            // 广告页面数据模型
  │  └─resources
  │─components/aggregated_login/src/main   
  │  ├─ets
  │  │  ├─common
  │  │  │      Constant.ets                   // 常量类
  │  │  │      Logger.ets                     // 日志类
  │  │  ├─components
  │  │  │      AgreementDialog.ets            // 协议弹窗组件
  │  │  │      LoginService.ets               // 登录组件
  │  │  ├─model
  │  │  │      Index.ets                      // 数据类型
  │  │  │      WXApiWrap.ets                  // 微信登录数据类型
  │  │  └─viewmodel
  │  │         AggregatedLoginVM.ets          // 登录组件数据模型
  │  └─resources
  │─components/aggregated_payment/src/main   
  │  ├─ets
  │  │  ├─common
  │  │  │      Constant.ets                   // 常量类
  │  │  ├─components
  │  │  │      AggregatedPaymentPicker.ets    // 支付组件
  │  │  ├─model
  │  │  │      Index.ets                      // 数据类型
  │  │  │      WXApiWrap.ets                  // 微信支付数据类型
  │  │  └─viewmodel
  │  │         AggregatedPaymentVM.ets        // 支付组件数据模型
  │  └─resources
  │─components/base_ui/src/main   
  │  ├─ets
  │  │  ├─components
  │  │  │      BaseTabs.ets                   // Tabs组件
  │  │  └──types
  │  │         Index.ets                      // 数据类型
  │  └─resources
  │─components/calorie_calculation/src/main   
  │  ├─ets
  │  │  ├─components
  │  │  │      BarChart.ets                   // 协议弹窗组件
  │  │  │      CalorieCalculation.ets         // 热量计算组件
  │  │  │      CaloriesSummary.ets            // 热量汇总组件
  │  │  │      FoodDiary.ets                  // 饮食计划组件
  │  │  ├─types
  │  │  │      Index.ets                      // 数据类型
  │  │  └─viewModels
  │  │         CaloriesSummaryVM.ets          // 热量计算数据模型
  │  └─resources
  │─components/featured_recipes/src/main   
  │  ├─ets
  │  │  ├─components
  │  │  │      FeaturedRecipes.ets            // 菜谱瀑布流组件
  │  │  │      RecommendedCard.ets            // 菜谱卡片
  │  │  │─types
  │  │  │      Index.ets                      // 数据类型
  │  │  └─utils
  │  │         LazyDataSource.ets             // 懒加载对象
  │  │         Logger.ets                     // 日志工具
  │  │         ObservedArray.ets              // 数组监听工具
  │  └─resources
  │─components/home_search/src/main   
  │  ├─ets
  │  │  ├─components
  │  │  │      HomeSearch.ets                 // 搜索组件
  │  │  │      SearchKeys.ets                 // 热搜词组件
  │  │  │      SearchResult.ets               // 搜索结果组件
  │  │  └─types
  │  │         Index.ets                      // 数据类型
  │  └─resources
  │─components/link_category/src/main   
  │  ├─ets
  │  │  ├─components
  │  │  │      LinkCategory.ets               // 分类列表组件
  │  │  └─types
  │  │         Index.ets                      // 数据类型
  │  └─resources
  │─components/personal_homepage/src/main   
  │  ├─ets
  │  │  ├─components
  │  │  │      BloggerInfomation.ets          // 个人介绍组件
  │  │  │      PersonalHomepage.ets           // 个人中心组件
  │  │  └─types
  │  │         Index.ets                      // 数据类型
  │  └─resources
  │─components/shopping_basket/src/main   
  │  ├─ets
  │  │  ├─components
  │  │  │      IngredientList.ets             // 用料列表
  │  │  │      PurchaseIngredients.ets        // 用料组件
  │  │  │      ShoppingBasket.ets             // 菜篮子组件
  │  │  └─types
  │  │         Index.ets                      // 数据类型
  │  └─resources
  │─components/upload_recipe/src/main   
  │  ├─ets
  │  │  ├─components
  │  │  │      UploadRecipe.ets               // 上传菜谱组件
  │  │  ├─types
  │  │  │      Index.ets                      // 数据类型
  │  │  └─viewmodel
  │  │         UploadRecipeVM.ets             // 上传菜谱数据模型
  │  └─resources
  │─features/calories/src/main   
  │  ├─ets
  │  │  ├─pages
  │  │  │      CaloriesPage.ets               // 热量页面
  │  │  │      DietPlanPage.ets               // 饮食计划食物列表页面
  │  │  │      SearchFoodPage.ets             // 食物搜索页面
  │  │  ├─types
  │  │  │      Index.ets                      // 数据对象
  │  │  └─viewModels
  │  │         CaloriesPageVM.ets             // 热量页面数据模型
  │  │         DietPlanPageVM.ets             // 食物列表页面数据模型
  │  │         SearchFoodPageVM.ets           // 食物搜索页面数据模型
  │  └─resources
  │─features/classification/src/main   
  │  ├─ets
  │  │  ├─constants
  │  │  │      Enums.ets                      // 枚举数据
  │  │  ├─pages
  │  │  │      ClassificationPage.ets         // 分类页面
  │  │  │      DishesPage.ets                 // 菜谱详情页面
  │  │  │      ShoppingBasketPage.ets         // 菜篮子页面
  │  │  ├─types
  │  │  │      Index.ets                      // 菜谱数据对象
  │  │  └─viewModels
  │  │         ClassificationVM.ets           // 分类页面数据模型
  │  │         DishesVM.ets                   // 菜谱详情页面数据模型
  │  └─resources
  │─features/home/src/main   
  │  ├─ets
  │  │  ├─pages
  │  │  │      BloggerProfilePage.ets         // 博主信息页面
  │  │  │      FollowersPage.ets              // 博主关注页面
  │  │  │      HomePage.ets                   // 首页页面
  │  │  │      SearchPage.ets                 // 搜索页面
  │  │  ├─types
  │  │  │      Index.ets                      // 首页数据对象
  │  │  └─viewModels
  │  │         BloggerProfilePageVM.ets       // 博主信息页面数据模型
  │  │         FollowersPageVM.ets            // 博主关注页面数据模型
  │  │         HomePageVM.ets                 // 首页页面数据模型
  │  │         SearchPageVM.ets               // 搜索页面数据模型
  │─features/mine/src/main   
  │  ├─ets
  │  │  ├─components
  │  │  │      ConfirmDialogComponent.ets     // 确认弹窗组件
  │  │  │      Recipes.ets                    // 菜谱卡片组件
  │  │  ├─mapper
  │  │  │      Index.ets                      // 数据映射
  │  │  ├─model
  │  │  │      Index.ets                      // 数据类型
  │  │  ├─pages
  │  │  │      BrowsingHistory.ets            // 浏览历史页面
  │  │  │      MemberCenterPage.ets           // 会员中心页面
  │  │  │      MinePage.ets                   // 我的页面
  │  │  │      MyCollection.ets               // 我的收藏页面
  │  │  │      NoticeCenterPage.ets           // 通知中心页面
  │  │  │      NoticeDetailPage.ets           // 通知详情页面
  │  │  │      PersonalInfo.ets               // 个人信息页面
  │  │  │      PrivacyPolicyDetailPage.ets    // 用户协议页面
  │  │  │      QuickLoginPage.ets             // 一键登录页面
  │  │  │      SettingsPage.ets               // 设置页面
  │  │  │      SideBarPage.ets                // 服务菜单页面
  │  │  │      TermsOfServicePage.ets         // 隐私政策页面
  │  │  │      UploadRecipe.ets               // 上传菜谱页面
  │  │  ├─types
  │  │  │      Index.ets                      // 抽象类
  │  │  ├─util
  │  │  │      MockApi.ets                    // 支付mock数据
  │  │  │      OrderInfoUtil.ets              // 支付mock参数工具
  │  │  │      SignUtils.ets                  // 支付mock签名工具
  │  │  └─viewModels
  │  │         BrowsingHistoryVM.ets          // 浏览历史页面数据模型
  │  │         MemberCenterPageVM.ets         // 会员中心页面数据模型
  │  │         MinePageVM.ets                 // 我的页面数据模型
  │  │         MyCollectionVM.ets             // 我的收藏页面数据模型
  │  │         MyRecipeVM.ets                 // 我的菜谱页面数据模型
  │  │         NoticeCenterPageVM.ets         // 通知中心页面数据模型
  │  │         SettingsPageVM.ets             // 设置页面数据模型
  │  │         UploadRecipeVM.ets             // 上传菜谱页面数据模型
  │  └─resources
  └─products/entry/src/main   
     ├─ets
     │  ├─entryability
     │  │      EntryAbility.ets               // 应用程序入口
     │  ├─entryformability
     │  │      EntryFormAbility.ets           // 卡片程序入口
     │  ├─pages
     │  │      Index.ets                      // 入口页面
     │  │      LaunchAdPage.ets               // 广告页面
     │  │      LaunchPage.ets                 // 开屏页面
     │  │      MainEntry.ets                  // 主页面
     │  │      PrivacyPolicyPage.ets          // 隐私协议页面
     │  ├─types
     │  │      Types.ets                      // 抽象类
     │  ├─viewModels
     │  │      MainEntryVM.ets                // 入口页面数据
     │  └─widget/pages
     │         WidgetCard.ets                 // 卡片页面
     └─resources

2. 关键代码解读

本篇代码非应用的全量代码,只包括应用的部分能力的关键代码。

若需获取全量代码,请查看模板集成章节。

1)个人信息

  • 华为账号一键登录

    ​javascript
    // components/aggregated_login/src/main/ets/components/LoginService.ets
    // 登录组件
    LoginWithHuaweiIDButton({
    params: {
    style: loginComponentManager.Style.BUTTON_CUSTOM,
    loginType: loginComponentManager.LoginType.QUICK_LOGIN,
    supportDarkMode: true,
    customButtonParams: {
    fontColor: loginComponentManager.FontColor.WHITE,
    backgroundColor: this.loginBtnBgColor,
    },
    },
    controller: this.controller,
    });

    ...

    // 登录方法控制器
    controller: loginComponentManager.LoginWithHuaweiIDButtonController =
    new loginComponentManager.LoginWithHuaweiIDButtonController()
    // 需要用户同意协议才能完成华为账号登录,请先设置协议状态为NOT_ACCEPTED,当用户同意协议后设置协议状态为ACCEPTED,才可以完成华为账号登录
    .setAgreementStatus(loginComponentManager.AgreementStatus.NOT_ACCEPTED)
    .onClickLoginWithHuaweiIDButton((error: BusinessError, response: loginComponentManager.HuaweiIDCredential) => {
    // 处理用户点击一键登录按钮逻辑,灰度传入undefined模拟流程,应用申请权限后,传入error
    this.handleLoginWithHuaweiIDButton(undefined, response);
    });

    ...

    // 处理点击一键登录后的方法
    handleLoginWithHuaweiIDButton(error: BusinessError | undefined,
    response: loginComponentManager.HuaweiIDCredential) {
    this.enableStatus = false;
    // if部分内容配置好可用的调试证书和client_id后再放开
    if (error) {
    Logger.error(this.domainId, this.logTag,
    Failed to click LoginWithHuaweiIDButton. Code is ${error.code}, message is ${error.message});
    this.error = error;
    if (error.code === LoginErrorCode.ERROR_CODE_NETWORK_ERROR) {
    AlertDialog.show(
    {
    message: 'No Internet connection. Check your network settings. ',
    offset: { dx: 0, dy: -12 },
    alignment: DialogAlignment.Bottom,
    autoCancel: false,
    confirm: {
    value: 'OK',
    action: () => {
    },
    },
    },
    );
    } else if (error.code === LoginErrorCode.ERROR_CODE_AGREEMENT_STATUS_NOT_ACCEPTED) {
    // 未同意协议,弹出协议弹框,推荐使用该回调方式
    this.agreementDialog.open();
    this.customerDiaLogOpen = true;
    } else if (error.code === LoginErrorCode.ERROR_CODE_LOGIN_OUT) {
    // 华为账号未登录提示
    this.showToast(r('app.string.not_login_in')); } else if (error.code === LoginErrorCode.ERROR_CODE_NOT_SUPPORTED) { // 账号不支持该scopes或permissions提示 this.showToast(r('app.string.scope_not_supported'));
    } else if (error.code === LoginErrorCode.ERROR_CODE_NOT_REQUIRED_SCOPE_OR_PERMISSION) {
    // 应用没有申请scope权限
    this.showToast(r('app.string.app_not_required_scopes_or_permissions')); } else { // 其他提示系统或服务异常 this.showToast(r('app.string.service_error'));
    }
    this.enableStatus = true;
    return;
    }
    try {
    if (this.isSelected) {
    // 配置好可用的调试证书和client_id后需要处理response
    Logger.info(this.domainId, this.logTag, 'Succeed in clicking LoginWithHuaweiIDButton.');
    if (this.response.idToken !== undefined) {
    this.response = response;
    // 模拟登录成功后回调
    this.loginFinishedCb(true, this.unionID)
    }
    // 模拟登录成功后回调
    this.loginFinishedCb(false)
    } else {
    this.agreementDialog.open();
    this.customerDiaLogOpen = true;
    }
    } catch (e) {
    Logger.info(this.domainId, this.logTag,
    Failed to LoginWithHuaweiIDButton, errCode: ${e.code}, errMessage: ${e.message});
    AlertDialog.show(
    {
    message: $r('app.string.service_error'),
    offset: { dx: 0, dy: -12 },
    alignment: DialogAlignment.Bottom,
    autoCancel: false,
    confirm: {
    value: 'OK',
    action: () => {
    },
    },
    },
    );
    } finally {
    this.enableStatus = true;
    }
    }

2)分类搜索

  • 分类列表

    ​javascript
    // components/link_category/src/main/ets/components/LinkCategory.ets
    // 列表页
    Row() {
    List({ space: 8, scroller: this.titleItemScroller }) {
    ForEach(this.recipeCategoryList, (item: RecipeCategory, index: number) => {
    ListItem() {
    this.leftListBuilder(item.name, index)
    }
    }, (item: RecipeCategory, index: number) => item.name + index)
    }
    .width(92)
    .height('100%')
    .backgroundColor('#F1F3F5')
    .listDirection(Axis.Vertical) // 排列方向
    .scrollBar(BarState.Off)
    .contentStartOffset(12)
    .contentEndOffset(12)

    复制代码
      List({ space: 12, scroller: this.scroller }) {
          ForEach(this.recipeCategoryList, (item: RecipeCategory) => {
              ListItemGroup({
                  header: this.listTitleBuilder(item.name),
                  space: 12,
              }) {
                  ListItem() {
                      Grid() {
                          ForEach(item.recipeList, (listItem: RecipeBriefInfo) => {
                              GridItem() {
                                  Column() {
                                      Image($r(`app.media.${listItem.thumbnail}`)).width(76).height(76).borderRadius(8)
                                      Text(listItem.title)
                                          .fontSize(14)
                                          .fontWeight(FontWeight.Medium)
                                          .fontColor($r('sys.color.font_primary'))
                                          .textAlign(TextAlign.Center)
                                          .constraintSize({ maxWidth: 76 })
                                          .maxLines(2)
                                          .margin({ top: 4 })
                                          .textOverflow({ overflow: TextOverflow.Ellipsis })
                                  }
                              }.onClick(() => {
                                  this.onRecipeClick(listItem)
                              })
    
                          }, (listItem: RecipeBriefInfo) => `${item.id}${listItem.id}`)
                      }
                      .rowsGap(8)
                      .columnsGap(8)
                      .columnsTemplate('1fr 1fr 1fr')
    
                  }
    
              }
          }, (item: RecipeCategory) => item.id.toString())
      }
      .layoutWeight(1)
      .height('100%')
      .margin({ left: 8, right: 16 })
      .scrollBar(BarState.Off)
      .sticky(StickyStyle.None)
      .contentStartOffset(12)
      .contentEndOffset(12)
      .onScrollIndex((start: number) => this.currentIndexChangeAction(start, false))

    }.layoutWeight(1)

    ...

    // 下标索引处理
    currentIndexChangeAction(index: number, isClassify: boolean): void {
    if (this.currentIndex !== index) {
    this.changeCurrentIndex(index);
    if (isClassify) {
    this.scroller.scrollToIndex(index);
    } else {
    this.titleItemScroller.scrollToIndex(index);
    }
    }
    }

3)热量计算

  • 环形图

    javascript
    // commons/lib_common/src/main/ets/utils/LoginUtils.ets
    // 环形组件
    Progress({ value: this.todayCalories, total: 2000, type: ProgressType.Ring })
    .width(176).style({ strokeWidth: 20 })
    .color(this.vm.gradientColor)

    ...

    // components/calorie_calculation/src/main/ets/viewModels/CaloriesSummaryVM.ets
    // 渐变色设置
    gradientColor: LinearGradient =
    new LinearGradient([{ color: '#C6F093', offset: 0.25 }, { color: '#83CE26', offset: 0.5 },
    { color: '#88DB23', offset: 0.85 }, { color: '#88DB24', offset: 1.0 }])

  • 柱状图

    javascript
    // components/calorie_calculation/src/main/ets/components/BarChart.ets
    // 柱状图参数设置
    this.name = this.xData[this.xData.length - 1]
    this.rate = this.seriesData[this.seriesData.length - 1]
    // Step1:必须:初始化图表配置构建类
    this.model = new BarChartModel();

    // Step2:配置图表指定样式,各部件间没有先后之分

    // 为图表添加数据选择的监听器
    this.model.setOnChartValueSelectedListener(this.valueSelectedListener);
    // 获取图表描述部件,设置图表描述部件不可用,即图表不进行绘制描述部件
    let description: Description | null = this.model.getDescription()
    if (description) {
    description.setEnabled(false);
    }

    // 获取图表图例部件,设置图表图例部件不可用
    let legend: Legend | null = this.model.getLegend();
    if (legend) {
    legend.setEnabled(false);
    }

    // 设置图表数据最大的绘制数,如果超过该数值,则不进行绘制图表的数值标签
    this.model.setMaxVisibleValueCount(7);

    // 为左Y轴设置LimitLine,可设置限制线的宽度,线段样式,限制标签的位置,标签字体大小等
    this.limitLine1 = new LimitLine(120, 'Upper Limit');
    this.limitLine1.setLineWidth(4);
    //设置虚线样式
    this.limitLine1.enableDashedLine(10, 10, 0);
    //设置标签位置
    this.limitLine1.setLabelPosition(LimitLabelPosition.RIGHT_TOP);
    this.limitLine1.setTextSize(10);

    this.limitLine2 = new LimitLine(50, 'Lower Limit');
    this.limitLine2.setLineWidth(4);
    this.limitLine2.enableDashedLine(10, 10, 0);
    this.limitLine2.setLineColor(Color.Yellow);
    this.limitLine2.setLabelPosition(LimitLabelPosition.RIGHT_BOTTOM);
    this.limitLine2.setTextSize(10);

    // 设置图表左Y轴信息
    this.leftAxis = this.model.getAxisLeft();
    if (this.leftAxis) {
    //设置绘制标签个数
    this.leftAxis.setLabelCount(7, false);
    //设置标签位置
    this.leftAxis.setPosition(YAxisLabelPosition.OUTSIDE_CHART)
    //设置距离顶部距离
    this.leftAxis.setSpaceTop(15);
    //设置最小值
    this.leftAxis.setAxisMinimum(0);
    //设置最大值
    this.leftAxis.setAxisMaximum(2000);
    this.leftAxis.setGridDashedLine({ dash: [2, 2, 0], offset: 0 })
    this.leftAxis.setTextColor('#E6000000')
    this.leftAxis.setTextSize(12)
    }

    // 设置图表右Y轴信息
    this.rightAxis = this.model.getAxisRight();
    if (this.rightAxis) {
    this.rightAxis.setEnabled(false);
    }

    class YAxisValueFormatter implements IAxisValueFormatter {
    getFormattedValue(value: number, axis: AxisBase): string {
    //将原本存在的对应的value转换成需要的字符串
    return value.toString();
    }
    }

    this.leftAxis?.setValueFormatter(new YAxisValueFormatter())

    // 设置X轴信息
    this.xAxis = this.model.getXAxis();
    if (this.xAxis) {
    this.xAxis.setPosition(XAxisPosition.BOTTOM);
    this.xAxis.setDrawGridLines(false);
    this.xAxis.setGranularity(1);
    this.xAxis.setLabelCount(this.xData.length);
    this.xAxis.setTextColor('#E6000000')
    this.xAxis.setTextSize(12)
    }

    class XAxisValueFormatter implements IAxisValueFormatter {
    data: string[] = []

    复制代码
      constructor(data: string[]) {
          this.data = data
      }
    
      getFormattedValue(value: number, axis: AxisBase): string {
          //将原本存在的对应的value转换成需要的字符串
          let day = new Date().getDay() - 1
          if (day === value) {
              return '今日'
          }
          return xMap[value];
      }

    }

    this.xAxis?.setValueFormatter(new XAxisValueFormatter(this.xData))
    // 生成图表数据
    let barData: BarData = this.getBarData();
    // 将数据与图表配置类绑定
    this.model.setData(barData);
    // 设置图表最大的X轴显示范围,如不设置,则默认显示全部数据
    this.model.setVisibleXRangeMaximum(20);

    ...

    // 柱状图组件
    BarChart({ model: this.model }).width('100%').height(190)

3. 模板集成

本模板提供了两种代码集成方式,供开发者自由选用。

1)整体集成(下载模板

开发者可以选择直接基于模板工程开发自己的应用工程。

  • 对接开发者自己的服务器接口,转换数据结构,展示真实的云侧数据。

commons/lib_common/src/main/ets/httprequest/HttpRequestApi.ets文件中的mock接口替换为真实的服务器接口。

commons/lib_common/src/main/ets/httprequest/HttpRequest.ets文件中将云侧开发者自定义的数据结构转换为端侧数据结构。

根据自己的业务内容修改模板,进行定制化开发。

2)按需集成

若开发者已搭建好自己的应用工程,但暂未实现其中的部分场景能力,可以选择取用其中的业务组件,集成在自己的工程中。

  • 根据API参考和示例代码,将组件集成在自己的对应场景中。

以上是第四期"餐饮行业-美食菜谱"行业优秀案例的内容,更多行业敬请期待~

欢迎下载使用行业模板"点击下载 ",若您有体验和开发问题,或者迫不及待想了解XX行业的优秀案例,欢迎在评论区留言,小编会快马加鞭为您解答~

同时诚邀您添加下方二维码加入"组件模板活动社群",精彩上新&活动不错过!

👉 本系列持续更新,欢迎点击帖子末尾左下角 ""收藏本帖!

|--------|------------------------------------|----------------------------------------------------------------------------------------------------------|
| 期数 | 帖子 | 链接 |
| 第1期 | HarmonyOS官方模板优秀案例 | 便捷生活行业 · 购物中心 | 点击查看 |
| 第2期 | HarmonyOS官方模板优秀案例 | 新闻行业 · 综合新闻 | 点击查看 |
| 第3期 | HarmonyOS官方模板优秀案例 | 教育行业 · 教育备考 | 点击查看 |
| 第4期 | HarmonyOS官方模板优秀案例 | 餐饮行业 · 美食菜谱 | 点击查看 |
| 第5期 | HarmonyOS官方模板优秀案例 | 工具行业 · 日历应用 | 点击查看 |
| 第6期 | 小编加急整理中,敬请期待 | |

👉 HarmonyOS组件模板相关推荐

  • 【活动ing】HarmonyOS组件/模板集成创新活动 ,报名时间截止2025年8月30日,点击查看
  • 鸿蒙应用开发者激励计划2025,点击查看
相关推荐
HarderCoder33 分钟前
重学仓颉-7类与接口完全指南:从基础到高级特性
harmonyos
HarderCoder4 小时前
重学仓颉-6枚举与模式匹配完全指南
harmonyos
li理4 小时前
鸿蒙应用开发完全指南:深度解析UIAbility、页面与导航的生命周期
前端·harmonyos
HarderCoder6 小时前
重学仓颉-5结构体(Struct)完全指南:从基础到高级用法
harmonyos
HarderCoder7 小时前
重学仓颉-4函数系统完全指南
harmonyos
HarderCoder12 小时前
重学仓颉-3基本数据类型详解:从理论到实践的全面指南
harmonyos
鸿蒙小灰13 小时前
鸿蒙OpenCV移植技术要点
opencv·harmonyos
鸿蒙先行者13 小时前
鸿蒙分布式能力调用失败解决方案及案例
分布式·harmonyos
大雷神1 天前
鸿蒙中应用闪屏解决方案
华为·harmonyos