OpenHarmony 实战开发——分布式购物车案例展示~

news2024/12/27 13:04:25

简介

分布式购物车demo 模拟的是我们购物时参加满减活动,进行拼单的场景;实现两人拼单时,其他一人添加商品到购物车,另外一人购物车列表能同步更新,且在购物车列表页面结算时,某一人结算对方也能实时知道结算金额和优惠金额。整个操作效果分为3个小动画,

  • 拉起对方用户

  • 添加商品到购物车列表

  • 购物车列表勾选

  • demo效果(HH-SCDAYU200)

工程目录

完整的项目结构目录如下

├─entry\\src\\main
│          │  config.json  应用配置文件
│          │ 
│          ├─ets
│          │  └─MainAbility
│          │      │  app.ets  ets应用程序主入口
│          │      │ 
│          │      ├─model
│          │      │      ArsData.ets     // 初始化我的页面数据
│          │      │      CommonLog.ets   // 日志类
│          │      │      GoodsData.ets   // 初始化商品信息数据类
│          │      │      MenuData.ets    // 初始化我的页面数据类
│          │      │      RemoteDeviceManager.ets  // 分布式拉起设备管理类
│          │      │      ShoppingCartDistributedData.ets  // 加入购物车分布式数据库
│          │      │      TotalSelectedDistributedData.ets // 结算购物车分布式数据库
│          │      │ 
│          │      └─pages
│          │              DetailPage.ets   // 商品详情页面
│          │              HomePage.ets     // 应用首页
│          │              MyPage.ets       // 我的页面
│          │              ShoppingCartListPage.ets  // 购物车列表页面
│     └─resources // 静态资源目录
│         ├─base
│         │  ├─element
│         │  ├─graphic
│         │  ├─layout
│         │  ├─media // 存放媒体资源
│         │  └─profile
│         └─rawfile

开发步骤

1. 新建OpenHarmony ETS项目

在DevEco Studio中点击File -> New Project ->[Standard]Empty Ability->Next,Language 选择ETS语言,最后点击Finish即创建成功。

2. 编写商品展示主页面

效果图如上可以分为两部分

2.1商品列表展示

1)首先在@entry组件入口build()中使用 Tabs作为容器,达到排行榜和推荐翻页的效果;

2)再通过 List 包裹 Row 布局依次写入 Column 包裹的三个 Text 组件和 Image 组件;

3)并通过 Navigator 组件实现点击商品跳转到商品详细页功能,页面跳转过程使用 pageTransition 转场动画

Tabs() {
       TabContent() {
         GoodsList({ goodsItems: this.goodsItems});
       }
       .tabBar("畅销榜")
       .backgroundColor(Color.White)

       TabContent() {
         GoodsList({ goodsItems: this.goodsItems});
       }
       .tabBar("推荐")
       .backgroundColor(Color.White)
     }
      Navigator({ target: 'pages/DetailPage' }) {
       Row({ space: '40lpx' }) {
         Column() {
           Text(this.goodsItem.title)
             .fontSize('28lpx')
           Text(this.goodsItem.content)
             .fontSize('20lpx')
           Text('¥' + this.goodsItem.price)
             .fontSize('28lpx')
             .fontColor(Color.Red)
         }
         .height('160lpx')
         .width('50%')
         .margin({ left: '20lpx' })
         .alignItems(HorizontalAlign.Start)

         Image(this.goodsItem.imgSrc)
           .objectFit(ImageFit.ScaleDown)
           .height('160lpx')
           .width('40%')
           .renderMode(ImageRenderMode.Original)
           .margin({ right: '20lpx', left: '20lpx' })

       }
       .height('180lpx')
       .alignItems(VerticalAlign.Center)
       .backgroundColor(Color.White)
     }
     .params({ goodsItem: this.goodsItem ,ShoppingCartsGoods:this.ShoppingCartsGoods})
     .margin({ left: '40lpx' })
   }
   // 转场动画使用系统提供的多种默认效果(平移、缩放、透明度等)
 pageTransition() {
   PageTransitionEnter({ duration: 1000 })
     .slide(SlideEffect.Left)
   PageTransitionExit({ duration: 1000  })
     .slide(SlideEffect.Right)
 }
