[Vue]-06-xiannvmall-tabbar

06-xiannvmall-tabbar通用组件封装

创建一个vue项目 xiannvmall-tabbar

vue create xiannvmall-tabbar

注意: ESLint直接选默认的就好,以免出现 indent 报错

tabbar 实现思路

  • 1、封装下方的tabbar组件,如何封装
  • 1.1 自定义 TabBar 组件,在APP中使用
  • 1.2 让 TabBar 处于页面底部,并且设置相关样式
  • 2、TabBar 中小item的业务内容(图片,文字)由外部决定
  • 2.1 定义插槽
  • 2.2 flex布局平分 TabBar
  • 3、自定义 TabBarItem, 可以传入图片和文字
  • 定义TabBarItem组件,定义两个插槽,分别对应图片,文字
  • 给两个插槽的外层包装一层div,用于设置样式且不会被插槽替换掉
  • 填充插槽,实现底部的 TabBar效果
  • 4、传入高亮图片
  • 定义另外一个插槽,插入active-icon的数据
  • 定义一个变量 isActiveItem, 通过v-show来决定是否显示高亮icon
  • 5、TabBarItem绑定路由数据
  • 安装路由: npm i vue-router -S
  • 完成 router/index.js 内容,以及创建对应的组件
  • main.js 中注册router
  • APP中加入 router-view 路由出口标签
  • 6、点击item跳转到对应路由,并且动态决定 isActiveItem
  • 监听item的点击,通过this.$router.push('url') || this.$router.replace('url')//无法回到上一页
  • 通过this.$route.path.indexOf(this.link) !== -1 来判断是否是 active
  • 7、动态计算active样式
  • 封装新的计算属性: this.isActiveItem ? {'color':'red'}:{}

tabbar组件关键代码

components/tabbar
TabBar.vue

<template>
  <!-- TabBar组件 -->
  <div class="tab-bar">
    <slot></slot>
  </div>
</template>
<script>
  export default {
    name: 'TabBar',
  }
</script>
<style scoped>
  /* 给 tab-bar 弹性布局 */
  .tab-bar {
    display: flex;
    height: 49px;

    background-color: #eee;

    /* 给 tabbar 固定到屏幕底端 */
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
  }
</style>

TabBarItem.vue

<template>
  <div class="tab-bar-item" @click="itemClick">
    <!-- 样式和指令都要用div包裹放在div里面才行,不然就被slot替换掉没效果 -->
    <div v-if="!isActiveItem === true">
      <slot name="item-icon"></slot>
    </div>
    <div v-else>
      <slot name="item-icon-active"></slot>
    </div>
    <div :class="{activeColor: isActiveItem}">
      <slot name="item-text"></slot>
    </div>
  </div>
</template>
<script>
  export default {
    name: 'TabBarItem',
    props: {
      PropPath: {
        type: String,
        default: ''
      }
    },
    data() {
      return {
        // isActiveItem: false, // 改用计算属性 isActiveItem
      }
    },
    computed: {
      isActiveItem() {
        // 浏览器 /home -> propPath="/home" = true
        // 浏览器 /home -> propPath="/category" = false
        // 浏览器 /home -> propPath="/cart" = false
        // 浏览器 /home -> propPath="/profile" = false
        // ...
        return this.$route.path.indexOf(this.PropPath) !== -1 ? true : false
      }
    },
    methods: {
      itemClick() {
        this.isActiveItem = true
        this.$router.push(this.PropPath)
      }
    }
  }
</script>
<style scoped>
  /* 给tab-bar-item 平分区域 */
  .tab-bar-item {
    flex: 1;
    /* 给item的文字居中 */
    text-align: center;
    font-size: 12px;
    height: 100%;

    /* 给tab-bar-item 弹性布局 主要的轴承为Y */
    display: flex;
    /* tab-bar-item 主轴从X改为Y,item中内容呈 上下 布局 */
    flex-direction: column;
    /* X轴区域内容居中 */
    align-items: center;
    /* Y轴区域内容居中 */
    justify-content: center;

    /* 盒子阴影 */
    box-shadow: 0 14px 1px rgba(0, 0, 0, .1);
  }

  /* 给 tab-bar-item 的宽度和高度一个固定的值 */
  .tab-bar-item img {
    width: 24px;
    height: 24px;
    /* 去掉 图片的默认的margin-bottom */
    vertical-align: middle;
    margin-top: 3px;
    margin-bottom: 2px;
  }

  /* 当前标题的高亮 */
  .activeColor {
    color: #fd5979;
  }
</style>

router/index.js

import Vue from 'vue'
import VueRouter from 'vue-router'

// 路由懒加载
const Home = () => import('@/views/home/Home')
const Category = () => import('@/views/category/Category')
const Cart = () => import('@/views/cart/Cart')
const Profile = () => import('@/views/profile/Profile')

Vue.use(VueRouter)

const routes = [{
    path: '/',
    redirect: '/home'
  },
  {
    path: '/home',
    name: 'Home',
    component: Home,
  },
  {
    path: '/category',
    name: 'Category',
    component: Category,
  },
  {
    path: '/cart',
    name: 'Cart',
    component: Cart,
  },
  {
    path: '/profile',
    name: 'Profile',
    component: Profile,
  },

]


const router = new VueRouter({
  routes,
})

export default router

main.js

import Vue from 'vue'
import App from './App.vue'

import router from './router'

Vue.config.productionTip = false

new Vue({
  router,
  render: h => h(App),
}).$mount('#app')

App.vue

<template>
  <div id="app">
    <!-- TabBar组件  开始-->
    <tab-bar>
      <!-- PropPath:  tab-bar-item接受的参数props-->
      <tab-bar-item PropPath="/home">
        <template #item-icon>
          <img src="@/assets/img/tabbar/home.svg" alt="">
        </template>
        <template #item-icon-active>
          <img src="@/assets/img/tabbar/home_active.svg" alt="">
        </template>
        <template #item-text>
          首页
        </template>
      </tab-bar-item>
      <tab-bar-item PropPath="/category">
        <template #item-icon>
          <img src="@/assets/img/tabbar/category.svg" alt="">
        </template>
        <template #item-icon-active>
          <img src="@/assets/img/tabbar/category_active.svg" alt="">
        </template>
        <template #item-text>
          分类
        </template>
      </tab-bar-item>
      <tab-bar-item PropPath="/cart">
        <template #item-icon>
          <img src="@/assets/img/tabbar/cart.svg" alt="">
        </template>
        <template #item-icon-active>
          <img src="@/assets/img/tabbar/cart_active.svg" alt="">
        </template>
        <template #item-text>
          购物车
        </template>
      </tab-bar-item>
      <tab-bar-item PropPath="/profile">
        <template #item-icon>
          <img src="@/assets/img/tabbar/profile.svg" alt="">
        </template>
        <template #item-icon-active>
          <img src="@/assets/img/tabbar/profile_active.svg" alt="">
        </template>
        <template #item-text>
          我的
        </template>
      </tab-bar-item>
    </tab-bar>
    <!-- TabBar组件  结束-->

    <!-- 路由视图出口 -->
    <router-view></router-view>
  </div>
</template>

<script>
  import TabBar from './components/tabbar/TabBar'
  import TabBarItem from './components/tabbar/TabBarItem'

  export default {
    name: 'App',
    components: {
      TabBar,
      TabBarItem,
    }
  }
</script>

<style scoped>
  @import url(assets/css/base.css);
</style>
上一篇:iOS 13 tabbar 字体颜色更改


下一篇:uniapp 条件编译的使用