鸿蒙应用ets之仿黑马智慧商城商品详情页

遇到一个问题:轮播图图片链接数组默认为空,则会导致闪退,所以需要添加一张默认图片

有大佬知道是怎么个情况吗?


前言

本章节还没有实现:加入购物车和立即购买两个功能。

涉及数据管理,内容较多,下期实现。所以本章节为UI界面的实现

需要用到黑马智慧商城接口。地址:wiki - 智慧商城-实战项目 (apifox.com)


一、效果展示

二、分析布局

2个红色框的内容固定,屏幕中间的内容可以滚动 


 

1.顶部(红色内容框)

这里头部封装成了一个组件

//头部
      Row(){
        topContent({title:'商品详情页'})
      }.width('100%')
//头部组件
import router from '@ohos.router'
@Component
export struct topContent{

  @State title:string = ''

  build(){
    Row(){
      Text('<').fontSize(25).fontWeight(FontWeight.Bold).margin({left:10})
        .onClick(()=>{
          router.back()
        })
      Text(this.title)
        .width('100%')
        .fontSize(25)
        .fontWeight(800)
        .fontColor(Color.Black)
        .textAlign(TextAlign.Center)
    }.width('100%').padding('2%')
  }
}

 

2.轮播图(黄色框内容)

我为数组中添加了一个默认的图片,不然会报错。。。

//轮播图
          Row(){
            Swiper(){
              ForEach(this.picList,(item:string)=>{
                Image(item)
                  .width('100%').objectFit(ImageFit.Contain)
              })
            }.width('100%')
            .index(1)
            .autoPlay(true)
          }.width('100%').padding('2%')

 

3.商品信息(绿色框内容) 

         //商品价格,售出数量
          Row({space:10}){
            Text(this.newPrice)
              .fontSize(27).fontColor(Color.Red).fontWeight(700)
            //如果价格等于0的话,则不展示
            Text(Number(this.oldPrice)>0?`¥${this.oldPrice}`:'')
              .fontSize(16).opacity(0.7).decoration({type:TextDecorationType.LineThrough})//线从字的中间穿过
            Blank()
            Text(`已售${this.sales}件`).fontSize(20)
          }.width('100%').padding('2%')
          //商品信息
          Row(){
            Text(this.goodsName).fontSize(25).maxLines(2).textOverflow({overflow:TextOverflow.Ellipsis})
          }.width('100%').padding('2%')
          //权益
          Row(){
            Text('支持七天无理由退货  |  48小时内发货')
            Blank()
            Text('>').fontSize(20).margin({right:7})
          }.width('100%').padding('2%').backgroundColor("#fff8f7f7")

 

4.评论(蓝色框内容) 

评论区的内容包含了用户的头像,昵称,评分,评论内容和评论日期等,所以将他封装成一个组件

//评论
          Row(){
            Column(){
              Row(){
                Text(`商品评价(${this.goodComments.length}条)`)
                Blank()
                Text('>').fontSize(20).margin({right:7})
              }.width('100%')
              //评论组件
              ForEach(this.goodComments,(item:any)=>{
                userComment({userPic:item.pic,userName:item.name,starNum:item.score,comments:item.comment,time:item.time})
                  .width('100%')
              })
            }
          }.width('100%').padding('2%').margin({top:5})
//评论区组件
@Component
export struct userComment{

  //用户头像
  @State userPic:string = ''
  //用户昵称
  @State userName:string = ''
  //几颗星
  @State starNum:number = 5
  //评论的内容
  @State comments:string = ''
  //时间
  @State time:string = ''

  build(){
    Column({space:5}){
      Row(){
        Image(this.userPic)
          .width('10%').objectFit(ImageFit.Contain).borderRadius(50)
        Text(this.userName)
          .width('40%').fontSize(20).fontWeight(600)
        Rating({rating:this.starNum})
          .stars(5)
      }.width('100%')
      Row(){
        Text(this.comments)
          .fontSize(20).fontWeight(FontWeight.Bold)
      }.width('100%')
      Row(){
        Text(this.time)
          .fontSize(15).opacity(0.5).fontColor("#ffababab")
      }.width('100%')
    }
    .width('100%').margin({top:5})
  }
}

 

 5.商品介绍