2.2底部导航栏

1)通过 Row 包裹三个 Image 组件,并添加onClick 点击事件,修改 @Consume 修饰的变量,从而改变 @Provide 装饰的变量,再通过条件渲染展示不同的页面内容;

Flex() {
        Image(this.iconPath[0])
          .objectFit(ImageFit.Cover)
          .height('60lpx')
          .width('60lpx')
          .margin({left:'50lpx',right:'40lpx'})
          .onClick(() => {
            this.iconPath[0] = this.iconPathSelectsTmp[0]
            this.iconPath[1] = this.iconPathTmp[1]
            this.iconPath[2] = this.iconPathTmp[2]
            this.currentPage = 1
          })
        Image(this.iconPath[1])
          .objectFit(ImageFit.Cover)
          .height('60lpx')
          .width('60lpx')
          .margin({left:'40lpx',right:'40lpx'})
          .onClick(() => {
            this.iconPath[0] = this.iconPathTmp[0]
            this.iconPath[1] = this.iconPathSelectsTmp[1]
            this.iconPath[2] = this.iconPathTmp[2]
            this.currentPage = 2
            this.remoteData.putData("shopping_cart", this.ShoppingCartsGoods)
          })
        Image(this.iconPath[2])
          .objectFit(ImageFit.Cover)
          .height('60lpx')
          .width('60lpx')
          .margin({left:'40lpx',right:'50lpx'})
          .onClick(() => {
            this.iconPath[0] = this.iconPathTmp[0]
            this.iconPath[1] = this.iconPathTmp[1]
            this.iconPath[2] = this.iconPathSelectsTmp[2]
            this.currentPage = 3
          })
      }
    .margin({top:'20lpx'})
    }
      
      Column() {
          if (this.currentPage == 1) {
            Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.End }) {
              Image($r("app.media.icon_share"))
                .objectFit(ImageFit.Cover)
                .height('60lpx')
                .width('60lpx')
            }
            .width("100%")
            .margin({ top: '20lpx', right: '50lpx' })
            .onClick(() => {
              this.playerDialog.open()
            })

            GoodsHome({ goodsItems: this.goodsItems})
          }
          else if (this.currentPage == 3) {
            //我的
            MyInfo()
          }
        }
3. 编写商品详细页面
3.1顶部滑动组件

1)滑动容器,提供切换子组件显示的能力;

Swiper() {
        ForEach(this.detailImages, item => {
          Image(item)
            .height('400lpx')
            .width('100%')
        })
      }
      .index(0)
      .autoPlay(true)
      .interval(3000)
      .indicator(true)
      .loop(true)
      .height('440lpx')
      .width('100%')
3.2 自定义弹框

1)通过 @CustomDialog 装饰器来创建自定义弹窗,使用方式可参考 自定义弹窗

2)规则弹窗效果如下,弹窗组成由两个 Text 和两个 Button 竖向排列组成;

所有我们可以在build()下使用 Flex 容器来包裹,组件代码如下:

@CustomDialog
struct CustomDialogExample {
  controller: CustomDialogController
  cancel: () => void
  confirm: () => void
  ShoppingCartsGoods: any[]

  build() {
    Flex() {
      Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
        Text('加入购物车成功')
          .fontColor("#000000")
          .fontSize('40lpx')
          .margin({ top: '20lpx', bottom: "20lpx" })

        Flex({ justifyContent: FlexAlign.SpaceAround }) {
          Button('取消')
            .onClick(() => {
              this.controller.close()
              this.cancel()
            }).backgroundColor(0xffffff).fontColor(Color.Black)
          Button('确定')
            .onClick(() => {
              this.controller.close()
              this.confirm()
            }).backgroundColor(0xffffff).fontColor(Color.Red)
        }.margin({ bottom: "20lpx" })
      }
    }
    .height('200lpx')
  }
}

