10 月 23 - 25 日,QCon 上海站即将召开,现在大会已开始正式报名,可以享受 8 折优惠 了解详情
写点什么

【宝藏贴】HarmonyOS 官方模板优秀案例 (第 1 期:便捷生活 · 购物中心)

  • 2025-07-28
    北京
  • 本文字数:3401 字

    阅读完需:约 11 分钟

🚀🚀🚀🚀

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

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

如何通过行业模板,快速高效完成项目开发?

HarmonyOS 官方模板优秀案例,带您找到答案!

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

 

【第 1 期】便捷生活行业· 购物中心

一、 概述

1. 行业洞察

1) 行业痛点:

  • 传统零售流量缺失,依赖第三方平台导流,佣金成本高且较难沉淀用户;

  • 离场排队缴费耗时、找车难、支付流程复杂是用户最大的“离场焦虑”;

  • 传统积分体系感知弱、兑换门槛高、缺乏即时激励,导致会员活跃度低;

  • 传统服务链接割裂(App、小程序、收银系统独立),用户需在不同平台跳转,体验碎片化;

  • 低频使用的独立 App 极易被用户遗忘删除;公众号/小程序需主动打开,入口深且触达率低。


2) 行业常用三方 SDK

SDK 链接:

支付宝 SDK 微信支付 SDK 银联 SDK 极光 SDK 同盾SDK

神策数据 SDK 腾讯微信 SDK 高德地图 百度地图 腾讯地图定位 高德地图定位 穿山甲广告 SDK 广点通广告SDK 友盟SDK 听云 SDK 声网 agoraSDK

腾讯浏览器服务 SDK 字节跳动火山引擎增长营销套件 SDK


2. 优秀案例概览(下载模板

购物中心元服务模板是基于以上行业分析实现的参考,为行业元服务提供了常用功能的开发样例,涵盖停车缴费、自助积分、店铺导购、个人钱包、券包等多个实用场景。

  • Stage 开发模型 + 声明式 UI 开发范式

  • 分层架构设计+ 组件化拆分,支持开发者在开发时既可以选择完整使用模板,也可以根据需求单独选用其中的业务组件。

  • 集成华为账号、支付等服务,只需做少量配置和定制即可快速实现华为账号的登录、停车缴费等功能。

基于本模板构建的【XX 购物中心】元服务已正式发布上线,开发者反馈模板高度适配业务需求,显著提升了团队开发效率。


 

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

 

二、 应用架构设计

1. 分层模块化设计

  • 产品定制层:专注于满足不同设备或使用场景的个性化需求,作为应用的入口,是用户直接互动的界面。

① 本实践暂时只支持直板机,为单 HAP 包形式,包含路由根节点、底部导航栏等。

  • 基础特性层:用于存放相对独立的功能 UI 和业务逻辑实现。

① 本实践的基础特性层将应用底部导航栏的每个选项拆分成一个独立的业务功能模块,包含首页和我的。

② 每个功能模块都具备高内聚、低耦合、可定制的特点,支持产品的灵活部署。

  • 公共能力层:存放公共能力,包括公共 UI 组件、数据管理、外部交互和工具库等共享功能。

① 本实践的公共能力层分为公共基础能力和可分可合组件,均打包为 HAR 包被上层业务组件引用。

② 公共基础能力包含日志、文件处理等工具类,公共类型定义,网络库,以及弹窗、加载等公共组件。

③ 可分可合组件将包含行业特点、可完全自闭环的能力抽出独立的组件模块,支持开发者在开发中单独集成使用,详见业务组件设计章节

本模板整体工程共分为 7 个包:

  • 产品定制层

① phone HAP 包,手机设备的主入口模块

  • 基础特性层

① business_home HSP 包,首页场景

② business_mine HSP 包,我的场景

  • 公共能力层

① lib_common HAR 包,封装全局工具方法及公共组件

② module_coupon HAR 包,优惠券组件

③ module_keyboard HAR 包,车牌键盘组件

④ module_points HAR 包,自助积分组件

详细工程结构可见工程结构章节


2. 业务组件设计

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

 

三、 行业场景技术方案

1. 账号管理

1) 场景说明

