<template>
  <div id="TabsView">
    <admin-layout style="height: 100%;">
      <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 v-show="!$route.meta.singlePage" :disabled="animate.disabled" :animate="animate.name" :direction="animate.direction" style="height: 100%;">
          <a-keep-alive :exclude-keys="excludeKeys" v-if="multiPage && cachePage" v-model="clearCaches">
            <router-view v-if="!refreshing" ref="tabContent" :key="$route.fullPath" />
          </a-keep-alive>
          <router-view ref="tabContent" v-else-if="!refreshing" />
        </page-toggle-transition>
        <template v-for="page in single">
          <single-page-view
            v-show="page.meta.singlePage === $route.meta.singlePage"
            :single-page-url="page.meta.singlePage"
            :full-path="page.fullPath"
            :key="page.meta.singlePage"
            @load="onSinglePageLoad" />
        </template>
      </div>
    </admin-layout>
  </div>
</template>

<script>
import AdminLayout from '@vue2-client/layouts/AdminLayout'
import Contextmenu from '@vue2-client/components/menu/Contextmenu'
import PageToggleTransition from '@vue2-client/components/transition/PageToggleTransition'
import { mapState, mapMutations } from 'vuex'
import { getI18nKey } from '@vue2-client/utils/routerUtil'
import AKeepAlive from '@vue2-client/components/cache/AKeepAlive'
import TabsHead from '@vue2-client/layouts/tabs/TabsHead'
import SinglePageView from '../SinglePageView'

