HarmonyOS应用开发ArkUI(TS)电商项目实战

news2024/11/23 11:50:39

项目介绍

本项目基于 HarmonyOS 的ArkUI框架TS扩展的声明式开发范式,关于语法和概念直接看官网官方文档地址:基于TS扩展的声明式开发范式,

工具版本: DevEco Studio 3.1 Canary1

SDK版本: 3.1.9.7(API Version 8 Release)

效果演示

页面解析

主框架

使用容器组件:Tabs 、TabContent、作为主框架,底部Tab使用自定义布局样式,设置点击事件,每次点击更换选中的索引。来切换内容页面。

使用自定义组件来实现:首页、分类、购物车、我的的搭建。

@Entry
@Component
struct MainFrame {
  @State selectIndex: number = 0
  private controller: TabsController = new TabsController()
  private tabBar = getTabBarList()

  // 内容
  @Builder Content() {
    Tabs({ controller: this.controller }) {
      TabContent() {HomeComponent()}
      TabContent() {ClassifyComponent()}
      TabContent() {ShoppingCartComponent({ isShowLeft: false })}
      TabContent() {MyComponent()}
    }
    .width('100%')
    .height(0)
    .animationDuration(0)
    .layoutWeight(1)
    .scrollable(false)
    .barWidth(0)
    .barHeight(0)
  }

  // 底部导航
  @Builder TabBar() {
    Row() {
      ForEach(this.tabBar, (item: TabBarModel, index) => {
        Column() {
          Image(this.selectIndex === index ? item.iconSelected : item.icon)
            .width(23).height(23).margin({ right: index === 2 ? 3 : 0 })
          Text(item.name)
            .fontColor(this.selectIndex === index ? '#dc1c22' : '#000000')
            .margin({ left: 1, top: 2 })
        }.layoutWeight(1)
        .onClick(() => {
          this.selectIndex = index
          this.controller.changeIndex(index)
        })
      }, item => item.name)
    }.width('100%').height(50)
    .shadow({ radius: 1, color: '#e3e2e2', offsetY: -1 })
  }

  build() {
    Column() {
      this.Content()
      this.TabBar()
    }.width('100%').height('100%')
  }
}
首页

因为顶部标题栏需要浮在内容之上,所以根布局使用 容器组件Stack ,内容布局最外层使用 容器组件Scroll 来实现滑动,内容整体分为3部分:

  1. 轮播图:使用 容器组件Swiper
  2. 菜单:使用 容器组件Flex 、默认横向布局,子布局宽度分为五等分,设置Flex的参数:wrap: FlexWrap.Wrap可实现自动换行。
  3. 商品列表:和菜单的实现方式一样,只不过宽度是二等分

向下滑动时标题栏显示功能:使用 容器组件Scroll 的属性方法:onScroll来判断y轴方向的偏移量,根据偏移量计算出比例,改变标题栏的透明度。

(以下是部分代码)


@Component
export struct HomeComponent {
  // 滑动的y偏移量
  private yTotalOffset = 0
  // 标题栏透明度
  @State titleBarOpacity: number = 0
  // 轮播图列表
  private banners = [
    $r("app.media.banner1"), $r("app.media.banner2"), $r("app.media.banner3"),
    $r("app.media.banner4"), $r("app.media.banner5"), $r("app.media.banner6"),
  ]
  // 菜单列表
  private menuList = getHomeMenuList()
  // 商品列表
  private goodsList: Array<HomeGoodsModel> = getHomeGoodsList()


  // 轮播图
  @Builder Banner() {...}

  // 菜单
  @Builder Menu() {....}

  // 商品列表
  @Builder GoodsList() {...}

  build() {
    Stack({ alignContent: Alignment.Top }) {
      Scroll() {
        Column() {
          this.Banner()
          this.Menu()
          this.GoodsList()
        }.backgroundColor(Color.White)
      }.scrollBar(BarState.Off)
      .onScroll((xOffset, yOffset) => {
        this.yTotalOffset += yOffset
        const yTotalOffsetVP = px2vp(this.yTotalOffset)
        // 轮播图高度 350
        const scale = yTotalOffsetVP / 200
        this.titleBarOpacity = scale
      })

      Row(){
        TitleBar({
          title: '首页',
          isShowLeft: false,
          isShowRight: true,
          rightImage: $r('app.media.search')
        })
      }.opacity(this.titleBarOpacity)
    }.width('100%').height('100%')
  }
}
详情页

