<template>
  <view class="tn-tabs-class tn-tabs" :class="[backgroundColorClass]" :style="{backgroundColor: backgroundColorStyle, marginTop: $tn.string.getLengthUnitValue(top, 'px')}">
    
    <!-- _tgetRect()对组件根节点无效，因为写了.in(this)，故这里获取内层接点尺寸 -->
    <view :id="id">
      <scroll-view scroll-x class="tn-tabs__scroll-view" :scroll-left="scrollLeft" scroll-with-animation>
        <view class="tn-tabs__scroll-view__box" :class="{'tn-tabs__scroll-view--flex': !isScroll}">
          <!-- item -->
          <view
            v-for="(item, index) in list"
            :key="index"
            :id="'tn-tabs__scroll-view__item-' + index"
            class="tn-tabs__scroll-view__item tn-text-ellipsis"
            :style="[tabItemStyle(index)]"
            @tap="clickTab(index)"
          >
            <tn-badge v-if="item[count] || item['count']" backgroundColor="tn-bg-red" fontColor="#FFFFFF" :absolute="true" :top="badgeOffset[0] || 0" :right="badgeOffset[1] || 0">{{ item[count] || item['count']}}</tn-badge>
            {{ item[name] || item['name'] }}
          </view>
          
          <!-- 底部滑块 -->
          <view v-if="showBar" class="tn-tabs__bar" :style="[tabBarStyle]"></view>
        </view>
      </scroll-view>
    </view>
  </view>
</template>

