You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							227 lines
						
					
					
						
							6.1 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							227 lines
						
					
					
						
							6.1 KiB
						
					
					
				
								<!-- 底部导航栏 -->
							 | 
						|
								<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>
							 |