export default {
  name: 'TabsView',
  i18n: require('./i18n'),
  components: { SinglePageView, TabsHead, PageToggleTransition, Contextmenu, AdminLayout, AKeepAlive },
  data () {
    return {
      clearCaches: [],
      pageList: [],
      activePage: '',
      menuVisible: false,
      refreshing: false,
      allSinglePages: [],
      excludeKeys: []
    }
  },
  computed: {
    ...mapState('setting', ['multiPage', 'cachePage', 'animate', 'layout', 'pageWidth']),
    ...mapState('account', ['single']),
    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
    }
  },
  created () {
    this.loadCacheConfig(this.$router?.options?.routes)
    this.loadCachedTabs()
    // 解决单页面为首页时首次登录不加载问题
    if (this.$route.meta.singlePage && this.single.length === 0) {
      this.allSinglePages.forEach(item => {
        if (this.$route.fullPath.includes(item.fullPath)) {
          this.setSingle(item)
        }
      })
    }
    const route = this.$route
    if (this.pageList.findIndex(item => item.path === route.fullPath) === -1) {
      this.pageList.push(this.createPage(route))
    }
    this.activePage = route.fullPath
    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) {
      if (!newRoute?.meta.noPusTabs) {
        this.activePage = newRoute.fullPath
      }
      const page = this.pageList.find(item => item.path.split('#')[0] === newRoute.fullPath.split('#')[0])
      if (!this.multiPage) {
        this.pageList = [this.createPage(newRoute)]
      } else if (page) {
        page.fullPath = newRoute.fullPath
      } else if (!page && !newRoute?.meta.noPusTabs) {
        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
      const page = this.pageList.find(item => item.path === key)
      this.$router.push(page.fullPath)
    },
    remove (key, next) {
      if (this.pageList.length === 1) {
        return this.$message.warning(this.$t('warn'))
      }
      this.setSingle({ path: key, delete: true })
      // 清除缓存
      let index = this.pageList.findIndex(item => item.path === key)
      this.clearCaches = this.pageList.splice(index, 1).map(page => page.cachedKey)
      if (next) {
        this.$router.push(next)
      } else if (key === this.activePage) {
        index = index >= this.pageList.length ? this.pageList.length - 1 : index
        this.activePage = this.pageList[index].path
        this.$router.push(this.activePage)
      }
    },
    refresh (key, page) {
      page = page || this.pageList.find(item => item.path === key)
      page.loading = true
      // 单页面刷新
      const singlePage = this.allSinglePages.find(item => item.fullPath === key)
      if (singlePage) {
        window.frames[singlePage.fullPath].location.reload()
        return
      }
      this.clearCache(page)
      if (key === this.activePage) {
        // eslint-disable-next-line no-return-assign
        this.reloadContent(() => page.loading = false)
      } else {
        // 其实刷新很快，加这个延迟纯粹为了 loading 状态多展示一会儿，让用户感知刷新这一过程
        // eslint-disable-next-line no-return-assign
        setTimeout(() => page.loading = false, 500)
      }
    },
    onSinglePageLoad (fullPath) {
      const page = this.pageList.find(item => item.fullPath === fullPath)
      page.loading = false
    },
    onContextmenu (pageKey, e) {
      if (pageKey) {
        e.preventDefault()
        e.meta = pageKey
        this.menuVisible = true
      }
    },
    onMenuSelect (key, target, pageKey) {
      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) {
      // 清除缓存
      const clearPages = this.pageList.filter(item => item.path !== pageKey && !item.unclose)
      this.clearCaches = clearPages.map(item => item.cachedKey)
      this.pageList = this.pageList.filter(item => !clearPages.includes(item))
      // 判断跳转
      if (this.activePage !== pageKey) {
        this.activePage = pageKey
        this.$router.push(this.activePage)
      }
    },
    closeLeft (pageKey) {
      const index = this.pageList.findIndex(item => item.path === pageKey)
      // 清除缓存
      const clearPages = this.pageList.filter((item, i) => i < index && !item.unclose)
      this.clearCaches = clearPages.map(item => item.cachedKey)
      this.pageList = this.pageList.filter(item => !clearPages.includes(item))
      // 判断跳转
      if (!this.pageList.find(item => item.path === this.activePage)) {
        this.activePage = pageKey
        this.$router.push(this.activePage)
      }
    },
    closeRight (pageKey) {
      // 清除缓存
      const index = this.pageList.findIndex(item => item.path === pageKey)
      const clearPages = this.pageList.filter((item, i) => i > index && !item.unclose)
      this.clearCaches = clearPages.map(item => item.cachedKey)
      this.pageList = this.pageList.filter(item => !clearPages.includes(item))
      // 判断跳转
      if (!this.pageList.find(item => item.path === this.activePage)) {
        this.activePage = pageKey
        this.$router.push(this.activePage)
      }
    },
    clearCache (page) {
      page._init_ = false
      this.clearCaches = [page.cachedKey]
    },
    reloadContent (onLoaded) {
      this.refreshing = true
      setTimeout(() => {
        this.refreshing = false
        this.$nextTick(() => {
          this.setCachedKey(this.$route)
          if (typeof onLoaded === 'function') {
            onLoaded.apply(this, [])
          }
        })
      }, 200)
    },
    pageName (page) {
      return this.$t(getI18nKey(page.keyPath))
    },
    /**
     * 添加监听器
     */
    addListener () {
      window.addEventListener('page:close', this.closePageListener)
      window.addEventListener('page:refresh', this.refreshPageListener)
      window.addEventListener('unload', this.unloadListener)
    },
    /**
     * 移出监听器
     */
    removeListener () {
      window.removeEventListener('page:close', this.closePageListener)
      window.removeEventListener('page:refresh', this.refreshPageListener)
      window.removeEventListener('unload', this.unloadListener)
    },
    /**
     * 页签关闭事件监听
     * @param event 页签关闭事件
     */
    closePageListener (event) {
      const { closeRoute, nextRoute } = event.detail
      const closePath = typeof closeRoute === 'string' ? closeRoute : closeRoute.path
      const path = closePath && closePath.split('?')[0]
      this.remove(path, nextRoute)
    },
    /**
     * 页面刷新事件监听
     * @param event 页签关闭事件
     */
    refreshPageListener (event) {
      const { pageKey } = event.detail
      const path = pageKey && pageKey.split('?')[0]
      this.refresh(path)
    },
    /**
     * 页面 unload 事件监听器，添加页签到 session 缓存，用于刷新时保留页签
     */
    unloadListener () {
      const tabs = this.pageList.map(item => ({ ...item, _init_: false }))
      sessionStorage.setItem(process.env.VUE_APP_TBAS_KEY, JSON.stringify(tabs))
    },
    createPage (route) {
      return {
        keyPath: route.matched[route.matched.length - 1].path,
        fullPath: route.fullPath,
        loading: false,
        path: route.fullPath,
        title: route.meta && route.meta.page && route.meta.page.title,
        unclose: route.meta && route.meta.page && (route.meta.page.closable === false)
      }
    },
    /**
     * 设置页面缓存的key
     * @param route 页面对应的路由
     */
    setCachedKey (route) {
      const page = this.pageList.find(item => item.keyPath === route.path)
      page.unclose = route.meta && route.meta.page && (route.meta.page.closable === false)
      if (!page._init_) {
        const vnode = this.$refs.tabContent.$vnode
        page.cachedKey = vnode.key + vnode.componentOptions.Ctor.cid
        page._init_ = true
      }
    },
    /**
     * 加载缓存的 tabs
     */
    loadCachedTabs () {
      const cachedTabsStr = sessionStorage.getItem(process.env.VUE_APP_TBAS_KEY)
      if (cachedTabsStr) {
        try {
          // 加载缓存的单页面
          const allCachedPath = JSON.parse(cachedTabsStr).map(str => { return str.fullPath })
          this.allSinglePages.forEach(item => {
            if (allCachedPath.includes(item.fullPath)) {
              // 如果缓存中有单页面  加载到 state.single
              this.setSingle(item)
            }
          })
          const cachedTabs = JSON.parse(cachedTabsStr)
          if (cachedTabs.length > 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)
        }
      }
    },
    loadCacheConfig (routes, pCache = true) {
      routes.forEach(item => {
        const cacheAble = item.meta?.page?.cacheAble ?? pCache ?? true
        if (item.meta && item.meta.singlePage) {
          // 找到所有单页面添加到 allSinglePages 中
          this.allSinglePages.push(item)
        }
        if (!cacheAble) {
          this.excludeKeys.push(new RegExp(`${item.path.replace(/:[^/]*/g, '[^/]*')}(\\?.*)?\\d*$`))
        }
        if (item.children) {
          this.loadCacheConfig(item.children, cacheAble)
        }
      })
    },
    ...mapMutations('setting', ['correctPageMinHeight']),
    ...mapMutations('account', ['setSingle'])
  }
}
</script>

<style lang="less">
#TabsView {
  height: 100%;
  .tabs-view {
    margin: -16px auto 8px;
    &.head.fixed {
      max-width: 100%;
    }
  }
  .tabs-view-content {
    position: relative;
    height: 100%;
    &.head.fixed {
      width: 100%;
      margin: 0 auto;
    }
  }
}
</style>