元服务打开后无需用户手动登录,通过静默登录后展示默认头像和默认昵称。

用户可自行选择点击关联账号按钮获取华为账号关联手机号,可将静默登录账号与用户历史注册账号关联,同步用户历史数据资产,例如头像、昵称、生日等。


2) 技术方案

  • 静默登录

① 通过 Account Kit 实现元服务静默登录

  • 手机号获取

① 通过 Scenario Fusion Kit 的快速验证手机号Button,向用户发起手机号授权申请;

② 经用户同意后,根据获取到的 Authorization Code,以及元服务服务器使用 Client ID、Client Secret 实现服务端开发


2. 停车缴费

1) 场景说明

点击车牌号输入框拉起自定义键盘,根据车牌的位数展示省份、市区编号、车牌等自定义内容。

点击新增车牌支持存储常用车牌。

 

2) 技术方案

  • 特殊键盘绑定

通过 TextInput 组件的customKeyboard属性,传入自定义 UI,实现特殊键盘和输入框的关联。

  • 常用车牌存储:

将车牌输入能力封装成独立组件,通过bindSheet方法对当前页面绑定半模态弹框,并复用车牌输入组件。


3. 会员积分

1) 场景说明

用户在商场内消费后,可通过扫描小票二维码或拍摄小票照片等方式提交自助积分申请。

 

2) 技术方案

  • 本模板使用 Scan Kit 提供的默认界面扫码能力,实现系统级体验一致的扫码界面以及相册扫码入口。开发者也可以通过自定义界面扫码实现更定制化的界面样式和功能。

  • 使用 Media Library Kit 提供的特定接口安全Picker拉起系统图库,用户可以自行选择相册内资源或拍摄照片,开发者通过获取到的图片 uri 进行后续的分享、上传等操作。


四、 模板代码

