<template>
  <view class="cv-tab-lists">
    <!-- tab按钮 -->
    <view class="cv-tab-lists-tab-area" v-if="disableTab == false">
      <scroll-view
        class="cv-tab-lists-tab-box"
        scroll-x="true"
        :scroll-left="localTabScrollLeft"
        :show-scrollbar="false"
        scroll-with-animation
        @scroll="onTabScroll"
      >
        <view
          class="cv-tab-lists-tab-item"
          v-bind:style="tabItemStyle"
          v-for="(item, index) in localTabLists"
          :key="index"
          :class="'cv-tab-lists-tab-item-id-' + (index + 1)"
        >
          <view class="cv-tab-lists-tab-item-area" @tap="changeTab(index)" :key="index">
            <view class="cv-tab-lists-tab-item-area-title-box">
              <view class="cv-tab-lists-tab-item-area-title-item">
                {{ item.tabTitle }}
                <view class="cv-tab-lists-tab-item-area-title-orderby" v-if="item.orderby">
                  <view class="cv-tab-lists-tab-item-area-title-asc">
                    <view
                      class="cv-tab-lists-tab-item-area-title-asc-item"
                      :class="
                        item.ordersort == 'ASC' && localTabIndex == index ? 'cv-tab-lists-tab-item-area-title-asc-selected' : ''
                      "
                    ></view>
                  </view>
                  <view class="cv-tab-lists-tab-item-area-title-desc">
                    <view
                      class="cv-tab-lists-tab-item-area-title-desc-item"
                      :class="
                        item.ordersort == 'DESC' && localTabIndex == index ? 'cv-tab-lists-tab-item-area-title-desc-selected' : ''
                      "
                    ></view>
                  </view>
                </view>
              </view>
            </view>
            <view
              class="cv-tab-lists-tab-item-area-border"
              :class="localTabIndex == index ? 'cv-tab-lists-tab-item-area-border-checked' : ''"
            ></view>
          </view>
        </view>
      </scroll-view>
    </view>
    <view class="cv-tab-lists-data-area" :style="disableTab == true ? 'height:100%;' : ''">
      <swiper
        class="cv-tab-lists-data-swiper"
        :current="localTabIndex"
        @change="changeSwiper"
        @animationfinish="onAnimationfinish"
        @transition="onTransition"
        :disableTouch="disableTouch"
      >
        <swiper-item v-for="(item, index) in localTabLists" :key="index" :show-scrollbar="true">
          <scroll-view
            class="cv-tab-lists-data-scroll-box"
            scroll-y="true"
            refresher-background="transparent"
            :refresher-enabled="localTabLists[index].enabled && disableEnabled === false"
            :refresher-triggered="localTabLists[index].triggered"
            :refresher-default-style="refresherDefaultStyle"
            @scrolltolower="onTolower(index)"
            @scrolltoupper="onToupper"
            @scroll="onScroll"
            @refresherrefresh="onRefresh"
            @refresherrestore="onRestore"
            @refresherabort="onAbort"
          >
            <view v-if="localDataLists[index].data.length > 0" :class="listsClass">
              <view v-for="(rows, i) in localDataLists[index].data" :key="i" style="display: contents !important">
                <slot :rows="rows" :index="i" :tabIndex="index" />
              </view>
            </view>
            <view
              class="cv-tab-lists-data-scroll-box-nodata-msg"
              v-if="
                localTabLists[index].loadMoreStatus == 'noMore' &&
                localDataLists[index].data.length <= 0 &&
                localTabLists[index].nodataMsg
              "
            >
              {{ localTabLists[index].nodataMsg }}
            </view>
            <cv-load-more
              :status="localTabLists[index].loadMoreStatus"
              @clickLoadMore="onTolower(index)"
              :contentText="loadMoreText"
              v-else
            />
          </scroll-view>
        </swiper-item>
      </swiper>
    </view>
  </view>
