Files
t/components/thorui/tui-tab/tui-tab.vue
T
2025-03-21 09:38:06 +08:00

274 lines
6.1 KiB
Vue

<template>
<scroll-view class="tui-scroll__view" :class="[isFixed && !isSticky?'tui-tabs__fixed':'',isSticky?'tui-tabs__sticky':'']"
:style="{height: height+'rpx',background:backgroundColor,top: isFixed || isSticky ? top + 'px' : 'auto',zIndex:isFixed || isSticky?zIndex:'auto'}"
:scroll-x="scrolling" :scroll-with-animation="scrolling" :scroll-left="scrollLeft">
<view class="tui-tabs__wrap">
<view class="tui-tabs__list" :class="[scroll ? 'tui-tabs__scroll' : '']" :style="{height: height+'rpx'}">
<view class="tui-tabs__item" :style="{height: height+'rpx'}" v-for="(item,index) in tabs" :key="index" @tap="handleClick"
:data-index="index">
<view class="tui-item__child" :style="{ color: currentTab == index ? selectedColor : color,fontSize: size + 'rpx',lineHeight: size + 'rpx',fontWeight: bold && currentTab == index ? 'bold' : 'normal'}">{{item}}</view>
</view>
<view class="tui-tabs__line" :class="[needTransition ? 'tui-transition' : '']" :style="{background: sliderBgColor,height:sliderHeight,borderRadius: sliderRadius,bottom: bottom,width: lineWidth+'px',transform: `translateX(${translateX}px)`}"></view>
</view>
</view>
</scroll-view>
</template>
<script>
export default {
name: 'tuiTab',
options: {
virtualHost: true
},
props: {
// 标签页数据源
tabs: {
type: Array,
default () {
return []
}
},
// 当前选项卡
current: {
type: Number,
default: 0
},
// 是否可以滚动
scroll: {
type: Boolean,
default: false
},
// tab高度 rpx
height: {
type: Number,
default: 80
},
backgroundColor: {
type: String,
default: '#fff'
},
//字体大小
size: {
type: Number,
default: 28
},
//字体颜色
color: {
type: String,
default: '#666'
},
//选中后字体颜色
selectedColor: {
type: String,
default: '#5677fc'
},
//选中后 是否加粗 ,未选中则无效
bold: {
type: Boolean,
default: false
},
//滑块高度
sliderHeight: {
type: String,
default: '2px'
},
//滑块背景颜色
sliderBgColor: {
type: String,
default: '#5677fc'
},
//滑块 radius
sliderRadius: {
type: String,
default: '2px'
},
//滑块bottom
bottom: {
type: String,
default: '0'
},
//是否固定
isFixed: {
type: Boolean,
default: false
},
//吸顶效果,为true时isFixed失效
isSticky: {
type: Boolean,
default: false
},
//isFixed=true时,tab top值 px
top: {
type: Number,
// #ifndef H5
default: 0,
// #endif
// #ifdef H5
default: 44
// #endif
},
zIndex: {
type: [Number, String],
default: 996
}
},
watch: {
/**
* 监听数据变化, 如果改变重新初始化参数
*/
tabs(newVal, oldVal) {
this.scrolling = false
// 异步加载数据时候, 延迟执行 init 方法, 防止无法正确获取 dom 信息
setTimeout(() => this.init(), 200);
},
/**
* 监听 currentTab 变化, 做对应处理
*/
current(newVal, oldVal) {
this.scrollByIndex(newVal);
}
},
created() {
this.currentTab = this.current;
},
mounted() {
this.init()
},
data() {
return {
/* 未渲染数据 */
windowWidth: 0, // 屏幕宽度
tabItems: [], // 所有 tab 节点信息
/* 渲染数据 */
scrolling: true, // 控制 scroll-view 滚动以在异步加载数据的时候能正确获得 dom 信息
needTransition: false, // 下划线是否需要过渡动画
translateX: 0, // 下划 line 的左边距离
lineWidth: 0, // 下划 line 宽度
scrollLeft: 0, // scroll-view 左边滚动距离
currentTab: 0
};
},
methods: {
/**
* 切换菜单
*/
handleClick(e) {
let index = Number(e.currentTarget.dataset.index)
this.$emit('change', {
index: index
});
this.scrollByIndex(index);
},
/**
* 滑动到指定位置
* @param tabCur: 当前激活的tabItem的索引
* @param needTransition: 下划线是否需要过渡动画, 第一次进来应设置为false
*/
scrollByIndex(tabCur, needTransition = true) {
let item = this.tabItems[tabCur];
if (!item) return;
let itemWidth = item.width || 0,
itemLeft = item.left || 0;
this.needTransition = needTransition;
this.currentTab = tabCur;
// 超出滚动的情况
if (this.scroll) {
// 保持滚动后当前 item '尽可能' 在屏幕中间
let scrollLeft = itemLeft - (this.windowWidth - itemWidth) / 2;
this.scrollLeft = scrollLeft;
this.translateX = itemLeft;
this.lineWidth = itemWidth
} else {
// 不超出滚动的情况
this.translateX = itemLeft;
this.lineWidth = itemWidth
}
},
/**
* 初始化函数
*/
init() {
const {
windowWidth
} = uni.getSystemInfoSync();
this.windowWidth = windowWidth || 375
const query = uni.createSelectorQuery().in(this);
query.selectAll(".tui-item__child").boundingClientRect((res) => {
this.scrolling = true;
this.tabItems = res;
this.scrollByIndex(this.currentTab, false);
}).exec();
}
}
};
</script>
<style scoped>
.tui-scroll__view {
width: 100%;
height: 80rpx;
overflow: hidden;
}
.tui-tabs__fixed {
position: fixed;
left: 0;
}
.tui-tabs__sticky {
position: sticky;
left: 0;
}
.tui-tabs__wrap {
padding-bottom: 20rpx;
}
.tui-tabs__list {
position: relative;
height: 80rpx;
display: flex;
}
.tui-tabs__scroll {
white-space: nowrap !important;
display: block !important;
}
.tui-tabs__scroll .tui-tabs__item {
padding: 0 30rpx;
display: inline-flex;
}
.tui-tabs__scroll .tui-item__child {
display: block !important;
}
.tui-tabs__item {
flex: 1;
text-align: center;
padding: 0 10rpx;
box-sizing: border-box;
transition: color 0.3s ease-in-out;
display: flex;
align-items: center;
justify-content: center;
}
.tui-item__child {
display: inline-block;
}
.tui-tabs__line {
position: absolute;
left: 0;
width: 0;
display: inline-block;
}
.tui-tabs__line.tui-transition {
transition: width 0.3s, transform 0.3s;
}
</style>