Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”
TIP 👉 The enemy should not be resolved knot, each look back. Ming Feng Menglong, Ancient and Modern Novels
preface
In our daily project development, we will involve the point navigation function when we do mobile terminal, so we encapsulate this bottom navigation component.
At the bottom of the navigation
BottomNav component property
1. value
- Select the value (that is, select the name value of the BottomNavPane)
- The value is a string
- Optional The name of the first BottomNavPane by default
2. lazy
- Whether an undisplayed content panel is delayed rendering
- The value is a Boolean type
- The default is false
Style requirements
- The component needs to be wrapped around elements that can be positioned relative to each other. Add styles:
position: relative
BottomNavPane component property
1. name
- English names
- The value is a string
- mandatory
2. icon
- Navigation icon Name
- The value is a string
- The value must be the same as the name of the SVG file in the SRC /assets/icon directory (the name value does not contain the suffix. SVG).
- mandatory
3. label
- The text displayed below the navigation icon
- The value is a string
- mandatory
4. scroll
- Is there a scroll bar
- The value is a Boolean type
- The default value is true
The sample
<template>
<div class="bottom-nav-wrap">
<BottomNav v-model="curNav" :lazy="true">
<BottomNavPane name="home" label="Home page" icon="home">
<h1>Home page content</h1>
</BottomNavPane>
<BottomNavPane name="oa" label="Office" icon="logo">
<h1>Content of the office</h1>
</BottomNavPane>
<BottomNavPane name="page2" label="I" icon="user">
<h1>Personal center</h1>
</BottomNavPane>
</BottomNav>
</div>
</template>
<script>
import { BottomNav, BottomNavPane } from '@/components/m/bottomNav'
export default {
name: 'BottomNavDemo'.components: {
BottomNav,
BottomNavPane
},
data () {
return {
curNav: ' '}}}</script>
<style lang="scss" scoped>
.bottom-nav-wrap {
position: absolute;
top: $app-title-bar-height;
bottom: 0;
left: 0;
right: 0;
}
</style>
Copy the code
BottomNav.vue
<template>
<div class="bottom-nav">
<div class="nav-pane-wrap">
<slot></slot>
</div>
<div class="nav-list">
<div class="nav-item"
v-for="info in navInfos"
:key="info.name"
:class="{active: info.name === curValue}"
@click="handleClickNav(info.name)">
<Icon class="nav-icon" :name="info.icon"></Icon>
<span class="nav-label">{{info.label}}</span>
</div>
</div>
</div>
</template>
<script>
import { generateUUID } from '@/assets/js/utils.js'
export default {
name: 'BottomNav'.props: {
// Select the navigation value.
value: String.// Whether the undisplayed content panel is delayed rendering
lazy: {
type: Boolean.default: false
}
},
data () {
return {
// The unique ID of the component instance
id: generateUUID(),
// The selected navigation value
curValue: this.value,
// Navigation information array
navInfos: [].// Array of navigation panel vUE instances
panes: []}},watch: {
value (val) {
this.curValue = val
},
curValue (val) {
this.$eventBus.$emit('CHANGE_NAV' + this.id, val)
this.$emit('cahnge', val)
}
},
mounted () {
this.calcPaneInstances()
},
beforeDestroy () {
this.$eventBus.$off('CHANGE_NAV' + this.id)
},
methods: {
// Compute navigation panel instance information
calcPaneInstances () {
if (this.$slots.default) {
const paneSlots = this.$slots.default.filter(vnode= > vnode.tag &&
vnode.componentOptions && vnode.componentOptions.Ctor.options.name === 'BottomNavPane')
const panes = paneSlots.map(({ componentInstance }) = > componentInstance)
const navInfos = paneSlots.map(({ componentInstance }) = > {
// console.log(componentInstance.name, componentInstance)
return {
name: componentInstance.name,
label: componentInstance.label,
icon: componentInstance.icon
}
})
this.navInfos = navInfos
this.panes = panes
if (!this.curValue) {
if (navInfos.length > 0) {
this.curValue = navInfos[0].name
}
} else {
this.$eventBus.$emit('CHANGE_NAV' + this.id, this.curValue)
}
}
},
// Navigate to click event handler
handleClickNav (val) {
this.curValue = val
}
}
}
</script>
<style lang="scss" scoped>
.bottom-nav {
display: flex;
flex-direction: column;
height: 100%;
.nav-pane-wrap {
flex: 1;
}
.nav-list {
flex: none;
display: flex;
height: 90px;
background-color: #FFF;
align-items: center;
border-top: 1px solid $base-border-color;
.nav-item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
line-height: 1;
text-align: center;
color: # 666;
.nav-icon {
font-size: 40px;/*yes*/
}
.nav-label {
margin-top: 6px;
font-size: 24px;/*yes*/
}
&.active {
position: relative;
color: $base-color; }}}}</style>
Copy the code
BottomNavPane.vue
<template>
<div v-if="canInit" class="bottom-nav-pane" v-show="show">
<Scroll v-if="scroll">
<slot></slot>
</Scroll>
<slot v-else></slot>
</div>
</template>
<script>
import Scroll from '@/components/base/scroll'
export default {
name: 'BottomNavPane'.components: {
Scroll
},
props: {
// TAB English name
name: {
type: String.required: true
},
// The TAB to display
label: {
type: String.required: true
},
// Icon name
icon: {
type: String.required: true
},
// Whether there is a scroll bar
scroll: {
type: Boolean.default: true
}
},
data () {
return {
// Whether to display
show: false.// Whether it has already been displayed
hasShowed: false}},computed: {
canInit () {
return (!this.$parent.lazy) || (this.$parent.lazy && this.hasShowed)
}
},
created () {
this.$eventBus.$on('CHANGE_NAV' + this.$parent.id, val= > {
if (val === this.name) {
this.show = true
this.hasShowed = true
} else {
this.show = false}}}})</script>
<style lang="scss" scoped>
.bottom-nav-pane {
height: 100%;
position: relative;
}
</style>
Copy the code
/** * Bottom icon navigation component */
import BaseBottomNav from './BottomNav.vue'
import BaseBottomNavPane from './BottomNavPane.vue'
export const BottomNav = BaseBottomNav
export const BottomNavPane = BaseBottomNavPane
Copy the code
“Feel free to discuss in the comments section.”