<script>
  import componentsColor from '../../libs/mixin/components_color.js'
  export default {
    mixins: [componentsColor],
    name: 'tn-tabs',
    props: {
      // 标签列表
      list: {
        type: Array,
        default() {
          return []
        }
      },
      // 列表数据tab名称的属性
      name: {
        type: String,
        default: 'name'
      },
      // 列表数据微标数量的属性
      count: {
        type: String,
        default: 'count'
      },
      // 当前活动的tab索引
      current: {
        type: Number,
        default: 0
      },
      // 菜单是否可以滑动
      isScroll: {
        type: Boolean,
        default: true
      },
      // 高度
      height: {
        type: Number,
        default: 80
      },
      // 距离顶部的距离(px)
      top: {
        type: Number,
        default: 0
      },
      // item的宽度
      itemWidth: {
        type: [String, Number],
        default: 'auto'
      },
      // 过渡动画时长
      duration: {
        type: Number,
        default: 0.3
      },
      // 选中时的颜色
      activeColor: {
        type: String,
        default: '#01BEFF'
      },
      // 未被选中时的颜色
      inactiveColor: {
        type: String,
        default: '#080808'
      },
      // 选中的item样式
      activeItemStyle: {
        type: Object,
        default() {
          return {}
        }
      },
      // 是否显示底部滑块
      showBar: {
        type: Boolean,
        default: true
      },
      // 底部滑块的宽度
      barWidth: {
        type: Number,
        default: 40
      },
      // 底部滑块的高度
      barHeight: {
        type: Number,
        default: 6
      },
      // 自定义底部滑块的样式
      barStyle: {
        type: Object,
        default() {
          return {}
        }
      },
      // 单个tab的左右内边距
      gutter: {
        type: Number,
        default: 30
      },
      // 微标的偏移数[top, right]
      badgeOffset: {
        type: Array,
        default() {
          return [20, 22]
        }
      },
      // 是否加粗字体
      bold: {
        type: Boolean,
        default: false
      }
    },
    computed: {
      // 底部滑块样式
      tabBarStyle() {
        let style = {
          width: this.$tn.string.getLengthUnitValue(this.barWidth),
          height: this.$tn.string.getLengthUnitValue(this.barHeight),
          borderRadius: `${this.barHeight / 2}rpx`,
          backgroundColor: this.activeColor,
          opacity: this.barMoveFirst ? 0 : 1,
          transform: `translate(${this.scrollBarLeft}px, -100%)`,
          transitionDuration: this.barMoveFirst ? '0s' : `${this.duration}s`
        }
        Object.assign(style, this.barStyle)
        return style
      },
      // tabItem样式
      tabItemStyle() {
        return index => {
          let style = {
            width: this.$tn.string.getLengthUnitValue(this.itemWidth),
            height: this.$tn.string.getLengthUnitValue(this.height),
            lineHeight: this.$tn.string.getLengthUnitValue(this.height),
            fontSize: this.fontSizeStyle || '28rpx',
            padding: this.isScroll ? `0 ${this.gutter}rpx` : '',
            flex: this.isScroll ? 'auto' : '1',
            transitionDuration: `${this.duration}s`
          }
          if (index === this.currentIndex) {
            if (this.bold) {
              style.fontWeight = 'bold'
            }
            style.color = this.activeColor
            Object.assign(style, this.activeItemStyle)
          } else {
            style.color = this.inactiveColor
          }
          return style
        }
      }
    },
    data() {
      return {
        // id值
        id: this.$tn.uuid(),
        // 滚动scroll-view的左边距离
        scrollLeft: 0,
        // 存放查询后tab菜单的节点信息
        tabQueryInfo: [],
        // 组件宽度
        componentWidth: 0,
        // 底部滑块的移动距离
        scrollBarLeft: 0,
        // 组件到屏幕左边的巨鹿
        componentLeft: 0,
        // 当前选中的itemIndex
        currentIndex: this.current,
        // 标记底部滑块是否第一次移动，第一次移动的时候不触发动画
        barMoveFirst: true
      }
    },
    watch: {
      // 监听tab的变化，重新计算tab菜单信息
      list(newValue, oldValue) {
        // list变化时，重置内部索引，防止出现超过数据边界的问题
        if (newValue.length !== oldValue.length) this.currentIndex = 0
        this.$nextTick(() => {
          this.init()
        })
      },
      current: {
        handler(val) {
          this.$nextTick(() => {
            this.currentIndex = val
            this.scrollByIndex()
          })
        },
        immediate: true
      }
    },
    mounted() {
      this.init()
    },
    methods: {
      // 初始化变量
      async init() {
        // 获取tabs组件的信息
        let tabRect = await this._tGetRect('#' + this.id)
        // 计算组件的宽度
        this.componentLeft = tabRect.left
        this.componentWidth = tabRect.width
        this.getTabRect()
      },
      // 点击tab菜单
      clickTab(index) {
        if (index === this.currentIndex) return
        this.$emit('change', index)
      },
      // 查询tab的布局信息
      getTabRect() {
        let query = uni.createSelectorQuery().in(this)
        // 遍历所有的tab
        for (let i = 0; i < this.list.length; i++) {
          query.select(`#tn-tabs__scroll-view__item-${i}`).fields({
            size: true,
            rect: true
          })
        }
        query.exec((res) => {
          this.tabQueryInfo = res
          // 初始滚动条和底部滑块的位置
          this.scrollByIndex()
        })
      },
      // 滚动scrollView，让活动的tab处于屏幕中间
      scrollByIndex() {
        // 当前获取tab的布局信息
        let tabInfo = this.tabQueryInfo[this.currentIndex]
        if (!tabInfo) return
        
        // 活动tab的宽度
        let tabWidth = tabInfo.width
        // 活动item的左边到组件左边的距离
        let offsetLeft = tabInfo.left - this.componentLeft
        // 计算scroll-view移动的距离
        let scrollLeft = offsetLeft - (this.componentWidth - tabWidth) / 2
        this.scrollLeft = scrollLeft < 0 ? 0 : scrollLeft
        
        // 计算当前滑块需要移动的距离，当前活动item的中点到左边的距离减去滑块宽度的一半
        let left = tabInfo.left + tabInfo.width / 2 - this.componentLeft
        
        // 计算当前活跃item到组件左边的距离
        this.scrollBarLeft = left - uni.upx2px(this.barWidth) / 2
        
        // 防止在计算时出错，所以延迟执行标记不是第一次移动
        if (this.barMoveFirst) {
          setTimeout(() => {
            this.barMoveFirst = false
          }, 100)
        }
      }
    }
  }
</script>

<style lang="scss" scoped>
  
  /* #ifndef APP-NVUE */
  ::-webkit-scrollbar {
    display: none;
    width: 0 !important;
    height: 0 !important;
    -webkit-appearance: none;
    background: transparent;
  }
  /* #endif */
  
  /* #ifdef H5 */
  // 通过样式穿透，隐藏H5下，scroll-view下的滚动条
  scroll-view ::v-deep ::-webkit-scrollbar {
  	display: none;
  	width: 0 !important;
  	height: 0 !important;
  	-webkit-appearance: none;
  	background: transparent;
  }
  /* #endif */
  
  .tn-tabs {
    &__scroll-view {
      position: relative;
      width: 100%;
      white-space: nowrap;
      
      &__box {
        position: relative;
        /* #ifdef MP-TOUTIAO */
        white-space: nowrap;
        /* #endif */
      }
      
      &__item {
        position: relative;
        /* #ifndef APP-NVUE */
        display: inline-block;
        /* #endif */
        text-align: center;
        transition-property: background-color, color;
      }
      
      &--flex {
        display: flex;
        flex-direction: row;
        justify-content: space-between;
      }
    }
    
    &__bar {
      position: absolute;
      bottom: 0;
    }
  }
</style>