1. 工程结构(下载模板

详细代码结构如下所示:

ShoppingMall

├─commons                           

 └─lib_common/src/main/ets

      ├─components  

        ├── AlertDialog.ets                          //警告对话框

        ├── AsWebRichText.ets                        //富文本编辑器

        ├── CallTelSheet.ets                         //电话呼叫

        ├── EmptyComp.ets                            //空白组件

        ├── LoadingDialog.ets                        //加载组件

        ├── LoginComp.ets                            //登录组件

        ├── NavHeaderBar.ets                         //页面导航栏头部组件               

        ├── PageHeaderComp.ets                       //页面标题组件

        └── SheetHeaderComp.ets                      //半模态标题组件

              

      ├─constants         

        ├── Common.ets                               //通用常量

        └── RouterMap.ets                            //路由表常量             

                         

      ├─httprequest    

        ├── AxiosHttp.ets                            // axios二次封装                   

        ├── AxiomRequest.ets                         //创建请求实例          

        ├── HttpRequest.ets                          //业务接口封装                   

        └── HttpRequestApi.ets                       //业务接口定义

      

      ├─mock/MockData.ets                              // mock数据

        

      ├─models

        ├── ParamsModel.ets                          //接口参数模型          

        ├── RequestModel.ets                         //接口请求模型

        ├── ResponseModel.ets                        //接口响应模型          

        ├── RouterModel.ets                          //路由跳转模型                   

        ├── StorageModel.ets                         //状态变量模型          

        └── TabBarModel.ets                          // Tab模型                    

              

      ├─utils

        ├── EmitUtils.ets                            //全局事件方法类

        ├── FileUtils.ets                            //文件处理方法类          

        ├── FormatUtils.ets                          //格式化方法类

        ├── GlobalUtils.ets                          //全局变量类          

        ├── Logger.ets                               //日志类

        ├── LoginUtils.ets                           //登录方法          

        ├── RouterUtil.ets                           //路由管理类      

        └── Utils.ets                                //通用方法     

      

      └─viewmodels/BaseViewModel.ets                   //基础viewmodel抽象类

├─components

 ├── module_coupon                                     //优惠券组件                     

 ├── module_keyboard                                   //车牌键盘组件

 └── module_points                                     //自助积分组件            

     

├─features

 ├─business_home/src/main/ets                          //首页模块             

    ├─components

      ├── AddPlateComp.ets                         //添加车牌组件

      └── GuideListComp.ets                        //指南列表组件                  

    ├─constants

      └── Constants.ets                            //常量定义             

    ├─pages

      ├── ConsiderateServicePage.ets               //尊享服务页面

      ├── FindCarPage.ets                          //寻车页面

      ├── FindCarResultPage.ets                    //寻车结果页面

      ├── ParkingPaymentPage.ets                   //停车缴费入口页面

      ├── PayDetailPage.ets                        //支付详情页

      ├── PayRecordPage.ets                        //缴费记录页面

      ├── PaySuccessPage.ets                       //支付成功页面

      ├── PlateNumberMgtPage.ets                   //车牌管理页面

      ├── PlateNumberPage.ets                      //停车缴费页面

      ├── ServiceDetailPage.ets                    //贴心服务二级页面

      ├── StoreGuideDetailPage.ets                 //店铺详情页面

      ├── StoreGuidePage.ets                       //店铺导航页面

      └── WebPage.ets                              //网页页面                  

    └─viewmodel

        ├── PayDetailViewModel.ets                   //支付详情视图模型

        ├── PayRecordViewModel.ets                   //缴费记录视图模型

        ├── PaySuccessViewModel.ets                  //支付成功视图模型

        ├── ServiceDetailViewModel.ets               //服务详细视图模型

        ├── StoreGuideDetailViewModel.ets            //店铺指南详细视图模型

        └── StoreGuideViewModel.ets                  //店铺指南视图模型      

 

 └─business_mine/src/main/ets                         //我的模块             

      ├──components

        ├── MenuComp.ets                            //菜单组件

        └── UserInfoComp.ets                        //用户信息组件

      ├──constants

        └── WalletConstants.ets                     //钱包常量

      ├──pages

        ├── MembershipManualPage.ets                //成员手册页面

        ├── MembershipPage.ets                      //成员页面

        ├── MinePage.ets                            //我的页面

        ├── MyCouponsPage.ets                       //我的优惠券页面

        ├── MyWalletPage.ets                        //我的钱包页面

        ├── PrivacyPage.ets                         //隐私政策页面

        ├── RechargeWalletPage.ets                  //充值钱包页面

        ├── SettingPage.ets                         //设置页面

        └── UserInfoPage.ets                        //用户信息页面

      └──viewmodel               

          ├── MinePageViewModel.ets                   //我的页面视图模型

          ├── MyWalletViewModel.ets                   //我的钱包页面视图模型

          ├── RechargeWalletViewModel.ets             //充值钱包页面视图模型

          ├── SettingPageViewModel.ets                //设置页面视图模型

          └── UserInfoViewModel.ets                   //用户信息页面视图模型

└─products

   └─entry/src/main/ets                

        ├── common/Constants.ets                        //常量定义

        ├── components/CustomTabBar.ets                 //自定义标签栏组件      

        ├── entryability/EntryAbility.ets               //主入口能力

        ├── entryformability/EntryFormAbility.ets       //表单主入口能力

        ├── pages

          ├── HomePage.ets                            //首页

          ├── Index.ets                               //入口页面

          ├── IndexPage.ets                           // Tab页面        

          └── IntroducePage.ets                       //商场介绍页面

        ├── utils/WidgetUtil.ets                        //卡片工具类

        ├── viewModels/IndexViewModel.ets               // Tab页面ViewModel      

        └── widget/pages/WidgetCard.ets                 //服务卡片        

 

2. 关键代码解读

1) 静默登录及手机号关联

  • 代码使用效果