3)在@entry创建CustomDialogController对象并传入弹窗所需参数,后面可通过该对象open()和close()方法进行打开和关闭弹窗;

dialogController: CustomDialogController = new CustomDialogController({
    builder: CustomDialogExample({
      cancel: this.onCancel,
      confirm: this.onAccept,
      ShoppingCartsGoods: this.ShoppingCartsGoods
    }),
    cancel: this.existApp,
    autoCancel: true
  })
  onCancel() {
    CommonLog.info('Callback when the first button is clicked')
  }

  onAccept() {
    CommonLog.info('Callback when the second button is clicked')
    router.push({
      uri: "pages/HomePage",
      params: { dataList: this.ShoppingCartsGoods }
    })
  }

  existApp() {
    CommonLog.info('Click the callback in the blank area')
  }
4. 添加分布式流转

分布式流转需要在同一网络下通过 DeviceManager组件 进行设备间发现和认证,获取到可信设备的deviceId调用 featureAbility.startAbility ,即可把应用程序流转到另一设备。

1)创建DeviceManager实例;

2)调用实例的startDeviceDiscovery(),开始设备发现未信任设备;

3)设置设备状态监听on(‘deviceFound’,callback),获取到未信任设备,并用discoverList变量进行维护;

4)传入未信任设备参数,调用实例authenticateDevice方法,对设备进行PIN码认证;

5)若是已信任设备,可通过实例的getTrustedDeviceListSync()方法来获取设备信息;

