feat: 商品分类页

This commit is contained in:
jqtmviyu 2025-04-24 18:18:33 +08:00
parent 4271a7a5dd
commit d1edf3a8fa
3 changed files with 690 additions and 3 deletions

View File

@ -1,7 +1,264 @@
<template>
<view class="">test</view>
<PageSkeleton v-if="isLoading" />
<view class="viewport" v-else>
<!-- 搜索框 -->
<view class="search">
<view class="input">
<text class="icon-search">女靴</text>
</view>
</view>
<!-- 分类 -->
<view class="categories">
<!-- 左侧一级分类 -->
<scroll-view class="primary" scroll-y>
<view
v-for="(item, index) in categoryList"
:key="item.id"
class="item"
:class="{ active: index === activeIndex }"
@tap="activeIndex = index"
>
<text class="name"> {{ item.name }} </text>
</view>
</scroll-view>
<!-- 右侧二级分类 -->
<scroll-view class="secondary" scroll-y :scroll-top="scrollTop" @scroll="handlerScrool">
<!-- 焦点图 -->
<JbcSwiper class="banner" :list="bannerList" />
<!-- 内容区域 -->
<view class="panel" v-for="item in childCategoryList" :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>
<script setup lang="ts"></script>
<script setup lang="ts">
import JbcSwiper from '@/components/JbcSwiper.vue'
import { getHomeBannerAPI } from '@/services/home'
import type { BannerItem } from '@/types/home'
import { onLoad } from '@dcloudio/uni-app'
import { computed, ref, watch, nextTick } from 'vue'
import { getCategoryTopAPI } from '@/services/category'
import type { CategoryTopItem } from '@/types/category'
import PageSkeleton from './components/PageSkeleton.vue'
<style lang="scss"></style>
//
const bannerList = ref<BannerItem[]>([])
const getBannerList = async () => {
const res = await getHomeBannerAPI(2)
bannerList.value = res.result
}
//
//
const categoryList = ref<CategoryTopItem[]>([])
const activeIndex = ref(0)
const getCategoryAPIList = async () => {
const res = await getCategoryTopAPI()
categoryList.value = res.result
}
//
const childCategoryList = computed(() => {
return categoryList.value[activeIndex.value]?.children || []
})
//
const scrollTop = ref(0)
// ,
// ,scrollTop,,scrollTop0
const scroolTopOld = ref(0)
type ScrollEvent = {
detail: {
scrollTop: number
scrollLeft: number
scrollHeight: number
scrollWidth: number
deltaX: number
deltaY: number
}
[key: string]: any
}
const handlerScrool = (e: ScrollEvent) => {
scroolTopOld.value = e.detail.scrollTop
}
watch(activeIndex, () => {
scrollTop.value = scroolTopOld.value
nextTick(() => {
scrollTop.value = 0
})
})
//
const isLoading = ref(true)
//
onLoad(async () => {
await Promise.all([getBannerList(), getCategoryAPIList()])
isLoading.value = false
})
</script>
<style lang="scss">
page {
height: 100%;
overflow: hidden;
}
.viewport {
height: 100%;
display: flex;
flex-direction: column;
}
.search {
padding: 0 30rpx 20rpx;
background-color: #fff;
.input {
display: flex;
align-items: center;
justify-content: space-between;
height: 64rpx;
padding-left: 26rpx;
color: #8b8b8b;
font-size: 28rpx;
border-radius: 32rpx;
background-color: #f3f4f4;
}
}
.icon-search {
&::before {
margin-right: 10rpx;
}
}
/* 分类 */
.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>

View File

