228 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Vue
		
	
	
			
		
		
	
	
			228 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Vue
		
	
	
<!-- 底部导航栏 -->
 | 
						||
<template>
 | 
						||
  <view class="u-tabbar">
 | 
						||
    <view
 | 
						||
      class="u-tabbar__content"
 | 
						||
      ref="u-tabbar__content"
 | 
						||
      @touchmove.stop.prevent=""
 | 
						||
      :class="[border && 'u-border-top', fixed && 'u-tabbar--fixed', { 'mid-tabbar': midTabBar }]"
 | 
						||
      :style="[tabbarStyle]"
 | 
						||
    >
 | 
						||
      <view class="u-tabbar__content__item-wrapper">
 | 
						||
        <slot></slot>
 | 
						||
      </view>
 | 
						||
      <view v-if="safeAreaInsetBottom" :style="[{ height: safeBottomHeight + 'px' }]"></view>
 | 
						||
    </view>
 | 
						||
    <view
 | 
						||
      class="u-tabbar__placeholder"
 | 
						||
      v-if="placeholder"
 | 
						||
      :style="{
 | 
						||
        height: placeholderHeight + 'px',
 | 
						||
      }"
 | 
						||
    ></view>
 | 
						||
  </view>
 | 
						||
</template>
 | 
						||
 | 
						||
