<template>
  <view class="u-wrap">
    <view class="u-menu-wrap">
      <scroll-view
        scroll-y
        scroll-with-animation
        class="u-tab-view menu-scroll-view"
        :scroll-top="scrollTop"
        :scroll-into-view="itemId"
      >
        <view
          v-for="(item, index) in cateList"
          :key="index"
          class="u-tab-item"
          :class="[current == index ? 'u-tab-item-active' : '']"
          @tap.stop="handleLeftMenuClick(index)"
        >
          <text class="u-line-1">{{ item.description }}</text>
        </view>
      </scroll-view>
      <scroll-view
        :scroll-top="scrollRightTop"
        scroll-y
        scroll-with-animation
        class="right-box"
        @scroll="rightScroll"
      >
        <view class="page-view">
          <view
            class="class-item"
            :id="'item' + index"
            v-for="(item, index) in cateList"
            :key="index"
          >
            <view class="item-title">
              <text>{{ item.description }}</text>
            </view>
            <view class="item-container">
              <view
                class="thumb-box"
                v-for="(child, childIndex) in item.children"
                :key="childIndex"
                @click="handleItemClick(child.id, child.name)"
              >
                <image
                  v-if="child.icon != ''"
                  class="item-menu-image"
                  :src="child.icon"
                  mode=""
                ></image>
                <view
                  v-else
                  class="item-menu-image row-c"
                  style="background-color: #f4f6f8"
                  ><text style="font-size: 20rpx; color: #d0d0d0"
                    >加载失败</text
                  ></view
                >
                <view class="item-menu-name">{{ child.name }}</view>
              </view>
            </view>
          </view>
        </view>
      </scroll-view>
    </view>
  </view>
</template>

<script>
import { buildTree } from '@/utils/tools.js';
export default {
  data() {
    return {
      scrollTop: 0, //tab标题的滚动条位置
      oldScrollTop: 0, // tab标题的滚动条旧位置
      current: 0, // 预设当前项的值
      menuHeight: 0, // 左边菜单的高度
      menuItemHeight: 0, // 左边菜单item的高度
      itemId: '', // 栏目右边scroll-view用于滚动的id
      cateList: [], // 渲染的数据
      arr: [], // 储存距离顶部高度的数组
      scrollRightTop: 0, // 右边栏目scroll-view的滚动条高度
      timer: null, // 定时器
    };
  },
  onLoad() {
    this.cateList = buildTree(this.getCache('categories'));
  },
  onReady() {
    this.getMenuItemTop();
  },
  methods: {
    /**
     * 获取一个目标元素的高度
     * @elClass 元素的类名
     * @dataVal 储存高度的对象
     */
    getElRect(elClass, dataVal) {
      new Promise((resolve, reject) => {
        const query = uni.createSelectorQuery().in(this);
        query
          .select('.' + elClass)
          .fields(
            {
              size: true,
            },
            res => {
              // 如果节点尚未生成，res值为null，循环调用执行
              if (!res) {
                setTimeout(() => {
                  this.getElRect(elClass);
                }, 10);
                return;
              }
              this[dataVal] = res.height;
              resolve();
            },
          )
          .exec();
      });
    },
    /**
     * 获取右边菜单每个item到顶部的距离
     * 储存到 arr 数组里面用于后面滚动判断
     */
    getMenuItemTop() {
      new Promise(resolve => {
        let selectorQuery = uni.createSelectorQuery();
        selectorQuery
          .selectAll('.class-item')
          .boundingClientRect(rects => {
            // 如果节点尚未生成，rects值为[](因为用selectAll，所以返回的是数组)，循环调用执行
            if (!rects.length) {
              setTimeout(() => {
                this.getMenuItemTop();
              }, 10);
              return;
            }
            rects.forEach(rect => {
              // 视情况而定，这里减去rects[0].top，是因为第一项顶部可能不是贴到导航栏(比如有个搜索框的情况)
              // this.arr.push(rect.top - rects[0].top);
              this.arr.push(rect.top);
              resolve();
            });
          })
          .exec();
      });
    },
    /**
     * 观测元素相交状态
     * 检测右边scroll-view的id为itemxx的元素与right-box的相交状态
     * 如果跟.right-box底部相交，就动态设置左边栏目的活动状态
     */
    async observer() {
      this.cateList.map((val, index) => {
        let observer = uni.createIntersectionObserver(this);
        observer
          .relativeTo('.right-box', {
            top: 0,
          })
          .observe('#item' + index, res => {
            if (res.intersectionRatio > 0) {
              let id = res.id.substring(4);
              this.leftMenuStatus(id);
            }
          });
      });
    },
    /**
     * 设置左边菜单的滚动状态
     * @index 传入的 ID
     */
    async leftMenuStatus(index) {
      this.current = index;
      // 如果为0，意味着尚未初始化
      if (this.menuHeight == 0 || this.menuItemHeight == 0) {
        await this.getElRect('menu-scroll-view', 'menuHeight');
        await this.getElRect('u-tab-item', 'menuItemHeight');
      }
      // 将菜单活动item垂直居中
      this.scrollTop =
        index * this.menuItemHeight +
        this.menuItemHeight / 2 -
        this.menuHeight / 2;
    },
    /**
     * 点击左边的栏目切换
     * @index 传入的 ID
     */
    async handleLeftMenuClick(index) {
      if (this.arr.length == 0) {
        await this.getMenuItemTop();
      }
      if (index == this.current) return;
      this.scrollRightTop = this.oldScrollTop;
      this.$nextTick(function () {
        this.scrollRightTop = this.arr[index];
        this.current = index;
        this.leftMenuStatus(index);
      });
    },
    /**
     * 右边菜单滚动
     * 如果不存在height2，意味着数据循环已经到了最后一个，设置左边菜单为最后一项即可
     */
    async rightScroll(e) {
      this.oldScrollTop = e.detail.scrollTop;
      if (this.arr.length == 0) {
        await this.getMenuItemTop();
      }
      if (this.timer) return;
      if (!this.menuHeight) {
        await this.getElRect('menu-scroll-view', 'menuHeight');
      }
      setTimeout(() => {
        // 节流
        this.timer = null;
        // scrollHeight为右边菜单垂直中点位置
        // let scrollHeight = e.detail.scrollTop + this.menuHeight / 2;
        // scrollHeight为右边菜单头部位置
        let scrollHeight = e.detail.scrollTop + 20;
        for (let i = 0; i < this.arr.length; i++) {
          let height1 = this.arr[i];
          let height2 = this.arr[i + 1];
          if (!height2 || (scrollHeight >= height1 && scrollHeight < height2)) {
            this.leftMenuStatus(i);
            return;
          }
        }
      }, 10);
    },
    handleItemClick(id,name){
      this.$Router.push('/pages/business/list')
    }
  },
};
</script>