① 当系统华为账号未登录时,打开本元服务模板,静默登录不成功,自动拉起系统半模态弹窗提示登录/注册华为账号;

② 当系统华为账号已登录时,打开本元服务模板,静默登录成功,显示“华为用户”;

③ 用户可以选择点击关联账号,将静默登录账号与已注册账号关联,为用户同步历史数据资产。注意若要完整体验该功能,对应包名的元服务需要完成对应开发前提工作

  • 核心代码实现

① 通过 AccountKit 实现静默登录。

// products/phone/src/main/ets/viewmodel/IndexViewModel.ets

private loginWithHuaweiID(): Promise<HuaweiIDResp> {

    return new Promise((resolve, reject) => {

      //创建静默登录请求

      let loginRequest = new authentication.HuaweiIDProvider().createLoginWithHuaweiIDRequest();

      loginRequest.forceLogin = false;

      let controller = new authentication.AuthenticationController();

      controller.executeRequest(loginRequest).then((data) => {

        let loginWithHuaweiIDResponse = data as authentication.LoginWithHuaweiIDResponse;

        let authCode = loginWithHuaweiIDResponse.data?.authorizationCode;

        let openId = loginWithHuaweiIDResponse.data?.openID;

        let unionId = loginWithHuaweiIDResponse.data?.unionID;

        //静默登录成功,返回账号相关的ID数据

        resolve({ openId, unionId, authCode } as HuaweiIDResp);

      }).catch((error: BusinessError) => {

        Logger.error(TAG, 'loginWithHuaweiID error: ' + JSON.stringify(error));

      }).finally(() => {

        //静默登录失败,返回空值

        resolve({

          openId: '',

          unionId: '',

          authCode: '',

        } as HuaweiIDResp);

      });

    });

  }

 

② 使用 Scenario Fusion Kit 的快速验证手机号Button请求云侧获取手机号需要的 authCode。

// commons/lib_common/src/main/ets/components/LoginComp.ets

FunctionalButton({

  params: {

    openType: functionalButtonComponentManager.OpenType.GET_PHONE_NUMBER,

    label: '',

    styleOption: {

      styleConfig: new functionalButtonComponentManager

        .ButtonConfig()

        .size({ width: 48, height: 48 })

        .borderRadius(24)

        .backgroundImage($r('app.media.ic_tab_code_pass'))

        .backgroundImageSize(ImageSize.Cover)

    },

  },

  controller: new functionalButtonComponentManager.FunctionalButtonController()

    .onGetPhoneNumber((err, data) => {

      if (err) {

        //使用该方法的元服务未获取scope权限时,使用mock账户返回结果。获取权限参考上一节【代码使用效果】的第三步【开发前提工作】

        LoginUtils.onBindFail(this.callback);

        return;

      }

      LoginUtils.onBindSuccess(data.code || '', this.callback);

    }),

})

 

③ 端侧使用获取到的 auth_code 调用接口,云侧参考服务端开发获取用户的手机号信息后,端侧将号码与登录用户进行关联,并持久化存储到本地。

// commons/lib_common/src/main/ets/utils/LoginUtils.ets

export class LoginUtils {

  /**

   *关联成功

   * @param code

   * @param callback

   */

  static onBindSuccess(code: string, callback?: () => void) {

    HttpRequestApi.getOpenLoginHm(encodeURIComponent(code)).then((res) => {

      if (res.code === HttpCode.SUCCESS) {

        let tel = res.data.userTel ?? ''

        let userInfo: UserInfoModel = AppStorageV2.connect(UserInfoModel, () => new UserInfoModel())!;

        userInfo.userInfo.userTel = `${tel.substring(0, tel.length - 8)}****${tel.substring(tel.length - 4)}`

        userInfo.isRelative = true

        if (callback) {

          callback();

        }

      } else {

        promptAction.showToast({ message: '账号关联失败,请重试~' })

      }

    }).catch(() => {

      promptAction.showToast({ message: '账号关联失败' })

    }).finally(() => {

      LoadingDialogUtil.close()

    })

  };

}

 