这里的内容数据通过接口获取的,是超文本内容,所以用到一个组件RichText展示就好了

 //商品描述
          Row(){
            Text('商品描述:')
              .width('100%').fontSize(20).textAlign(TextAlign.Start)
          }.width('100%').padding('2%').margin({top:5})
          Row(){
            RichText(this.descText)
              .width('100%').height('100%')
          }.width('100%').padding('2%')

 

6.底部菜单栏(红色框内容) 

 @Builder menu(){
    Row(){
      Column(){
        Image($rawfile('icon/sy.png'))
          .width('6%')
        Text('首页')
      }
      Column(){
        Image($rawfile('icon/gwc.png'))
          .width('6%')
        Text('购物车')
      }
      Button('加入购物车')
        .width('30%').backgroundColor("#ffffaa00")
        .onClick(()=>{
          this.isShowPanel = true
        })
      Button('立即购买')
        .width('30%').backgroundColor("#FF4400")
    }.width('100%').justifyContent(FlexAlign.SpaceBetween).padding('2%')
  }

注意:当我们点击加入购物车按钮的时候,需要展示这个效果

 

需要弹出一个结算面板,所以我们底部的菜单栏应该要用一个stack层叠布局展示2者效果

即:默认展示菜单栏,当用户点击加入购物车按钮的时候,则需要将原菜单栏的位置隐藏,

展示商品的购买信息。代码如下。


 

7.商品购买信息

 @Builder pan(){
    Column({space:10}){
      Stack(){
        Text('商品信息')
          .width('100%').textAlign(TextAlign.Center)
        Row(){
          Text('✖').margin({right:20})
            .onClick(()=>{
              this.isShowPanel = false
            })
        }.width('100%').justifyContent(FlexAlign.End)
      }.width('100%')
      Row({space:5}){
        Image(this.picList[1])
          .width('30%')
        Column({space:10}){
          Text((Number(this.newPrice) * this.num.price).toString())
            .fontSize(30).fontWeight(700).fontColor(Color.Red)
          Text(`库存:${this.stock}`)
        }.width('70%').alignItems(HorizontalAlign.Start)
      }.width('100%')
      Row(){
        Text('数量').fontSize(25).fontWeight(800)
        step({num:$num})
      }.width('100%').justifyContent(FlexAlign.SpaceBetween)
      Row(){
        Button('加入购物车')
          .width('90%').backgroundColor("#ffffaa00")
          .onClick(()=>{
            
          })
      }.width('100%').justifyContent(FlexAlign.Center)
    }.width('100%').height('35%').borderRadius({topLeft:20,topRight:20}).padding('2%')
  }

 

8.底部实现层叠布局 

用一个状态变量标记状态

//底部菜单栏
      Row(){
        Stack(){
          if(this.isShowPanel){
            this.pan()
          }else{
            this.menu()
          }
        }
      }.width('100%')

三 、获取数据

注意:商品id,是前一页路由传值传递过来获取的商品id

1.获取商品详情页数据

//获取商品详情页数据
  async getShopDetailData(id){
    const data = await axios.get(`https://smart-shop.itheima.net/index.php?s=/api/goods/detail&goodsId=${id}`)
    //简化
    const con = data.data.data.detail
    //轮播图数据
    const len1 = con.goods_images.length
    const con1 = con.goods_images
    for (let i = 0; i < len1; i++) {
      this.picList.push(con1[i].preview_url)
    }
    //获取商品新价格
    this.newPrice = con.goods_price_min
    //获取商品旧价格
    this.oldPrice = con.line_price_min
    //获取商品售出数量
    this.sales = con.goods_sales
    //获取商品名称
    this.goodsName = con.goods_name
    //获取商品描述
    this.descText = con.content
    //获取商品库存数
    this.stock = con.stock_total
  }

2.获取商品评论数据

默认展示2条评论数据

  //获取商品评论数据
  async getShopCommentData(id){
    const data = await axios.get(`https://smart-shop.itheima.net/index.php?s=/api/comment/listRows&goodsId=${id}`)
    //const len = data.data.data.list.length
    const con = data.data.data.list
    for (let i = 0; i < 2; i++) {
      var item = {
        pic:con[i].user.avatar_url,//用户头像
        name:con[i].user.nick_name,//用户名称
        score:con[i].score,//用户评分
        comment:con[i].content,//用户评论内容
        time:con[i].create_time//用户评论的时间
      }
      this.goodComments.push(item)
    }
  }

