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.”

Hope to finish watching friends can give a thumbs-up, encourage once