和首页类似,根布局使用容器组件Stack,内容布局最外层使用容器组件Scroll 来实现滑动。因为详情页有相同布局,使用装饰器@Builder来抽离公共布局。

向下滑动时标题栏显示功能原理和首页一样

(以下是部分代码)

import router from '@ohos.router';

@Entry
@Component
struct GoodsDetail {
  ....
  // 轮播图
  @Builder Banner() {...}

  // 内容item
  @Builder ContentItem(title: string, content: string, top=1) {
    Row() {
      Text(title).fontSize(13).fontColor('#808080')
      Text(content).fontSize(13).margin({ left: 10 }).fontColor('#ff323232')
      Blank()
      Image($r('app.media.arrow')).width(12).height(18)
    }.width('100%').padding(13)
    .backgroundColor(Color.White).margin({ top: top })
  }
  // 内容
  @Builder Content() {
    ...
    this.ContentItem('邮费', '满80包邮', 7)
    this.ContentItem('优惠', '减5元')
    this.ContentItem('规格', '山核桃坚果曲奇; x3', 7)
    this.ContentItem('配送', '北京市朝阳区大塔路33号')
  }

  // 评论
  @Builder Comment() {...}

  // 商品参数item
  @Builder GoodsParamItem(title: string, content: string) {
    Row() {
      Text(title).width(100).fontSize(13).fontColor('#808080')
      Text(content).fontSize(13).fontColor('#ff323232')
        .layoutWeight(1).padding({ right: 20 })
    }.width('100%').padding({ top: 15 })
    .alignItems(VerticalAlign.Top)
  }
  // 商品参数
  @Builder GoodsParam() {...}

  build() {
    Stack() {
      Scroll() {
        Column() {
          this.Banner()
          this.Content()
          this.Comment()
          this.GoodsParam()
        }
      }.scrollBar(BarState.Off)
      .onScroll((xOffset, yOffset) => {
        this.yTotalOffset += yOffset
        const yTotalOffsetVP = px2vp(this.yTotalOffset)
        // 轮播图高度 350
        const scale = yTotalOffsetVP / 350
        this.titleBarBgTmOpacity = 1 - scale
        if (scale > 0.4) {
          this.titleBarBgWhiteOpacity = scale - 0.2
        } else {
          this.titleBarBgWhiteOpacity = 0
        }
      })

      this.TopBottomOperateBar()
    }.width('100%').height('100%')
    .backgroundColor('#F4F4F4')
  }
}
分类

此页面很简单,就是左右两个容器组件List、一个宽度30%,一个宽度70%,使用循环渲染组件ForEach来渲染列表。

(以下是部分代码)

@Component
export struct ClassifyComponent {
  // 搜索
  @Builder Search() {...}

  // 内容
  @Builder Content() {
    Row() {
      // 左分类
      List() {...}.width('30%')

      // 右分类
      List({scroller:this.scroller}) {...}.width('70%')

    }.width('100%').layoutWeight(1)
    .alignItems(VerticalAlign.Top)
  }

  build() {
    Column() {
      this.Search()
      this.Content()
    }.width('100%').height('100%')
    .backgroundColor(Color.White)
  }
}
购物车

此界面功能:全选、单选、删除商品、更改商品的数量。当有商品选中,右上角删除按钮显示。更改商品数量同步更新合计的总价格。

使用List组件来实现列表。数据数组使用装饰器@State定义。数据更新必须是更改数组的项

(以下是部分代码)

@Component
export struct ShoppingCartComponent {
  ...
  // 商品详情
  @Builder GoodsItem(item: ShoppingCartModel, index: number) {...}

  build() {
    Column() {
      TitleBar()
        
      if (this.list.length > 0) {
        // 列表  
        List() {...}.layoutWeight(1)
        // 结算布局
        Row() {...}
      } else {
        Row() {
          Text('空空如也~')
        }
      }
    }.width('100%').height('100%')
    .backgroundColor('#F4F4F4')
  }

  selectOperation(item: ShoppingCartModel, index: number) {
    // 替换元素,才能更改数组,这样页面才能更新
    let newItem = {...item}
    newItem.isSelected = !item.isSelected
    this.list.splice(index, 1, newItem)

    this.calculateTotalPrice()
  }

