<template>
  <admin-layout>
    <contextmenu :itemList="menuItemList" :visible.sync="menuVisible" @select="onMenuSelect" />
    <tabs-head v-if="multiPage" :active="activePage" :page-list="pageList" @change="changePage" @close="remove"
      @refresh="refresh" @contextmenu="onContextmenu" />
    <div :class="['tabs-view-content', layout, pageWidth]" :style="`margin-top: ${multiPage ? -24 : 0}px`">
      <page-toggle-transition :disabled="animate.disabled" :animate="animate.name" :direction="animate.direction">
        <a-keep-alive :exclude-keys="excludeKeys" v-if="multiPage && cachePage" v-model="clearCaches">
          <router-view v-if="!refreshing" ref="tabContent" :key="$route.path" />
        </a-keep-alive>
        <router-view ref="tabContent" v-else-if="!refreshing" />
      </page-toggle-transition>
    </div>
  </admin-layout>
</template>

<script>
import AdminLayout from '@/layouts/AdminLayout' // 加载布局组件 管理布局
import Contextmenu from '@/components/menu/Contextmenu' // 加载菜单组件 上下文菜单
import PageToggleTransition from '@/components/transition/PageToggleTransition' // 加载页面切换动画组件
import { mapState, mapMutations } from 'vuex' // 加载vuex mapState、mapMutations
import { getI18nKey } from '@/utils/routerUtil' // 加载路由工具 getI18nKey 获取国际化key
import AKeepAlive from '@/components/cache/AKeepAlive' // 加载缓存组件 AKeepAlive
import TabsHead from '@/layouts/tabs/TabsHead' // 加载页签组件 TabsHead

