uniapp 顶部tab + 占满剩余高度的内容区域swiper


javascript 复制代码
<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 || 0
					swiperHeight.value = screenHeight - data.height - safeAreaInsets
				}).exec()
			}
		})
	}

	// 切换 Tab
	const switchTab = (index : number) => {
		currentTab.value = index
	}

	// 滑动 swiper 时同步 Tab
	const 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>
相关推荐
单身的人上天堂4 小时前
开发中使用iconfont预览太麻烦?我开发了一款VSCode插件来提升效率
前端·javascript·visual studio code
爱吃大芒果4 小时前
Flutter 本地存储方案:SharedPreferences、SQFlite 与 Hive
开发语言·javascript·hive·hadoop·flutter·华为·harmonyos
weixin_395448914 小时前
“一次性拼接 RM+FSD 做单次前向/反向”的方案
前端·javascript·推荐算法
爱吃大芒果4 小时前
Flutter 路由进阶:命名路由、动态路由与路由守卫实现
开发语言·javascript·flutter·华为·ecmascript
一只爱吃糖的小羊4 小时前
深入 React 原理:Reconciliation
前端·javascript·react.js
未来之窗软件服务4 小时前
幽冥大陆(五十二)V10酒店门锁SDK TypeScript——东方仙盟筑基期
前端·javascript·typescript·酒店门锁·仙盟创梦ide·东方仙盟·东方仙盟sdk
企微自动化4 小时前
Java 实现 Token 安全缓存:使用 ReentrantLock 和单例模式实现并发安全的 Token 管理器
开发语言·javascript·ecmascript
未来之窗软件服务4 小时前
浏览器开发CEF(二十一)C#浏览器 Promise模式调用——东方仙盟元婴期
前端·javascript·html·仙盟创梦ide·东方仙盟·东方仙盟vos智能浏览器
阿蒙Amon5 小时前
JavaScript学习笔记:13.Promise
javascript·笔记·学习
爱吃大芒果5 小时前
Flutter 动画实战:隐式动画、显式动画与自定义动画控制器
开发语言·javascript·flutter·ecmascript·gitcode