6)将设备信息中的deviceId传入 `featureAbility.startAbility方法,实现流转;

7)流转接收方可通过 featureAbility.getWant()获取到发送方携带的数据;

项目中将上面设备管理封装至RemoteDeviceManager,通过RemoteDeviceManager的四个方法来动态维护deviceList设备信息列表,实现分布式流转只需要在deviceList中获取deviceId,然后调用featureAbility.startAbility并携带数据,即可实现分布式流转。

5.分布式数据管理

分布式数据管理 要求两个或多个设备在同一网络,才能监听到数据库的改变,从而渲染页面;开发步骤:

1)创建一个KVManager对象实例,用于管理数据库对象;

2)通过指定Options和storeId,创建并获取KVStore数据库,如下是参数说明;需要先通过createKVManager构建一个KVManager实例;

参数名类型必填说明
storeIdstring数据库唯一标识符,长度不大于 MAX_STORE_ID_LENGTH。
optionsOptions创建KVStore实例的配置信息。

3)KVStore数据库实例, KVStore.put提供增加数据的方法,如下是参数说明;

参数名类型必填说明
keystring要添加数据的key,不能为空且长度不大于 MAX_KEY_LENGTH 。
valueUint8Arraystringnumber
callbackAsyncCallback回调函数。

4) KVStore数据库实例,KVStore.on订阅指定类型的数据变更通知;一般监听远端设备变化,再进行相应操作达到分布式数据共享的效果;

本d项目通过storeId 值不同,创建了两个数据库,分别是ShoppingCartsInfo类和TotalData类,ShoppingCartsInfo应用添加商品到购物车,TotalData应用在购物车列表进行勾选结算;如下是TotalData类流程

如下是ShoppingCartsInfo类流程

项目下载和导入

1)git下载

git clone https://gitee.com/openharmony-sig/knowledge_demo_shopping.git  --depth=1

2)项目导入

打开DevEco Studio,点击File->Open->下载路径/FA/Shopping/DistributedShoppingCart

写在最后

  • 如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
  • 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
  • 想要获取更多完整鸿蒙最新学习资源,请移步前往小编:https://gitee.com/MNxiaona/733GH

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1631095.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【无监督+自然语言】 GPT,BERT, GPT-2,GPT-3 生成式预训练模型方法概述 (Generative Pre-Traning)

主要参考 【GPT,GPT-2,GPT-3 论文精读【李沐论文精读】-2022.03.04】 https://www.bilibili.com/video/BV1AF411b7xQ/ 大语言模型综述: https://blog.csdn.net/imwaters/article/details/137019747 GPT与chatgpt的关系 图源:L…

JMeter的下载安装与使用(Mac)

1、下载地址​​​​​​https://jmeter.apache.org/download_jmeter.cgi 2、下载Binaries 下的apache-jmeter5.5.tgz 3、解压 4、启动 在bin目录下打开终端,输入sh jmeter 出现jmeter首页界面,即为成功。 5、使用 5.1 语言选择 option选项卡&am…

State.initState() must be a void method without an `async` keyword错误解析

文章目录 报错问题报错的代码 错误原因解决方法解析 另外的方法 报错问题 State.initState() must be a void method without an async keyword如下图: 报错的代码 报错的代码如下: overridevoid initState() async{super.initState();await getConf…

LoRA 引领多模态模型革命,大模型的微调方案

基于LoRA微调多模态大模型 随着 ChatGPT 的火爆,大模型时代降临,普通人难以进行全量微调。 参数高效微调技术应运而生,为科研人员和开发者提供了微调大模型的机会。 用 LoRA 微调 2.7B 参数的 blip2-opt 模型,提升图生文能力&am…

嵌入式学习——C语言基础——day11

1. 字符型数组和字符串的传参 1.1 常量和变量的区别&#xff08;难点&#xff09; 一般常量不能被修改&#xff0c;变量才能被修改 #include <stdio.h> int main(void) { char str[] {"hello world"};//定义数组&#xff0c;数组名为指针常量 char …

数据结构和算法:贪心

贪心算法 贪心算法是一种常见的解决优化问题的算法&#xff0c;其基本思想是在问题的每个决策阶段&#xff0c;都选择当前看起来最优的选择&#xff0c;即贪心地做出局部最优的决策&#xff0c;以期获得全局最优解。 贪心算法和动态规划都常用于解决优化问题。它们之间存在一…

干货整理:好用的文件加密软件有哪些

说到文件加密&#xff0c;想必大家都很熟悉&#xff0c;文件加密已经普遍应用&#xff0c;文件加密是一种重要的安全措施&#xff0c;可以确保数据的机密性、完整性和可用性&#xff0c;降低因数据泄露或丢失带来的风险。 下面小编给大家分享几款常用的加密软件&#xff0c;大…

纯血鸿蒙APP实战开发——评论组件案例实现

介绍 评论组件在目前市面上的短视频app中是一种很常见的场景&#xff0c;本案例使用全局状态保留能力弹窗来实现评论组件。点击评论按钮弹出评论组件&#xff0c;点击空白处隐藏该组件&#xff0c;再次点击评论按钮则会恢复上一次浏览的组件状态。 效果图预览 使用说明 点击…

DDP示例

https://zhuanlan.zhihu.com/p/602305591 https://zhuanlan.zhihu.com/p/178402798 关于模型保存与加载 &#xff1a; 其实分为保存 有module和无module2种 &#xff1b; &#xff08;上面知乎这篇文章说带时带module) 关于2种带与不带的说明&#xff1a; https://blog.csdn.…

Oracle中rman使用记录

最近在项目中&#xff0c;遇到使用RMAN的操作来恢复数据库中某个时间归档日志&#xff0c;RMAN的原理和理解&#xff0c;网友们百度了解一下。我重点将实操部分了。直接上实验环节&#xff0c;让网友更懂。&#xff08;特别提醒&#xff1a;我是1:1用VMware克隆数据库进行RMAN还…

构建高效智能的理赔业务系统:保险科技的未来

随着保险行业的发展和科技的不断进步&#xff0c;理赔业务作为保险服务的重要环节&#xff0c;也在不断演进和改进。传统的理赔流程可能存在效率低下、信息不透明等问题&#xff0c;而现代化的理赔业务系统则能够通过数字化、智能化等手段提升理赔服务的质量和效率&#xff0c;…

Java集成结巴中文分词器、Springboot项目整合jieba分词,实现语句最精确的切分、自定义拆词

文章目录 一、jieba介绍二、集成三、原理四、自定义拆词4.1、方式一&#xff1a;在源码的dict.txt中修改然后重新打包(推荐)4.2、新建文件自定义拆词 五、其他问题 一、jieba介绍 jieba是一个分词器&#xff0c;可以实现智能拆词&#xff0c;最早是提供了python包&#xff0c;…

Qt | 窗口的显示及可见性|标题、透明度、启用/禁用|窗口标志、设置其他属性|获取窗口部件、设置父部件|鼠标光标

​显示事件:QEvent::show,处理函数为 showEvent(QShowEvent*) 隐藏事件:QEvent::hide,处理函数为 hideEvent(QHideEvent* ) 01 QWidget 类中与可见性有关的属性 visible:bool 访问函数: bool isVisible() const; virtual void setVisible(bool visible); 02 QWid…

高频面试题:在浏览器搜索框中输入一个URL的完整请求过程?

相信很多小伙伴在校招或者社招面试中都遇到过这个问题 面试官&#xff1a;小伙子&#xff0c;了解 在浏览器搜索框中输入一个URL的完整请求过程吗&#xff1f;详细说说我&#xff1a;eeemm&#xff0c;不太清出具体的过程。整体过程应该是HTTP请求的过程。 如果在面试中不能很…

【C++】---STL容器适配器之底层deque浅析

【C】---STL容器适配器之底层deque浅析 一、deque的使用二、deque的原理1、deque的结构2、deque的底层结构&#xff08;1&#xff09;deque的底层空间&#xff08;2&#xff09;deque如何支持随机访问、deque迭代器 3、deque的优缺点&#xff08;1&#xff09;deque的优势&…

【golang学习之旅】报错:a declared but not used

目录 报错原因解决方法参考 报错 代码很简单&#xff0c;如下所示。可以发现a和b都飙红了&#xff1a; 运行后就会出现报错&#xff1a; 报错翻译过来就是a已经声明但未使用。当时我很疑惑&#xff0c;在其他语言中从来没有这种情况。况且这里的b不是赋值了吗&#xff0c;怎…

Sarcasm detection论文解析 | 通过阅读进行讽刺推理-Reasoning with sarcasm by reading in-between

论文地址 论文地址&#xff1a;[1805.02856] Reasoning with Sarcasm by Reading In-between (arxiv.org) 论文首页 笔记大纲 通过阅读进行讽刺推理论文笔记 &#x1f4c5;出版年份:2018&#x1f4d6;出版期刊:&#x1f4c8;影响因子:&#x1f9d1;文章作者:Tay Yi,Luu Anh…

制作一个RISC-V的操作系统十六-系统调用

文章目录 用户态和内核态mstatus设置模式切换核心流程封装代码背景解释代码示例解析解释目的 用户态和内核态 mstatus设置 此时UIE设置为1和MPIE为1&#xff0c;MPP设置为0 代表当前权限允许UIE中断发生&#xff0c;并且在第一个mret后将权限恢复为用户态&#xff0c;同时MIE也…

17 大数据定制篇-shell编程

第 17 章大数据定制篇-Shell 编程 17.1 为什么要学习 Shell 编程 Linux 运维工程师在进行服务器集群管理时&#xff0c;需要编写 Shell 程序来进行服务器管理。 对于 JavaEE 和 Python 程序员来说&#xff0c;工作的需要&#xff0c;你的老大会要求你编写一些 Shell 脚本进行…

ERP系统和SRM系统有什么关系?

一、什么是ERP系统和SRM系统&#xff1f; ERP系统是一种集成化的管理软件&#xff0c;能够帮助企业实现资源的优化配置&#xff0c;提高运营效率。ERP系统涵盖了企业的各个方面&#xff0c;包括财务、采购、库存、生产、销售、人力资源等&#xff0c;通过对这些方面的管理&…