@ -0,0 +1,417 @@
<template name="skeleton">
<view class="sk-container">
<view class="viewport viewport">
<view class="search search">
<view class="input input">
<text class="icon-search sk-transparent sk-text-14-2857-658 sk-text">女靴</text>
</view>
</view>
<view class="categories categories">
<scroll-view :scroll-y="true" class="primary primary">
<view class="item active sk-pseudo sk-pseudo-circle">
<text class="name sk-transparent sk-text-14-2857-317 sk-text">居家</text>
</view>
<view class="item sk-pseudo sk-pseudo-circle">
<text class="name sk-transparent sk-text-14-2857-470 sk-text">美食</text>
</view>
<view class="item sk-pseudo sk-pseudo-circle">
<text class="name sk-transparent sk-text-14-2857-656 sk-text">服饰</text>
</view>
<view class="item sk-pseudo sk-pseudo-circle">
<text class="name sk-transparent sk-text-14-2857-571 sk-text">母婴</text>
</view>
<view class="item sk-pseudo sk-pseudo-circle">
<text class="name sk-transparent sk-text-14-2857-54 sk-text">个护</text>
</view>
<view class="item sk-pseudo sk-pseudo-circle">
<text class="name sk-transparent sk-text-14-2857-314 sk-text">严选</text>
</view>
<view class="item sk-pseudo sk-pseudo-circle">
<text class="name sk-transparent sk-text-14-2857-77 sk-text">数码</text>
</view>
<view class="item sk-pseudo sk-pseudo-circle">
<text class="name sk-transparent sk-text-14-2857-585 sk-text">运动</text>
</view>
<view class="item sk-pseudo sk-pseudo-circle">
<text class="name sk-transparent sk-text-14-2857-96 sk-text">杂项</text>
</view>
</scroll-view>
<scroll-view :scroll-y="true" class="secondary secondary" scroll-top="0">
<view is="components/JbcSwiper" class="banner banner">
<view class="carousel JbcSwiper--carousel">
<swiper :circular="true" :current="0" :interval="3000" :autoplay="false">
<swiper-item
style="
position: absolute;
width: 100%;
height: 100%;
transform: translate(0%, 0px) translateZ(0px);
"
>
<navigator class="navigator JbcSwiper--navigator" hover-class="none">
<image class="image JbcSwiper--image sk-image" mode="aspectFill"></image>
</navigator>
</swiper-item>
<swiper-item
style="
position: absolute;
width: 100%;
height: 100%;
transform: translate(100%, 0px) translateZ(0px);
"
>
<navigator class="navigator JbcSwiper--navigator" hover-class="none">
<image class="image JbcSwiper--image sk-image" mode="aspectFill"></image>
</navigator>
</swiper-item>
</swiper>
<view class="indicator JbcSwiper--indicator">
<text class="dot JbcSwiper--dot active JbcSwiper--active"></text>
<text class="dot JbcSwiper--dot"></text>
<text class="dot JbcSwiper--dot"></text>
<text class="dot JbcSwiper--dot"></text>
<text class="dot JbcSwiper--dot"></text>
</view>
</view>
</view>
<view class="panel panel">
<view class="title title">
<text class="name sk-transparent sk-text-27-2727-635 sk-text">居家生活用品</text>
</view>
<view class="section section">
<navigator class="goods goods" hover-class="none">
<image class="image sk-image"></image>
<view class="name ellipsis sk-transparent sk-text-14-2857-313 sk-text"
>钻石陶瓷涂层多用锅18cm 小奶锅</view
>
<view class="price price">
<text class="symbol sk-transparent sk-opacity">¥</text>
<text class="number sk-transparent sk-text-14-2857-254 sk-text">149.00</text>
</view>
</navigator>
<navigator class="goods goods" hover-class="none">
<image class="image sk-image"></image>
<view class="name ellipsis sk-transparent sk-text-14-2857-51 sk-text"
>极光限定 珠光蓝珐琅锅</view
>
<view class="price price">
<text class="symbol sk-transparent sk-opacity">¥</text>
<text class="number sk-transparent sk-text-14-2857-645 sk-text">199.00</text>
</view>
</navigator>
</view>
</view>
<view class="panel panel">
<view class="title title">
<text class="name sk-transparent sk-text-27-2727-249 sk-text">收纳</text>
</view>
<view class="section section">
<navigator class="goods goods" hover-class="none">
<image class="image sk-image"></image>
<view class="name ellipsis sk-transparent sk-text-14-2857-70 sk-text"
>给衣柜减减肥真空防潮压缩袋</view
>
<view class="price price">
<text class="symbol sk-transparent sk-opacity">¥</text>
<text class="number sk-transparent sk-text-14-2857-442 sk-text">79.00</text>
</view>
</navigator>
<navigator class="goods goods" hover-class="none">
<image class="image sk-image"></image>
<view class="name ellipsis sk-transparent sk-text-14-2857-343 sk-text"
>爆款明星好物抽屉式透明储物柜</view
>
<view class="price price">
<text class="symbol sk-transparent sk-opacity">¥</text>
<text class="number sk-transparent sk-text-14-2857-258 sk-text">129.00</text>
</view>
</navigator>
<navigator class="goods goods" hover-class="none">
<image class="image sk-image"></image>
<view class="name ellipsis sk-transparent sk-text-14-2857-321 sk-text"
>衣柜省空间神器棉麻涤·收纳挂袋</view
>
<view class="price price">
<text class="symbol sk-transparent sk-opacity">¥</text>
<text class="number sk-transparent sk-text-14-2857-693 sk-text">55.00</text>
</view>
</navigator>
<navigator class="goods goods" hover-class="none">
<image class="image sk-image"></image>
</navigator>
<navigator class="goods goods" hover-class="none">
<image class="image sk-image"></image>
</navigator>
<navigator class="goods goods" hover-class="none">
<image class="image sk-image"></image>
</navigator>
</view>
</view>
</scroll-view>
</view>
</view>
</view>
</template>
<style>
.sk-transparent {
color: transparent !important;
}
.sk-text-14-2857-658 {
background-image: linear-gradient(
transparent 14.2857%,
#eeeeee 0%,
#eeeeee 85.7143%,
transparent 0%
) !important;
background-size: 100% 38.0435rpx;
position: relative !important;
}
.sk-text {
background-origin: content-box !important;
background-clip: content-box !important;
background-color: transparent !important;
color: transparent !important;
background-repeat: repeat-y !important;
}
.sk-text-14-2857-317 {
background-image: linear-gradient(
transparent 14.2857%,
#eeeeee 0%,
#eeeeee 85.7143%,
transparent 0%
) !important;
background-size: 100% 35.5072rpx;
position: relative !important;
}
.sk-text-14-2857-470 {
background-image: linear-gradient(
transparent 14.2857%,
#eeeeee 0%,
#eeeeee 85.7143%,
transparent 0%
) !important;
background-size: 100% 35.5072rpx;
position: relative !important;
}
.sk-text-14-2857-656 {
background-image: linear-gradient(
transparent 14.2857%,
#eeeeee 0%,
#eeeeee 85.7143%,
transparent 0%
) !important;
background-size: 100% 35.5072rpx;
position: relative !important;
}
.sk-text-14-2857-571 {
background-image: linear-gradient(
transparent 14.2857%,
#eeeeee 0%,
#eeeeee 85.7143%,
transparent 0%
) !important;
background-size: 100% 35.5072rpx;
position: relative !important;
}
.sk-text-14-2857-54 {
background-image: linear-gradient(
transparent 14.2857%,
#eeeeee 0%,
#eeeeee 85.7143%,
transparent 0%
) !important;
background-size: 100% 35.5072rpx;
position: relative !important;
}
.sk-text-14-2857-314 {
background-image: linear-gradient(
transparent 14.2857%,
#eeeeee 0%,
#eeeeee 85.7143%,
transparent 0%
) !important;
background-size: 100% 35.5072rpx;
position: relative !important;
}
.sk-text-14-2857-77 {
background-image: linear-gradient(
transparent 14.2857%,
#eeeeee 0%,
#eeeeee 85.7143%,
transparent 0%
) !important;
background-size: 100% 35.5072rpx;
position: relative !important;
}
.sk-text-14-2857-585 {
background-image: linear-gradient(
transparent 14.2857%,
#eeeeee 0%,
#eeeeee 85.7143%,
transparent 0%
) !important;
background-size: 100% 35.5072rpx;
position: relative !important;
}
.sk-text-14-2857-96 {
background-image: linear-gradient(
transparent 14.2857%,
#eeeeee 0%,
#eeeeee 85.7143%,
transparent 0%
) !important;
background-size: 100% 35.5072rpx;
position: relative !important;
}
.sk-text-27-2727-635 {
background-image: linear-gradient(
transparent 27.2727%,
#eeeeee 0%,
#eeeeee 72.7273%,
transparent 0%
) !important;
background-size: 100% 59.7826rpx;
position: relative !important;
}
.sk-text-14-2857-313 {
background-image: linear-gradient(
transparent 14.2857%,
#eeeeee 0%,
#eeeeee 85.7143%,
transparent 0%
) !important;
background-size: 100% 30.4348rpx;
position: relative !important;
}
.sk-opacity {
opacity: 0 !important;
}
.sk-text-14-2857-254 {
background-image: linear-gradient(
transparent 14.2857%,
#eeeeee 0%,
#eeeeee 85.7143%,
transparent 0%
) !important;
background-size: 100% 32.971rpx;
position: relative !important;
}
.sk-text-14-2857-51 {
background-image: linear-gradient(
transparent 14.2857%,
#eeeeee 0%,
#eeeeee 85.7143%,
transparent 0%
) !important;
background-size: 100% 30.4348rpx;
position: relative !important;
}
.sk-text-14-2857-645 {
background-image: linear-gradient(
transparent 14.2857%,
#eeeeee 0%,
#eeeeee 85.7143%,
transparent 0%
) !important;
background-size: 100% 32.971rpx;
position: relative !important;
}
.sk-text-27-2727-249 {
background-image: linear-gradient(
transparent 27.2727%,
#eeeeee 0%,
#eeeeee 72.7273%,
transparent 0%
) !important;
background-size: 100% 59.7826rpx;
position: relative !important;
}
.sk-text-14-2857-70 {
background-image: linear-gradient(
transparent 14.2857%,
#eeeeee 0%,
#eeeeee 85.7143%,
transparent 0%
) !important;
background-size: 100% 30.4348rpx;
position: relative !important;
}
.sk-text-14-2857-442 {
background-image: linear-gradient(
transparent 14.2857%,
#eeeeee 0%,
#eeeeee 85.7143%,
transparent 0%
) !important;
background-size: 100% 32.971rpx;
position: relative !important;
}
.sk-text-14-2857-343 {
background-image: linear-gradient(
transparent 14.2857%,
#eeeeee 0%,
#eeeeee 85.7143%,
transparent 0%
) !important;
background-size: 100% 30.4348rpx;
position: relative !important;
}
.sk-text-14-2857-258 {
background-image: linear-gradient(
transparent 14.2857%,
#eeeeee 0%,
#eeeeee 85.7143%,
transparent 0%
) !important;
background-size: 100% 32.971rpx;
position: relative !important;
}
.sk-text-14-2857-321 {
background-image: linear-gradient(
transparent 14.2857%,
#eeeeee 0%,
#eeeeee 85.7143%,
transparent 0%
) !important;
background-size: 100% 30.4348rpx;
position: relative !important;
}
.sk-text-14-2857-693 {
background-image: linear-gradient(
transparent 14.2857%,
#eeeeee 0%,
#eeeeee 85.7143%,
transparent 0%
) !important;
background-size: 100% 32.971rpx;
position: relative !important;
}
.sk-image {
background: #efefef !important;
}
.sk-pseudo::before,
.sk-pseudo::after {
background: #efefef !important;
background-image: none !important;
color: transparent !important;
border-color: transparent !important;
}
.sk-pseudo-rect::before,
.sk-pseudo-rect::after {
border-radius: 0 !important;
}
.sk-pseudo-circle::before,
.sk-pseudo-circle::after {
border-radius: 50% !important;
}
.sk-container {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: hidden;
background-color: transparent;
}
</style>

13
src/services/category.ts Normal file
View File

@ -0,0 +1,13 @@
import type { CategoryTopItem } from '@/types/category'
import { http } from '@/utils/http'
/**
*
* @returns
*/
export const getCategoryTopAPI = () => {
return http<CategoryTopItem[]>({
url: '/category/top',
method: 'GET',
})
}