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>