
1、数据模型定义
(1)定义数据模型
//定义单个数据
export default class Goods{
// 商品Id
goodId:number
// 商品名称
goodName:string
// 商品图片
imageUrl:ResourceStr
// 商品价格
goodPrice:number
// 商品类型id
goodTypeId:number
constructor(goodId: number, goodName: string, imageUrl: ResourceStr, goodPrice: number, goodTypeId: number) {
this.goodId = goodId
this.goodName = goodName
this.imageUrl = imageUrl
this.goodPrice = goodPrice
this.goodTypeId = goodTypeId
}
}
// 定义商品类型数据
export class GoodsType{
// 商品类型id
goodTypeId:number
// 商品类型名称
goodTypeName:string
// 商品列表
goodList:Array<Goods>
constructor(goodTypeId: number, goodTypeName: string, goodList: Array<Goods>) {
this.goodTypeId = goodTypeId
this.goodTypeName = goodTypeName
this.goodList = goodList
}
}
// 定义两个数据的集合
export class LinkGoods{
// 商品类型id
goodTypeId:number
// 商品类型名称
goodTypeName:string
// 商品Id
goodId:number
// 商品名称
goodName:string
// 商品图片
imageUrl:ResourceStr
// 商品价格
goodPrice:number
constructor(goodTypeId: number, goodTypeName: string, goodId: number, goodName: string, imageUrl: ResourceStr,
goodPrice: number) {
this.goodTypeId = goodTypeId
this.goodTypeName = goodTypeName
this.goodId = goodId
this.goodName = goodName
this.imageUrl = imageUrl
this.goodPrice = goodPrice
}
}
(2)定义数据,生成一个包含所有商品分类及其对应商品列表的数组
import Goods, { GoodsType, LinkGoods } from "../model/Goods"
export class NavGoodsModel{
//设置方法,获取商品分类列表数据
getLinkData():Array<GoodsType>{
// 定义返回的商品分类列表数据
const goodsTypeData:GoodsType[]=[]
// 定义商品分类id
let goodsTypeId:number=0
LINK_DATA.forEach((item:LinkGoods)=>{
// 首先判断当前商品所在的分类是否存在
if (goodsTypeId!==item.goodTypeId) {
// 不存在的话,创建对应的商品分类
let goodsTypeItem:GoodsType=new GoodsType(item.goodTypeId,item.goodTypeName,[])
goodsTypeData.push(goodsTypeItem)
}
// 当前商品分类存在的话,添加具体商品信息模型
let goodsItem:Goods=new Goods(item.goodId,item.goodName,item.imageUrl,item.goodPrice,goodsTypeId)
goodsTypeData[goodsTypeData.length-1].goodList.push(goodsItem)
goodsTypeId=item.goodTypeId
})
return goodsTypeData
}
}
let navGoodsModel=new NavGoodsModel()
export default navGoodsModel as NavGoodsModel
// 创建所链接数据
const LINK_DATA: LinkGoods[] = [
new LinkGoods(1, '热门商品', 1, 'HUAWEI P60 Art', $r('app.media.ic_img_1'), 0),
new LinkGoods(1, '热门商品', 2, 'HUAWEI P60 Pro', $r('app.media.ic_img_2'), 5000),
new LinkGoods(1, '热门商品', 3, 'HUAWEI Mate 50 Pro 4G', $r('app.media.ic_img_3'), 5000),
new LinkGoods(1, '热门商品', 4, 'HUAWEI 畅享 60 X ', $r('app.media.ic_img_4'), 2220),
new LinkGoods(1, '热门商品', 5, 'Matebook E 2023', $r('app.media.ic_img_5'), 2560),
new LinkGoods(1, '热门商品', 6, 'Matebook D16 2023', $r('app.media.ic_img_6'), 0),
new LinkGoods(1, '热门商品', 7, 'Matebook E Go', $r('app.media.ic_img_7'), 0),
new LinkGoods(1, '热门商品', 8, 'HUAWEI MatePad Paper', $r('app.media.ic_img_8'), 4000),
new LinkGoods(1, '热门商品', 9, 'MatePad Pro 11', $r('app.media.ic_img_9'), 6500),
new LinkGoods(1, '热门商品', 10, '华为智慧屏 V98', $r('app.media.ic_img_10'),3000),
new LinkGoods(1, '热门商品', 11, '华为智慧屏 V85', $r('app.media.ic_img_11'), 5000),
new LinkGoods(2, '手机', 12, 'HUAWEI P60 Art', $r('app.media.ic_img_1'), 0),
new LinkGoods(2, '手机', 13, 'HUAWEI P60 Pro', $r('app.media.ic_img_2'), 5000),
new LinkGoods(2, '手机', 14, 'HUAWEI Mate 50 Pro 4G', $r('app.media.ic_img_3'), 5000),
new LinkGoods(2, '手机', 15, 'HUAWEI 畅享 60 X ', $r('app.media.ic_img_4'), 2220),
new LinkGoods(2, '手机', 16, 'HUAWEI 畅享 60 Pro', $r('app.media.ic_img_21'), 1599),
new LinkGoods(3, '穿戴', 18, 'HUAWEI WATCH 4 Pro', $r('app.media.ic_img_22'), 3699),
new LinkGoods(3, '穿戴', 19, 'HUAWEI WATCH 4', $r('app.media.ic_img_23'), 2699),
new LinkGoods(3, '穿戴', 20, 'HUAWEI 手环8', $r('app.media.ic_img_24'), 319),
new LinkGoods(3, '穿戴', 21, 'HUAWEI 通话手环B7', $r('app.media.ic_img_25'), 1199),
new LinkGoods(3, '穿戴', 22, 'HUAWEI GT 3 Pro 系列', $r('app.media.ic_img_26'), 2688),
new LinkGoods(4, '平板', 24, 'MatePad Air', $r('app.media.ic_img_27'), 2899),
new LinkGoods(4, '平板', 25, 'HUAWEI MatePad Paper', $r('app.media.ic_img_8'), 4000),
new LinkGoods(4, '平板', 26, 'MatePad Pro 11', $r('app.media.ic_img_9'), 6500),
new LinkGoods(4, '平板', 27, 'MatePad Pro 12.6', $r('app.media.ic_img_28'), 4199),
new LinkGoods(4, '平板', 28, 'MatePad SE 10.4 2023款', $r('app.media.ic_img_29'), 1499),
new LinkGoods(5, '笔记本', 29, 'Matebook E 2023', $r('app.media.ic_img_5'), 2560),
new LinkGoods(5, '笔记本', 30, 'Matebook D16 2023', $r('app.media.ic_img_6'), 0),
new LinkGoods(5, '笔记本', 31, 'Matebook E Go', $r('app.media.ic_img_7'), 0),
new LinkGoods(5, '笔记本', 32, 'Matebook X Pro 2023', $r('app.media.ic_img_30'), 11999),
new LinkGoods(5, '笔记本', 33, 'Matebook 13s 2023', $r('app.media.ic_img_31'), 7299),
new LinkGoods(6, '智慧屏', 36, '华为智慧屏 V98', $r('app.media.ic_img_10'),3000),
new LinkGoods(6, '智慧屏', 37, '华为智慧屏 V85', $r('app.media.ic_img_11'), 5000),
]
2、定义左侧商品类型列表