<style scoped>
.u-wrap {
  /* #ifdef H5 */
  height: calc(100vh - var(--window-top));
  /* #endif */
  display: flex;
  flex-direction: column;
  height: 100vh;
}

.u-search-box {
  padding: 18rpx 30rpx;
}

.u-menu-wrap {
  flex: 1;
  display: flex;
  overflow: hidden;
}

.u-tab-view {
  width: 200rpx;
  height: 100%;
}

.u-tab-item {
  height: 110rpx;
  background: #f6f6f6;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 26rpx;
  color: #444;
  font-weight: 400;
  line-height: 1;
}

.u-tab-item-active {
  position: relative;
  color: #06a446;
  font-size: 30rpx;
  font-weight: 500;
  background: #d6ffe7;
}

.u-tab-item-active::before {
  content: '';
  position: absolute;
  border-left: 4px solid #06a446;
  height: 52rpx;
  left: 0;
  top: 29rpx;
}

.u-tab-view {
  height: 100%;
}

.right-box {
  background-color: rgb(250, 250, 250);
}

.page-view {
  padding: 16rpx;
}

.class-item {
  margin-bottom: 30rpx;
  background-color: #fff;
  padding: 16rpx;
  border-radius: 8rpx;
}

.class-item:last-child {
  min-height: 100vh;
}

.item-title {
  font-size: 26rpx;
  color: #06a446;
  font-weight: bold;
}

.item-menu-name {
  margin-top: 8rpx;
  font-weight: normal;
  font-size: 24rpx;
  color: #06a446;
}

.item-container {
  display: flex;
  flex-wrap: wrap;
}

.thumb-box {
  width: 33.333333%;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  margin-top: 20rpx;
}

.item-menu-image {
  width: 120rpx;
  height: 120rpx;
}
</style>