export default {
  name: 'TabsView', // 组件名称 页签视图
  i18n: require('./i18n'), // 国际化 国际化配置
  components: { TabsHead, PageToggleTransition, Contextmenu, AdminLayout, AKeepAlive },
  // 注册组件 页签头、页面切换动画组件、上下文菜单、管理布局、AKeepAlive
  data() {
    return {
      clearCaches: [], // 清除缓存
      pageList: [], // 页签列表
      activePage: '', // 激活页签
      menuVisible: false, // 菜单可见
      refreshing: false, // 刷新中
      excludeKeys: [] // 排除键
    }
  },
  computed: {
    ...mapState('setting', ['multiPage', 'cachePage', 'animate', 'layout', 'pageWidth']),
    // 获取vuex数据 获取多页签、缓存页签、动画、布局、页面宽度
    menuItemList() { // 菜单项列表
      return [
        { key: '1', icon: 'vertical-right', text: this.$t('closeLeft') }, // 关闭左侧 
        { key: '2', icon: 'vertical-left', text: this.$t('closeRight') }, // 关闭右侧
        { key: '3', icon: 'close', text: this.$t('closeOthers') }, // 关闭其他
        { key: '4', icon: 'sync', text: this.$t('refresh') }, // 刷新
      ]
    },
    tabsOffset() { // 页签偏移
      return this.multiPage ? 24 : 0 // 如果多页签 返回24 否则返回0
    }
  },
  created() { // 创建
    this.loadCacheConfig(this.$router?.options?.routes) // 加载缓存配置
    this.loadCachedTabs() // 加载缓存页签
    const route = this.$route // 获取路由
    if (this.pageList.findIndex(item => item.path === route.path) === -1) { // 如果页签列表中找不到当前路由
      this.pageList.push(this.createPage(route)) // 添加页签
    }
    this.activePage = route.path // 激活页签为当前路由的i18n keywords
    if (this.multiPage) { // 如果多页签
      this.$nextTick(() => { // 下一次更新
        this.setCachedKey(route) // 设置缓存键
      })
      this.addListener() // 添加监听
    }
  },
  mounted() { // 挂载
    this.correctPageMinHeight(-this.tabsOffset) // 修正页面最小高度
  },
  beforeDestroy() { // 销毁前
    this.removeListener() // 移除监听
    this.correctPageMinHeight(this.tabsOffset) // 修正页面最小高度
  },
  watch: { // 监听
    '$router.options.routes': function (val) { // 监听路由配置
      this.excludeKeys = [] // 排除键
      this.loadCacheConfig(val) // 加载缓存配置
    },
    '$route': function (newRoute) { // 监听路由
      this.activePage = newRoute.path // 激活页签为当前路由的i18n keywords
      const page = this.pageList.find(item => item.path === newRoute.path) // 获取页签
      if (!this.multiPage) { // 如果不是多页签
        this.pageList = [this.createPage(newRoute)] // 页签列表为当前路由
      } else if (page) { // 如果页签存在
        page.fullPath = newRoute.fullPath // 页签的fullPath为当前路由的fullPath
      } else if (!page) { // 如果页签不存在
        this.pageList.push(this.createPage(newRoute)) // 添加页签
      }
      if (this.multiPage) { // 如果多页签
        this.$nextTick(() => { // 下一次更新
          this.setCachedKey(newRoute) // 设置缓存键
        })
      }
    },
    'multiPage': function (newVal) { // 监听多页签
      if (!newVal) { // 如果不是多页签
        this.pageList = [this.createPage(this.$route)] // 页签列表为当前路由
        this.removeListener() // 移除监听
      } else { // 如果是多页签
        this.addListener() // 添加监听
      }
    },
    tabsOffset(newVal, oldVal) { // 监听页签偏移
      this.correctPageMinHeight(oldVal - newVal) // 修正页面最小高度
    }
  },
  methods: {
    changePage(key) { // 切换页签
      this.activePage = key // 激活页签为key 
      const page = this.pageList.find(item => item.path === key) // 获取页签
      this.$router.push(page.fullPath)  // 跳转到页签的fullPath
    },

    //找到定义了beforeTabClose方法 的页面组件
    findPageComponent(component , key ) {
      if(!key){
        return null;
      }
      let target = null;

      let find = (children) => {
        children.forEach(item => {
          if (item.beforeTabClose && item.pageKeys && item.pageKeys.filter(a=> key.indexOf(a) == 0).length) {
            target = item;
            return;
          }
          if (item.$children && item.$children.length) {
            find(item.$children)
          }
        })
      }

      find(component.$children);


      return target;
    },

    remove(key, next,noCheck) { // 移除页签 
      if (this.pageList.length === 1) { // 如果页签列表长度为1 只有一个页签
        return this.$message.warning(this.$t('warn')) // 提示警告 不能关闭最后一个页签
      }
      let doRemove = () => {
        //清除缓存
        let index = this.pageList.findIndex(item => item.path === key) // 获取页签索引
        this.clearCaches = this.pageList.splice(index, 1).map(page => page.cachedKey) // 清除缓存为页签的cachedKey
        if (next) { // 如果next为true
          this.$router.push(next) // 跳转到next
        } else if (key === this.activePage) { // 如果key等于激活页签
          index = index >= this.pageList.length ? this.pageList.length - 1 : index // 如果索引大于等于页签列表长度 则索引为页签列表长度-1 否则为索引
          this.activePage = this.pageList[index].path // 激活页签为页签列表索引的path
          this.$router.push(this.activePage) // 跳转到激活页签
        }
      };
      if(noCheck){
        doRemove();
        return;
      }
      let pageComponent = this.findPageComponent(this,key);
     
      if (pageComponent) {

        pageComponent.beforeTabClose().then(()=>{
          doRemove(); //通过了才调用关闭
        });
        return;
      }
    
      doRemove();


    },
    refresh(key, page) { // 刷新页签
      page = page || this.pageList.find(item => item.path === key) // 获取页签
      page.loading = true // 页签的loading为true
      this.clearCache(page) // 清除缓存
      if (key === this.activePage) { // 如果key等于激活页签
        this.reloadContent(() => page.loading = false) // 重新加载内容
      } else {
        // 其实刷新很快，加这个延迟纯粹为了 loading 状态多展示一会儿，让用户感知刷新这一过程
        setTimeout(() => page.loading = false, 500) // 500毫秒后 页签的loading为false
      }
    },
    onContextmenu(pageKey, e) { // 右键菜单
      if (pageKey) { // 如果页签key存在
        e.preventDefault() // 阻止默认事件
        e.meta = pageKey // 事件的meta为页签key
        this.menuVisible = true // 菜单可见为true
      }
    },
    onMenuSelect(key, target, pageKey) { // 菜单选择
      switch (key) { // switch key
        case '1': this.closeLeft(pageKey); break // 关闭左侧页签
        case '2': this.closeRight(pageKey); break // 关闭右侧页签
        case '3': this.closeOthers(pageKey); break // 关闭其他页签
        case '4': this.refresh(pageKey); break // 刷新页签
        default: break  // 默认
      }
    },
    closeOthers(pageKey) { // 关闭其他页签 pageKey为页签key
      // 清除缓存
      const clearPages = this.pageList.filter(item => item.path !== pageKey && !item.unclose)
      // 清除页签为页签列表过滤出不等于页签key并且unclose为false的页签
      this.clearCaches = clearPages.map(item => item.cachedKey) // 清除缓存为清除页签的cachedKey
      this.pageList = this.pageList.filter(item => !clearPages.includes(item)) // 页签列表为页签列表过滤出不包含清除页签的页签
      // 判断跳转
      if (this.activePage != pageKey) { // 如果激活页签不等于页签key
        this.activePage = pageKey // 激活页签为页签key
        this.$router.push(this.activePage) // 跳转到激活页签
      }
    },
    closeLeft(pageKey) { // 关闭左侧页签 pageKey为页签key
      const index = this.pageList.findIndex(item => item.path === pageKey) // 获取页签索引
      // 清除缓存
      const clearPages = this.pageList.filter((item, i) => i < index && !item.unclose) // 清除页签为页签列表过滤出索引小于页签索引并且unclose为false的页签
      // 清除页签为页签列表过滤出索引小于页签索引并且unclose为false的页签
      this.clearCaches = clearPages.map(item => item.cachedKey) // 清除缓存为清除页签的cachedKey
      this.pageList = this.pageList.filter(item => !clearPages.includes(item)) // 页签列表为页签列表过滤出不包含清除页签的页签
      // 判断跳转
      if (!this.pageList.find(item => item.path === this.activePage)) { // 如果页签列表找不到激活页签
        this.activePage = pageKey // 激活页签为页签key
        this.$router.push(this.activePage) // 跳转到激活页签
      }
    },
    closeRight(pageKey) { // 关闭右侧页签 pageKey为页签key
      // 清除缓存
      const index = this.pageList.findIndex(item => item.path === pageKey) // 页签索引为页签列表找到页签key的索引
      const clearPages = this.pageList.filter((item, i) => i > index && !item.unclose)
      // 清除页签为页签列表过滤出索引大于页签索引并且unclose为false的页签
      this.clearCaches = clearPages.map(item => item.cachedKey) // 清除缓存为清除页签的cachedKey
      this.pageList = this.pageList.filter(item => !clearPages.includes(item)) // 页签列表为页签列表过滤出不包含清除页签的页签
      // 判断跳转
      if (!this.pageList.find(item => item.path === this.activePage)) { // 如果页签列表找不到激活页签
        this.activePage = pageKey // 激活页签为页签key
        this.$router.push(this.activePage) // 跳转到激活页签
      }
    },
    clearCache(page) { // 清除缓存 page为页签
      page._init_ = false  // 初始化为false
      this.clearCaches = [page.cachedKey] // 清除缓存为页签的cachedKey
    },
    reloadContent(onLoaded) { // 重新加载内容 onLoaded为加载完成回调
      this.refreshing = true // 刷新中为true
      setTimeout(() => { // 延时
        this.refreshing = false // 刷新中为false
        this.$nextTick(() => { // 下一帧
          this.setCachedKey(this.$route) // 设置缓存key
          if (typeof onLoaded === 'function') { // 如果加载完成回调为函数 
            onLoaded.apply(this, []) // 执行加载完成回调
          }
        })
      }, 200) // 延时200毫秒
    },
    pageName(page) { // 页签名称 page为页签
      return this.$t(getI18nKey(page.keyPath)) // 返回国际化key
    },
    /**
     * 添加监听器
     */
    addListener() { // 添加监听器
      window.addEventListener('page:closeOthers', this.closeOthersListener)
      window.addEventListener('page:close', this.closePageListener) // 添加关闭页签监听器
      window.addEventListener('page:refresh', this.refreshPageListener) // 添加刷新页签监听器
      window.addEventListener('unload', this.unloadListener) // 添加卸载监听器
    },
    /**
     * 移出监听器
     */
    removeListener() { // 移出监听器

      window.removeEventListener('page:closeOthers', this.closeOthersListener)
      window.removeEventListener('page:close', this.closePageListener) // 移出关闭页签监听器
      window.removeEventListener('page:refresh', this.refreshPageListener) // 移出刷新页签监听器
      window.removeEventListener('unload', this.unloadListener) // 移出卸载监听器
    },

    closeOthersListener(event) {
      this.closeOthers(event.detail) // 移出页签
    },
    /**
     * 页签关闭事件监听
     * @param event 页签关闭事件
     */
    closePageListener(event) { // 页签关闭事件监听 event为页签关闭事件
      const { closeRoute, nextRoute } = event.detail // 页签关闭路由为页签关闭事件的detail的closeRoute，下一个路由为页签关闭事件的detail的nextRoute
      const closePath = typeof closeRoute === 'string' ? closeRoute : (closeRoute.path || closeRoute.closeRoute)
 


      // 页签关闭路径为页签关闭路由为字符串时为页签关闭路由，否则为页签关闭路由的path
      const path = closePath && closePath.split('?')[0] // 路径等于为页签关闭路径并且以？分割的第一个
 
      this.remove(path, nextRoute, true) // 移出页签
    },
    /**
     * 页面刷新事件监听
     * @param event 页签关闭事件
     */
    refreshPageListener(event) { // 页面刷新事件监听 event为页签关闭事件
      const { pageKey } = event.detail // 页签key为页签关闭事件的detail的pageKey
      const path = pageKey && pageKey.split('?')[0] // 路径等于为页签key并且以？分割的第一个
      this.refresh(path) // 刷新页签
    },
    /**
     * 页面 unload 事件监听器，添加页签到 session 缓存，用于刷新时保留页签
     */
    unloadListener() { // 页面 unload 事件监听器
      const tabs = this.pageList.map(item => ({ ...item, _init_: false }))
      // 页签列表为页签列表的map，map的参数为页签列表的每一项，返回值为页签列表的每一项的副本，初始化为false
      sessionStorage.setItem(process.env.VUE_APP_TBAS_KEY, JSON.stringify(tabs)) // 添加页签到session缓存
    },
    createPage(route) { // 创建页签 route为路由
      return { // 返回页签
        keyPath: route.matched[route.matched.length - 1].path, // 页签key路径为路由的匹配的最后一个路径
        fullPath: route.fullPath, loading: false, // 页签全路径为路由的全路径，加载中为false
        path: route.path, // 页签路径为路由的路径
        title: route.meta && route.meta.page && route.meta.page.title, // 页签标题为路由的元数据的页签的标题
        unclose: route.meta && route.meta.page && (route.meta.page.closable === false), // 不可关闭为路由的元数据的页签的可关闭为false
      }
    },
    /**
     * 设置页面缓存的key
     * @param route 页面对应的路由
     */
    setCachedKey(route) { // 设置页面缓存的key route为路由
      const page = this.pageList.find(item => item.path === route.path)
      // 页签为页签列表的find，find的参数为页签列表的每一项，返回值为页签列表的每一项的路径等于路由的路径
      page.unclose = route.meta && route.meta.page && (route.meta.page.closable === false) // 不可关闭为路由的元数据的页签的可关闭为false
      if (!page._init_) { // 如果页签初始化为false
        const vnode = this.$refs.tabContent.$vnode // 虚拟节点为页签内容的虚拟节点
        page.cachedKey = vnode.key + vnode.componentOptions.Ctor.cid // 缓存key为虚拟节点的key加上虚拟节点的组件选项的构造函数的cid
        page._init_ = true // 页签初始化为true
      }
    },
    /**
     * 加载缓存的 tabs
     */
    loadCachedTabs() { // 加载缓存的 tabs
      const cachedTabsStr = sessionStorage.getItem(process.env.VUE_APP_TBAS_KEY) // 缓存的页签字符串为session缓存的页签key
      if (cachedTabsStr) { // 如果缓存的页签字符串存在
        try {
          const cachedTabs = JSON.parse(cachedTabsStr) // 缓存的页签为缓存的页签字符串的json解析
          if (cachedTabs.length > 0) { // 如果缓存的页签的长度大于0
            this.pageList = cachedTabs // 页签列表为缓存的页签
          }
        } catch (e) { // 捕获异常
          console.warn('failed to load cached tabs, got exception:', e) // 打印警告信息
        } finally { // 最终
          sessionStorage.removeItem(process.env.VUE_APP_TBAS_KEY) // 移除session缓存的页签key
        }
      }
    },
    loadCacheConfig(routes, pCache = true) { // 加载缓存配置 routes为路由，pCache为父缓存
      routes.forEach(item => { // 遍历路由
        const cacheAble = item.meta?.page?.cacheAble ?? pCache ?? true // 缓存为路由的元数据的页签的缓存可为true
        if (!cacheAble) { // 如果缓存不可
          this.excludeKeys.push(new RegExp(`${item.path}\\d+$`)) // 排除的key为路由的路径加上数字
        }
        if (item.children) { // 如果路由的子路由存在
          this.loadCacheConfig(item.children, cacheAble) // 加载缓存配置为路由的子路由，缓存可
        }
      })
    },
    ...mapMutations('setting', ['correctPageMinHeight']) // 映射设置的正确页面最小高度
  }
}
</script>

<style scoped lang="less">
.tabs-view {
  margin: -16px auto 8px;

  &.head.fixed {
    max-width: 1400px;
  }
}

.tabs-view-content {
  position: relative;

  &.head.fixed {
    width: 1400px;
    margin: 0 auto;
  }
}
</style>