<script>
 | 
						||
  // #ifdef APP-NVUE
 | 
						||
  const dom = uni.requireNativePlugin('dom');
 | 
						||
  // #endif
 | 
						||
  /**
 | 
						||
   * Tabbar 底部导航栏
 | 
						||
   * @description 此组件提供了自定义tabbar的能力。
 | 
						||
   * @property {String | Number}	value				当前匹配项的name
 | 
						||
   * @property {Boolean}			safeAreaInsetBottom	是否为iPhoneX留出底部安全距离(默认 true )
 | 
						||
   * @property {Boolean}			border				是否显示上方边框(默认 true )
 | 
						||
   * @property {String | Number}	zIndex				元素层级z-index(默认 1 )
 | 
						||
   * @property {String}			activeColor			选中标签的颜色(默认 '#1989fa' )
 | 
						||
   * @property {String}			inactiveColor		未选中标签的颜色(默认 '#7d7e80' )
 | 
						||
   * @property {Boolean}			fixed				是否固定在底部(默认 true )
 | 
						||
   * @property {Boolean}			placeholder			fixed定位固定在底部时,是否生成一个等高元素防止塌陷(默认 true )
 | 
						||
   * @property {Object}			customStyle			定义需要用到的外部样式
 | 
						||
   *
 | 
						||
   */
 | 
						||
 | 
						||
  import { deepMerge, addStyle, sleep } from '@/sheep/helper';
 | 
						||
  import sheep from '@/sheep';
 | 
						||
 | 
						||
  export default {
 | 
						||
    name: 'su-tabbar',
 | 
						||
    props: {
 | 
						||
      customStyle: {
 | 
						||
        type: [Object, String],
 | 
						||
        default: () => ({}),
 | 
						||
      },
 | 
						||
      customClass: {
 | 
						||
        type: String,
 | 
						||
        default: '',
 | 
						||
      },
 | 
						||
      // 跳转的页面路径
 | 
						||
      url: {
 | 
						||
        type: String,
 | 
						||
        default: '',
 | 
						||
      },
 | 
						||
      // 页面跳转的类型
 | 
						||
      linkType: {
 | 
						||
        type: String,
 | 
						||
        default: 'navigateTo',
 | 
						||
      },
 | 
						||
      // 当前匹配项的name
 | 
						||
      value: {
 | 
						||
        type: [String, Number, null],
 | 
						||
        default: '',
 | 
						||
      },
 | 
						||
      // 是否为iPhoneX留出底部安全距离
 | 
						||
      safeAreaInsetBottom: {
 | 
						||
        type: Boolean,
 | 
						||
        default: true,
 | 
						||
      },
 | 
						||
      // 是否显示上方边框
 | 
						||
      border: {
 | 
						||
        type: Boolean,
 | 
						||
        default: true,
 | 
						||
      },
 | 
						||
      // 元素层级z-index
 | 
						||
      zIndex: {
 | 
						||
        type: [String, Number],
 | 
						||
        default: 10,
 | 
						||
      },
 | 
						||
      // 选中标签的颜色
 | 
						||
      activeColor: {
 | 
						||
        type: String,
 | 
						||
        default: '#1989fa',
 | 
						||
      },
 | 
						||
      // 未选中标签的颜色
 | 
						||
      inactiveColor: {
 | 
						||
        type: String,
 | 
						||
        default: '#7d7e80',
 | 
						||
      },
 | 
						||
      // 是否固定在底部
 | 
						||
      fixed: {
 | 
						||
        type: Boolean,
 | 
						||
        default: true,
 | 
						||
      },
 | 
						||
      // fixed定位固定在底部时,是否生成一个等高元素防止塌陷
 | 
						||
      placeholder: {
 | 
						||
        type: Boolean,
 | 
						||
        default: true,
 | 
						||
      },
 | 
						||
      midTabBar: {
 | 
						||
        type: Boolean,
 | 
						||
        default: false,
 | 
						||
      },
 | 
						||
    },
 | 
						||
    data() {
 | 
						||
      return {
 | 
						||
        placeholderHeight: 0,
 | 
						||
        safeBottomHeight: sheep.$platform.device.safeAreaInsets.bottom,
 | 
						||
      };
 | 
						||
    },
 | 
						||
    computed: {
 | 
						||
      tabbarStyle() {
 | 
						||
        const style = {
 | 
						||
          zIndex: this.zIndex,
 | 
						||
        };
 | 
						||
        // 合并来自父组件的customStyle样式
 | 
						||
        return deepMerge(style, addStyle(this.customStyle));
 | 
						||
      },
 | 
						||
      // 监听多个参数的变化,通过在computed执行对应的操作
 | 
						||
      updateChild() {
 | 
						||
        return [this.value, this.activeColor, this.inactiveColor];
 | 
						||
      },
 | 
						||
      updatePlaceholder() {
 | 
						||
        return [this.fixed, this.placeholder];
 | 
						||
      },
 | 
						||
    },
 | 
						||
    watch: {
 | 
						||
      updateChild() {
 | 
						||
        // 如果updateChildren中的元素发生了变化,则执行子元素初始化操作
 | 
						||
        this.updateChildren();
 | 
						||
      },
 | 
						||
      updatePlaceholder() {
 | 
						||
        // 如果fixed,placeholder等参数发生变化,重新计算占位元素的高度
 | 
						||
        this.setPlaceholderHeight();
 | 
						||
      },
 | 
						||
    },
 | 
						||
    created() {
 | 
						||
      this.children = [];
 | 
						||
    },
 | 
						||
    mounted() {
 | 
						||
      this.setPlaceholderHeight();
 | 
						||
    },
 | 
						||
    methods: {
 | 
						||
      updateChildren() {
 | 
						||
        // 如果存在子元素,则执行子元素的updateFromParent进行更新数据
 | 
						||
        this.children.length && this.children.map((child) => child.updateFromParent());
 | 
						||
      },
 | 
						||
      getRect(selector, all) {
 | 
						||
        return new Promise((resolve) => {
 | 
						||
          uni.createSelectorQuery()
 | 
						||
            .in(this)
 | 
						||
            [all ? 'selectAll' : 'select'](selector)
 | 
						||
            .boundingClientRect((rect) => {
 | 
						||
              if (all && Array.isArray(rect) && rect.length) {
 | 
						||
                resolve(rect);
 | 
						||
              }
 | 
						||
              if (!all && rect) {
 | 
						||
                resolve(rect);
 | 
						||
              }
 | 
						||
            })
 | 
						||
            .exec();
 | 
						||
        });
 | 
						||
      },
 | 
						||
      // 设置用于防止塌陷元素的高度
 | 
						||
      async setPlaceholderHeight() {
 | 
						||
        if (!this.fixed || !this.placeholder) return;
 | 
						||
        // 延时一定时间
 | 
						||
        await sleep(20);
 | 
						||
        // #ifndef APP-NVUE
 | 
						||
        this.getRect('.u-tabbar__content').then(({ height = 50 }) => {
 | 
						||
          // 修复IOS safearea bottom 未填充高度
 | 
						||
          this.placeholderHeight = height;
 | 
						||
        });
 | 
						||
        // #endif
 | 
						||
 | 
						||
        // #ifdef APP-NVUE
 | 
						||
        dom.getComponentRect(this.$refs['u-tabbar__content'], (res) => {
 | 
						||
          const { size } = res;
 | 
						||
          this.placeholderHeight = size.height;
 | 
						||
        });
 | 
						||
        // #endif
 | 
						||
      },
 | 
						||
    },
 | 
						||
  };
 | 
						||
</script>
 | 
						||
 | 
						||
<style lang="scss" scoped>
 | 
						||
  .u-tabbar {
 | 
						||
    display: flex;
 | 
						||
    flex: 1;
 | 
						||
    justify-content: center;
 | 
						||
 | 
						||
    &__content {
 | 
						||
      display: flex;
 | 
						||
      flex-direction: column;
 | 
						||
      background-color: #fff;
 | 
						||
      box-shadow: 0px -2px 4px 0px rgba(51, 51, 51, 0.06);
 | 
						||
 | 
						||
      &__item-wrapper {
 | 
						||
        height: 50px;
 | 
						||
        display: flex;
 | 
						||
        justify-content: space-around;
 | 
						||
        align-items: center;
 | 
						||
      }
 | 
						||
    }
 | 
						||
 | 
						||
    .mid-tabbar {
 | 
						||
      border-radius: 30rpx 30rpx 0 0;
 | 
						||
    }
 | 
						||
 | 
						||
    &--fixed {
 | 
						||
      position: fixed;
 | 
						||
      bottom: -1px;
 | 
						||
      left: 0;
 | 
						||
      right: 0;
 | 
						||
    }
 | 
						||
  }
 | 
						||
</style>
 |