</template>
<script>
import mpMixin from '../../libs/mixin/mpMixin.js';
import mixin from '../../libs/mixin/mixin.js';
export default {
  mixins: [mpMixin, mixin],
  name: 'cvTabLists',
  emits: ['input', 'update:dataList', 'change', 'dataLists'],
  props: {
    tabsConfig: {
      type: [Array, Function],
      default() {
        return [];
      }
    },
    /* 是否禁止左右滑动切换 */
    disableTouch: {
      type: Boolean,
      default() {
        return false;
      }
    },
    /* 是否禁止显示tab */
    disableTab: {
      type: Boolean,
      default() {
        return false;
      }
    },
    /* 是否禁止下拉刷新 */
    disableEnabled: {
      type: Boolean,
      default() {
        return false;
      }
    },
    disableStyle: {
      type: [Boolean, String],
      default: false
    },
    /* 监听更新数据通知 ,接收时间戳 */
    onUpdate: {
      type: Number,
      default() {
        return 0;
      }
    },
    listsClass: {
      type: String,
      default() {
        return '';
      }
    },
    /* 接口公共参数 */
    publicParam: {
      type: Object,
      default() {
        return {};
      }
    },
    publicFunc: {
      type: Function,
      default() {
        return null;
      }
    },
    tabIndex: {
      type: Number,
      default() {
        return 0;
      }
    }
  },
  data() {
    return {
      localTabLists: [],
      localTabIndex: 0,
      localDataLists: [],
      scrollTop: 0, //当前列顶部位置
      triggered: false,
      tabItemStyle: '',
      windowWidth: 414,
      localTabScrollLeft: 0,
      tabScrollLeft: 0,
      loadMoreText: { contentdown: '上拉或点击显示更多', contentrefresh: '正在加载...', contentnomore: '没有更多数据了' }
    };
  },
  computed: {
    refresherDefaultStyle() {
      if (Object.prototype.toString.call(this.disableStyle) === '[object Boolean]') {
        return this.disableStyle ? 'none' : 'black';
      } else if (Object.prototype.toString.call(this.disableStyle) === '[object String]') {
        switch (this.disableStyle) {
          case 'white':
            return 'white';
          case 'none':
            return 'none';
          default:
          case 'black':
            return 'black';
        }
      }
    }
  },
  watch: {
    tabsConfig: {
      deep: true, //深度监听设置为 true
      handler(newV, oldV) {
        this._dealtabsConfig();
      }
    },
    localDataLists(newArr) {
      // console.log('< cvjs >：监听localDataLists的变动');
    },
    localTabIndex(newArr) {
      // console.log('< cvjs >：监听localTabIndex的变动');
    },
    onUpdate(newArr) {
      //重置所有数据
      //console.log('< cvjs >：监听onUpdate', this.onUpdate)
      this._dealtabsConfig();
    },
    publicParam: {
      deep: true, //深度监听设置为 true
      handler(newV) {
        this._dealtabsConfig();
      }
    }
  },
  created() {
    uni.getSystemInfo({
      success: (res) => {
        this.windowWidth = res.windowWidth;
      }
    });
  },
  mounted() {
    this._dealtabsConfig();
  },
  methods: {
    _dealtabsConfig() {
      let localTabLists = [];
      let localDataLists = [];
      for (const key in this.tabsConfig) {
        localTabLists.push({ ...this.tabsConfig[key] });
        localTabLists[key].enabled = true; //开启自定义下拉刷新
        localTabLists[key].loadMoreStatus = 'more'; //cv-load-more 组件 status 状态
        localTabLists[key].scrollTop = 0;
        localTabLists[key].ordersort = this.tabsConfig[key].ordersort == 'DESC' ? 'DESC' : 'ASC'; //默认倒叙
        localTabLists[key].pagesize = this.tabsConfig[key].pagesize || 10; //页长
        localDataLists[key] = {
          page: 1,
          data: []
        };
      }
      this.localTabLists = localTabLists;
      this.localDataLists = localDataLists;
      this.localTabIndex = this.localTabIndex || this.tabIndex;

      if (localTabLists.length > 0 && this.disableTab === false) {
        this._dealTabWidth(localTabLists);
      }
      this.getData();
    },
    /* 设置tab宽度 */
    _dealTabWidth(localTabLists) {
      let tabItemWidth = 0,
        tabWidth = 0,
        tabItemStyle = '';
      /* #ifdef APP-PLUS */
      let query = uni.createSelectorQuery().in(this);
      for (var i = 1; i <= localTabLists.length; ++i) {
        query
          .select('.cv-tab-lists-tab-item-id-' + i)
          .boundingClientRect((data) => {
            tabItemWidth += 40;
            if (data) {
              tabItemWidth += data.width;
            }
          })
          .exec();
      }
      query
        .select('.cv-tab-lists-tab-box')
        .boundingClientRect((data) => {
          tabWidth = data.width;
          /* 写在这里面是为了兼容微信小程序，如果写在外面这句会先执行 */
          if (tabItemWidth <= tabWidth) {
            tabItemStyle += 'width:calc(100% / ' + localTabLists.length + ' - 40px);';
          }
          tabItemStyle += 'top:0;';
          this.tabItemStyle = tabItemStyle;
          this.$forceUpdate();
        })
        .exec();
      /* #endif */
      /* #ifndef APP-PLUS */
      setTimeout(() => {
        for (var i = 1; i <= localTabLists.length; ++i) {
          uni
            .createSelectorQuery()
            .in(this)
            .select('.cv-tab-lists-tab-item-id-' + i)
            .boundingClientRect((data) => {
              tabItemWidth += 40;
              if (data) {
                tabItemWidth += data.width;
              }
            })
            .exec();
        }
        uni
          .createSelectorQuery()
          .in(this)
          .select('.cv-tab-lists-tab-box')
          .boundingClientRect((data) => {
            tabWidth = data.width;
            /* 写在这里面是为了兼容微信小程序，如果写在外面这句会先执行 */
            if (tabItemWidth <= tabWidth) {
              tabItemStyle += 'width:calc(100% / ' + localTabLists.length + ' - 40px);';
            }
            tabItemStyle += 'top:0;';
            this.tabItemStyle = tabItemStyle;
            this.$forceUpdate();
          })
          .exec();
      }, 0);
      /* #endif */
    },
    /* 处理滚动条距离左边的距离 */
    _dealTabScrollLeft() {
      if (this.disableTab == true) {
        return;
      }
      const i = this.localTabIndex + 1,
        windowWidth = this.windowWidth;
      let tabLeft = 0,
        localTabScrollLeft = this.tabScrollLeft,
        tabWidth = 0;
      uni
        .createSelectorQuery()
        .in(this)
        .select('.cv-tab-lists-tab-item-id-' + i)
        .boundingClientRect((data) => {
          tabLeft = data.left - 20;
          tabWidth = data.width + 40;
          localTabScrollLeft += tabLeft - windowWidth / 2 + tabWidth / 2;
          this.localTabScrollLeft = localTabScrollLeft;
        })
        .exec();
    },
    onTabScroll(e) {
      this.tabScrollLeft = e.detail.scrollLeft;
    },
    changeTab(e) {
      if (e != this.localTabIndex) {
        this.localTabIndex = e;
      } else {
        // console.log('< cvjs >：点击当前页Tab 判断是否支持排序切换');
        if (this.localTabLists[e].loadMoreStatus == 'loading') {
          console.warn('< cvjs >：请不要点这么快，上次请求还没结束呢');
          return;
        }
        if (this.localTabLists[e].orderby) {
          //console.warn('< cvjs >：支持排序的Tab哦');
          if (this.localTabLists[e].ordersort == 'DESC') {
            this.localTabLists[e].ordersort = 'ASC';
            //console.warn(this.localTabLists[e].orderby, '排序改为：', this.localTabLists[e].ordersort,)
          } else {
            this.localTabLists[e].ordersort = 'DESC';
            //console.warn(this.localTabLists[e].orderby, '排序改为：', this.localTabLists[e].ordersort)
          }
          // console.log('< cvjs >：重置当前列数据重新发起请求');
          this.$set(this.localDataLists[e], 'page', 1);
          this.$set(this.localDataLists[e], 'data', []);
          this.$forceUpdate();
          this.getData();
        }
      }
    },
    changeSwiper(e) {
      // console.log('< cvjs >：changeSwiper 监听Swiper变化', e.detail.current);
      let current = e.detail.current;
      this.localTabIndex = current;
      this.$emit('change', this.localTabIndex);
      if (this.localDataLists[current].data.length <= 0) {
        // console.log('< cvjs >：第一次打开此区初始化数据');
        this.getData();
      }
      this._dealTabScrollLeft();
      this.$set(this.localTabLists[current], 'enabled', true);
    },
    getData() {
      const localTabLists = this.localTabLists; //Tab数组
      const localTabIndex = this.localTabIndex; //当前区域坐标
      const localDataLists = this.localDataLists; //滑块缓存数据

      if (localTabLists.length <= 0) {
        console.warn('< cvjs >：localTabLists 参数为有误');
        return;
      }
      this.localTabLists[localTabIndex].loadMoreStatus = 'loading';
      let apiUrl = localTabLists[localTabIndex].apiUrl;
      let page = 1;
      if (localDataLists[localTabIndex]) {
        page = localDataLists[localTabIndex].page;
      }
      let apiParam = {
        pagesize: localTabLists[localTabIndex].pagesize,
        page: page
      };
      apiParam = Object.assign(apiParam, this.publicParam); //公共参数
      apiParam = Object.assign(apiParam, localTabLists[localTabIndex].apiParam); //当前列参数
      //排序字段
      if (localTabLists[localTabIndex].orderby) {
        apiParam[localTabLists[localTabIndex].orderby] = localTabLists[localTabIndex].ordersort;
      }
      // console.warn('< cvjs >：apiParam', apiParam)

      if (localTabLists[localTabIndex].apiFunc) {
        if (typeof localTabLists[localTabIndex].apiFunc == 'function') {
          //使用私有方法请求
          localTabLists[localTabIndex]
            .apiFunc(apiParam)
            .then((successRes) => {
              this.dealDataList(successRes.data, localTabIndex, page);
            })
            .catch((error) => {
              this.dealDataList([], localTabIndex, page);
              // console.log('< cvjs >：error', error);
            });
        } else {
          console.warn('< cvjs >：', '拿到 apiFunc 开始请求,此处代码不全');
        }
      } else if (this.publicFunc) {
        /* 使用公共方法请求 */
        if (localTabLists[localTabIndex].apiFlag) {
          //有标识
          this.publicFunc(localTabLists[localTabIndex].apiFlag, apiParam)
            .then((successRes) => {
              this.dealDataList(successRes.data, localTabIndex, page);
            })
            .catch((error) => {
              this.dealDataList([], localTabIndex, page);
              // console.log('< cvjs >：error', error);
            });
        } else {
          //无标识
          this.publicFunc(apiParam)
            .then((successRes) => {
              this.dealDataList(successRes.data, localTabIndex, page);
            })
            .catch((error) => {
              this.dealDataList([], localTabIndex, page);
              // console.log('< cvjs >：error', error);
            });
        }
      } else if (localTabLists[localTabIndex].apiUrl) {
        console.warn('< cvjs >：', '拿到apiUrl 开始请求,此处代码不全');
      } else {
        console.warn('< cvjs >：', '参数有误：apiFunc、apiUrl  至少有一个不为空');
        setTimeout(() => {
          this.localTabLists[localTabIndex].triggered = false;
          //console.log('< cvjs >：请求结束，关闭下拉刷新状态', this.localTabLists[this.localTabIndex].enabled, this.localTabLists[this.localTabIndex].triggered)
          this.localTabLists[localTabIndex].loadMoreStatus = 'noMore';
          this.$forceUpdate();
          setTimeout(() => {
            this.localTabLists[localTabIndex].triggered = 'restore';
            //console.log('< cvjs >：需要重置===========下啦状态')
            this.$forceUpdate();
          }, 10);
        }, 3000);
      }
    },
    /* 整理列表数据 */
    dealDataList(data = [], index, page = 0) {
      let dataLists = [];
      if (!data || data.length <= 0) {
        //没有更多数据了
        this.localTabLists[index].loadMoreStatus = 'noMore';
        this.$forceUpdate();
      } else {
        /* 判断父级是否有解析数据要求 */
        if (this.localTabLists[index].parseFunc) {
          data = this.localTabLists[index].parseFunc(data);
        }
        if (this.localDataLists[index]) {
          dataLists = [...this.localDataLists[index].data];
        }
        for (const key in data) {
          dataLists.push(data[key]);
        }
        this.$set(this.localDataLists[index], 'data', dataLists);
        this.$set(this.localDataLists[index], 'page', page + 1);
        this.$forceUpdate();
        if (data.length < this.localTabLists[index].pagesize) {
          this.localTabLists[index].loadMoreStatus = 'noMore';
        } else {
          this.localTabLists[index].loadMoreStatus = 'more';
        }
      }
      this.$emit('dataLists', { tabIndex: index, data: dataLists });
      if (this.localTabLists[index].triggered == true) {
        // console.log('< cvjs >：关闭下拉刷新状态');
        setTimeout(() => {
          this.localTabLists[index].triggered = false;
          this.$forceUpdate();
        }, 100);
      }
      this.$forceUpdate();
    },
    /* swiper-item 的位置发生改变时会触发 transition 事件，event.detail = {dx: dx, dy: dy}，支付宝小程序暂不支持dx, dy */
    onTransition(e) {
      if (this.localTabLists[this.localTabIndex].enabled !== false) {
        // console.log('< cvjs >：swiper-item 左右滑动时 锁定‘scroll-view’下拉刷新功能', e)
        this.$set(this.localTabLists[this.localTabIndex], 'enabled', false);
      }
    },
    /* 动画结束时会触发 animationfinish 事件，event.detail = {current: current, source: source} */
    onAnimationfinish(e) {
      if (this.localTabLists[this.localTabIndex].enabled !== true && this.localTabLists[this.localTabIndex].scrollTop < 45) {
        // console.log('< cvjs >：swiper-item 动画结束时 开启‘scroll-view’下拉刷新功能', e)
        this.$set(this.localTabLists[this.localTabIndex], 'enabled', true);
      }
    },
    onScroll(e) {
      const scrollTop = e.detail.scrollTop;
      this.localTabLists[this.localTabIndex].scrollTop = scrollTop;
      if (scrollTop == 0 && this.localTabLists[this.localTabIndex].enabled != true) {
        // this.localTabLists[this.localTabIndex].enabled = true;
        this.$forceUpdate();
        // console.log('< cvjs >：滑到顶部了 可以开启下拉刷新了', this.localTabLists[this.localTabIndex].enabled);
      } else if (scrollTop > 0 && this.localTabLists[this.localTabIndex].enabled != false) {
        this.$set(this.localTabLists[this.localTabIndex], 'enabled', false);
        // console.log('< cvjs >：没在顶部 必须禁止下拉刷新了', this.localTabLists[this.localTabIndex].enabled);
      }
    },
    /* 监听回到顶部 */
    onToupper(e) {
      // console.log('监听回到顶部', e);
      this.$set(this.localTabLists[this.localTabIndex], 'enabled', true);
    },
    /* 监听触底 */
    onTolower(e) {
      // console.log('滚到底部咯', e)
      switch (this.localTabLists[e].loadMoreStatus) {
        case 'noMore':
          console.warn('< cvjs >：没有更多了哦', e);
          break;
        case 'loading':
          console.warn('< cvjs >：当前列上一次请求还未结束，请稍后', e);
          break;
        default:
          this.getData();
      }
    },
    /* 自定义下拉刷新被触发 */
    onRefresh() {
      console.warn('< cvjs >：自定义下拉刷新被触发咯========');
      if (this.localTabLists[this.localTabIndex].loadMoreStatus == 'loading') {
        console.warn('< cvjs >：当前列上一次请求还未结束，请稍后');
        return;
      }
      if (this.localTabLists[this.localTabIndex].triggered == true) {
        // console.log('< cvjs >：正在处理下拉刷新业务，因此终止此次触发-----一般情况不会走到这里')
        return;
      }
      // console.log('重置当前列数据');
      this.$set(this.localTabLists[this.localTabIndex], 'triggered', true);
      this.$set(this.localDataLists[this.localTabIndex], 'page', 1);
      this.$set(this.localDataLists[this.localTabIndex], 'data', []);
      this.$forceUpdate();
      this.getData();
    },
    /* 自定义下拉刷新被复位 */
    onRestore() {
      // console.log('< cvjs >：自定义下拉刷新被复位 【onRestore】');
      //this.localTabLists[this.localTabIndex].triggered = 'restore';
    },
    /* 自定义下拉刷新被中止	 */
    onAbort(e) {
      // console.log('< cvjs >：自定义下拉刷新被中止,中止后默认开启下拉刷新，****此处代码运行效率低需要优化****	');
      //检测是否因满足开启下拉刷新而未开启
      if (this.localTabLists[this.localTabIndex].enabled !== true) {
        // this.$set(this.localTabLists[this.localTabIndex], 'enabled', true)
      }
    }
  }
};
</script>
<style>
/* #ifdef MP-WEIXIN */
cv-tab-lists {
  position: relative;
  display: block;
}
/* #endif */
.cv-tab-lists {
  position: absolute;
  height: 100%;
  overflow: auto;
  width: 100%;
}
.cv-tab-lists-tab-area {
  height: 40px;
  background: #ffffff;
  border-bottom: solid 1px rgba(0, 0, 0, 0.1);
}
.cv-tab-lists-tab-area::-webkit-scrollbar {
  display: none;
  /* Chrome Safari */
}
.cv-tab-lists-tab-box {
  width: 100%;
  height: 40px;
  white-space: nowrap;
  font-size: 0;
}
.cv-tab-lists-tab-item {
  display: inline-block;
  width: auto;
  height: 40px;
  margin: 0 20px;
  /* transition: all 0.4s; */
  position: relative;
  top: 40px;
}
.cv-tab-lists-tab-item-area-title-box {
  font-size: 14px;
  padding: 8px 0;
  display: flex;
  height: 20px;
  box-sizing: unset;
}
.cv-tab-lists-data-scroll-box-nodata-msg {
  height: 40px;
  line-height: 40px;
  font-size: 15px;
  color: #999999;
  text-align: center;
}
.cv-tab-lists-tab-item-area-title-item {
  margin: auto;
  position: relative;
}
.cv-tab-lists-tab-item-area-title-orderby {
  height: 20px;
  position: absolute;
  top: 0;
  right: -17px;
}
.cv-tab-lists-tab-item-area-title-asc,
.cv-tab-lists-tab-item-area-title-desc {
  height: 6px;
  width: 12px;
}
.cv-tab-lists-tab-item-area-title-asc {
  margin-top: -3px;
}
.cv-tab-lists-tab-item-area-title-asc-item {
  transition: all 0.2s;
  width: 0;
  height: 0;
  border-width: 6px;
  border-style: solid;
  border-top-color: transparent;
  border-right-color: transparent;
  border-bottom-color: #dedede;
  border-left-color: transparent;
}
.cv-tab-lists-tab-item-area-title-asc-selected {
  border-bottom-color: #ff5400;
}
.cv-tab-lists-tab-item-area-title-desc {
  margin-top: 8px;
}
.cv-tab-lists-tab-item-area-title-desc-item {
  transition: all 0.2s;
  width: 0;
  height: 0;
  border-width: 6px;
  border-style: solid;
  border-top-color: #dedede;
  border-right-color: transparent;
  border-bottom-color: transparent;
  border-left-color: transparent;
}
.cv-tab-lists-tab-item-area-title-desc-selected {
  border-top-color: #ff5400;
}
.cv-tab-lists-tab-item-area-border {
  height: 2px;
  background-color: #ffffff;
  border-radius: 2px;
  margin: 0 1px;
  transition: all 0.2s;
}
.cv-tab-lists-tab-item-area-border-checked {
  background-color: #ff5400;
}
.cv-tab-lists-data-area {
  height: calc(100% - 40px - 1px);
}
.cv-tab-lists-data-swiper {
  height: 100%;
}
.cv-tab-lists-data-scroll-box {
  height: 100%;
}
</style>
