uniapp 顶部tab + 占满剩余高度的内容区域swiper
<template><view class="page-container"><!-- 顶部 Tab 栏 --><view class="tabs-container"><scroll-view scroll-x class="tabs-scroll" :show-scrollbar="false"><view class="tabs"><view v-for="(tab, index) in tabs" :key="index" class="tab-item" :class="{ active: currentTab === index }"@click="switchTab(index)">{{ tab }}<view class="underline" v-if="currentTab === index"></view></view></view></scroll-view></view><!-- 占满剩余高度的 swiper --><swiper class="content-swiper" :current="currentTab" @change="onSwiperChange":style="{ height: swiperHeight + 'px' }"><swiper-item v-for="(tab, index) in tabs" :key="index" class="swiper-item"><scroll-view scroll-y class="content-scroll" :show-scrollbar="false"><!-- 内容区域 --><view class="content-box"><text class="title">{{ tab }} 的内容</text><view v-for="item in 20" :key="item" class="demo-item">{{ tab }} 的内容项 {{ item }}</view></view></scroll-view></swiper-item></swiper></view>
</template><script setup lang="ts">import { ref, getCurrentInstance } from 'vue'import { onLoad, onShow } from '@dcloudio/uni-app'const currentTab = ref(0)const tabs = ref(['全部', '矿场咨询', '安全教育', '岗位规范'])const swiperHeight = ref(500) // 初始高度,后续动态计算// 动态计算高度const calcSwiperHeight = () => {uni.getSystemInfo({success: (res) => {// 获取屏幕高度const screenHeight = res.windowHeight// 获取 tabs 容器高度(需要确保 DOM 已渲染)// 在 setup 中获取组件实例const instance = getCurrentInstance()const query = uni.createSelectorQuery().in(instance?.proxy)query.select('.tabs-container').boundingClientRect(data => {// 计算剩余高度 = 屏幕高度 - tabs 高度 - 安全区域(可选)const safeAreaInsets = res.safeAreaInsets?.top || 0swiperHeight.value = screenHeight - data.height - safeAreaInsets}).exec()}})}// 切换 Tabconst switchTab = (index : number) => {currentTab.value = index}// 滑动 swiper 时同步 Tabconst onSwiperChange = (e : any) => {currentTab.value = e.detail.current}// 初始化时计算高度onLoad(() => {calcSwiperHeight()})
</script><style scoped lang="scss">/* 页面容器 */.page-container {height: 100vh;display: flex;flex-direction: column;}/* 顶部 Tab 样式 */.tabs-container {position: sticky;top: 0;background: #fff;z-index: 999;box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.1);/* 确保 tabs 容器高度固定 */height: 90rpx;}/* swiper 容器 */.content-swiper {flex: 1;width: 100%;}/* swiper-item 内部滚动区域 */.content-scroll {height: 100%;padding: 30rpx;box-sizing: border-box;}/* 内容区域样式 */.content-box {background: #f5f5f5;border-radius: 16rpx;padding: 30rpx;}.title {font-size: 32rpx;font-weight: bold;margin-bottom: 30rpx;display: block;}.demo-item {padding: 20rpx;margin-bottom: 20rpx;background: #fff;border-radius: 8rpx;box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.05);}/* Tab 样式 */.tabs-scroll {white-space: nowrap;width: 100%;}.tabs {display: inline-flex;padding: 20rpx 30rpx;}.tab-item {position: relative;padding: 20rpx 40rpx;font-size: 28rpx;color: #666;transition: all 0.3s;}.tab-item.active {color: #007AFF;font-weight: 500;}.underline {position: absolute;bottom: 0;left: 50%;transform: translateX(-50%);width: 60%;height: 6rpx;background: #007AFF;border-radius: 3rpx;animation: underlineShow 0.3s ease-out;}@keyframes underlineShow {from {width: 0;opacity: 0;}to {width: 60%;opacity: 1;}}
</style>