mpvue微信小程序自定义导航栏NavBar组件封装

mpvue微信小程序自定义导航栏形态有4种如图:

mpvue微信小程序自定义导航栏NavBar组件封装_第1张图片

形态使用如下

// 第一种
<div class="place-holder" :style="{ height: menuSettings.navBarHeight + 'px'}">div>
<nav-bar title="司机首页" />
// 第二种
<div class="place-holder" :style="{ height: menuSettings.navBarHeight + 'px'}">div>
<nav-bar title="自助排队" :isGoBack="true" :isGoBackEvent="true" />
// 第三种
<div class="place-holder" :style="{ height: menuSettings.navBarHeight + 'px'}">div>
<nav-bar title="我的任务" :showSearch="true" @search="search" />
// 第四种
<div class="place-holder" :style="{ height: menuSettings.navBarHeight + 'px'}">div>
<nav-bar title="自助排队" :isGoBack="true" :backMain="true" :isGoBackEvent="true" />
注释:
isGoBack:表是否显示返回icon
isGoBackEvent:表点击返回后是否需要判断确定离开当前页面(作用于新增页面离开时是否需要提示用户当前数据未保存,是否确定离开);紧随@goBack事件
backMain:表是否显示返回主页icon
showSearch:表是否显示搜索icon

小程序自定义导航栏设计

分为三步思路:

1.确定自定义导航栏高度
2.确定右侧囊体宽高以确定自定义左侧囊体
3.具体细节

1、确定不同机型下导航栏的高度(即小程序自由导航高度)

新建一个systemSetting.js文件,代码如下

const statusBar = {
  android: 24,
  iPhone: 20,
  newModel: 44, // 长屏下默认高度
};
const navigationBar = {
  default: 44,
};
const navBarStyle = {
  background: "",
  color: "",
  textAlignAndroid: "left",
  textAlignIPhone: "center",
  textAlign: "center",
  fontSize: 18,
};
export { statusBar, navigationBar, navBarStyle };

2、确定囊体大小

根据wx.getMenuButtonBoundingClientRect()接口的返回值包括了囊体的宽高,在组件代码中即可调用来取得

3、组件具体实现

a.根据前面获取到的导航栏高和囊体大小,将这些值存入vuex以后续调用
b.在模板处定义对应的不同场景--有home及back或search或单独的back囊体或无该左侧囊体;在js的判断中根据具体条件场景去进一步判断是否需要显示哪一种左侧囊体。
c.在各自对应的需要加入导航栏的页面引入该组件,因为原导航栏占位不再且自定义导航栏position被设置为fixed,故记得在使用的页面对应引入高度占位。如下place-holder的div
    

基于mpvue自定义导航栏NavBar组件完整代码

<template>
  <div class="navigation-bar flex-box flex-ver-v" @click.stop="full" :style="navigationBarStyle">
    
    <div v-if="showSearch" :style="searchStyle" class="search-home">
      <van-icon @click.stop="searchShow" v-if="!show" name="search" color="#fff" size="20px" />
      <div class="seach-content" v-if="show">
        <van-search
          :value="accountInput"
          placeholder="请输入搜索关键词"
          use-action-slot
          show-action
          focus
          @change="onChange"
        >
          <view slot="action" @click.stop="onClick">搜索view>
        van-search>
      div>
    div>
    
    <div v-else class="back-margin-left" :style="searchStyle">
      
      <div v-if="isGoBack&&backMain" class="back-home">
        <div
          class="back flex-box flex-ver"
          @click.stop="goBack"
          v-if="isGoBack"
          :style="{height: menuSettings.height + 'px'}"
        >
          <van-icon name="arrow-left" />
        div>
        <div class="line">div>
        <div
          class="home flex-box flex-ver"
          @click="goHome"
          v-if="backMain"
          :style="{height: menuSettings.height + 'px'}"
        >
          <van-icon name="wap-home-o" />
        div>
      div>
      
      <div v-else>
        <div
          class="flex-box flex-ver-v"
          @click.stop="goBack"
          v-if="isGoBack"
          :style="{height: menuSettings.height + 'px'}"
        >
          <van-icon name="arrow-left" color="#fff" size="20px" />
        div>
      div>
    div>
    
    <div v-if="!show" class="title" :style="titleStyle">{{title}}div>
    
    <div class="back-margin-right" :style="rightStyle">div>
    
    <div
      v-if="show"
      :class="[show ?'fullopacity':'']"
      :style="{top: height + 'px'}"
      @click.stop="full"
    >div>
  div>
template>

