<template>
  <div class="tab-layout">
    <a-tabs
      type="editable-card"
      class="tab-bar"
      :hide-add="true"
      :active-key="activePage"
      @change="changePage"
      @edit="editPage"
      @contextmenu="onContextmenu"
    >
      <a-tab-pane
        v-for="page in pageList"
        :key="page.fullPath"
        :closable="pageList.length > 1"
      >
        <template #tab>
          <span :data-key="page.fullPath" class="tab-item">
            <span class="tab-title">{{ page.meta.title }}</span>
          </span>
        </template>
      </a-tab-pane>
    </a-tabs>
    <PageToggleTransition name="fadeIn">
      <keep-alive :exclude="dustbin">
        <router-view />
      </keep-alive>
    </PageToggleTransition>
    <ContextMenu
      :list="menuItems"
      :visible.sync="menuVisible"
      @select="onMenuSelect"
    />
  </div>
</template>

<script>
import { message } from "ant-design-vue";
import { last } from "lodash-es";
import ContextMenu from "../components/common/ContextMenu";
import PageToggleTransition from "../components/transition/PageToggleTransition";

export default {
  name: "TabLayout",
  components: { PageToggleTransition, ContextMenu },
  data() {
    return {
      pageList: [],
      dustbin: [],
      activePage: "",
      menuVisible: false,
      menuItems: [
        { key: "1", icon: "arrow-left", text: "关闭左侧" },
        { key: "2", icon: "arrow-right", text: "关闭右侧" },
        { key: "3", icon: "close", text: "关闭其它" },
      ],
    };
  },
  watch: {
    $route: {
      immediate: true,
      handler(route) {
        this.activePage = route.fullPath;
        this.putCache(route);
        const index = this.pageList.findIndex(
          (item) => item.fullPath === route.fullPath
        );
        if (index === -1) {
          this.pageList.push(route);
        }
      },
    },
  },
  methods: {
    log(data) {
      console.log(data);
    },
    changePage(key) {
      this.activePage = key;
      this.$router.push(key);
    },
    editPage(key, action) {
      if (action === "remove") {
        this.remove(key);
      }
    },
    remove(key) {
      if (this.pageList.length <= 1) {
        return message.info("最后一页了哦~");
      }
      let curIndex = this.pageList.findIndex((item) => item.fullPath === key);
      const { matched } = this.pageList[curIndex];
      const componentName = last(matched).components.default.name;
      this.dustbin.push(componentName);
      this.pageList.splice(curIndex, 1);
      // 如果删除的是当前页才需要跳转
      if (key === this.activePage) {
        // 判断向左跳还是向右跳
        curIndex =
          curIndex >= this.pageList.length
            ? this.pageList.length - 1
            : curIndex;
        const page = this.pageList[curIndex];
        this.$router.push(page.fullPath).finally(() => {
          this.dustbin.splice(0); // 重置，否则会影响到某些组件的缓存
        });
      }
    },
    /**
     * 右键菜单
     */
    onContextmenu(e) {
      const key = getTabKey(e.target);
      if (!key) return;

      e.preventDefault();
      this.menuVisible = true;
    },
    onMenuSelect(key, target) {
      const tabKey = getTabKey(target);
      switch (key) {
        case "1":
          this.closeLeft(tabKey);
          break;
        case "2":
          this.closeRight(tabKey);
          break;
        case "3":
          this.closeOthers(tabKey);
          break;
        default:
          break;
      }
    },
    closeOthers(tabKey) {
      const index = this.pageList.findIndex((item) => item.fullPath === tabKey);
      for (const route of this.pageList) {
        if (route.fullPath !== tabKey) {
          this.clearCache(route);
        }
      }
      const page = this.pageList[index];
      this.pageList = [page];
      this.activePage = page.fullPath;
      this.$router.push(this.activePage).catch((e) => e);
    },
    closeLeft(tabKey) {
      const index = this.pageList.findIndex((item) => item.fullPath === tabKey);
      this.pageList.forEach((route, i) => {
        if (i < index) {
          this.clearCache(route);
        }
      });
      const restPages = this.pageList.slice(index);
      this.pageList = restPages;
      // 判断当前activePage是否在将要删除的页面中
      const curActivePage = restPages.find(
        (item) => item.fullPath === this.activePage
      );
      if (!curActivePage) {
        this.activePage = restPages[0].fullPath;
        this.$router.push(this.activePage).catch((e) => e);
      }
    },
    closeRight(tabKey) {
      const index = this.pageList.findIndex((item) => item.fullPath === tabKey);
      this.pageList.forEach((route, i) => {
        if (i > index) {
          this.clearCache(route);
        }
      });
      const restPages = this.pageList.slice(0, index + 1);
      this.pageList = restPages;
      // 判断当前activePage是否在将要删除的页面中
      const curActivePage = restPages.find(
        (item) => item.fullPath === this.activePage
      );
      if (!curActivePage) {
        this.activePage = last(restPages).fullPath;
        this.$router.push(this.activePage).catch((e) => e);
      }
    },
    /**
     * 缓存控制
     */
    clearCache(route) {
      const componentName = last(route.matched).components.default.name;
      this.dustbin.push(componentName); // 清除
    },
    putCache(route) {
      const componentName = last(route.matched).components.default.name;
      if (this.dustbin.includes(componentName)) {
        this.dustbin = this.dustbin.filter((item) => item !== componentName);
      }
    },
  },
};