2) 动态服务卡片

  • 代码使用效果

① 将停车服务卡片加桌后,可显示剩余车位和用户积分(当前为纯端模拟数据);

② 点击卡片刷新按钮将刷新剩余车位数,并实时同步给元服务内停车缴费页面;

③ 在元服务内缴费消耗积分后,最新剩余积分实时同步给桌面卡片。

  • 核心代码实现

① 通过 commonEventManager 公共事件管理实现卡片事件的注册和实时通信。

import { preferences } from '@kit.ArkData';

import { BusinessError, commonEventManager } from '@kit.BasicServicesKit';

import { formBindingData, formProvider } from '@kit.FormKit';

import { Logger } from 'lib_common';

import { CardManager } from 'module_points';

 

const TAG = '[WidgetUtil]';

 

export class WidgetUtil {

  // ...

  private static readonly _formClickEventName: string = 'form_click_event_name';

 

  //发布卡片按钮点击事件

  public static publishFormClick(formId: string) {

    commonEventManager.publish(

      WidgetUtil._formClickEventName,

      { data: formId },

      (err: BusinessError) => {

        if (err) {

          Logger.error(

            TAG,

            `Failed to publish form_click_event_name. Code is ${err.code}, message is ${err.message}`,

          );

        } else {

          Logger.info(TAG, 'Succeeded in publishing form_click_event_name.');

        }

      },

    );

  }

 

  //订阅卡片按钮点击事件

  public static async subscribeFormClick(ctx: Context) {

    let subscriber: commonEventManager.CommonEventSubscriber | undefined =

      undefined;

    let subscribeInfo: commonEventManager.CommonEventSubscribeInfo = {

      events: [WidgetUtil._formClickEventName],

      publisherPermission: '',

    };

    commonEventManager.createSubscriber(subscribeInfo, (err1, data1) => {

      if (data1) {

        subscriber = data1;

        //订阅公共事件回调

        commonEventManager.subscribe(subscriber, async (err2, data2) => {

          if (data2) {

            Logger.info(TAG, 'Succeeded in creating subscribeFormClick.');

            const formData = CardManager.get().getCardData(true);

            //在回调中获取当前的卡片数据,并通过formProvider.updateForm方法刷新卡片数据

            formProvider.updateForm(

              data2.data,

              formBindingData.createFormBindingData(formData),

            );

          }

        });

      }

    });

  }

 

  // ...

}

 

3. 模板集成

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

1) 整体集成

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

  • 模板代码获取:

① 通过 IDE 插件创建模板工程,开发指导

② 通过生态市场下载源码, 下载地址

③ 通过开源仓访问源码,仓库地址

  • 打开模板工程,根据 README 说明中的快速入门章节,将自己的应用信息配置在模板工程内,即可运行并查看模板效果。


 

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

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


 

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


 

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


2) 按需集成

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

  • 组件代码获取:

① 通过 IDE 插件下载组件源码。开发指导

② 通过生态市场下载组件源码。 下载地址

  • 下载组件源码,根据 README 中的说明,将组件包配置在自己的工程中。


 

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

 

以上是本期“便捷生活行业”行业优秀案例的内容,更多行业敬请期待~

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

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



👉本系列持续更新,欢迎收藏本帖!

第一期:【宝藏贴】HarmonyOS官方模板优秀案例 (第1期:便捷生活 · 购物中心)


👉HarmonyOS 组件模板相关推荐

  • 【活动 ing】HarmonyOS 组件/模板集成创新活动,报名时间截止 2025 年 8 月 30 日,点击查看

 

