效果如图:
上代码:
<script setup lang="ts">
import { onShow } from '@dcloudio/uni-app'
import { nextTick, getCurrentInstance } from 'vue'
import ClassifySkeleton from './ClassifySkeleton'
import { isH5 } from '@/utils/is'
const instance = getCurrentInstance()
const list = ref([])
/** 每个锚点与到顶部距离 */
const listItemTops = ref([])
/** 左侧选中id */
const leftMenuId = ref(0)
/** 是否是滑动右侧 */
const isRightScroll = ref(true)
/** 锚点 */
const scrollInto = ref('')
const loading = ref(false)
const commonConfig = useCommonConfig()
onMounted(async () => {
await getGoodsCategory()
await nextTick(() => {
getElementTop()
})
})
onShow(() => {
setTabBarBadge()
})
/**
* 获取商品分类
*/
const getGoodsCategory = async () => {
try {
loading.value = true
const data = await commonConfig.getCategory()
list.value = data
leftMenuId.value = data?.[0]?.id
} finally {
loading.value = false
}
}
/**
* 获取距离顶部的高度
*
* @param selector
*/
const getScrollTop = (selector) => {
return new Promise((resolve) => {
const query = uni.createSelectorQuery().in(instance)
query
.select(selector)
.boundingClientRect((data) => {
resolve(data.top)
})
.exec()
})
}
/**
* 获取元素顶部距离
*/
const getElementTop = async () => {
const eTops = toRaw(list.value).map(async ({ id }) => {
const top = await getScrollTop(`#leftMenu-${id}`)
return top - 80
})
Promise.all(eTops).then((data) => {
listItemTops.value = data
})
}
/**
* 右侧滚动监听
*
* @param e
*/
const onRightScroll = (e) => {
if (!isRightScroll.value) return
const top = e.detail.scrollTop
const index = listItemTops.value.findIndex((item, index) => listItemTops.value[index + 1] >= top)
leftMenuId.value = list.value[Math.max(0, index)].id
}
/**
* 点击左侧菜单
*
* @param id 左侧菜单id
*/
const handleClickLeftMenu = (id) => {
isRightScroll.value = false
leftMenuId.value = id
scrollInto.value = `leftMenu-${id}`
}
/**
* 页面跳转 产品中心
*/
const toPage = (item, data) => {
item = toRaw(item)
data = toRaw(data)
nav.navigateTo({
url: 'ProductCenter',
data: {
id: data.id,
name: data.name,
pId: item.id,
pName: item.name,
},
})
}
/**
* scroll-view样式
*/
const scrollViewStyle = {
height: isH5 ? 'calc(100vh - 94px)' : '100vh',
}
</script>
<template>
<ClassifySkeleton v-if="loading" :skeletonStyle="scrollViewStyle" />
<view v-else class="flex">
<!-- 左侧分类列表 -->
<scroll-view
class="w-24 bg-[#f8f8f8]"
:style="scrollViewStyle"
scroll-y
:scroll-with-animation="true"
>
<view
v-for="item in list"
:key="item.id"
:data-index="item.id"
:class="`px-2 py-4 text-sm ${item.id == leftMenuId ? 'font-bold bg-white' : ''}`"
@click="handleClickLeftMenu(item.id)"
>
{{ item.name }}
</view>
</scroll-view>
<!-- 右侧商品列表 -->
<scroll-view
class="flex-1 px-2"
:style="scrollViewStyle"
scroll-y
:scroll-into-view="scrollInto"
:scroll-with-animation="true"
@touchstart="isRightScroll = true"
@scroll="onRightScroll"
enhanced
:show-scrollbar="false"
>
<view class="mb-10 py-4" :id="`leftMenu-${item.id}`" v-for="item in list" :key="item.id">
<view class="text-md mb-2 font-bold">{{ item.name }}</view>
<view class="pt-5 grid grid-cols-3">
<view
class="text-center pb-6"
v-for="i in item.children"
:key="i.id"
@click="() => toPage(item, i)"
>
<image class="w-8 h-8" :src="i.appImage" />
<view class="text-xs">{{ i.name }}</view>
</view>
</view>
</view>
</scroll-view>
</view>
</template>