import { GoodsType, LinkGoods } from "../model/Goods"
import NavGoodsModel from "../viewmodel/NavGoodsModel"
@Entry
@Component
export struct Index {
// 定义左侧商品类型滑动条
private goodsTypeScroller:Scroller=new Scroller()
// 定义商品类型数据
@State dataArr: GoodsType[] = []
// 定义商品类型被选中的状态
@State isChecked: boolean = false
// 定义当前选中的index
@State currentIndex:number=0
aboutToAppear(): void {
this.dataArr = NavGoodsModel.getLinkData()
}
build() {
Row() {
List({scroller:this.goodsTypeScroller}) {
ForEach(this.dataArr, (item: GoodsType, index: number) => {
ListItem() {
this.typeGoodsItem(item.goodTypeName,index)
}
}, (item: GoodsType) => item.goodTypeId + '')
}.height('100%')
.width(100)
.backgroundColor('#efefef')
.alignListItem(ListItemAlign.Start)
.divider({strokeWidth:1})
}
.height('100%')
.width('100%')
}
// 定义商品类型,单个样式
@Builder
typeGoodsItem(goodTypeName: string,index:number) {
Row() {
Text(goodTypeName)
.fontSize(14)
.fontColor(this.currentIndex==index ? '#ff131212' : '#ff757579')
.textAlign(TextAlign.Center)
}.width(100)
.height(70)
.justifyContent(FlexAlign.Center)
.backgroundColor(this.currentIndex===index?'#fffaf7f7':'#efefef')
.onClick(() => {
this.currentIndex=index
})
}
}
3、定义右侧商品列表
(1)定义头部样式

// 定义商品分类展示所有数据的头部样式
@Builder
headerGoodType(goodTypeName: string) {
Row() {
Text(goodTypeName)
.fontSize(18)
.fontColor('#ff131212')
.textAlign(TextAlign.Center)
}
.height(70)
.width('100%')
.justifyContent(FlexAlign.Start)
.backgroundColor('#fffaf7f7')
}
(2)定义单个商品数据

