|
|
@@ -1,22 +1,76 @@
|
|
|
<!-- layout/components/Sidebar/index.vue -->
|
|
|
<template>
|
|
|
<div class="sidebar-container" :class="{ collapsed: isCollapse }">
|
|
|
- <div class="menu-list">
|
|
|
- <div
|
|
|
- v-for="item in menuList"
|
|
|
- :key="item.path"
|
|
|
- :class="['menu-item', { active: isActive(item.path) }]"
|
|
|
- @click="handleMenuClick(item)"
|
|
|
- >
|
|
|
- <!-- 使用原本的 svg 图标 -->
|
|
|
- <svg-icon
|
|
|
- v-if="item.meta?.icon"
|
|
|
- :icon-class="item.meta.icon"
|
|
|
- class="menu-icon"
|
|
|
- />
|
|
|
- <span class="menu-text">{{ item.meta?.title }}</span>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
+ <el-menu
|
|
|
+ :default-active="activeMenu"
|
|
|
+ :collapse="isCollapse"
|
|
|
+ background-color="#304156"
|
|
|
+ text-color="#bfcbd9"
|
|
|
+ active-text-color="#409eff"
|
|
|
+ :collapse-transition="false"
|
|
|
+ unique-opened
|
|
|
+ class="sidebar-menu"
|
|
|
+ >
|
|
|
+ <template v-for="item in menuList" :key="item.path">
|
|
|
+ <!-- 有子菜单的项 -->
|
|
|
+ <el-sub-menu
|
|
|
+ v-if="item.children && item.children.length > 0"
|
|
|
+ :index="getFullPath(item.path)"
|
|
|
+ >
|
|
|
+ <template #title>
|
|
|
+ <svg-icon v-if="item.meta?.icon" :icon-class="item.meta.icon" />
|
|
|
+ <span>{{ item.meta?.title }}</span>
|
|
|
+ </template>
|
|
|
+ <!-- 二级菜单 -->
|
|
|
+ <template v-for="child in item.children" :key="child.path">
|
|
|
+ <el-sub-menu
|
|
|
+ v-if="child.children && child.children.length > 0"
|
|
|
+ :index="getFullPath(child.path, item.path)"
|
|
|
+ >
|
|
|
+ <template #title>
|
|
|
+ <svg-icon
|
|
|
+ v-if="child.meta?.icon"
|
|
|
+ :icon-class="child.meta.icon"
|
|
|
+ />
|
|
|
+ <span>{{ child.meta?.title }}</span>
|
|
|
+ </template>
|
|
|
+ <!-- 三级菜单 -->
|
|
|
+ <el-menu-item
|
|
|
+ v-for="subChild in child.children"
|
|
|
+ :key="subChild.path"
|
|
|
+ :index="getFullPath(subChild.path, child.path, item.path)"
|
|
|
+ @click="handleMenuClick(subChild, child, item)"
|
|
|
+ >
|
|
|
+ <svg-icon
|
|
|
+ v-if="subChild.meta?.icon"
|
|
|
+ :icon-class="subChild.meta.icon"
|
|
|
+ />
|
|
|
+ <span>{{ subChild.meta?.title }}</span>
|
|
|
+ </el-menu-item>
|
|
|
+ </el-sub-menu>
|
|
|
+ <!-- 二级菜单(无子菜单) -->
|
|
|
+ <el-menu-item
|
|
|
+ v-else
|
|
|
+ :index="getFullPath(child.path, item.path)"
|
|
|
+ @click="handleMenuClick(child, item)"
|
|
|
+ >
|
|
|
+ <svg-icon v-if="child.meta?.icon" :icon-class="child.meta.icon" />
|
|
|
+ <span>{{ child.meta?.title }}</span>
|
|
|
+ </el-menu-item>
|
|
|
+ </template>
|
|
|
+ </el-sub-menu>
|
|
|
+
|
|
|
+ <!-- 没有子菜单的一级菜单项 -->
|
|
|
+ <el-menu-item
|
|
|
+ v-else
|
|
|
+ :index="getFullPath(item.path)"
|
|
|
+ @click="handleMenuClick(item)"
|
|
|
+ >
|
|
|
+ <svg-icon v-if="item.meta?.icon" :icon-class="item.meta.icon" />
|
|
|
+ <span>{{ item.meta?.title }}</span>
|
|
|
+ </el-menu-item>
|
|
|
+ </template>
|
|
|
+ </el-menu>
|
|
|
|
|
|
<div class="collapse-btn" @click="toggleCollapse">
|
|
|
<span class="btn-text">{{ isCollapse ? "▶" : "◀" }}</span>
|
|
|
@@ -46,8 +100,17 @@ export default {
|
|
|
|
|
|
const isCollapse = computed(() => appStore.sidebar.isCollapse);
|
|
|
|
|
|
+ // 当前激活的菜单
|
|
|
+ const activeMenu = computed(() => {
|
|
|
+ return route.path;
|
|
|
+ });
|
|
|
+
|
|
|
+ // 获取菜单列表
|
|
|
const menuList = computed(() => {
|
|
|
const routes = permissionStore.sidebarRouters || [];
|
|
|
+ console.log("sidebarRouters:", routes);
|
|
|
+ console.log("topMenuKey:", props.topMenuKey);
|
|
|
+
|
|
|
if (!routes.length) return [];
|
|
|
|
|
|
if (props.topMenuKey) {
|
|
|
@@ -65,18 +128,65 @@ export default {
|
|
|
return [];
|
|
|
});
|
|
|
|
|
|
- const isActive = (path) => {
|
|
|
- return route.path.includes(path);
|
|
|
+ // 获取完整路径
|
|
|
+ const getFullPath = (path, parentPath = "", grandParentPath = "") => {
|
|
|
+ if (path.startsWith("/")) {
|
|
|
+ return path;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 三级菜单:topMenuKey + 一级/二级/三级
|
|
|
+ if (grandParentPath && parentPath) {
|
|
|
+ return (
|
|
|
+ props.topMenuKey +
|
|
|
+ "/" +
|
|
|
+ grandParentPath +
|
|
|
+ "/" +
|
|
|
+ parentPath +
|
|
|
+ "/" +
|
|
|
+ path
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ // 二级菜单:topMenuKey + 一级/二级
|
|
|
+ if (parentPath) {
|
|
|
+ return props.topMenuKey + "/" + parentPath + "/" + path;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 一级菜单:topMenuKey + 一级
|
|
|
+ return props.topMenuKey + "/" + path;
|
|
|
};
|
|
|
|
|
|
- const handleMenuClick = (item) => {
|
|
|
- let fullPath = item.path;
|
|
|
- if (!fullPath.startsWith("/") && props.topMenuKey) {
|
|
|
- fullPath = props.topMenuKey + "/" + fullPath;
|
|
|
+ // 点击菜单
|
|
|
+ const handleMenuClick = (
|
|
|
+ item,
|
|
|
+ parentItem = null,
|
|
|
+ grandParentItem = null,
|
|
|
+ ) => {
|
|
|
+ let fullPath = "";
|
|
|
+
|
|
|
+ if (grandParentItem && parentItem) {
|
|
|
+ // 三级菜单:topMenuKey/一级/二级/三级
|
|
|
+ fullPath =
|
|
|
+ props.topMenuKey +
|
|
|
+ "/" +
|
|
|
+ grandParentItem.path +
|
|
|
+ "/" +
|
|
|
+ parentItem.path +
|
|
|
+ "/" +
|
|
|
+ item.path;
|
|
|
+ } else if (parentItem) {
|
|
|
+ // 二级菜单:topMenuKey/一级/二级
|
|
|
+ fullPath = props.topMenuKey + "/" + parentItem.path + "/" + item.path;
|
|
|
+ } else {
|
|
|
+ // 一级菜单:topMenuKey/一级
|
|
|
+ fullPath = props.topMenuKey + "/" + item.path;
|
|
|
}
|
|
|
+
|
|
|
+ console.log("跳转路径:", fullPath);
|
|
|
router.push(fullPath);
|
|
|
};
|
|
|
|
|
|
+ // 收起/展开
|
|
|
const toggleCollapse = () => {
|
|
|
appStore.sidebar.isCollapse = !appStore.sidebar.isCollapse;
|
|
|
};
|
|
|
@@ -84,7 +194,8 @@ export default {
|
|
|
return {
|
|
|
menuList,
|
|
|
isCollapse,
|
|
|
- isActive,
|
|
|
+ activeMenu,
|
|
|
+ getFullPath,
|
|
|
handleMenuClick,
|
|
|
toggleCollapse,
|
|
|
};
|
|
|
@@ -94,73 +205,55 @@ export default {
|
|
|
|
|
|
<style scoped>
|
|
|
.sidebar-container {
|
|
|
- width: 210px;
|
|
|
- background-color: #304156;
|
|
|
height: 100%;
|
|
|
- transition: width 0.2s;
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
- overflow: hidden;
|
|
|
-}
|
|
|
-
|
|
|
-.sidebar-container.collapsed {
|
|
|
- width: 64px;
|
|
|
+ background-color: #304156;
|
|
|
}
|
|
|
|
|
|
-.menu-list {
|
|
|
+.sidebar-menu {
|
|
|
flex: 1;
|
|
|
+ border: none;
|
|
|
overflow-y: auto;
|
|
|
- padding: 10px 0;
|
|
|
+ overflow-x: hidden;
|
|
|
}
|
|
|
|
|
|
-.menu-item {
|
|
|
- padding: 0 20px;
|
|
|
- height: 44px;
|
|
|
- line-height: 44px;
|
|
|
- color: #bfcbd9;
|
|
|
- cursor: pointer;
|
|
|
- white-space: nowrap;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- gap: 12px;
|
|
|
+:deep(.el-menu) {
|
|
|
+ border-right: none;
|
|
|
+ background-color: #304156;
|
|
|
}
|
|
|
|
|
|
-/* 收起时的样式 */
|
|
|
-.sidebar-container.collapsed .menu-item {
|
|
|
- justify-content: center;
|
|
|
- padding: 0;
|
|
|
+:deep(.el-sub-menu__title) {
|
|
|
+ color: #bfcbd9;
|
|
|
+ background-color: #304156;
|
|
|
}
|
|
|
|
|
|
-.menu-item:hover {
|
|
|
- background-color: #263445;
|
|
|
- color: #fff;
|
|
|
+:deep(.el-sub-menu__title:hover) {
|
|
|
+ background-color: #263445 !important;
|
|
|
+ color: #fff !important;
|
|
|
}
|
|
|
|
|
|
-.menu-item.active {
|
|
|
- background-color: #409eff;
|
|
|
- color: #fff;
|
|
|
+:deep(.el-menu-item) {
|
|
|
+ color: #bfcbd9;
|
|
|
+ background-color: #304156;
|
|
|
}
|
|
|
|
|
|
-/* 图标样式 */
|
|
|
-.menu-icon {
|
|
|
- width: 20px;
|
|
|
- height: 20px;
|
|
|
- flex-shrink: 0;
|
|
|
+:deep(.el-menu-item:hover) {
|
|
|
+ background-color: #263445 !important;
|
|
|
+ color: #fff !important;
|
|
|
}
|
|
|
|
|
|
-/* 文字样式 */
|
|
|
-.menu-text {
|
|
|
- font-size: 14px;
|
|
|
+:deep(.el-menu-item.is-active) {
|
|
|
+ background-color: #409eff !important;
|
|
|
+ color: #fff !important;
|
|
|
}
|
|
|
|
|
|
-/* 收起时隐藏文字 */
|
|
|
-.sidebar-container.collapsed .menu-text {
|
|
|
+.sidebar-container.collapsed :deep(.el-sub-menu__title span) {
|
|
|
display: none;
|
|
|
}
|
|
|
|
|
|
-/* 收起时图标居中 */
|
|
|
-.sidebar-container.collapsed .menu-icon {
|
|
|
- margin: 0;
|
|
|
+.sidebar-container.collapsed :deep(.el-menu-item span) {
|
|
|
+ display: none;
|
|
|
}
|
|
|
|
|
|
.collapse-btn {
|
|
|
@@ -170,6 +263,7 @@ export default {
|
|
|
color: #bfcbd9;
|
|
|
cursor: pointer;
|
|
|
border-top: 1px solid #1f2d3d;
|
|
|
+ background-color: #304156;
|
|
|
}
|
|
|
|
|
|
.collapse-btn:hover {
|