#
Junjie
3 天以前 686fe55892de7bf8d206cddbead77a5fbdb0e091
src/main/webapp/views/index.html
@@ -228,6 +228,44 @@
      color: rgba(255, 255, 255, 0.58);
    }
    .aside-loading {
      padding: 4px 12px 12px;
      box-sizing: border-box;
    }
    .aside-skeleton-item {
      position: relative;
      height: 46px;
      margin: 0 10px 6px;
      overflow: hidden;
      border-radius: 10px;
      background: rgba(255, 255, 255, 0.08);
    }
    .aside-skeleton-item::after {
      content: "";
      position: absolute;
      inset: 0;
      transform: translateX(-100%);
      background: linear-gradient(90deg,
      rgba(255, 255, 255, 0) 0%,
      rgba(255, 255, 255, 0.16) 48%,
      rgba(255, 255, 255, 0) 100%);
      animation: asideSkeletonShimmer 1.25s ease-in-out infinite;
    }
    .aside-skeleton-item:nth-child(3n) {
      width: calc(100% - 28px);
    }
    .aside-skeleton-item:nth-child(4n + 2) {
      width: calc(100% - 44px);
    }
    .aside-skeleton-item:nth-child(5n) {
      width: calc(100% - 36px);
    }
    .aside-footer {
      padding: 14px 16px 16px;
      border-top: 1px solid rgba(255, 255, 255, 0.08);
@@ -450,6 +488,12 @@
      }
    }
    @keyframes asideSkeletonShimmer {
      100% {
        transform: translateX(100%);
      }
    }
    .ai-drawer-layer {
      box-shadow: -8px 0 24px rgba(0, 0, 0, 0.15) !important;
      border-radius: 8px 0 0 8px !important;
@@ -500,7 +544,16 @@
        </el-input>
      </div>
      <el-scrollbar class="aside-scroll" v-loading="menuLoading">
      <el-scrollbar class="aside-scroll">
        <div v-if="menuLoading" class="aside-loading" aria-hidden="true">
          <div
              v-for="n in 8"
              :key="'menu-skeleton-' + n"
              class="aside-skeleton-item">
          </div>
        </div>
        <template v-else>
        <el-menu
            ref="sideMenu"
            class="side-menu"
@@ -530,12 +583,13 @@
          </el-submenu>
        </el-menu>
        <div class="aside-empty" v-if="!menuLoading && filteredMenus.length === 0">
          <div class="aside-empty" v-if="filteredMenus.length === 0">
          <el-empty
              :image-size="80"
              :description="menuKeyword ? '没有匹配菜单' : '当前账号没有可用菜单'">
          </el-empty>
        </div>
        </template>
      </el-scrollbar>
      <div class="aside-footer" v-show="!isCollapse">
@@ -606,7 +660,6 @@
            class="page-tabs"
            v-model="activeTab"
            type="card"
            @tab-click="handleTabClick"
            @tab-remove="removeTab">
          <el-tab-pane
              v-for="tab in tabs"
@@ -695,7 +748,7 @@
    data: function () {
      return {
        isCollapse: false,
        menuLoading: false,
        menuLoading: true,
        pageLoading: true,
        loadingText: "正在加载页面...",
        menuKeyword: "",
@@ -712,6 +765,8 @@
        fakeVisible: false,
        fakeRunning: false,
        fakeStatusInterval: null,
        menuSyncVersion: 0,
        menuSyncTimer: null,
        userName: localStorage.getItem(USER_STORAGE_KEY) || "管理员",
        aiLayerIndex: null,
        aiTipIndex: null
@@ -812,7 +867,7 @@
    watch: {
      activeTab: function () {
        var tab = this.getTabByName(this.activeTab);
        this.activeMenuKey = tab ? (tab.menuKey || this.findMenuKeyByUrl(tab.url)) : "";
        this.syncMenuStateByUrl(tab ? tab.url : HOME_TAB_CONFIG.url);
        this.pageLoading = !!(tab && !tab.loaded);
        if (this.pageLoading) {
          this.loadingText = "正在加载 “" + tab.title + "” ...";
@@ -847,6 +902,10 @@
      if (this.userSyncTimer) {
        clearInterval(this.userSyncTimer);
        this.userSyncTimer = null;
      }
      if (this.menuSyncTimer) {
        clearTimeout(this.menuSyncTimer);
        this.menuSyncTimer = null;
      }
      if (this.aiTipIndex) {
        layer.close(this.aiTipIndex);
@@ -961,6 +1020,9 @@
      hasTab: function (name) {
        return !!this.getTabByName(name);
      },
      isHomeTabUrl: function (url) {
        return this.resolveViewSrc(url || HOME_TAB_CONFIG.url) === this.resolveViewSrc(HOME_TAB_CONFIG.url);
      },
      getTabByName: function (name) {
        var i;
        for (i = 0; i < this.tabs.length; i++) {
@@ -995,7 +1057,7 @@
        this.loadingText = "正在加载 “" + tab.title + "” ...";
        this.pageLoading = !tab.loaded;
        this.activeTab = tab.name;
        this.activeMenuKey = tab.menuKey || this.findMenuKeyByUrl(tab.url);
        this.syncMenuStateByUrl(tab.url);
      },
      openHomeTab: function () {
        this.addOrActivateTab(HOME_TAB_CONFIG);
@@ -1006,7 +1068,7 @@
      closeAllTabs: function () {
        this.tabs = [this.createTab(HOME_TAB_CONFIG)];
        this.activeTab = HOME_TAB_CONFIG.url;
        this.activeMenuKey = "";
        this.syncMenuStateByUrl(HOME_TAB_CONFIG.url);
        this.pageLoading = true;
        this.loadingText = "正在加载 “控制中心” ...";
        this.persistTabs();
@@ -1048,9 +1110,6 @@
        this.activeTab = nextTabName;
        this.persistTabs();
      },
      handleTabClick: function () {
        this.activeMenuKey = this.findMenuKeyByUrl(this.activeTabUrl);
      },
      handleFrameLoad: function (name) {
        var tab = this.getTabByName(name);
@@ -1099,6 +1158,24 @@
            item = group.subMenu[j];
            if (item.url === normalized) {
              return item.tabKey;
            }
          }
        }
        return "";
      },
      findMenuGroupIndexByUrl: function (url) {
        var normalized = this.resolveViewSrc(url);
        var i;
        var j;
        var group;
        var item;
        for (i = 0; i < this.menus.length; i++) {
          group = this.menus[i];
          for (j = 0; j < group.subMenu.length; j++) {
            item = group.subMenu[j];
            if (item.url === normalized) {
              return "group-" + group.menuId;
            }
          }
        }
@@ -1188,6 +1265,62 @@
          menu.close(openedMenus[i]);
        }
      },
      syncMenuStateByUrl: function (url) {
        var targetUrl = this.resolveViewSrc(url || HOME_TAB_CONFIG.url);
        var activeMenuKey = "";
        var groupIndex = "";
        var that = this;
        var syncVersion;
        var applyMenuState;
        if (!this.isHomeTabUrl(targetUrl)) {
          activeMenuKey = this.findMenuKeyByUrl(targetUrl);
          if (activeMenuKey) {
            groupIndex = this.findMenuGroupIndexByUrl(targetUrl);
          }
        }
        this.activeMenuKey = activeMenuKey;
        this.defaultOpeneds = groupIndex ? [groupIndex] : [];
        this.menuSyncVersion += 1;
        syncVersion = this.menuSyncVersion;
        applyMenuState = function () {
          var menu = that.$refs.sideMenu;
          var openedMenus;
          if (syncVersion !== that.menuSyncVersion) {
            return;
          }
          if (!menu) {
            return;
          }
          openedMenus = menu.openedMenus ? menu.openedMenus.slice() : [];
          if (!groupIndex) {
            if (openedMenus.length) {
              that.collapseAllMenus();
            }
            return;
          }
          if (openedMenus.indexOf(groupIndex) > -1) {
            return;
          }
          if (openedMenus.length) {
            that.collapseAllMenus();
          }
          menu.open(groupIndex);
        };
        this.$nextTick(function () {
          applyMenuState();
          if (that.menuSyncTimer) {
            clearTimeout(that.menuSyncTimer);
            that.menuSyncTimer = null;
          }
          that.menuSyncTimer = setTimeout(function () {
            applyMenuState();
            that.menuSyncTimer = null;
          }, 160);
        });
      },
      loadMenu: function () {
        var that = this;
        this.menuLoading = true;
@@ -1199,11 +1332,7 @@
            that.menuLoading = false;
            if (res.code === 200) {
              that.menus = that.normalizeMenuData(res.data || []);
              that.defaultOpeneds = [];
              that.activeMenuKey = that.findMenuKeyByUrl(that.activeTabUrl);
              that.$nextTick(function () {
                that.collapseAllMenus();
              });
              that.syncMenuStateByUrl(that.activeTabUrl);
            } else if (res.code === 403) {
              top.location.href = baseUrl + "/login";
            } else {
@@ -1448,7 +1577,7 @@
        };
        window.admin.changeTheme = window.admin.changeTheme || function () {};
        window.admin.activeNav = function (url) {
          that.activeMenuKey = that.findMenuKeyByUrl(that.resolveViewSrc(url));
          that.syncMenuStateByUrl(that.resolveViewSrc(url));
        };
        window.index = window.index || {};