TarBar组件案例
2023年5月8日
第1步(简单实现)
// App.vue
<template>
<div id="app">
<div id="tab-bar">
<div class="tab-bar-item">
<img src="../../assets/img/tabbar/home.svg" alt="">
<div>主页</div>
</div>
<div class="tab-bar-item">
<img src="../../assets/img/tabbar/category.svg" alt="">
<div>分类</div>
</div>
<div class="tab-bar-item">
<img src="../../assets/img/tabbar/shopcart.svg" alt="">
<div>购物车</div>
</div>
<div class="tab-bar-item">
<img src="../../assets/img/tabbar/profile.svg" alt="">
<div>我的</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "App",
components: {
},
};
</script>
<style>
// margin: 0, padding: 0
@import "./assets/css/reset.css";
#tab-bar {
display: flex;
background-color: #f6f6f6;
/* 置于页面最低端 */
position: fixed;
left: 0;
right: 0;
bottom: 0;
/* 阴影 */
box-shadow: 0 -1px 1px rgba(100, 100, 100, 0.2);
}
.tab-bar-item {
flex: 1;
text-align: center;
height: 49px;
}
.tab-bar-item img {
width: 24px;
height: 24px;
}
</style>
第2步(封装TabBar组件,包含TarBarItem组件)
// TabBar.vue
<template>
<div id="tab-bar">
<div class="tab-bar-item">
<img src="../../assets/img/tabbar/home.svg" alt="">
<div>主页</div>
</div>
<div class="tab-bar-item">
<img src="../../assets/img/tabbar/category.svg" alt="">
<div>分类</div>
</div>
<div class="tab-bar-item">
<img src="../../assets/img/tabbar/shopcart.svg" alt="">
<div>购物车</div>
</div>
<div class="tab-bar-item">
<img src="../../assets/img/tabbar/profile.svg" alt="">
<div>我的</div>
</div>
</div>
</template>
<script>
export default {
name: "TabBar",
};
</script>
<style scoped>
#tab-bar {
display: flex;
background-color: #f6f6f6;
/* 置于页面最低端 */
position: fixed;
left: 0;
right: 0;
bottom: 0;
/* 阴影 */
box-shadow: 0 -1px 1px rgba(100, 100, 100, 0.2);
}
.tab-bar-item {
flex: 1;
text-align: center;
height: 49px;
}
.tab-bar-item img {
width: 24px;
height: 24px;
}
</style>
// App.vue
<template>
<div id="app">
<tab-bar></tab-bar>
</div>
</template>
<script>
import TabBar from './components/tabbar/TabBar.vue';
export default {
name: "App",
components: {
TabBar,
},
};
</script>
<style>
@import "./assets/css/reset.css";
</style>
第3步(使用插槽封装TabBarItem组件和TabBar组件)
// TabBarItem.vue
<template>
<div class="tab-bar-item">
// <img src="../../assets/img/tabbar/home.svg" alt="">
// <div>主页</div>
<slot name="item-icon"></slot>
<slot name="item-text"></slot>
</div>
</template>
<script>
export default({
name: "TabBarItem"
})
</script>
<style scoped>
.tab-bar-item {
flex: 1;
text-align: center;
height: 49px;
font-size: 14px;
}
.tab-bar-item img {
width: 24px;
height: 24px;
margin-top: 3px;
vertical-align: center;
}
</style>
// TabBar.vue
<template>
<div class="tab-bar">
<slot></slot>
</div>
</template>
<script>
export default {
name: "TabBar",
};
</script>
<style scoped>
#tab-bar {
display: flex;
background-color: #f6f6f6;
/* 置于页面最低端 */
position: fixed;
left: 0;
right: 0;
bottom: 0;
/* 阴影 */
box-shadow: 0 -1px 1px rgba(100, 100, 100, 0.2);
}
</style>
// App.vue
<template>
<div id="app">
<tab-bar>
<tab-bar-item>
// 具名插槽
<img slot="item-icon" src="./assets/img/tabbar/home.svg" alt="">
<div slot="item-text">主页</div>
</tab-bar-item>
<tab-bar-item>
<img slot="item-icon" src="./assets/img/tabbar/category.svg" alt="">
<div slot="item-text">分类</div>
</tab-bar-item>
<tab-bar-item>
<img slot="item-icon" src="./assets/img/tabbar/shopcart.svg" alt="">
<div slot="item-text">购物车</div>
</tab-bar-item>
<tab-bar-item>
<img slot="item-icon" src="./assets/img/tabbar/profile.svg" alt="">
<div slot="item-text">我的</div>
</tab-bar-item>
</tab-bar>
</div>
</template>
<script>
import TabBar from './components/tabbar/TabBar.vue';
import TabBarItem from './components/tabbar/TabBarItem.vue';
export default {
name: "App",
components: {
TabBar,
TabBarItem
},
};
</script>
<style>
@import "./assets/css/reset.css";
</style>
第4步(实现路由功能)
创建组件,配置路由映射
// router.index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
const Home = () => import('../views/home/Home.vue')
const Category =() => import('../views/category/Category.vue')
const Cart =() => import('../views/cart/Cart.vue')
const Profile =() => import('../views/profile/Profile.vue')
Vue.use(VueRouter)
const routes = [
{
path: '/',
redirect: '/home'
},
{
path: '/home',
component: Home
},
{
path: '/category',
component: Category
},
{
path: '/cart',
component: Cart
},
{
path: '/profile',
component: Profile
}
]
const router = new VueRouter ({
routes,
mode: 'history'
})
export default router
// TabBarItem.vue
<template>
<div class="tab-bar-item" @click="itemClick">
<slot name="item-icon"></slot>
<slot name="item-text"></slot>
</div>
</template>
<script>
export default({
name: 'TabBarItem',
// 父传子(传路径)
props: {
path: String
},
methods: {
itemClick() {
this.$router.replace(this.path).catch(err => err)
}
}
})
</script>
// App.vue
<template>
<div id="app">
<tab-bar>
<tab-bar-item path="/home">
<img slot="item-icon" src="./assets/img/tabbar/home.svg" alt="">
<div slot="item-text">主页</div>
</tab-bar-item>
<tab-bar-item path="/category">
<img slot="item-icon" src="./assets/img/tabbar/category.svg" alt="">
<div slot="item-text">分类</div>
</tab-bar-item>
<tab-bar-item path="/cart">
<img slot="item-icon" src="./assets/img/tabbar/shopcart.svg" alt="">
<div slot="item-text">购物车</div>
</tab-bar-item>
<tab-bar-item path="/profile">
<img slot="item-icon" src="./assets/img/tabbar/profile.svg" alt="">
<div slot="item-text">我的</div>
</tab-bar-item>
</tab-bar>
</div>
</template>
<script>
import TabBar from './components/tabbar/TabBar.vue';
import TabBarItem from './components/tabbar/TabBarItem.vue';
export default {
name: "App",
components: {
TabBar,
TabBarItem
},
};
</script>
<style>
@import "./assets/css/reset.css";
</style>
第5步(实现激活颜色变化功能)
// App.vue
<template>
<div id="app">
<tab-bar>
<tab-bar-item path="/home">
<img slot="item-icon" src="./assets/img/tabbar/home.svg" alt="">
<!-- 增添激活样式 -->
<img slot="item-icon-active" src="./assets/img/tabbar/home_active.svg" alt="">
<div slot="item-text">主页</div>
</tab-bar-item>
<tab-bar-item path="/category">
<img slot="item-icon" src="./assets/img/tabbar/category.svg" alt="">
<img slot="item-icon-active" src="./assets/img/tabbar/category_active.svg" alt="">
<div slot="item-text">分类</div>
</tab-bar-item>
<tab-bar-item path="/cart">
<img slot="item-icon" src="./assets/img/tabbar/shopcart.svg" alt="">
<img slot="item-icon-active" src="./assets/img/tabbar/shopcart_active.svg" alt="">
<div slot="item-text">购物车</div>
</tab-bar-item>
<tab-bar-item path="/profile">
<img slot="item-icon" src="./assets/img/tabbar/profile.svg" alt="">
<img slot="item-icon-active" src="./assets/img/tabbar/profile_active.svg" alt="">
<div slot="item-text">我的</div>
</tab-bar-item>
</tab-bar>
</div>
</template>
<script>
import TabBar from './components/tabbar/TabBar.vue';
import TabBarItem from './components/tabbar/TabBarItem.vue';
export default {
name: "App",
components: {
TabBar,
TabBarItem
},
};
</script>
<style>
@import "./assets/css/reset.css";
</style>
// TabBarItem.vue
<template>
<div class="tab-bar-item" @click="itemClick">
<div v-if="!isActive"><slot name="item-icon"></slot></div>
<div v-else><slot name="item-icon-active"></slot></div>
<div :class="{active: isActive}"><slot name="item-text"></slot></div>
</div>
</template>
<script>
export default({
name: 'TabBarItem',
// 父传子(传路径)
props: {
path: String
},
data() {
return {
// isActive: false
}
},
computed: {
isActive() {
// return this.$route.path.indexOf(this.path) !== -1
return this.$route.path === this.path
}
},
methods: {
itemClick() {
this.$router.replace(this.path).catch(err => err)
}
}
})
</script>
<style scoped>
.tab-bar-item {
flex: 1;
text-align: center;
height: 49px;
font-size: 14px;
}
.tab-bar-item img {
width: 24px;
height: 24px;
margin-top: 3px;
vertical-align: center;
}
.active {
color:red
}
</style>
第6步(动态传入激活颜色)
<// App.vue
<template>
<div id="app">
<tab-bar>
// 从<tab-bar-item>父组件传入一个activeColor属性
<tab-bar-item path="/home" activeColor="pink">
<img slot="item-icon" src="./assets/img/tabbar/home.svg" alt="">
<img slot="item-icon-active" src="./assets/img/tabbar/home_active.svg" alt="">
<div slot="item-text">主页</div>
</tab-bar-item>
<tab-bar-item path="/category" activeColor="blue">
<img slot="item-icon" src="./assets/img/tabbar/category.svg" alt="">
<img slot="item-icon-active" src="./assets/img/tabbar/category_active.svg" alt="">
<div slot="item-text">分类</div>
</tab-bar-item>
<tab-bar-item path="/cart" activeColor="green">
<img slot="item-icon" src="./assets/img/tabbar/shopcart.svg" alt="">
<img slot="item-icon-active" src="./assets/img/tabbar/shopcart_active.svg" alt="">
<div slot="item-text">购物车</div>
</tab-bar-item>
<tab-bar-item path="/profile">
<img slot="item-icon" src="./assets/img/tabbar/profile.svg" alt="">
<img slot="item-icon-active" src="./assets/img/tabbar/profile_active.svg" alt="">
<div slot="item-text">我的</div>
</tab-bar-item>
</tab-bar>
</div>
</template>
<script>
import TabBar from './components/tabbar/TabBar.vue';
import TabBarItem from './components/tabbar/TabBarItem.vue';
export default {
name: "App",
components: {
TabBar,
TabBarItem
},
};
</script>
<style>
@import "./assets/css/reset.css";
</style>
<template>
<div class="tab-bar-item" @click="itemClick">
<div v-if="!isActive"><slot name="item-icon"></slot></div>
<div v-else><slot name="item-icon-active"></slot></div>
<div :style="activeStyle"><slot name="item-text"></slot></div>
</div>
</template>
<script>
export default({
name: 'TabBarItem',
// 父传子(传路径)
props: {
path: String,
// 接收父组件传入的activeColor
activeColor: {
type: String,
default: 'red'
}
},
data() {
return {
}
},
computed: {
isActive() {
// return this.$route.path.indexOf(this.path) !== -1
return this.$route.path === this.path
},
activeStyle() {
return this.isActive ? {color: this.activeColor} : {}
}
},
methods: {
itemClick() {
this.$router.replace(this.path).catch(err => err)
}
}
})
</script>
<style scoped>
.tab-bar-item {
flex: 1;
text-align: center;
height: 49px;
font-size: 14px;
}
.tab-bar-item img {
width: 24px;
height: 24px;
margin-top: 3px;
vertical-align: center;
}
.active {
color:red
}
</style>
第7步(最终版本)
将App.vue中关于TabBar的组件信息全部封装到MainTabBar.vue中
App.vue
// App.vue
<template>
<div id="app">
<main-tab-bar/>
</div>
</template>
<script>
import MainTabBar from "./components/mainTabbar/MainTabBar.vue";
export default {
name: "App",
components: {
MainTabBar,
},
};
</script>
<style>
@import "./assets/css/reset.css";
</style>
MainTabBar.vue
// MainTabBar.vue
<template>
<tab-bar>
<tab-bar-item path="/home" activeColor="red">
// cli4可以通过@(@=src)给路径起别名(@/assets/..)
<img slot="item-icon" src="@/assets/img/tabbar/home.svg" alt="">
<img slot="item-icon-active" src="@/assets/img/tabbar/home_active.svg" alt="">
<div slot="item-text">主页</div>
</tab-bar-item>
<tab-bar-item path="/category" activeColor="red">
<img slot="item-icon" src="@/assets/img/tabbar/category.svg" alt="">
<img slot="item-icon-active" src="@/assets/img/tabbar/category_active.svg" alt="">
<div slot="item-text">分类</div>
</tab-bar-item>
<tab-bar-item path="/cart" activeColor="red">
<img slot="item-icon" src="@/assets/img/tabbar/shopcart.svg" alt="">
<img slot="item-icon-active" src="@/assets/img/tabbar/shopcart_active.svg" alt="">
<div slot="item-text">购物车</div>
</tab-bar-item>
<tab-bar-item path="/profile">
<img slot="item-icon" src="@/assets/img/tabbar/profile.svg" alt="">
<img slot="item-icon-active" src="@/assets/img/tabbar/profile_active.svg" alt="">
<div slot="item-text">我的</div>
</tab-bar-item>
</tab-bar>
</template>
<script>
import TabBar from '@/components/tabbar/TabBar.vue';
import TabBarItem from '@/components/tabbar/TabBarItem.vue';
export default {
name: 'MainTabBar',
components: {
TabBar,
TabBarItem
}
}
</script>
TabBar.vue
// TabBar.vue
<template>
<div id="tab-bar">
<slot></slot>
</div>
</template>
<script>
export default {
name: "TabBar",
};
</script>
<style scoped>
#tab-bar {
display: flex;
background-color: #f6f6f6;
/* 置于页面最低端 */
position: fixed;
left: 0;
right: 0;
bottom: 0;
/* 阴影 */
box-shadow: 0 -1px 1px rgba(100, 100, 100, 0.2);
}
</style>
TabBarItem.vue
// TabBarItem.vue
<template>
<div class="tab-bar-item" @click="itemClick">
<div v-if="!isActive"><slot name="item-icon"></slot></div>
<div v-else><slot name="item-icon-active"></slot></div>
<div :style="activeStyle"><slot name="item-text"></slot></div>
</div>
</template>
<script>
export default({
name: 'TabBarItem',
// 父传子(传路径)
props: {
path: String,
activeColor: {
type: String,
default: 'red'
}
},
data() {
return {
}
},
computed: {
isActive() {
// return this.$route.path.indexOf(this.path) !== -1
return this.$route.path === this.path
},
activeStyle() {
return this.isActive ? {color: this.activeColor} : {}
}
},
methods: {
itemClick() {
this.$router.replace(this.path).catch(err => err)
}
}
})
</script>
<style scoped>
.tab-bar-item {
flex: 1;
text-align: center;
height: 49px;
font-size: 14px;
}
.tab-bar-item img {
width: 24px;
height: 24px;
margin-top: 3px;
vertical-align: center;
}
.active {
color:red
}
</style>
router.index.js
// router.index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
const Home = () => import('../views/home/Home.vue')
const Category =() => import('../views/category/Category.vue')
const Cart =() => import('../views/cart/Cart.vue')
const Profile =() => import('../views/profile/Profile.vue')
Vue.use(VueRouter)
const routes = [
{
path: '/',
redirect: '/home'
},
{
path: '/home',
component: Home
},
{
path: '/category',
component: Category
},
{
path: '/cart',
component: Cart
},
{
path: '/profile',
component: Profile
}
]
const router = new VueRouter ({
routes,
mode: 'history'
})
export default router
Loading...