From 686fe55892de7bf8d206cddbead77a5fbdb0e091 Mon Sep 17 00:00:00 2001
From: Junjie <fallin.jie@qq.com>
Date: 星期日, 08 三月 2026 19:59:29 +0800
Subject: [PATCH] #
---
src/main/webapp/views/index.html | 227 ++++++++++++++++++++++++++++++++++++++++++++------------
1 files changed, 178 insertions(+), 49 deletions(-)
diff --git a/src/main/webapp/views/index.html b/src/main/webapp/views/index.html
index b9bf30f..37d2110 100644
--- a/src/main/webapp/views/index.html
+++ b/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,42 +544,52 @@
</el-input>
</div>
- <el-scrollbar class="aside-scroll" v-loading="menuLoading">
- <el-menu
- ref="sideMenu"
- class="side-menu"
- :default-active="activeMenuKey"
- :collapse="isCollapse"
- :collapse-transition="false"
- :default-openeds="defaultOpeneds"
- unique-opened
- background-color="transparent"
- text-color="#c6d1df"
- active-text-color="#ffffff">
- <el-submenu
- v-for="group in filteredMenus"
- :key="'group-' + group.menuId"
- :index="'group-' + group.menuId">
- <template slot="title">
- <i :class="resolveMenuIcon(group.menuCode)"></i>
- <span>{{ group.menu }}</span>
- </template>
- <el-menu-item
- v-for="item in group.subMenu"
- :key="item.tabKey"
- :index="item.tabKey"
- @click="handleMenuSelect(group, item)">
- {{ item.name }}
- </el-menu-item>
- </el-submenu>
- </el-menu>
-
- <div class="aside-empty" v-if="!menuLoading && filteredMenus.length === 0">
- <el-empty
- :image-size="80"
- :description="menuKeyword ? '娌℃湁鍖归厤鑿滃崟' : '褰撳墠璐﹀彿娌℃湁鍙敤鑿滃崟'">
- </el-empty>
+ <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"
+ :default-active="activeMenuKey"
+ :collapse="isCollapse"
+ :collapse-transition="false"
+ :default-openeds="defaultOpeneds"
+ unique-opened
+ background-color="transparent"
+ text-color="#c6d1df"
+ active-text-color="#ffffff">
+ <el-submenu
+ v-for="group in filteredMenus"
+ :key="'group-' + group.menuId"
+ :index="'group-' + group.menuId">
+ <template slot="title">
+ <i :class="resolveMenuIcon(group.menuCode)"></i>
+ <span>{{ group.menu }}</span>
+ </template>
+ <el-menu-item
+ v-for="item in group.subMenu"
+ :key="item.tabKey"
+ :index="item.tabKey"
+ @click="handleMenuSelect(group, item)">
+ {{ item.name }}
+ </el-menu-item>
+ </el-submenu>
+ </el-menu>
+
+ <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: "姝e湪鍔犺浇椤甸潰...",
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 = "姝e湪鍔犺浇 鈥�" + 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 = "姝e湪鍔犺浇 鈥�" + 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 = "姝e湪鍔犺浇 鈥滄帶鍒朵腑蹇冣�� ...";
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 || {};
--
Gitblit v1.9.1