uniapp+vue3+ts --微信小程序tab导航可以上下滚动选中选项组件代码

uniapp+vue3+ts --微信小程序tab导航可以上下滚动选中选项组件代码

废话不多说,直接上代码。

组件代码:

javascript 复制代码
<template>
    <view class="scroll-tabs-container">
        <view class="radiusz bg-white pt-[10rpx] z-[999]" :class="{ 'scroll-tabs-sticky': sticky }">
            <u-tabs
                id="tabScrollTop"
                ref="tabScrollTop"
                :list="tabs"
                :current="active"
                @change="handleChangeTab"
                :active-color="mainColor"
                bg-color="transparent"
                :bar-width="90"
                font-size="24"
                :gutter="26"
                name="title"
            >
            </u-tabs>
        </view>
        <view class="content">
            <view
                class="px-[20rpx] pt-[20rpx] pb-[40rpx] w-full bg-white mb-[30rpx] box-border"
                v-for="(item, index) in tabs"
                :key="item.id"
                :class="'tabs' + index"
            >
                <view v-if="item.is_show == 1">
                    <view class="text-center"
                        ><text class="pr-[10rpx]">---------</text>{{ item.title
                        }}<text class="pl-[10rpx]">---------</text></view
                    >
                    <view class="mt-[40rpx]">
                        <u-parse :html="item.description"></u-parse>
                    </view>
                    <view class="mt-[40rpx]"
                        ><apply-btn
                            :customClass="customClass"
                            btnText=""
                            @popTrue="popTrue"
                        ></apply-btn
                    ></view>
                </view>
            </view>
        </view>
    </view>
</template>

<script lang="ts" setup>
const { proxy } = getCurrentInstance() as any
import cache from '@/utils/cache'
import { onShow } from '@dcloudio/uni-app'
/**
 * @description props参数说明
 * @property [number] scrollTop 外部传入的滚动条高度
 * @property [array] tabs tabs
 * @property [number] current 设置默认的tabIndex
 * @property [number]  stickyTop 固定定位的高度
 * @property [boolean] sticky 是否设置tab为固定定位
 * @property [number] itemOffsetTop 自定义滚动相隔间距,到达会切换tab
 * @property [object]  tabOptions tab的配置
 * @property [boolean] scrollTab 滚动时是否切换到指定的tab
 * @property [boolean] clickScroll 点击时是否滚动到相应位置
 * @property [number ] duration  滚动持续时长
 * @property [number] offsetTop 点击滚动到某一模块时的偏离值,每次点击滚动都会减去这个偏离值,这个值的单位是px,
 */
const mainColor = ref(cache.get('btnCon').btn_bgColor)
const customClass = ref('m-auto') //按钮样式
const props = defineProps({
    value: {
        type: String || Number,
        default: null
    },
    scrollTab: {
        type: Boolean,
        default: true
    },
    clickScroll: {
        type: Boolean,
        default: true
    },
    scrollTop: {
        type: Number || String,
        default: null
    },
    tabs: {
        type: Array,
        default: () => [] as any[]
    },
    // 设置默认的tabIndex
    current: {
        type: Number,
        default: 0
    },
    stickyTop: {
        type: String,
        default: null
    },
    // 是否将tab设为固定定位
    sticky: {
        type: Boolean,
        default: true
    },
    // 自定义间距 ,当 top小于等于这个距离的时候会切换tab
    itemOffsetTop: {
        type: Number,
        default: 60
    },
    tabOptions: {
        type: Object,
        default: () => ({})
    },
    duration: {
        type: Number,
        default: 300
    },
    offsetTop: {
        type: Number,
        default: 0
    }
})
const tabTopHeight = ref(0)
const active = ref(0)
const click = ref(false)
const timer = ref()
watch(
    [() => props.scrollTop, () => active.value],
    ([newScrollTop, newActive], [oldScrollTop, oldActive]) => {
        if (newScrollTop) {
            if (!click.value) {
                scrollToTab()
            }
        }
        if (newActive != oldActive) {
            nextTick(() => {
                scrollToElement()
            })
        }
    },
    { immediate: true }
)

onMounted(() => {
    getScrollTabTopHeight()
})
onShow(() => {
    isPopup.value = false
})
/**
 * @description: 跳转加盟申请页面
 * @param {*} isLogin 是否登陆
 * @return {*}
 */
const popTrue = (isLogin: boolean) => {
    if (!isLogin) {
        isPopup.value = !isLogin
    } else {
        uni.navigateTo({
            url: linkUrl.value
        })
    }
}

const getScrollTabTopHeight = async () => {
    // 获取tab的高度
    const query = (await createSelectorQueryForThis('#tabScrollTop', false)) as any
    if (!query) return
    tabTopHeight.value = query.height
}

const createSelectorQueryForThis = (selector: string, all: any) => {
    return new Promise((resolve) => {
        proxy
            .createSelectorQuery()
            [all ? 'selectAll' : 'select'](selector)
            .boundingClientRect((rect: any) => {
                resolve(rect)
            })
            .exec()
    })
}