  // 加/减操作
  addOrSubtractOperation(item: ShoppingCartModel, index: number, type: -1 | 1) {...}

  // 计算合计
  calculateTotalPrice() {
    let total = 0
    let selectedCount = 0
    for (const item of this.list) {
      if (item.isSelected) {
        selectedCount++
        total += item.newPrice * item.count
      }
    }
    this.totalPrice = total
    this.isAllSelected = selectedCount === this.list.length
  }
}

结尾

此项目没有比较难的点,都是照着官方文档,查阅API做出来的,依靠着声明式UI的简洁和强大,页面搭建效率很高。

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

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

相关文章

说说你对数据结构-树的理解

对树 - 二叉搜索树的理解 二叉搜索树是一种常见的二叉树结构&#xff0c;它具有以下特点&#xff1a; 每个节点最多只有两个子节点&#xff0c;分别称为左子节点和右子节点&#xff1b;对于任意节点&#xff0c;其左子树中的所有节点均小于该节点&#xff0c;其右子树中的所有…

linux 回收站机制(笔记)

Linux下回收站机制https://mp.weixin.qq.com/s/H5Y8VRcaOhFZFXzR8yQ7yg 功能 &#xff1a;设立回收站&#xff0c;并且可定时清空回收站。 一、建议将alias rm 改成别的。 比如alias rmm &#xff0c;同时修改rm -rf ~/.trash/* 改成 rmm -rf ~/.trash/* 不然影响rm 的正常使…

JUC:synchronized优化——锁的升级过程(偏向锁->轻量级锁->重量级锁)以及内部实现原理

文章目录 锁的类型轻量级锁重量级锁自旋优化偏向锁偏向锁的细节偏向锁的撤销批量重偏向批量撤销锁消除 锁的类型 重量级锁、轻量级锁、偏向锁。 加锁过程&#xff1a;偏向->轻量级->重量级 轻量级锁 轻量级锁的使用场景&#xff1a;如果一个对象虽然有多线程要加锁&am…

OpenHarmony Neptune开发板-MQTT连接华为IoT平台

本示例将演示如何在Neptune开发板上使用MQTT协议连接华为IoT平台,使用的是ATH20温湿度传感器模块与Neptune开发板 本示例实现AHT20温湿度数据上报华为IoT平台,IoT平台下发命令控制LED灯的开关 使用W800 SDK功能包中libemqtt来实现连接华为IoT平台 程序设计 初始化 一、MQT…

k8s 基础入门

1.namespace k8s中的namespace和docker中namespace是两码事&#xff0c;可以理解为k8s中的namespace是为了多租户&#xff0c;dockers中的namespace是为了网络、资源等隔离 2.deployment kubectl create #新建 kubectl aply #新建 更新 升级&#xff1a; 滚动升级&#x…

Find a way (DFS BFS)

//新生训练 #include <iostream> #include <algorithm> #include <queue> using namespace std; using PII pair<int, int>;const int N 205; const int inf 0x3f3f3f3f; int n, m; int l;int dis1[N][N]; int dis2[N][N];int dx[] {0, 0, -1, 1};…

HarmonyOS入门--页面和自定义组件生命周期

文章目录 页面和自定义组件生命周期页面生命周期组件生命周期生命周期的调用时机 页面和自定义组件生命周期 生命周期流程如下图所示&#xff0c;下图展示的是被Entry装饰的组件&#xff08;首页&#xff09;生命周期。 自定义组件和页面的关系&#xff1a; 自定义组件&…

zabbix绑定钉钉进行通知,网页端添加JavaScript,无脑式操作

文章目录 前言一、编辑zabbix告警JavaScript脚本二、代码如下:编辑消息模板,自定义markdown格式的消息。总结前言 随着人工智能的不断发展,zabbix监控这门技术也越来越重要,一下进入正题。 一、编辑zabbix告警JavaScript脚本 没有没接可以新增媒介 其中URL是你的机器人地…

Linux系统——网络管理

此文章以红帽Linux9版本为例进行讲解。 红帽Linux9版本的网络管理十分全面&#xff0c;可在多处进行网络配置的修改&#xff0c;但需要注意的是&#xff0c;在9版本内&#xff0c;用户可在配置文件内进行网络配置的修改&#xff0c;但系统不会执行修改的命令&#xff0c;而在9之…

redis之主从复制、哨兵模式

