效果预览
完整范例代码
页面 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><viewclass="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"><navigatorv-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
}