uniapp再次封装uni-nav-bar导航栏组件

发布于:2025-03-26 ⋅ 阅读:(34) ⋅ 点赞:(0)
<!-- components/custom-nav-bar/custom-nav-bar.vue -->
<template>
  <view class="custom-nav" :style="{ backgroundColor: bgColor }">
    <!-- 状态栏占位 -->
    <view class="status-bar" :style="{ height: statusBarHeight + 'px' }"></view>
    
    <!-- 导航栏内容区 -->
    <view class="nav-content" :style="navContentStyle">
      <view class="nav-left" @click="onClickLeft">
        <slot name="left">
          <view v-if="leftIcon" class="left-icon">
            <uni-icons :type="leftIcon" size="20" color="#333"></uni-icons>
          </view>
        </slot>
      </view>
      
      <view class="nav-center">
        <slot name="center">
          <text class="nav-title" :style="{ color: titleColor }">{{ title }}</text>
        </slot>
      </view>
      
      <view class="nav-right">
        <slot name="right">
          <view v-if="rightIcon" class="right-icon" @click="onClickRight">
            <uni-icons :type="rightIcon" size="20" color="#333"></uni-icons>
          </view>
        </slot>
      </view>
    </view>
  </view>
  
  <!-- 占位,防止内容被遮挡 -->
  <view v-if="fixed" class="nav-placeholder" :style="placeholderStyle"></view>
</template>

<script>
export default {
  name: 'CustomNavBar',
  props: {
    // 标题
    title: {
      type: String,
      default: ''
    },
    // 标题颜色
    titleColor: {
      type: String,
      default: '#333333'
    },
    // 左侧图标
    leftIcon: {
      type: String,
      default: 'left'
    },
    // 右侧图标
    rightIcon: {
      type: String,
      default: ''
    },
    // 背景颜色
    bgColor: {
      type: String,
      default: '#FFFFFF'
    },
    // 是否固定在顶部
    fixed: {
      type: Boolean,
      default: true
    }
  },
  
  data() {
    return {
      statusBarHeight: 0, // 状态栏高度
      navBarHeight: 44, // 导航栏高度
      menuButtonInfo: null // 胶囊按钮信息
    }
  },
  
  computed: {
    // 导航栏内容样式
    navContentStyle() {
      let style = {
        height: this.navBarHeight + 'px'
      }
      
      // #ifdef MP-WEIXIN
      if (this.menuButtonInfo) {
        style = {
          ...style,
          height: (this.menuButtonInfo.height + 8) + 'px',
          paddingTop: (this.menuButtonInfo.top - this.statusBarHeight) + 'px',
          paddingRight: (this.systemInfo.windowWidth - this.menuButtonInfo.left) + 'px'
        }
      }
      // #endif
      
      return style
    },
    
    // 占位符样式
    placeholderStyle() {
      return {
        height: (this.statusBarHeight + this.navBarHeight) + 'px'
      }
    }
  },
  
  created() {
    this.initNavBar()
  },
  
  methods: {
    // 初始化导航栏
    initNavBar() {
      // 获取系统信息
      const systemInfo = uni.getSystemInfoSync()
      this.systemInfo = systemInfo
      this.statusBarHeight = systemInfo.statusBarHeight
      
      // #ifdef MP-WEIXIN
      // 获取胶囊按钮信息
      this.menuButtonInfo = uni.getMenuButtonBoundingClientRect()
      this.navBarHeight = (this.menuButtonInfo.height + 8)
      // #endif
      
      // #ifdef APP-PLUS
      // 判断是否是全面屏
      const isIphoneX = this.isIphoneX(systemInfo)
      if (isIphoneX) {
        this.navBarHeight = 48
      }
      // #endif
    },
    
    // 判断是否是全面屏
    isIphoneX(systemInfo) {
      const model = systemInfo.model.toLowerCase()
      const isIphoneX = model.includes('iphone x') || 
                       model.includes('iphone 11') ||
                       model.includes('iphone 12') ||
                       model.includes('iphone 13') ||
                       model.includes('iphone 14') ||
                       model.includes('iphone 15')
      return isIphoneX
    },
    
    // 左侧点击事件
    onClickLeft() {
      this.$emit('clickLeft')
    },
    
    // 右侧点击事件
    onClickRight() {
      this.$emit('clickRight')
    }
  }
}
</script>

<style lang="scss">
.custom-nav {
  position: relative;
  
  &.fixed {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 999;
  }
}

.status-bar {
  width: 100%;
}

.nav-content {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 15px;
  box-sizing: border-box;
  
  .nav-left, .nav-right {
    min-width: 44px;
    height: 100%;
    display: flex;
    align-items: center;
  }
  
  .nav-center {
    flex: 1;
    text-align: center;
    
    .nav-title {
      font-size: 16px;
      font-weight: 500;
    }
  }
  
  .left-icon, .right-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 100%;
  }
}

/* #ifdef MP-WEIXIN */
.custom-nav {
  padding-right: var(--window-right, 0);
  
  .nav-content {
    .nav-left {
      padding-left: 10px;
    }
    
    .nav-right {
      padding-right: 10px;
    }
  }
}
/* #endif */

/* #ifdef APP-PLUS */
.custom-nav {
  .nav-content {
    padding: 0 12px;
  }
}
/* #endif */
</style>

使用示例

<!-- pages/index/index.vue -->
<template>
  <view class="page">
    <!-- 基础用法 -->
    <custom-nav-bar 
      title="页面标题"
      @clickLeft="handleBack"
    />
    
    <!-- 自定义样式 -->
    <custom-nav-bar 
      title="自定义导航栏"
      titleColor="#FFFFFF"
      bgColor="#007AFF"
      rightIcon="more"
      @clickLeft="handleBack"
      @clickRight="handleMore"
    />
    
    <!-- 自定义内容 -->
    <custom-nav-bar>
      <template #left>
        <view class="custom-left">返回</view>
      </template>
      
      <template #center>
        <view class="custom-center">
          <image src="/static/logo.png" mode="aspectFit" />
        </view>
      </template>
      
      <template #right>
        <view class="custom-right">
          <button size="mini">操作</button>
        </view>
      </template>
    </custom-nav-bar>
    
    <!-- 页面内容 -->
    <view class="content">
      页面内容
    </view>
  </view>
</template>

<script>
export default {
  methods: {
    handleBack() {
      uni.navigateBack({
        delta: 1
      })
    },
    
    handleMore() {
      uni.showToast({
        title: '点击了更多'
      })
    }
  }
}
</script>

<style lang="scss">
.page {
  min-height: 100vh;
  background-color: #f5f5f5;
}

.content {
  padding: 15px;
}

.custom-left {
  font-size: 14px;
  color: #333;
}

.custom-center {
  height: 30px;
  
  image {
    height: 100%;
    width: auto;
  }
}

.custom-right {
  button {
    margin: 0;
  }
}
</style>

组件的主要特点:

  1. 自适应状态栏高度
  1. 支持自定义标题、图标和颜色
  1. 支持自定义左中右三个插槽
  1. 适配微信小程序胶囊按钮
  1. 适配 iPhone 全面屏
  1. 支持固定顶部

使用方法:

  1. 将组件文件放入项目的 components 目录
  2. 在页面中引入并注册组件