<script>
import { statusBar, navigationBar, navBarStyle } from '@/utils/systemSetting'
import variables from '@/static/styles/variables.scss'
export default {
  name: 'NavBar',
  props: {
    title: {
      type: String,
      default: ''
    },
    // title字体颜色
    color: {
      type: String,
      default: `${variables.mainWhite}`
    },
    // navBar背景颜色
    background: {
      type: String,
      default: `${variables.mainBg}`
    },
    // 是否显示搜索icon
    showSearch: {
      type: Boolean,
      default: false
    },
    // 是否显示返回首页icon
    backMain: {
      type: Boolean,
      default: false
    },
    // 是否返回上一层
    isGoBack: {
      type: Boolean,
      default: false
    },
    // 返回上一层触发方法
    isGoBackEvent: {
      type: Boolean,
      default: false
    }
  },
  /**
   * 组件的初始数据
   */
  data () {
    return {
      paddingTop: statusBar.android, // 默认为android大部分普通机型高度
      height: navigationBar.default + statusBar.android,
      barStyle: {},
      accountInput: '', // 搜索内容
      marginTop: 0,
      scale: 1,
      show: false,
      showBackIconLength: 1
    }
  },
  computed: {
    menuSettings () {
      return this.$store.state.common.menuSettings
    },
    userType () {
      return this.$store.state.user.userType
    },
    // navBar样式
    navigationBarStyle () {
      let background = this.background || this.barStyle.background || ''
      return `padding-top:${this.paddingTop}px;height:${this.height}px;background:${background};`
    },
    // 左侧搜索icon或显示返回上一步及显示返回首页样式
    searchStyle () {
      return `min-width:${this.menuSettings.width}px;height: ${this.menuSettings.height}px;transform:scale(${this.scale})`
    },
    // nav标题样式
    titleStyle () {
      let color = this.color || this.barStyle.color
      return `text-align:${this.barStyle.textAlign};color: ${color};font-size:${this.barStyle.fontSize}px;line-height:${this.barStyle.height}px`
    },
    // 右侧胶囊样式
    rightStyle () {
      return `min-width:${this.menuSettings.width}px;height: ${this.menuSettings.height}px;`
    }
  },
  mounted () {
    // 获取系统信息
    const systemInfo = wx.getSystemInfoSync()
    const ratio = systemInfo.screenHeight / systemInfo.screenWidth // 高宽比例
    const isNewModel = ratio >= 2
    const isIPhone = systemInfo.model.indexOf('iPhone') >= 0
    const barHeight = systemInfo.statusBarHeight || (isNewModel ? statusBar.newModel : isIPhone ? statusBar.iPhone : statusBar.android)
    this.paddingTop = barHeight
    this.height = barHeight + navigationBar.default
    let barStyle = { ...navBarStyle }
    barStyle.height = navigationBar.default
    this.barStyle = barStyle
    this.getMenuSettings(1, 3)
    this.scale = 1 - (0.5 / this.menuSettings.height)
    this.marginTop = this.menuSettings.top - systemInfo.statusBarHeight + 4
  },
  /**
   * 组件的方法列表
   */
  methods: {
    // vuex存储menuSettings
    getMenuSettings (current, count) {
      // 获取胶囊信息
      let menuSettings = wx.getMenuButtonBoundingClientRect()
      // console.log('menuSettings', menuSettings)
      if (!menuSettings.height || menuSettings.height === 0) {
        if (current > 3) {
          return
        }
        setTimeout(() => {
          this.getMenuSettings(current + 1, count)
        }, 200)
      } else {
        menuSettings.navBarHeight = this.height
        this.$store.commit('common/SET_MENU_SETTINGS', menuSettings)
      }
    },
    // 点击搜索icon,显示输入框
    searchShow () {
      this.show = true
      this.accountInput = ''
    },
    // 输入的内容赋值
    onChange (e) {
      this.accountInput = e.mp.detail
    },
    // 点击搜索按钮,并将值传出
    onClick () {
      this.$emit('search', this.accountInput)
      setTimeout(() => {
        this.show = false
      }, 500)
    },
    // 隐藏搜索框
    full () {
      // console.log(111, this.menuSettings)
      this.show = false
    },
    // 返回首页
    goHome () {
      // 供应商端
      if (this.userType === '0') {
        wx.reLaunch({
          url: '/pages/supplierTask/main'
        })
      }
      // 司机端
      if (this.userType === '1') {
        wx.reLaunch({
          url: '/pages/driverIndex/main'
        })
      }
    },
    // 返回上一层
    goBack () {
      const pages = getCurrentPages()
      this.$emit('goBack')
      if (this.isGoBack && this.isGoBackEvent && pages.length > 1) {
        wx.navigateBack({
          delta: 1
        })
      }
    }
  }
}
script>

<style lang="scss" scoped>
.navigation-bar {
  width: 100vw;
  height: 60px;
  display: flex;
  justify-content: space-between;
  position: fixed;
  top: 0;
  left: 0;
  box-sizing: border-box;
  z-index: 1000;
}
.flex-box{ display:-webkit-box;display:-webkit-flex;display: flex;}
.flex-ver{align-items:center;justify-content: center;}
.flex-ver-v{align-items:center;}
.search-home {
  display: flex;
  align-items: center;
  padding-left: 10px;
  box-sizing: border-box;
  overflow: hidden;
  .back {
    flex: 1;
  }
}
.back-margin-right {
  margin-right: 10px;
}
.back-home {
  display: flex;
  align-items: center;
  background: rgba(255, 255, 255, 0.6);
  border-radius: 16px;
  margin-left: 10px;
  box-sizing: border-box;
  box-shadow: 0 0 1px rgb(207, 207, 207);
  overflow: hidden;
  .back {
    flex: 1;
  }
  .home {
    flex: 1;
  }
  .line {
    width: 1px;
    height: 20px;
    background: rgba(0, 0, 0, 0.2);
    transform: scaleX(0.5);
  }
}
.title {
  flex: 1;
  font-size: 16px;
  box-sizing: border-box;
  padding: 0 2px;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.fullopacity {
  position: fixed;
  left: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  background: rgb(1, 1, 1);
  transition: all 2s;
  opacity: 0.5;
}
style>

注意

每个页面使用NavBar组件的上面都要加如下这句代码来占位

  <div class="place-holder" :style="{ height: menuSettings.navBarHeight + 'px'}">div>

你可能感兴趣的:(mpvue组件封装,小程序,组件化,mpvue)