总结

完整的UI代码:

  //接收路由传值的id
  @State ids:object = router.getParams()
  //商品id
  @State gid:string = ''
  //轮播图数据
  @State picList:string[]=['https://tse2-mm.cn.bing.net/th/id/OIP-            C.qtX9ct2NjPOXaefc5Gpe_AHaC_?w=197&h=80&c=7&r=0&o=5&dpr=1.5&pid=1.7']
  //商品新价格
  @State newPrice:string = ''
  //商品旧价格
  @State oldPrice:string = ''
  //商品库存
  @State stock:string = ''
  //已售数量
  @State sales:string|number = ''
  //商品名称
  @State goodsName:string = ''
  //商品评论数据
  @State goodComments:any[]=[]
  //商品描述
  @State descText:string = ''
  //是否展示加入购物车面板
  @State isShowPanel:boolean = false
  //商品数量
  @State num:prices = new prices(1)

 aboutToAppear(){
    var ids = JSON.parse(JSON.stringify(this.ids))
    this.gid = ids.id
    this.getShopDetailData(ids.id)
    this.getShopCommentData(ids.id)
  }


//获取商品详情页数据
  async getShopDetailData(id){
    const data = await axios.get(`https://smart-shop.itheima.net/index.php?s=/api/goods/detail&goodsId=${id}`)
    //简化
    const con = data.data.data.detail
    //轮播图数据
    const len1 = con.goods_images.length
    const con1 = con.goods_images
    for (let i = 0; i < len1; i++) {
      this.picList.push(con1[i].preview_url)
    }
    //获取商品新价格
    this.newPrice = con.goods_price_min
    //获取商品旧价格
    this.oldPrice = con.line_price_min
    //获取商品售出数量
    this.sales = con.goods_sales
    //获取商品名称
    this.goodsName = con.goods_name
    //获取商品描述
    this.descText = con.content
    //获取商品库存数
    this.stock = con.stock_total
  }


