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>
相关推荐
老毛肚3 小时前
jeecg-boot-base-core 02 day
javascript·python
烬羽8 小时前
后端返回的 JSON 字符串,浏览器怎么"看懂"的?——Ajax 全链路拆解
javascript
半个落月9 小时前
一个新手用 Bun + Axios 调通 DeepSeek API 的实践记录
javascript
不好听6139 小时前
深入理解链表:线性数据结构的另一面
javascript·数据结构
林希_Rachel_傻希希9 小时前
学React治好了我的焦虑症,1小时速通React 前20分钟。
前端·javascript·面试
小林ixn9 小时前
从 Ajax 到异步编程:JSON 序列化、Event Loop 与 XHR 请求完全解析
javascript
丷丩10 小时前
MapLibre GL JS第47课:添加动画图标
javascript·gis·动画·mapbox·maplibre
快乐的哈士奇10 小时前
【Next.js实战①】Gmail API 按柜号检索邮件:OAuth 双 Cookie 与搜索 Fallback
开发语言·javascript·ecmascript
云水一下10 小时前
Vue.js从零到精通系列(五):全局状态管理——Pinia 核心与实践
前端·javascript·vue.js
kmblack111 小时前
javascript计算年龄
开发语言·javascript·ecmascript