uview-plus中二级菜单左右联动更改为uni-app+vue3+vite写法
uview-plus3.0重磅发布,全面的Vue3移动组件库
该插件使用的vue2写法,但支持vue3引用,在此基础上修改为uni-app+vue3+vite;
<template> <view class="u-wrap mainClass"> <!-- <back-header :title="pageTitle" :back-text="backText"></back-header> --> <view class="u-search-box"> <view class="u-search-inner"> <u-icon name="search" color="#909399" :size="28"></u-icon> <text class="u-search-text">搜索</text> </view> </view> <view class="u-menu-wrap"> <scroll-view scroll-y scroll-with-animation class="u-tab-view menu-scroll-view" :scroll-top="scrollTop" :scroll-into-view="itemId"> <view v-for="(item,index) in tabbar" :key="index" class="u-tab-item" :class="[current == index ? 'u-tab-item-active' : '']" @tap.stop="swichMenu(index)"> <text class="u-line-1">{{item.name}}</text> </view> </scroll-view> <scroll-view :scroll-top="scrollRightTop" scroll-y scroll-with-animation class="right-box" @scroll="rightScroll"> <view class="page-view"> <view class="class-item" :id="'item' + index" v-for="(item , index) in tabbar" :key="index"> <view class="item-title"> <text>{{item.name}}</text> </view> <view class="item-container"> <view class="thumb-box" v-for="(item1, index1) in item.foods" :key="index1"> <image class="item-menu-image" :src="item1.icon" mode=""></image> <view class="item-menu-name">{{item1.name}}</view> </view> </view> </view> </view> </scroll-view> </view> </view> </template> <script lang="ts" setup> // import BackHeader from '../../../components/publicNavbar.vue'; import { defineComponent, ref, onMounted, onBeforeUnmount, getCurrentInstance } from 'vue'; import classifyData from '@/common/classify.data.js'; const scrollTop = ref(0); const oldScrollTop = ref(0); const current = ref(0); const menuHeight = ref(0); const menuItemHeight = ref(0); const itemId = ref(''); const tabbar = classifyData; const menuItemPos = ref([]); const arr = ref([]); const scrollRightTop = ref(0); let timer : NodeJS.Timeout | null = null; const pageTitle = ref('商品') const backText = ref('') const instance = getCurrentInstance(); onMounted(() => { getMenuItemTop(); }); const swichMenu = async (index : number) => { if (arr.value.length === 0) { await getMenuItemTop(); } if (index === current.value) return; scrollRightTop.value = oldScrollTop.value; // 异步更新后使用 nextTick 确保能够正确获取最新的 DOM await new Promise<void>((resolve) => { setTimeout(() => { scrollRightTop.value = arr.value[index]; current.value = index; leftMenuStatus(index); resolve(); }, 0); }); }; const getElRect = (elClass : string, dataVal : string) => { const query = uni.createSelectorQuery().in(instance); query.select('.' + elClass).boundingClientRect((res) => { if (!res) { setTimeout(() => { getElRect(elClass, dataVal); }, 10); return; } instance.ctx[dataVal] = res.height; }).exec(); }; const observer = () => { tabbar.forEach((val, index) => { let observer = uni.createIntersectionObserver(this); observer.relativeTo('.right-box', { top: 0 }).observe('#item' + index, (res) => { if (res.intersectionRatio > 0) { let id = Number(res.id.substring(4)); leftMenuStatus(id); } }); }); }; const leftMenuStatus = (index : number) => { current.value = index; if (menuHeight.value == 0 || menuItemHeight.value == 0) { getElRect('menu-scroll-view', 'menuHeight'); getElRect('u-tab-item', 'menuItemHeight'); } scrollTop.value = index * menuItemHeight.value + menuItemHeight.value / 2 - menuHeight.value / 2; }; const getMenuItemTop = () => { let selectorQuery = uni.createSelectorQuery(); selectorQuery.selectAll('.class-item').boundingClientRect((rects) => { if (!rects.length) { setTimeout(() => { getMenuItemTop(); }, 10); return; } rects.forEach((rect, index) => { arr.value.push(rect.top - rects[0].top); }); }).exec(); }; const rightScroll = async (e : any) => { oldScrollTop.value = e.detail.scrollTop; if (arr.value.length === 0) { await getMenuItemTop(); } if (timer) return; if (!menuHeight.value) { getElRect('menu-scroll-view', 'menuHeight'); } setTimeout(() => { timer = null; let scrollHeight = e.detail.scrollTop + menuHeight.value / 2; for (let i = 0; i < arr.value.length; i++) { let height1 = arr.value[i]; let height2 = arr.value[i + 1]; if (!height2 || (scrollHeight >= height1 && scrollHeight < height2)) { leftMenuStatus(i); return; } } }, 10); }; onBeforeUnmount(() => { if (timer) clearTimeout(timer); }); </script> <style lang="scss" scoped> .u-wrap { height: calc(100vh); /* #ifdef H5 */ height: calc(100vh - var(--window-top)); /* #endif */ display: flex; flex-direction: column; } .u-search-box { padding: 18rpx 30rpx; } .u-menu-wrap { flex: 1; display: flex; overflow: hidden; } .u-search-inner { background-color: rgb(234, 234, 234); border-radius: 100rpx; display: flex; align-items: center; padding: 10rpx 16rpx; } .u-search-text { font-size: 26rpx; color: $u-tips-color; margin-left: 10rpx; } .u-tab-view { width: 200rpx; height: 100%; } .u-tab-item { height: 110rpx; background: #f6f6f6; box-sizing: border-box; display: flex; align-items: center; justify-content: center; font-size: 26rpx; color: #444; font-weight: 400; line-height: 1; } .u-tab-item-active { position: relative; color: #000; font-size: 30rpx; font-weight: 600; background: #fff; } .u-tab-item-active::before { content: ""; position: absolute; border-left: 4px solid $u-primary; height: 32rpx; left: 0; top: 39rpx; } .u-tab-view { height: 100%; } .right-box { background-color: rgb(250, 250, 250); } .page-view { padding: 16rpx; } .class-item { margin-bottom: 30rpx; background-color: #fff; padding: 16rpx; border-radius: 8rpx; } .class-item:last-child { min-height: 100vh; } .item-title { font-size: 26rpx; color: $u-main-color; font-weight: bold; } .item-menu-name { font-weight: normal; font-size: 24rpx; color: $u-main-color; } .item-container { display: flex; flex-wrap: wrap; } .thumb-box { width: 33.333333%; display: flex; align-items: center; justify-content: center; flex-direction: column; margin-top: 20rpx; } .item-menu-image { width: 120rpx; height: 120rpx; } </style>