//获取商品评论数据
  async getShopCommentData(id){
    const data = await axios.get(`https://smart-shop.itheima.net/index.php?s=/api/comment/listRows&goodsId=${id}`)
    //const len = data.data.data.list.length
    const con = data.data.data.list
    for (let i = 0; i < 2; i++) {
      var item = {
        pic:con[i].user.avatar_url,
        name:con[i].user.nick_name,
        score:con[i].score,
        comment:con[i].content,
        time:con[i].create_time
      }
      this.goodComments.push(item)
    }
  }


 build(){
    Column({space:5}){
      //头部
      Row(){
        topContent({title:'商品详情页'})
      }.width('100%')

      Scroll(){
        Column(){
          //轮播图
          Row(){
            Swiper(){
              ForEach(this.picList,(item:string)=>{
                Image(item)
                  .width('100%').objectFit(ImageFit.Contain)
              })
            }.width('100%')
            .index(1)
            .autoPlay(true)
          }.width('100%').padding('2%')
          //商品价格,售出数量
          Row({space:10}){
            Text(this.newPrice)
              .fontSize(27).fontColor(Color.Red).fontWeight(700)
            //如果价格等于0的话,则不展示
            Text(Number(this.oldPrice)>0?`¥${this.oldPrice}`:'')
              .fontSize(16).opacity(0.7).decoration({type:TextDecorationType.LineThrough})//线从字的中间穿过
            Blank()
            Text(`已售${this.sales}件`).fontSize(20)
          }.width('100%').padding('2%')
          //商品信息
          Row(){
            Text(this.goodsName).fontSize(25).maxLines(2).textOverflow({overflow:TextOverflow.Ellipsis})
          }.width('100%').padding('2%')
          //权益
          Row(){
            Text('支持七天无理由退货  |  48小时内发货')
            Blank()
            Text('>').fontSize(20).margin({right:7})
          }.width('100%').padding('2%').backgroundColor("#fff8f7f7")
          //评论
          Row(){
            Column(){
              Row(){
                Text(`商品评价(${this.goodComments.length}条)`)
                Blank()
                Text('>').fontSize(20).margin({right:7})
              }.width('100%')
              //评论组件
              ForEach(this.goodComments,(item:any)=>{
                userComment({userPic:item.pic,userName:item.name,starNum:item.score,comments:item.comment,time:item.time})
                  .width('100%')
              })
            }
          }.width('100%').padding('2%').margin({top:5})
          //商品描述
          Row(){
            Text('商品描述:')
              .width('100%').fontSize(20).textAlign(TextAlign.Start)
          }.width('100%').padding('2%').margin({top:5})
          Row(){
            RichText(this.descText)
              .width('100%').height('100%')
          }.width('100%').padding('2%')
        }
      }.width('100%').layoutWeight(1).scrollBar(BarState.Off)
      //底部菜单栏
      Row(){
        Stack(){
          if(this.isShowPanel){
            this.pan()
          }else{
            this.menu()
          }
        }
      }.width('100%')
    }
    .width('100%').height('100%')
  }


  @Builder menu(){
    Row(){
      Column(){
        Image($rawfile('icon/sy.png'))
          .width('6%')
        Text('首页')
      }
      Column(){
        Image($rawfile('icon/gwc.png'))
          .width('6%')
        Text('购物车')
      }
      Button('加入购物车')
        .width('30%').backgroundColor("#ffffaa00")
        .onClick(()=>{
          this.isShowPanel = true
        })
      Button('立即购买')
        .width('30%').backgroundColor("#FF4400")
    }.width('100%').justifyContent(FlexAlign.SpaceBetween).padding('2%')
  }

  @Builder pan(){
    Column({space:10}){
      Stack(){
        Text('商品信息')
          .width('100%').textAlign(TextAlign.Center)
        Row(){
          Text('✖').margin({right:20})
            .onClick(()=>{
              this.isShowPanel = false
            })
        }.width('100%').justifyContent(FlexAlign.End)
      }.width('100%')
      Row({space:5}){
        Image(this.picList[1])
          .width('30%')
        Column({space:10}){
          Text((Number(this.newPrice) * this.num.price).toString())
            .fontSize(30).fontWeight(700).fontColor(Color.Red)
          Text(`库存:${this.stock}`)
        }.width('70%').alignItems(HorizontalAlign.Start)
      }.width('100%')
      Row(){
        Text('数量').fontSize(25).fontWeight(800)
        step({num:$num})
      }.width('100%').justifyContent(FlexAlign.SpaceBetween)
      Row(){
        Button('加入购物车')
          .width('90%').backgroundColor("#ffffaa00")
          .onClick(()=>{
           
          })
      }.width('100%').justifyContent(FlexAlign.Center)
    }.width('100%').height('35%').borderRadius({topLeft:20,topRight:20}).padding('2%')
  }
}



下期实现加入购物车和立即购买功能

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ETS(Easy Template System)是CSDN开发的一套模板引擎,用于前端页面开发。在鸿蒙系统中,ETS也是可以使用的,以下是一个简单的例子,演示如何使用ETS实现页面跳转。 1. 在项目中创建一个ETS模板文件,例如"index.ets",并在其中添加一个页面跳转的按钮: ``` <!DOCTYPE html> <html> <head> <title>Page Title</title> </head> <body> <h1>Welcome to my website</h1> <button onclick="jumpToPage()">Jump to new page</button> <script type="text/javascript"> function jumpToPage() { // 页面跳转逻辑 } </script> </body> </html> ``` 2. 在鸿蒙应用的JS文件中导入ETS模板引擎,并使用它来渲染页面: ``` import { createElement } from '@vue/composition-api'; import Ets from '@system.ets'; export default { data: { ets: null, }, onInit() { this.ets = new Ets({ baseUrl: '/common/', // ETS模板所在的目录 }); // 渲染页面 this.ets.render('index.ets', {}, (html) => { const container = createElement('div'); container.setInnerHTML(html); this.$refs.page.setContent(container); }); }, }; ``` 3. 在页面跳转函数中,使用鸿蒙提供的路由API来进行页面跳转: ``` <script type="text/javascript"> import router from '@system.router'; function jumpToPage() { router.push({ uri: '/pages/new-page/new-page', }); } </script> ``` 在上面的例子中,我们通过ETS模板引擎和鸿蒙提供的路由API,实现了一个简单的页面跳转功能。当用户点击页面上的按钮时,应用会跳转到名为"new-page"的页面。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值