Skip to content

底部导航栏

一、组件

vue
<template>
    <view class="tab_bar_wrap">
        <view class="items">
            <view :class="{
				'item':true,
				'bright':nTabbarIndex === 0
			}" @click="goTabBar" data-url="" data-index="0">
                <image v-if="nTabbarIndex === 0" class="icon" src="@/static/image/tabbar/fun2.png"
                       mode=""></image>
                <image v-else class="icon" src="@/static/image/tabbar/fun.png" mode=""></image>
                示例
            </view>
            <view :class="{
				'item':true,
				'bright':nTabbarIndex === 1
			}" @click="goTabBar" data-url="" data-index="1">
                <image v-if="nTabbarIndex === 1" class="icon" src="@/static/image/tabbar/flow2.png" mode=""></image>
                <image v-else class="icon" src="@/static/image/tabbar/flow.png" mode=""></image>
                瀑布流
            </view>
            <view :class="{
				'item':true,
				'bright':nTabbarIndex === 2
			}" @click="goTabBar" data-url="" data-index="2">
                <image v-if="nTabbarIndex === 2" class="icon" src="@/static/image/tabbar/my2.png" mode=""></image>
                <image v-else class="icon" src="@/static/image/tabbar/my.png" mode=""></image>
                我的
            </view>
        </view>
    </view>
</template>
<script>
export default {
    props: {
        nTabbarIndex: {
            type: Number,
            default: 0
        }
    },
    data() {
        return {}
    },
    methods: {
        goTabBar(e) {
            const i = parseInt(e.currentTarget.dataset.index);
            if (this.nTabbarIndex === i) return;
            this.$emit('goTabBar', i);
        }
    }
}
</script>

<style lang="scss" scoped>
.tab_bar_wrap {
    width: 100vw;
    background: $uni-bg-color;
    position: fixed;
    left: 0;
    bottom: 0;
    z-index: 99999;
    border-top: 1px solid $uni-bg-color-grey;
    
    .items {
        width: 100%;
        height: 100 rpx;
        padding-bottom: env(safe-area-inset-bottom);
        display: flex;
        
        .item {
            flex: 1;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            position: relative;
            
            text-align: center;
            color: $uni-text-color-grey;
            font-size: 20 rpx;
            font-family: PingFangSC-Regular, PingFang SC;
            font-weight: 400;
            line-height: 24 rpx;
        }
        
        .index_icon {
            width: 68 rpx;
            height: 68 rpx;
        }
        
        .bright {
            color: $uni-color-primary;
        }
        
        .icon {
            width: 52 rpx;
            height: 52 rpx;
        }
    }
}
</style>

二、使用

index.vue

vue
<template>
    <!-- 布局 -->
    <view class="tabbar_wrap">
        <nav-bar v-show="objNavData.show" :title="objNavData.title || ''" :isBack="false"></nav-bar>
        <view :class="[sAnimation,{'silder2':!isShowTabBar}]"
              :style="'height: calc(100% - '+(objNavData.show?navHeight:0)+'px);'">
            <el-home ref="home" v-show="nTabbarIndex === 0"></el-home>
            <el-flow ref="flow" v-show="nTabbarIndex === 1"></el-flow>
            <el-my ref="my" v-show="nTabbarIndex === 2"></el-my>
        </view>
        <tab-bar v-show="isShowTabBar" :nTabbarIndex="nTabbarIndex" @goTabBar="goTabBar"></tab-bar>
        
        <user-info-auth ref="user-info-auth"/>
    </view>
</template>

<script>
import oShareMixin from '/src/mixin/share.js';

import tabBar from '/src/components/tabbar.vue';
import navBar from '/src/components/navbar.vue';
import elHome from '/src/pages/demo/home/index.vue';
import elFlow from '/src/pages/demo/flow/index.vue';
import elMy from '/src/pages/demo/my/index.vue';

import userInfoAuth from "/src/components/userinfoauth/index.2.0.vue";

export default {
    mixins: [oShareMixin],
    components: {
        tabBar,
        navBar,
        elHome,
        elFlow,
        elMy,
        userInfoAuth
    },
    computed: {
        isShowTabBar() {
            return this.$store.state.isShowTabBar
        }
    },
    data() {
        return {
            objNavData: {
                show: true,
                title: '前端规范'
            },
            navHeight: 80,
            option: {},
            nTabbarIndex: -1,
            sAnimation: 'slide', //默认动画
            isCutover: true
        }
    },
    onLoad(option) {
        //获取页面元素
        this.home = this.$refs.home;
        this.flow = this.$refs.flow;
        this.my = this.$refs.my;
        this.option = option;
        
        const wxInfo = this.$getWxInfo();
        this.navHeight = wxInfo.navHeight;
        
        this.pageInit(0);
    },
    onShow() {
        this.$store.commit('showTabBer', true)
        if(this.nTabbarIndex == 2){
            this.currentEl.upDate();
        }
    },
    methods: {
        //初始化
        pageInit(index = 0) {
            this.goTabBar(index);
        },
        goTabBar(i) {
            if (this.isCutover) {
                this.isCutover = false;
                this.nTabbarIndex = i;
                
                this.currentEl = null;
                let objNavData = {
                    show: true,
                    title: '前端示例',
                };
                switch (i) {
                    case 0:
                        this.currentEl = this.home;
                        objNavData = {
                            show: true,
                            title: '前端示例',
                        };
                        break;
                    case 1:
                        this.currentEl = this.flow;
                        objNavData = {
                            show: true,
                            title: '瀑布流',
                        };
                        break;
                    case 2:
                        this.currentEl = this.my;
                        objNavData = {
                            show: true,
                            title: '我的',
                        };
                        break;
                }
                this.objNavData = objNavData;
                /* 切换动画 */
                this.sAnimation = "slide slide_animation";
                setTimeout(() => {
                    this.isCutover = true;
                    this.sAnimation = "slide";
                }, 200)

                //调用当前组件的upData方法
                if (this.currentEl && this.currentEl.upDate) {
                    this.$init(() => {
                        this.currentEl.upDate();
                    })
                }
            }
        },
    }
}
</script>

<style lang="scss" scoped>
.fade-transform-leave-active,
.fade-transform-enter-active {
    transition: all 0.5s;
}

.fade-transform-enter {
    opacity: 0;
    transform: translateY(-30px);
}

.fade-transform-leave-to {
    opacity: 0;
    transform: translateY(-30px);
}

.tabbar_wrap {
    width: 100vw;
    height: 100vh;
}

.slide {
    width: 100%;
    box-sizing: border-box;
    //底部安全距离 + tabbar高度
    padding-bottom: calc(env(safe-area-inset-bottom) + 100rpx);
    position: relative;
    left: 0;
}

.slide_animation {
    animation: slide 0.2s 1;
}

@keyframes slide {
    from {
        left: 100vw;
    }
    
    to {
        left: 0;
    }
}
</style>

三、注意事项

  • 上述方式中,每个tebbar页是以组件的形式引入;
  • 上述方式中,每个tebbar页都需要有一个upDate方法,切换tabbar时会调用;
  • 如果现在页面返回时更新tabbar页的数据,需要在index.vue里的onShow判并调用this.currentEl.upDate()
  • tebbar页中,可通过this.$parent.goTabBar(1),切换到索引为1的tabbar页;