/**
 * 获取Tab标签下dom节点中自定义的数据，递归向下查找最多3层（观察Tab组件渲染后的DOM得出）
 * 该方式属于hack手段，不得已为之
 * @param{HTMLElement} target event.target
 * @param depth 深度
 */
function getTabKey(target, depth = 0) {
  if (depth > 2 || !target) {
    return null;
  }
  return target.dataset.key || getTabKey(target.firstElementChild, ++depth);
}
</script>

<style lang="less" scoped>
.tab-layout {
  background: #fff;
  padding: 12px 16px 0;
  margin-bottom: 16px;
  height: 48px !important;
  border-radius: 12px;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05);

  .tab-bar {
    background: #fff;
    margin-bottom: 12px;

    :deep(.ant-tabs-bar) {
      margin: 0;
      border: none;
      background: linear-gradient(
        to right,
        rgba(255, 255, 255, 0.98),
        rgba(255, 255, 255, 0.95)
      );
      backdrop-filter: blur(20px);
      border-radius: 10px;
      box-shadow: 0 2px 16px rgba(0, 0, 0, 0.04);
      padding: 4px;
    }

    :deep(.ant-tabs-nav-container) {
      height: 44px !important;
      padding: 0 8px;
    }

    :deep(.ant-tabs-tab) {
      position: relative;
      margin: 0 6px;
      height: 38px !important;
      line-height: 38px !important;
      background: rgba(250, 250, 250, 0.9);
      border: 1px solid rgba(240, 240, 240, 0.8);
      border-radius: 8px;
      transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
      padding: 0 20px;
      font-size: 14px;
      font-weight: 500;
      color: #666;

      .tab-item {
        display: inline-flex;
        align-items: center;
        gap: 8px;

        .tab-title {
          transition: all 0.3s;
          white-space: nowrap;
          max-width: 120px;
          overflow: hidden;
          text-overflow: ellipsis;
        }

        .close-btn {
          display: inline-flex;
          align-items: center;
          justify-content: center;
          width: 18px;
          height: 18px;
          border-radius: 50%;
          opacity: 0.4;
          transition: all 0.3s;

          .anticon {
            font-size: 12px;
            transition: all 0.3s;
          }

          &:hover {
            opacity: 1;
            background: rgba(255, 77, 79, 0.1);
            color: #ff4d4f;
            transform: rotate(90deg);
          }
        }
      }

      &::before {
        content: "";
        position: absolute;
        inset: 0;
        border-radius: 8px;
        padding: 1px;
        background: linear-gradient(
          45deg,
          transparent,
          rgba(24, 144, 255, 0.15)
        );
        -webkit-mask: linear-gradient(#fff 0 0) content-box,
          linear-gradient(#fff 0 0);
        mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
        -webkit-mask-composite: xor;
        mask-composite: exclude;
        opacity: 0;
        transition: opacity 0.3s;
      }

      &:hover {
        color: #1890ff;
        background: rgba(230, 247, 255, 0.95);
        transform: translateY(-1px);
        box-shadow: 0 4px 12px rgba(24, 144, 255, 0.12);

        &::before {
          opacity: 1;
        }

        .close-btn {
          opacity: 0.6;
        }
      }

      &.ant-tabs-tab-active {
        background: #fff;
        border-color: rgba(24, 144, 255, 0.3);
        color: #1890ff;
        box-shadow: 0 4px 16px rgba(24, 144, 255, 0.15);

        &::before {
          background: linear-gradient(45deg, #1890ff30, #40a9ff30);
          opacity: 1;
        }

        .tab-title {
          font-weight: 600;
        }

        .close-btn {
          opacity: 0.6;

          &:hover {
            opacity: 1;
          }
        }
      }
    }

    :deep(.ant-tabs-ink-bar) {
      display: none;
    }
  }
}

// 右键菜单样式优化
:deep(.context-menu) {
  background: rgba(255, 255, 255, 0.98);
  backdrop-filter: blur(20px);
  border-radius: 10px;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.08), 0 2px 8px rgba(0, 0, 0, 0.04);
  border: 1px solid rgba(240, 240, 240, 0.8);
  padding: 6px;

  .menu-item {
    padding: 10px 16px;
    margin: 2px;
    cursor: pointer;
    transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
    border-radius: 8px;
    font-size: 14px;
    color: #666;
    display: flex;
    align-items: center;
    gap: 8px;

    &:hover {
      background: rgba(24, 144, 255, 0.08);
      color: #1890ff;
      transform: translateX(2px);
    }

    .anticon {
      font-size: 14px;
      transition: transform 0.3s;
    }

    &:hover .anticon {
      transform: scale(1.1);
    }
  }
}
</style>
