123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273 |
- <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(), 0);
- },
- /**
- * 监听 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>
|