优化菜单加载

This commit is contained in:
2025-11-04 10:08:50 +08:00
parent ca1d265e34
commit 34035fb007
13 changed files with 351 additions and 35 deletions
+20 -3
View File
@@ -58,7 +58,7 @@
</template>
<script setup>
import { ref, computed, onMounted } from 'vue';
import { ref, computed, onMounted, onUnmounted } from 'vue';
import { useRouter, useRoute } from 'vue-router';
import { useAllDataStore } from '@/stores';
import { getAllMenus } from '@/api/menu';
@@ -245,6 +245,17 @@ onMounted(() => {
setTimeout(() => {
fetchMenus();
}, 100);
// 监听菜单缓存刷新事件
window.addEventListener('menu-cache-refreshed', fetchMenus);
});
// 组件卸载时清理事件监听
onUnmounted(() => {
if (themeObserver) {
themeObserver.disconnect();
}
window.removeEventListener('menu-cache-refreshed', fetchMenus);
});
// 计算属性:统一排序所有菜单项(不再区分有无子菜单)
@@ -294,8 +305,14 @@ const handleMenuSelect = (index) => {
}
const menuItem = findMenuItemByPath(list.value, index);
if (menuItem && menuItem.route) {
emit('menu-click', menuItem);
if (menuItem && menuItem.path) {
// 使用 path 而不是 route,确保菜单项有路径
const menuToEmit = {
...menuItem,
route: menuItem.path, // 兼容性:添加 route 字段
label: menuItem.name || menuItem.label
};
emit('menu-click', menuToEmit);
}
};
+126 -7
View File
@@ -16,6 +16,16 @@
</el-breadcrumb>
</div>
<div class="r-content">
<!-- 更新缓存按钮 -->
<el-button
circle
:icon="Refresh"
@click="refreshCache"
class="refresh-cache-btn"
:loading="cacheLoading"
title="更新菜单缓存"
/>
<!-- 主题切换按钮 -->
<el-button
circle
@@ -50,8 +60,9 @@ import { ref, computed, onMounted, onUnmounted } from "vue";
import { useRouter, useRoute } from "vue-router";
import { useAllDataStore } from "@/stores";
import { useAuthStore } from "@/stores/auth";
import { User, SwitchButton, Sunny, Moon } from '@element-plus/icons-vue';
import { getAllMenus } from '@/api/menu';
import { User, SwitchButton, Sunny, Moon, Refresh } from '@element-plus/icons-vue';
import { getAllMenus } from '@/api/menu';
import { ElMessage } from 'element-plus';
const router = useRouter();
const route = useRoute();
@@ -69,14 +80,95 @@ interface Breadcrumb {
}
const menuList = ref<Menu[]>([]);
const cacheLoading = ref(false);
const MENU_CACHE_KEY = 'menu_cache';
async function loadMenu() {
// 从缓存加载菜单
function loadMenuFromCache(): Menu[] | null {
try {
const cached = localStorage.getItem(MENU_CACHE_KEY);
if (cached) {
const menuData = JSON.parse(cached);
// 检查缓存是否过期(可选:设置过期时间,这里暂时不设置)
return menuData;
}
} catch (error) {
console.error('Failed to load menu from cache', error);
}
return null;
}
// 保存菜单到缓存
function saveMenuToCache(menus: Menu[]) {
try {
localStorage.setItem(MENU_CACHE_KEY, JSON.stringify(menus));
} catch (error) {
console.error('Failed to save menu to cache', error);
}
}
// 从API加载菜单
async function loadMenuFromAPI(updateCache = true) {
try {
const res = await getAllMenus();
menuList.value = res.data || [];
const menus = res.data || [];
if (updateCache && menus.length > 0) {
saveMenuToCache(menus);
}
return menus;
} catch (error) {
console.error('Failed to load menu', error);
menuList.value = [];
console.error('Failed to load menu from API', error);
return [];
}
}
// 加载菜单(优先从缓存)
async function loadMenu() {
// 先尝试从缓存加载
const cachedMenus = loadMenuFromCache();
if (cachedMenus && cachedMenus.length > 0) {
menuList.value = cachedMenus;
// 异步更新缓存(后台更新,不阻塞UI)
loadMenuFromAPI(true).then(menus => {
if (menus.length > 0) {
menuList.value = menus;
}
});
} else {
// 缓存不存在,从API加载
menuList.value = await loadMenuFromAPI(true);
}
}
// 更新缓存(手动刷新)
async function refreshCache() {
cacheLoading.value = true;
try {
const menus = await loadMenuFromAPI(true);
if (menus.length > 0) {
menuList.value = menus;
// 重新加载动态路由
const { loadAndAddDynamicRoutes, resetDynamicRoutes } = await import('@/router/index');
// 重置路由加载状态,强制重新加载
resetDynamicRoutes();
await loadAndAddDynamicRoutes();
// 等待路由完全加载(给Vue Router一些时间更新路由表)
await new Promise(resolve => setTimeout(resolve, 200));
// 触发菜单刷新事件,通知CommonAside组件刷新菜单
window.dispatchEvent(new CustomEvent('menu-cache-refreshed'));
ElMessage.success('菜单缓存和路由更新成功');
} else {
ElMessage.warning('未获取到菜单数据');
}
} catch (error) {
console.error('Failed to refresh cache', error);
ElMessage.error('更新缓存失败,请检查网络连接');
} finally {
cacheLoading.value = false;
}
}
@@ -119,7 +211,7 @@ const handleCollapse = () => {
store.state.isCollapse = !store.state.isCollapse;
};
const handleCommand = (command) => {
const handleCommand = async (command) => {
if (command === 'profile') {
router.push('/user/userProfile');
} else if (command === 'logout') {
@@ -130,6 +222,18 @@ const handleCommand = (command) => {
//清除租户数据
localStorage.removeItem('tenant');
sessionStorage.removeItem('tenant');
//清除tabs_list缓存
localStorage.removeItem('tabs_list');
localStorage.removeItem('active_tab');
sessionStorage.removeItem('tabs_list');
// 清除菜单缓存
localStorage.removeItem(MENU_CACHE_KEY);
// 重置 tabs store 状态
const { useTabsStore } = await import('@/stores');
const tabsStore = useTabsStore();
tabsStore.resetTabs();
router.push('/login');
}
};
@@ -254,6 +358,21 @@ onUnmounted(() => {
align-items: center;
gap: 16px;
.refresh-cache-btn,
.theme-toggle-btn {
// 使用 Element Plus 的填充色变量
background-color: var(--el-fill-color-light);
border-color: var(--el-border-color);
color: var(--el-text-color-primary);
margin-left: 0 !important;
&:hover {
background-color: var(--el-fill-color);
border-color: var(--el-border-color-dark);
color: var(--el-color-primary);
}
}
.user {
width: 40px;
height: 40px;