// 定义单个商品数据
@Builder goodItem(good:Goods){
Row(){
Image(good.imageUrl)
.width(80)
.height(140)
.aspectRatio(1)
Column(){
Text(good.goodName)
.fontSize(18)
.fontWeight(500)
.maxLines(2)
.textOverflow({overflow:TextOverflow.Ellipsis})
Text(good.goodPrice.toString())
.fontColor(Color.Red)
}.height('100%')
.layoutWeight(1)
.padding({top:20,bottom:20,left:20})
.justifyContent(FlexAlign.SpaceBetween)
.alignItems(HorizontalAlign.Start)
}.width('100%')
.height(120)
.backgroundColor(Color.White)
}
(3)定义右侧滑动效果

List() {
ForEach(this.dataArr, (goodsType: GoodsType, index: number) => {
ListItemGroup({ header: this.headerGoodType(goodsType.goodTypeName) }) {
ForEach(goodsType.goodList, (goods: Goods, index: number) => {
ListItem() {
this.goodItem(goods)
}
})
}.divider({ strokeWidth: 1 })
}, (item: GoodsType) => item.goodTypeName)
}
.layoutWeight(1)
.height('100%')
.backgroundColor('#efefef')
.alignListItem(ListItemAlign.Start)
.sticky(StickyStyle.Header)
(4)滑动联动效果
①点击左侧商品类型,右侧跳转到对应的商品


②滑动右侧商品,左侧商品类型也滑动到对应位置


4、Index页面完整代码
import Goods, { GoodsType, LinkGoods } from "../model/Goods"
import NavGoodsModel from "../viewmodel/NavGoodsModel"
@Entry
@Component
export struct Index {
// 定义左侧商品类型滑动条
private goodsTypeScroller: Scroller = new Scroller()
// 定义右侧商品滑动条
private goodsScroller: Scroller = new Scroller()
// 定义商品类型数据
@State dataArr: GoodsType[] = []
// 定义商品类型被选中的状态
@State isChecked: boolean = false
// 定义当前选中的index
@State currentIndex: number = 0
aboutToAppear(): void {
this.dataArr = NavGoodsModel.getLinkData()
}
build() {
Row() {
List({ scroller: this.goodsTypeScroller }) {
ForEach(this.dataArr, (item: GoodsType, index: number) => {
ListItem() {
this.typeGoodsItem(item.goodTypeName, index)
}
}, (item: GoodsType) => item.goodTypeName)
}
.height('100%')
.width(100)
.backgroundColor('#efefef')
.alignListItem(ListItemAlign.Start)
.divider({ strokeWidth: 1 })
List({ scroller: this.goodsScroller }) {
ForEach(this.dataArr, (goodsType: GoodsType, index: number) => {
ListItemGroup({ header: this.headerGoodType(goodsType.goodTypeName) }) {
ForEach(goodsType.goodList, (goods: Goods, index: number) => {
ListItem() {
this.goodItem(goods)
}
})
}.divider({ strokeWidth: 1 })
}, (item: GoodsType) => item.goodTypeName)
}
.layoutWeight(1)
.height('100%')
.backgroundColor('#efefef')
.alignListItem(ListItemAlign.Start)
.sticky(StickyStyle.Header)
.onScrollIndex((start: number, end: number) => {
if (this.currentIndex !== start) {
this.currentIndex=start
this.goodsTypeScroller.scrollToIndex(this.currentIndex)
}
})
}
.height('100%')
.width('100%')
}
// 定义商品类型,单个样式
@Builder
typeGoodsItem(goodTypeName: string, index: number) {
Row() {
Text(goodTypeName)
.fontSize(14)
.fontColor(this.currentIndex == index ? '#ff131212' : '#ff757579')
.textAlign(TextAlign.Center)
}
.width(100)
.height(70)
.justifyContent(FlexAlign.Center)
.backgroundColor(this.currentIndex === index ? '#fffaf7f7' : '#efefef')
.onClick(() => {
this.currentIndex = index
this.goodsScroller.scrollToIndex(index)
})
}
// 定义商品分类展示所有数据的头部样式
@Builder
headerGoodType(goodTypeName: string) {
Row() {
Text(goodTypeName)
.fontSize(18)
.fontColor('#ff131212')
.textAlign(TextAlign.Center)
}
.height(70)
.width('100%')
.justifyContent(FlexAlign.Start)
.backgroundColor('#fffaf7f7')
}
// 定义单个商品数据
@Builder
goodItem(good: Goods) {
Row() {
Image(good.imageUrl)
.width(80)
.height(140)
.aspectRatio(1)
Column() {
Text(good.goodName)
.fontSize(18)
.fontWeight(500)
.maxLines(2)
.textOverflow({ overflow: TextOverflow.Ellipsis })
Text(good.goodPrice.toString())
.fontColor(Color.Red)
}
.height('100%')
.layoutWeight(1)
.padding({ top: 20, bottom: 20, left: 20 })
.justifyContent(FlexAlign.SpaceBetween)
.alignItems(HorizontalAlign.Start)
}.width('100%')
.height(120)
.backgroundColor(Color.White)
}
}