<template>
  <div class="tab-control">
    <el-menu
      ref="menu"
      class="tab-control__menu"
      mode="horizontal"
      :default-active="activeMenu"
    >
      <template v-for="item in mainMenus">
        <!--设置 disabled 和 绑定 vmData 是为了实现双击关闭选项卡功能, 详见 setEventListener 方法, 同时为 disabled 重设样式-->
        <el-menu-item
          ref="item"
          class="tab-control__menu-item"
          :key="item.index"
          :index="item.index"
          :route="item.route"

          :vmData="item"
          disabled
        >
          <!--onselectstart="return false;" 用于取消双击选中文字-->
          <span class="menu-name" onselectstart="return false;">{{item.name}}</span>
          <i v-if="item.name !== '首页'" class="el-icon-close" @click.stop="closeTabMenu(item)"></i>
        </el-menu-item>
      </template>
      <el-submenu v-show="subMenus.length" index="subMenu">
        <!--<i slot="title" class="el-icon-caret-bottom"></i>-->
        <template v-for="item in subMenus">
          <el-menu-item
            ref="item"
            class="tab-control__menu-item"
            :key="item.index"
            :index="item.index"
            :route="item.route"

            :vmData="item"
            disabled
          >
            <span class="menu-name" onselectstart="return false;">{{item.name}}</span>
            <i class="el-icon-close" @click.stop="closeTabMenu(item)"></i>
          </el-menu-item>
        </template>
      </el-submenu>
    </el-menu>
  </div>
</template>

<script>
import { createNamespacedHelpers } from 'vuex'
import MENU from '@/config/menu.js'