2025-07-28 14:186046

评论

发布
暂无评论

还有谁比阿里人更懂SpringCloud Alibaba 呢?P8大牛纯手打笔记免费分享!

Java架构之路

Java 程序员 架构 面试 编程语言

区分Protobuf 3中缺失值和默认值

Gopher指北

protobuf Go 语言

恕我直言!有了这份MySQL学习文档,你收藏夹里的其他MySQL学习资料都可以扔了

Java架构之路

Java 程序员 架构 面试 编程语言

JS&Swift

ios swift

话题讨论 | 程序员自己电脑中毒是甚么体验?

xcbeyond

话题讨论

用60行代码实现一个高性能的圣诞抽抽乐H5小游戏(含源码)

徐小夕

Java 大前端 H5游戏 H5

超详细讲解!Android面试真题解析火爆全网,搞懂这些直接来阿里入职

欢喜学安卓

android 编程 程序员 面试 移动开发

区块链信息共享应用落地搭建解决方案

t13823115967

区块链+ 区块链应用 信息共享

Mybatis动态映射,so easy啦

田维常

某美女的程序员老公半夜都还不回家,原来是偷偷在公司看Redis+JVM+Spring cloud+MySQL技术文档

Java架构之路

Java 程序员 架构 面试 编程语言

Github 2020 年度报告:你以为新冠击溃了开发者?不!他们创造了更多代码...

阿里巴巴云原生

开源 Serverless 程序员 代码

SGY奇点交易所系统软件开发|SGY奇点交易所APP开发

系统开发

智慧警务开发,二维码定位报警系统搭建

t13823115967

智慧公安 智慧公安扫码

iOS面试基础知识 (一)

iOSer

ios 面试 runtime 编程开发 iOS Developer

助力孩子走上学霸之路,K12学习神器现已面世!

E科讯

请问如何短时间突击 Java 通过面试?

Java架构师迁哥

区块链BaaS应用平台开发

13828808769

新思科技最新报告显示开源安全是首要考虑因素

InfoQ_434670063458

Scala中String和Int隐式转换的问题分析

木子李G

scala 大数据 编程 隐式转换

刚拿到蚂蚁金服架构师offer!大佬教你如何成为offer收割机

比伯

Java 编程 架构 面试 计算机

港美股交易系统开发框架构造简述篇

软件开发大鱼V15988750073

国际配售 港股交易系统开发 证券交易系统 资管系统 港股打新系统

了解OAuth2.0

环信

动态高并发时为什么推荐ReentrantLock而不是Synchronized?

moon聊技术

JVM 并发 synchronized ReentrantLock 锁升级

四面腾讯pcg后端开发岗,一个星期面完成功拿到20K的offer。分享面经

Java架构之路

Java 程序员 架构 面试 编程语言

DolphinDB与Elasticserach在金融数据集上的性能对比测试

DolphinDB

数据处理 金融 时序数据库 tsdb DolphinDB

阿里架构师经验分享!啃完999页Android面试高频宝典,面试心得体会

欢喜学安卓

android 程序员 面试 移动开发

服务于阿里、滴滴、华为等一线互联网公司的分布式消息中间件RocketMQ核心笔记

Java架构追梦

Java 架构 面试 RocketMQ 消息中间件

《数据结构与抽象:Java语言描述》.pdf

田维常

数据结构

仅凭这份Java大纲笔记,我如愿拿到了阿里offer。

Java架构之路

Java 程序员 架构 面试 编程语言

EPBC环保生态链系统开发案例丨环保生态链EPBC源码平台

系统开发咨询1357O98O718

环保链APP系统开发案例

SGY奇点交易所系统软件APP开发

系统开发

【宝藏贴】HarmonyOS官方模板优秀案例
(第1期:便捷生活 · 购物中心)_HarmonyOS_HarmonyOS_InfoQ精选文章