const createSelectorQuery = (selector: any, all: any) => {
    return new Promise((resolve) => {
        proxy
            .createSelectorQuery()
            [all ? 'selectAll' : 'select'](selector)
            .boundingClientRect((rect: any) => {
                resolve(rect)
            })
            .exec()
    })
}
const emit = defineEmits<{
    (event: 'onChange', index: number): void
    (event: 'input', active: any): void
}>()
const handleChangeTab = (index: any) => {
    active.value = index
    click.value = true
}
// 点击滑动到指定元素
const scrollToElement = async () => {
    if (!click.value) return false
    const tab = props.tabs[active.value]
    if (!tab) return
    const { scroll_id } = tab as any
    if (!scroll_id) return false
    clearTimeout(timer.value)
    const queryData = (await createSelectorQuery(`.${scroll_id}`, false)) as any
    if (tabTopHeight.value === 0) await getScrollTabTopHeight()
    if (!queryData) {
        click.value = true
        return false
    }
    let scrollTop = props.scrollTop + queryData.top - props.offsetTop
    scrollTop -= tabTopHeight.value
    // 页面滚动函数
    uni.pageScrollTo({
        scrollTop,
        duration: props.duration,
        success: () => {
            timer.value = setTimeout(() => {
                click.value = false
            }, props.duration + 500)
        }
    })
}
const scrollToTab = async () => {
    if (!props.scrollTab && !click.value) return false
    const length = props.tabs.length
    let allClass = ''
    for (let i = 0; i < length; i++) {
        const { scroll_id } = props.tabs[i] as any
        allClass += i < length - 1 ? `.${scroll_id},` : `.${scroll_id}`
    }
    const queryData = (await createSelectorQuery(allClass, true)) as any

    for (let i = 0; i < queryData.length; i++) {
        if (queryData[i].top <= props.itemOffsetTop) {
            active.value = i
        }
    }
}
</script>

<style lang="scss" scoped>
.scroll-tabs-container {
    .status_bar {
        display: none;
        // height: var(--status-bar-height);
    }

    .scroll-tabs-sticky {
        z-index: 9999;
        overflow: hidden;
        -webkit-transform: translateZ(0);
        transform: translate3d(0, 0, 0);
        background: white;
    }
}
.radiusz {
    border-radius: 26rpx 26rpx 0 0;
    margin-top: -98rpx;
    border-bottom: 2px solid #eee;
    position: fixed;
    width: 100%;
}
</style>

页面调用代码:

javascript 复制代码
<template>
  <view class="bg-[#F3F4F6] w-full">
     <scroll-tabs
           :value="current"
           :tabs="graphicIntroduction"
           :tabOptions="{ label: 'title', activeColor: '#222', barColor: '#ff9f0f' }"
           :offsetTop="180"
           :scrollTop="scrollTop"
           :sticky="true"
           :itemOffsetTop="300 + statusBarHeight"
           :clickScroll="clickScroll"
       >
       </scroll-tabs>
   </view>
 </template>
 <script lang="ts" setup>
import { getMiniNavigation } from '@/api/shop'
import { onLoad, onPageScroll } from '@dcloudio/uni-app'

const bannerImg = ref('')
const graphicIntroduction = ref([]) as any
const current = ref(0) // tab默认索引

const scrollTop = ref(0)
const clickScroll = ref(false)
const stickyTop = ref()
const statusBarHeight = ref()
onPageScroll((e: any) => {
    scrollTop.value = e.scrollTop
})

onLoad(() => {
    getMiniNavigationData()
    // getHeight()
    uni.getSystemInfo({
        success: function (e: any) {
            stickyTop.value = e.statusBarHeight + 44 + 'px'
            statusBarHeight.value = e.statusBarHeight
        }
    })
})

/**
 * @description: 获取首页轮播图和图文介绍
 * @return {*}
 */
const getMiniNavigationData = async () => {
    const { data } = await getMiniNavigation({ type: 'plan' })
    bannerImg.value = data.bannerImg
    const list = data.graphicIntroduction
    list.forEach((item: any) => {
        if (item.is_show == 1) {
            graphicIntroduction.value.push(item)
        }
    })
    graphicIntroduction.value.forEach((item: any, index: number) => {
        item.scroll_id = `tabs${index}`
    })
}
</script>

效果图:

相关推荐
瑶琴AI前端1 小时前
uniapp实现H5和微信小程序获取当前位置(腾讯地图)
微信小程序·小程序·uni-app
龙哥·三年风水3 小时前
群控系统服务端开发模式-应用开发-前端框架
分布式·vue·群控系统
丁总学Java6 小时前
页面、组件、应用、生命周期(微信小程序)
微信小程序·小程序·生命周期
耶啵奶膘8 小时前
uniapp-是否删除
linux·前端·uni-app
cs_dn_Jie10 小时前
钉钉 H5 微应用 手机端调试
前端·javascript·vue.js·vue·钉钉
清灵xmf12 小时前
TypeScript 类型进阶指南
javascript·typescript·泛型·t·infer
Yaml412 小时前
智能化健身房管理:Spring Boot与Vue的创新解决方案
前端·spring boot·后端·mysql·vue·健身房管理
断墨先生14 小时前
uniapp—android原生插件开发(3Android真机调试)
android·uni-app
guai_guai_guai15 小时前
uniapp
前端·javascript·vue.js·uni-app
Amd79419 小时前
Nuxt.js 应用中的 prepare:types 事件钩子详解
typescript·自定义·配置·nuxt·构建·钩子·类型