在前幾篇內容中已完成菜單、角色及菜單權限等相關開發,若要在左側菜單根據用戶角色動態展示菜單,需對 Sidebar 中的相關數據進行修改。鑒于其他相關方法及類型已在前文實現,本文不再重復闡述。
1 修改 Sidebar 組件
在 src/layout/components/Sidebar/index.vue 中修改路由數據來源,更新為動態生成的菜單,代碼如下:
//src/layout/components/Sidebar/index.vue
<template><div><!-- 側邊欄Logo組件,根據配置決定是否顯示,并響應側邊欄折疊狀態 --><logo v-if="sidebarLogo" :collapse="sidebar.opened"></logo><!-- Element UI菜單組件,配置了背景色、文本顏色和激活項顏色 --><el-menuborder-noneclass="sidebar-container-menu":default-active="defaultActive":background-color="variables.menuBg":text-color="variables.menuText":active-text-color="theme":collapse="sidebar.opened"><!-- 動態生成菜單項,menuRoutes包含了所有需要顯示的菜單項 --><sidebar-itemv-for="route in menuRoutes":key="route.path":item="route"/><!-- 增加父路徑,用于el-menu-item渲染的時候拼接 --></el-menu></div>
</template><script lang="ts" setup>
// 導入應用狀態管理相關模塊
import { useAppStore } from "@/stores/app";
// 導入樣式變量
import variables from "@/style/variables.module.scss";
// 導入設置狀態管理
import { useSettingStore } from "@/stores/settings";
// 導入菜單狀態管理
import { useMenuStore } from "@/stores/menu";// 獲取菜單狀態
const meuStore = useMenuStore();// 計算屬性:響應式菜單數據,來自狀態管理中的權限菜單樹
const menuRoutes = computed(() => meuStore.state.authMenuTreeData);// 獲取當前路由信息
const route = useRoute();// 獲取側邊欄狀態(展開/折疊)
const { sidebar } = useAppStore();// 計算屬性:當前激活菜單項,默認使用當前路由路徑
const defaultActive = computed(() => {// .....return route.path;
});// 獲取設置狀態
const settingsStore = useSettingStore();
// 計算屬性:主題顏色,響應式更新
const theme = computed(() => settingsStore.settings.theme);// 計算屬性:側邊欄Logo顯示控制,響應式更新
const sidebarLogo = computed(() => settingsStore.settings.sidebarLogo);
</script><style scoped></style>
2 修改 SidebarItem 組件
修改?src/layout/components/Sidebar/SidebarItem.vue 中的路徑解析方法,代碼如下:
//src/layout/components/Sidebar/SidebarItem.vue
<template><!-- 當菜單項沒有設置hidden為true時顯示 --><template v-if="!item.meta?.hidden"><!-- 情況1:如果只有一個子菜單且沒有設置alwaysShow,直接渲染子菜單項 --><sidebar-item-linkv-if="filteredChildren.length <= 1 && !item.meta?.alwaysShow":to="resolvePath(singleChildRoute.path)"><el-menu-item :index="resolvePath(singleChildRoute.path)"><el-icon v-if="iconName"><!-- 顯示菜單項圖標 --><svg-icon :icon-name="iconName" /></el-icon><!-- 菜單項標題 --><template #title>{{ singleChildRoute.meta.title }}</template></el-menu-item></sidebar-item-link><!-- 情況2:有多個子菜單或設置了alwaysShow,渲染為下拉菜單 --><el-sub-menu v-else :index="item.path"><template #title><el-icon v-if="iconName"><!-- 父菜單項圖標 --><svg-icon :icon-name="iconName" /></el-icon><!-- 父菜單項標題 --><span>{{ item.meta?.title }}</span></template><!-- 遞歸渲染子菜單項 --><sidebar-itemv-for="child of filteredChildren":key="child.path":item="child"></sidebar-item></el-sub-menu></template>
</template><script lang="ts" setup>
// 導入路徑校驗工具和菜單項類型定義
import { isExternal } from "@/utils/validate";
import type { ITreeItemDataWithMenuData } from "@/utils/generateTree";// 定義組件props,接收菜單項數據
const { item } = defineProps<{item: ITreeItemDataWithMenuData;
}>();// 計算屬性:過濾掉隱藏的子菜單項
const filteredChildren = computed(() =>(item.children || []).filter((child) => !child.meta?.hidden)
);// 計算屬性:確定當前要渲染的菜單項
// 如果只有一個子菜單,渲染子菜單;否則渲染當前菜單項
const singleChildRoute = computed(() =>filteredChildren.value.length === 1 ? filteredChildren.value[0] : { ...item }
);// 計算屬性:獲取菜單項圖標
const iconName = computed(() => singleChildRoute.value.meta?.icon);// 路徑解析函數:處理菜單項路徑
// 外部鏈接直接返回,內部鏈接可能需要拼接父路徑
const resolvePath = (childPath: string) => {if (isExternal(childPath)) {return childPath;}return childPath;
};
</script>
下一篇將繼續探討 權限指令的實現,敬請期待~?