第一种方法 :使用<scroll>中的 scroll-into-view 属性
缺点:无法保持选中tab居中
代码:
<template>
<view class="help-list-view">
<scroll-view
scroll-x
:scroll-into-view="scrollinto"
:scroll-with-animation="true"
:show-scrollbar="false"
class="dir-box"
>
<view
class="scroll-row-item"
@click="changeTab(index)"
v-for="(item, index) in tabBars"
:key="index"
:id="'tab' + index"
>
<text :class="tabIndex === index ? 'main-text-color' : 'default-text-color'">
{{ item.directoryName }}
</text>
</view>
</scroll-view>
<swiper
:style="{ height: fullHeight + 'px' }"
:duration="150"
:current="tabIndex"
@change="onChangeTab"
>
<swiper-item v-for="(item, index) in tabBars" :key="index">
<scroll-view scroll-y="true" :show-scrollbar="false" class="tab-content">
<HelpList @clickItem="clickItem" :dataList="item.articles" />
</scroll-view>
</swiper-item>
</swiper>
</view>
</template>
<script>
import { mobileDirectoryList } from '@/api/helpCenter'
import HelpList from './components/helpList.vue'
export default {
components: { HelpList },
data() {
return {
tabIndex: 0,
scrollinto: 'tab0',
articleType: 3,
fullHeight: 0,
tabBars: [{ directoryName: '暂无', list: [{ title: '暂无' }] }]
}
},
onLoad(e) {
if (e.name == 'help') {
this.articleType = 3
uni.setNavigationBarTitle({
title: '帮助中心'
})
} else if (e.name == 'policy') {
this.articleType = 4
uni.setNavigationBarTitle({
title: '政策说明'
})
}
},
onShow() {
mobileDirectoryList({ directoryId: this.articleType }).then(res => {
console.log(res)
this.tabBars = res
})
},
async onPullDownRefresh() {
await this.getData()
uni.stopPullDownRefresh()
},
mounted() {
var that = this
uni.getSystemInfo({
success: function (res) {
that.fullHeight = res.windowHeight - 40
}
})
},
methods: {
// 获取目录列表
getData() {
return mobileDirectoryList({ directoryId: 3 }).then(res => {
this.tabBars = res
})
},
// 切换选项卡
changeTab(index) {
if (this.tabIndex === index) {
return
}
this.tabIndex = index
this.scrollinto = 'tab' + index
console.log(this.tabIndex, this.scrollinto)
},
// 监听滑动列表
onChangeTab(e) {
this.changeTab(e.detail.current)
},
clickItem(e) {
const path = this.articleType==3?`?id=${e.id}&type=${this.articleType}`:`?id=${e.id}&type=${this.articleType}`
uni.navigateTo({
url: `pages_b/pages/my-center/help-center/showArticle${path}`
})
}
}
}
</script>
<style lang="scss" scoped>
.help-list-view {
@include content-height(0px);
padding-top: 12px;
box-sizing: border-box;
width: 100%;
.main-text-color {
font-weight: 600;
font-size: 14px;
color: #ff680c;
}
.default-text-color {
font-weight: 400;
font-size: 14px;
color: #000000;
}
.dir-box {
white-space: nowrap;
width: 100%;
padding: 6px 18px;
padding-right: 0px;
border-bottom: 1px #cccccc solid;
box-sizing: border-box;
}
.scroll-row-item {
display: inline-block;
text-align: center;
font-size: 12px;
margin-right: 22px;
}
.tab-content {
box-sizing: border-box;
@include content-height(39px);
}
}
</style>
第二种方法:通过动态的scrollLeft 设置
核心思想为:初始记录一下tab项 距离滚动条初始位置的值,每次点击计算并将其滚动到中间
此为scrollTab组件:
<template>
<view>
<scroll-view
class="navigator-box"
style="white-space: nowrap"
:enable-flex="true"
:scroll-left="scrollLeft"
:show-scrollbar="false"
scroll-x="true"
>
<view
@click="e => scrollToIt(index, false, e, item)"
:class="_active == index ? 'item active' : 'item'"
:key="item[keyField]"
v-for="(item, index) in tabList"
>
{{ item[showField] }}
</view>
</scroll-view>
</view>
</template>
<script>
export default {
props: {
tabList: {
type: Array,
default() {
return []
}
},
active: {
type: Number,
default: 0
},
keyField: {
type: String,
default: 'seriesName'
},
showField: {
type: String,
default: 'navigationName'
}
},
data() {
return {
scrollLeft: 0
}
},
computed: {
_active: {
get() {
return this.active
},
set(val) {
this.$emit(`update:active`, val)
}
}
},
watch: {
tabList:{
handler(v) {
if (v.length > 0) {
// 延迟执行 此处h5编译时机拿不到节点
this.$nextTick(() => {
this.recordPos()
})
}
},
immediate:true,
deep:true
}
},
options: {
styleIsolation: 'shared'
},
methods: {
/**
* 获取tab各个元素的初始位置
*/
recordPos() {
const query = uni.createSelectorQuery().in(this)
query.selectAll('.navigator-box .item').boundingClientRect()
query.exec(res => {
this.offsetLefts = res[0]
})
},
scrollToIt(index, noClick = true, e, item = {}) {
// if(!this.offsetLefts){
// this.recordPos()
// }
if (index === this._active) return false
this._active = index
// 获取屏幕宽度
const systemInfo = uni.getSystemInfoSync()
const screenWidth = systemInfo.windowWidth
this.$emit('scrollToIt', { index, noClick, e, item })
// 计算左侧滚动宽度,为原始位置减去屏幕宽度一半,不足则滚动置为0
this.$nextTick(() => {
const activeEle = this.offsetLefts[index]
const scrollLeft = activeEle.left - (screenWidth / 2 - activeEle.width / 2)
this.scrollLeft = Math.max(0, scrollLeft)
})
}
}
}
</script>
<style lang="scss" scoped>
::v-deep .navigator-box .uni-scroll-view-content {
display: flex;
}
.navigator-box {
background: #fff;
position: sticky;
top: 88rpx;
z-index: 22;
box-sizing: border-box;
font-size: 28rpx;
}
/* #ifndef H5 */
.navigator-box {
top: 0px;
}
/* #endif */
.navigator-box .title {
margin-bottom: 10px;
color: #fff;
font-size: 16px;
}
.navigator-box .item {
// 此处间隔要使用padding 因为要拿到width
padding: 20rpx 30rpx;
color: #333;
text-align: center;
font-size: 24rpx;
display: inline-block;
}
.item.active {
color: #ff680c;
}
</style>
使用:
<ScrollTab
ref="scrollTab"
:tabList="tabBars"
keyField="id"
showField="directoryName"
:active.sync="tabIndex"
@scrollToIt="scrollToIt"
/>