效果预览
完整范例代码
页面 src\pages\category\category.vue
<script setup lang="ts"> import { getCategoryTopAPI } from '@/apis/category' import type { CategoryTopItem } from '@/types/category' import { onLoad } from '@dcloudio/uni-app' import { computed, ref } from 'vue' // 获取分类列表数据 const categoryList = ref<CategoryTopItem[]>([]) const getCategoryTopData = async () => { const res = await getCategoryTopAPI() categoryList.value = res.result } // 提取当前二级分类数据 const subCategoryList = computed(() => { return categoryList.value[activeIndex.value]?.children || [] }) // 高亮下标 const activeIndex = ref(0) onLoad(() => { getCategoryTopData() }) </script> <template> <view class="viewport"> <!-- 分类 --> <view class="categories"> <!-- 左侧:一级分类 --> <scroll-view class="primary" scroll-y> <view class="item" v-for="(item, index) in categoryList" :key="item.id" :class="{ active: index === activeIndex }" @tap="activeIndex = index" > <text class="name"> {{ item.name }} </text> </view> </scroll-view> <!-- 右侧:二级分类 --> <scroll-view class="secondary" scroll-y> <!-- 内容区域 --> <view class="panel" v-for="item in subCategoryList" :key="item.id"> <view class="title"> <text class="name">{{ item.name }}</text> <navigator class="more" hover-class="none">全部</navigator> </view> <view class="section"> <navigator v-for="goods in item.goods" :key="goods.id" class="goods" hover-class="none" :url="`/pages/goods/goods?id=${goods.id}`" > <image class="image" :src="goods.picture"></image> <view class="name ellipsis">{{ goods.name }}</view> <view class="price"> <text class="symbol">¥</text> <text class="number">{{ goods.price }}</text> </view> </navigator> </view> </view> </scroll-view> </view> </view> </template> <style lang="scss"> page { height: 100%; overflow: hidden; } .viewport { height: 100%; display: flex; flex-direction: column; } /* 分类 */ .categories { flex: 1; min-height: 400rpx; display: flex; } /* 一级分类 */ .primary { overflow: hidden; width: 180rpx; flex: none; background-color: #f6f6f6; .item { display: flex; justify-content: center; align-items: center; height: 96rpx; font-size: 26rpx; color: #595c63; position: relative; &::after { content: ''; position: absolute; left: 42rpx; bottom: 0; width: 96rpx; border-top: 1rpx solid #e3e4e7; } } .active { background-color: #fff; &::before { content: ''; position: absolute; left: 0; top: 0; width: 8rpx; height: 100%; background-color: #27ba9b; } } } .primary .item:last-child::after, .primary .active::after { display: none; } /* 二级分类 */ .secondary { background-color: #fff; .carousel { height: 200rpx; margin: 0 30rpx 20rpx; border-radius: 4rpx; overflow: hidden; } .panel { margin: 0 30rpx 0rpx; } .title { height: 60rpx; line-height: 60rpx; color: #333; font-size: 28rpx; border-bottom: 1rpx solid #f7f7f8; .more { float: right; padding-left: 20rpx; font-size: 24rpx; color: #999; } } .more { &::after { font-family: 'erabbit' !important; content: '\e6c2'; } } .section { width: 100%; display: flex; flex-wrap: wrap; padding: 20rpx 0; .goods { width: 150rpx; margin: 0rpx 30rpx 20rpx 0; &:nth-child(3n) { margin-right: 0; } image { width: 150rpx; height: 150rpx; } .name { padding: 5rpx; font-size: 22rpx; color: #333; } .price { padding: 5rpx; font-size: 18rpx; color: #cf4444; } .number { font-size: 24rpx; margin-left: 2rpx; } } } } </style>
接口 src\apis\category.ts
import type { CategoryTopItem } from '@/types/category' import { http } from '@/utils/http' /** * 分类列表 */ export const getCategoryTopAPI = () => { return http<CategoryTopItem[]>({ method: 'GET', url: '/category/top', }) }
类型声明 src\types\category.d.ts
import type { GoodsItem } from './global' /** 一级分类项 */ export type CategoryTopItem = { /** 二级分类集合[ 二级分类项 ] */ children: CategoryChildItem[] /** 一级分类id */ id: string /** 一级分类图片集[ 一级分类图片项 ] */ imageBanners: string[] /** 一级分类名称 */ name: string /** 一级分类图片 */ picture: string } /** 二级分类项 */ export type CategoryChildItem = { /** 商品集合[ 商品项 ] */ goods: GoodsItem[] /** 二级分类id */ id: string /** 二级分类名称 */ name: string /** 二级分类图片 */ picture: string }
src\types\global.d.ts
/** 通用商品类型 */ export type GoodsItem = { /** 商品描述 */ desc: string /** 商品折扣 */ discount: number /** id */ id: string /** 商品名称 */ name: string /** 商品已下单数量 */ orderNum: number /** 商品图片 */ picture: string /** 商品价格 */ price: number }