const { mapState } = createNamespacedHelpers('framework')
export default {
  name: 'TabControl',
  created () {
  },
  mounted () {
    window.onresize = () => {
      this.mainMenus = [...this.tabControlMenus]
      this.tabChange = true
    }
  },
  computed: {
    ...mapState(['activeMenu', 'openMenuId', 'tabControlMenus']),
  },
  watch: {
    tabChange (val) {
      if (!this.changing && val) {
        this.changing = true
        this.mainMenus = [...this.tabControlMenus]
        this.$nextTick(() => {
          this.setEventListener()

          const breadWidth = document.querySelector('.head__right').offsetWidth
          const conHeadWidth = document.querySelector('.con-head').offsetWidth
          const maxWidth = conHeadWidth - breadWidth - 40 - 10 - 22 - 93 // 40的内边距, 10的间隔, 22的子菜单图标及边距, 93的返回按钮及边距
          let tabWidth = 0
          // 默认 mainMenus 取所有值
          const tabEls = Array.from(document.querySelectorAll('.tab-control__menu>.tab-control__menu-item'))
          this.subMenus = []
          tabEls.map((item, index) => {
            tabWidth += item.offsetWidth // 累加菜单长度
            if (tabWidth > maxWidth) {
              // 超出的部分推入子菜单集合
              this.subMenus.push(this.tabControlMenus[index])
              this.maxIndex = this.maxIndex || index
            } else {
              this.subMenus = []
              this.maxIndex = undefined
            }
          })
          if (this.maxIndex) {
            // 刷新 mainMenus
            this.mainMenus = [...this.tabControlMenus].slice(0, this.maxIndex || this.tabControlMenus.length)
            // 设置子菜单弹框样式, 写入CSS不生效, 因为最开始不存在这个元素
            const menuPop = document.querySelector('.el-menu--popup')
            if (menuPop) {
              menuPop.style.maxHeight = '600px'
              menuPop.style.overflow = 'hidden auto'
            }
          }
          this.tabChange = false
          this.changing = false
          // 什么骚操作, 解决了 "菜单从子菜单进入主菜单后, 失活后再次激活, 高亮会失效" 的 bug
          // 从源码结构看, 之前 bug 是因为 rootMenu 没有存住 item, 导致找不到对应 activeIndex 的菜单, 所以这里手动 addItem (是 ElMeun 源码中的方法), 而且得放在 nextTick 中才行.
          this.$nextTick(() => {
            this.$refs.item.map(item => {
              item.rootMenu.addItem(item)
            })
          })
        })
      }
    },
    activeMenu () {
      this.tabChange = true
    },
    tabControlMenus () {
      this.tabChange = true
    }
  },

  data () {
    return {
      tabChange: false,
      mainMenus: [], // 主菜单集合
      subMenus: [], // 子菜单集合
      maxIndex: undefined, // 最多容纳的菜单 index
    }
  },
  methods: {
    handleSelect (keyPath) {
      // step1: 提交 vuex 设置 activeMenu
      const pathArr = keyPath.split('-')
      const index = pathArr[0]
      const route = MENU[index] // 一级路由
      let title = route.name
      let breadcrumb = [route]

      if (pathArr.length > 1) {
        const subIndex = pathArr[1]
        const subMenus = route.children
        let subRoute = subMenus[subIndex] // 二级子路由
        title = subRoute.name
        breadcrumb.push(subRoute)
      }
      this.$store.commit('framework/changeBreadcrumb', breadcrumb)
      this.$nextTick(() => {
        // 存入激活的id
        this.$store.commit('framework/changeActiveMenu', keyPath)
        // 点击时存入当前展开的菜单id
        this.$store.commit('framework/changeOpenMenuId', index)
      })
    },
    // 关闭选项卡
    closeTabMenu (menuItem) {
      const menuIndex = this.tabControlMenus.findIndex(item => item.index === menuItem.index)
      // step1: 判断如果为激活的 index 则跳转到 index - 1 的选项卡, 提交 vuex 设置 activeMenu
      if (menuItem.index === this.activeMenu) {
        this.$router.currentRoute.meta.keepAlive = false // 关闭路由缓存状态
        const lastMenu = this.tabControlMenus[menuIndex - 1]
        this.handleSelect(lastMenu.index)
        this.$router.push(lastMenu.route)
      } else {
        // 关闭路由缓存状态
        const menuRoutes = this.$router.options.routes.find(item => item.name === 'framework')
        menuRoutes.children.find(item => {
          if (item.name === this.tabControlMenus[menuIndex].route.name) {
            item.meta.keepAlive = false
          }
          return item.name === this.tabControlMenus[menuIndex].route.name
        })
      }
      // step2: 提交 mutation 给 vuex 去除 index 对应的数据
      this.$store.commit('framework/closeTabControlMenu', menuIndex)
      menuItem.immutableName && this.$store.commit('framework/restoreSideMenu', menuItem)
    },
    // 单击跳转路由
    routeTo (item) {
      if (item.clickTime === undefined) {
        item.clickTime = 0
      } else {
        ++item.clickTime
      }
      setTimeout(() => {
        let activeMenu = this.activeMenu
        let openMenuId = this.openMenuId
        if (item.clickTime === 0 || item.name === '首页') {
          // 单击
          for (const item of this.tabControlMenus) {
            // 在三级子页面的话, 设置缓存, 便于点击菜单回归时保持在子页面而不是回到二级主列表页面
            if (item.index === this.activeMenu && item.route.name !== this.$route.name) {
              !item.immutableName && (item.immutableName = item.route.name)// 缓存旧路由名
              item.route.name = this.$route.name // 更新路由名, 便于回来时还在路由
              this.tabChange = true
              break
            }
          }
          const pathArr = item.index.split('-') // 取菜单 key
          const index = pathArr[0]
          const route = MENU[index] // 一级路由
          // let title = route.name
          let breadcrumb = [route]

          if (pathArr.length > 1) {
            const subIndex = pathArr[1]
            const subMenus = route.children
            let subRoute = subMenus[subIndex] // 二级子路由
            // title = subRoute.name
            breadcrumb.push(subRoute)
          }
          activeMenu = item.index
          openMenuId = index
          this.$store.commit('framework/changeBreadcrumb', breadcrumb)
          // 存入激活的id
          this.$store.commit('framework/changeActiveMenu', activeMenu)
          // 点击时存入当前展开的菜单id
          this.$store.commit('framework/changeOpenMenuId', openMenuId)
          this.$router.push(item.route)
        } else if (item.clickTime === 1) {
          // 直接 else 导致双击会删除两个菜单
          // 双击
          this.closeTabMenu(item)
        }
        item.clickTime = undefined
      }, 200) // 双击的时间间隔
    },
    // 设置监听器
    setEventListener () {
      // 避免重复设置事件, 只选择没有标识的元素
      const tabLis = document.querySelectorAll('.tab-control__menu-item:not([has-set-listener])')
      // 为所有 menu-item 添加单击事件, 根据源码使用 disabled 使原始事件失效, 配合修改样式
      for (let i = 0; i < tabLis.length; ++i) {
        tabLis[i].addEventListener('click', e => {
          const vmData = tabLis[i].__vue__.$attrs.vmData
          vmData && this.routeTo(vmData) // 一定要给 menu-item 设置 vmData, 这里才能调用处理函数
        })
        tabLis[i].setAttribute('has-set-listener', true)
      }
    }
  },
}
</script>

<style lang="scss" scoped>
.el-menu.el-menu--horizontal {
  height: 50px;
  border-bottom-color: transparent;
  background: transparent;
  /deep/
  .el-submenu {
    .el-submenu__title {
      height: 50px;
      line-height: 50px;
      padding: 0 5px;
    }
    .el-submenu__icon-arrow {
      margin-left: initial;
    }
  }
}
.tab-control__menu-item {
  height: 50px!important;
  line-height: 50px!important;
  .el-icon-close {
    width: 0;
    margin: 0 0 0 2px;
    color: #909399;
    font-size: 12px;
    vertical-align: middle;
    &:hover, &:active {
      color: #303133;
      font-size: 14px;
      margin: 0 1px;
    }
  }
  &.el-menu-item.is-disabled {
    opacity: initial;
    cursor: pointer;
    &:hover {
      cursor: pointer;
    }
  }
}
/*子菜单列表框*/
ul.el-menu.el-menu--popup.el-menu--popup-bottom-start {
  max-height: 600px;
  overflow: hidden auto;
}
</style>
