鸿蒙应用ets之仿黑马智慧商城商品详情页
鸿蒙应用开发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%')
}
}
下期实现加入购物车和立即购买功能
更多推荐
所有评论(0)