一 redis群集有三种模式 主从复制&#xff1a; 主从复制是高可用Redis的基础&#xff0c;哨兵和集群都是在主从复制基础上实现高可用的。 主从复制主要实现了数据的多机备份&#xff0c;以及对于读操作的负载均衡和简单的故障恢复。 缺陷&#xff1a; 故障恢复无法自动化&…

西圣、飞利浦、万魔开放式耳机好用吗?热门产品硬核测评对比

在无线耳机市场中&#xff0c;开放式耳机因其独特的佩戴方式和出色的听音体验逐渐受到消费者的青睐&#xff0c;西圣、飞利浦、万魔作为业内知名品牌&#xff0c;都推出了各自的开放式耳机产品&#xff0c;备受关注&#xff0c;那么这些热门品牌的开放式耳机究竟好用吗&#xf…

力扣热门算法题 174. 地下城游戏,189. 轮转数组,198. 打家劫舍

174. 地下城游戏&#xff0c;189. 轮转数组&#xff0c;198. 打家劫舍&#xff0c;每题做详细思路梳理&#xff0c;配套Python&Java双语代码&#xff0c; 2024.03.31 可通过leetcode所有测试用例。 目录 174. 地下城游戏 解题思路 完整代码 Python Java 189. 轮转数…

【Web应用技术基础】JavaScript(1)——案例:猜数字

上一个博客发了视频。这个博客因为不能插入视频&#xff0c;所以给大家一张一张截图的 点击“重新开始一局游戏” <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"…

[leetcode]剑指 Offer 29. 顺时针打印矩阵

前言&#xff1a;剑指offer刷题系列 问题&#xff1a; 输入一个矩阵&#xff0c;按照从外向里以顺时针的顺序依次打印出每一个数字。 示例&#xff1a; 输入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8,9]] 输出&#xff1a;[1,2,3,6,9,8,7,4,5]思路&#xff1a; 看到这个…

Lambda表达式,Stream流

文章目录 Lambda表达式作用前提函数式接口特点 语法省略模式和匿名对象类的区别 Stream流思想作用三类方法获取方法单列集合(Collection[List,Set双列集合Map(不能直接获取)数组同一类型元素(Stream中的静态方法) 常见的中间方法终结方法收集方法 Optional类 Lambda表达式 作用…

客户案例丨拓数派向量计算引擎PieCloudVector助力东吴证券AIGC应用升级

1.项目背景 随着人工智能技术的不断创新和应用&#xff0c;我们可以看到人工智能在各个领域的应用越来越广泛。深度学习技术在图像识别、语音识别、自然语言处理等领域表现出色。机器学习算法的改进将解决更多实际问题&#xff0c;如增强学习、迁移学习和联合学习等&#xff…

一个瓶盖和电子工程师以及机械工程师能有什么联系?

过小的产品空间是一种罪 我也曾经是喜欢将东西做的小巧&#xff0c;其实这是不归路&#xff0c;特别是对于产品&#xff0c;一不小心&#xff0c;就会发生可怕的问题. 最近某厂供应的一批一体机&#xff0c;设计上有严重缺陷&#xff0c;外壳做小了&#xff0c;散热孔开的太小…

什么是智慧驿站?智慧驿站有哪些功能?创新型智慧公厕解说

近年来&#xff0c;随着智能科技的迅速发展&#xff0c;人们对于城市生活的期望也逐渐提升。作为城市基础设施的一部分&#xff0c;智慧驿站应运而生。它不仅是一座智慧公厕&#xff0c;更是集合了多种功能&#xff0c;给我们带来全新的城市生活体验。本文以智慧驿站智慧公厕源…

使用Vite创建一个vue3项目

一、vite是什么&#xff1f; vite 是一种新型前端构建工具&#xff0c;能够显著提升前端开发体验。它主要由两部分组成&#xff1a; 一个开发服务器&#xff0c;它基于原生 ES 模块提供了丰富的内建功能&#xff0c;如速度快到惊人的模块热更新&#xff08;HMR&#xff09;。 …

如何监控特权帐户,保护敏感数据

IT基础设施的增长导致员工可以访问的凭据和资源数量急剧增加。每个组织都存储关键信息&#xff0c;这些信息构成了做出关键业务决策的基石。与特权用户共享这些数据可以授予他们访问普通员工没有的凭据的权限。如果特权帐户凭证落入不法分子之手&#xff0c;它们可能被滥用&…