|
|
|
@ -7,11 +7,12 @@ |
|
|
|
:collapse="isCollapse" |
|
|
|
:background-color="settings.sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground" |
|
|
|
:text-color="settings.sideTheme === 'theme-dark' ? variables.menuColor : variables.menuLightColor" |
|
|
|
:unique-opened="true" |
|
|
|
:unique-opened="false" |
|
|
|
:active-text-color="variables.menuColorActive" |
|
|
|
:collapse-transition="false" |
|
|
|
:default-openeds="defaultOpeneds" |
|
|
|
mode="vertical" |
|
|
|
@open="handleMenuOpen" |
|
|
|
@close="handleMenuClose" |
|
|
|
> |
|
|
|
<sidebar-item |
|
|
|
v-for="(route, index) in displayedRoutes" |
|
|
|
@ -27,29 +28,57 @@ |
|
|
|
|
|
|
|
<script> |
|
|
|
import { mapGetters, mapState } from "vuex"; |
|
|
|
import path from "path"; |
|
|
|
import { isExternal } from "@/utils/validate"; |
|
|
|
import Logo from "./Logo"; |
|
|
|
import SidebarItem from "./SidebarItem"; |
|
|
|
import variables from "@/assets/styles/variables.scss"; |
|
|
|
|
|
|
|
// 与 SidebarItem.resolvePath 保持一致,用于推算各级子菜单展开后的层级深度 |
|
|
|
function resolveMenuPath(basePath, routePath) { |
|
|
|
if (isExternal(routePath)) return routePath; |
|
|
|
return path.resolve(basePath, routePath); |
|
|
|
} |
|
|
|
|
|
|
|
export default { |
|
|
|
components: { SidebarItem, Logo }, |
|
|
|
data() { |
|
|
|
return { |
|
|
|
// 当前处于展开状态的子菜单 index(即其路由路径) |
|
|
|
openMenuIndexes: [], |
|
|
|
}; |
|
|
|
}, |
|
|
|
computed: { |
|
|
|
...mapState(["settings"]), |
|
|
|
...mapGetters(["sidebarRouters", "sidebar"]), |
|
|
|
...mapGetters(["sidebarRouters", "sidebar", "roles"]), |
|
|
|
// admin 账号显示全部导航,其余账号只显示默认菜单 |
|
|
|
isAdminAccount() { |
|
|
|
if ((this.roles || []).indexOf("admin") !== -1) return true; |
|
|
|
return this.$store.state.user.username === "admin"; |
|
|
|
}, |
|
|
|
displayedRoutes() { |
|
|
|
const temporarilyHiddenRootMenus = new Set([ |
|
|
|
"设备管理", |
|
|
|
"系统管理", |
|
|
|
"区域管理", |
|
|
|
"系统监控", |
|
|
|
"基础数据", |
|
|
|
]); |
|
|
|
const filterHiddenNavigation = (routes = [], depth = 0) => routes.reduce((result, route) => { |
|
|
|
const temporarilyHiddenRootMenus = this.isAdminAccount |
|
|
|
? new Set() |
|
|
|
: new Set([ |
|
|
|
"设备管理", |
|
|
|
"系统管理", |
|
|
|
"区域管理", |
|
|
|
"系统监控", |
|
|
|
"基础数据", |
|
|
|
]); |
|
|
|
// “点位管理(实施用)”即后端配置在 /site-management 之外的“点位管理”菜单: |
|
|
|
// 仅管理员可见;常规点位管理(/site-management)所有角色都可见 |
|
|
|
const isImplementationPointsMenu = (route) => { |
|
|
|
const title = String(route.meta && route.meta.title || "").split("|")[0]; |
|
|
|
const isTaskManagement = title === "巡视任务管理"; |
|
|
|
const isImplementationPoints = title === "点位管理" && |
|
|
|
return title === "点位管理" && |
|
|
|
route.path !== "/site-management" && |
|
|
|
route.path !== "/inspection-workspace/points"; |
|
|
|
}; |
|
|
|
const filterHiddenNavigation = (routes = [], depth = 0) => routes.reduce((result, route) => { |
|
|
|
const title = String(route.meta && route.meta.title || "").split("|")[0]; |
|
|
|
// 后端菜单可能仍返回旧名“巡视任务管理”,两种命名都要隐藏 |
|
|
|
const isTaskManagement = ["巡检任务管理", "巡视任务管理"].includes(title); |
|
|
|
const isImplementationPoints = !this.isAdminAccount && isImplementationPointsMenu(route); |
|
|
|
const isTemporarilyHiddenRootMenu = depth === 0 && temporarilyHiddenRootMenus.has(title); |
|
|
|
if (isTaskManagement || isImplementationPoints || isTemporarilyHiddenRootMenu) return result; |
|
|
|
|
|
|
|
@ -82,7 +111,10 @@ export default { |
|
|
|
children: patrolRoutes, |
|
|
|
}); |
|
|
|
const routePath = this.$route.path; |
|
|
|
if (routePath === "/index" || routePath === "/" || routePath === "/xfdh/index" || patrolRoutes.some(route => routePath.indexOf(route.path + "/") === 0 || routePath === route.path)) { |
|
|
|
if ( |
|
|
|
!this.isAdminAccount && |
|
|
|
(routePath === "/index" || routePath === "/" || routePath === "/xfdh/index" || patrolRoutes.some(route => routePath.indexOf(route.path + "/") === 0 || routePath === route.path)) |
|
|
|
) { |
|
|
|
return patrolRoutes; |
|
|
|
} |
|
|
|
return [...patrolRoutes, ...otherRoutes]; |
|
|
|
@ -97,7 +129,7 @@ export default { |
|
|
|
"/video-preview": "/inspection-workspace/video-preview", |
|
|
|
"/video-playback": "/inspection-workspace/video-playback", |
|
|
|
}; |
|
|
|
return visibleRoutes |
|
|
|
const workspaceMenus = visibleRoutes |
|
|
|
.filter((route) => workspacePaths[route.path]) |
|
|
|
.map((route) => { |
|
|
|
const workspacePath = workspacePaths[route.path]; |
|
|
|
@ -118,6 +150,11 @@ export default { |
|
|
|
}] : [], |
|
|
|
}; |
|
|
|
}); |
|
|
|
// 工作区模式下管理员同样能看到点位管理(实施用) |
|
|
|
if (this.isAdminAccount) { |
|
|
|
return [...workspaceMenus, ...visibleRoutes.filter(isImplementationPointsMenu)]; |
|
|
|
} |
|
|
|
return workspaceMenus; |
|
|
|
}, |
|
|
|
activeMenu() { |
|
|
|
const route = this.$route; |
|
|
|
@ -128,10 +165,35 @@ export default { |
|
|
|
} |
|
|
|
return path; |
|
|
|
}, |
|
|
|
defaultOpeneds() { |
|
|
|
if (this.isCollapse) return []; |
|
|
|
const paths = (this.displayedRoutes || []).map(route => route.path); |
|
|
|
return paths.filter(path => path === "/inspection-menu"); |
|
|
|
// 各级子菜单的 index -> 所属层级(1 为顶级分组,2 为分组内的子分组…)。 |
|
|
|
// SidebarItem 嵌套时路径按 item.path 拼接两次(父 basePath 已拼过一次, |
|
|
|
// index 再拼一次),这里必须保持一致才能对上 el-menu 的 open/close index。 |
|
|
|
submenuDepthMap() { |
|
|
|
const map = {}; |
|
|
|
const walk = (routes, parentBase, depth) => { |
|
|
|
(routes || []).forEach(route => { |
|
|
|
if (!route || route.hidden) return; |
|
|
|
const itemBase = resolveMenuPath(parentBase, route.path); |
|
|
|
const visibleChildren = (route.children || []).filter(child => !child.hidden); |
|
|
|
if (route.alwaysShow || visibleChildren.length > 1) { |
|
|
|
map[resolveMenuPath(itemBase, route.path)] = depth; |
|
|
|
walk(route.children, itemBase, depth + 1); |
|
|
|
} else if (visibleChildren.length === 1) { |
|
|
|
walk(route.children, itemBase, depth); |
|
|
|
} |
|
|
|
}); |
|
|
|
}; |
|
|
|
walk(this.displayedRoutes, "", 1); |
|
|
|
return map; |
|
|
|
}, |
|
|
|
// 当前展开的子菜单里最深处于第几层,0 表示没有展开任何分组 |
|
|
|
openDepthLevel() { |
|
|
|
let max = 0; |
|
|
|
(this.openMenuIndexes || []).forEach(index => { |
|
|
|
const depth = this.submenuDepthMap[index]; |
|
|
|
if (depth && depth > max) max = depth; |
|
|
|
}); |
|
|
|
return max; |
|
|
|
}, |
|
|
|
variables() { |
|
|
|
return variables; |
|
|
|
@ -139,25 +201,27 @@ export default { |
|
|
|
isCollapse() { |
|
|
|
return !this.sidebar.opened; |
|
|
|
}, |
|
|
|
hasNestedMenus() { |
|
|
|
return this.displayedRoutes.some(route => this.hasVisibleNestedMenu(route)); |
|
|
|
}, |
|
|
|
}, |
|
|
|
watch: { |
|
|
|
hasNestedMenus: { |
|
|
|
openDepthLevel: { |
|
|
|
immediate: true, |
|
|
|
handler(hasNestedMenus) { |
|
|
|
this.$emit("menu-depth-change", hasNestedMenus); |
|
|
|
handler(level) { |
|
|
|
this.$emit("menu-depth-change", level); |
|
|
|
}, |
|
|
|
}, |
|
|
|
}, |
|
|
|
methods: { |
|
|
|
hasVisibleNestedMenu(route) { |
|
|
|
const visibleChildren = (route.children || []).filter(child => !child.hidden); |
|
|
|
if (route.alwaysShow || visibleChildren.length > 1) { |
|
|
|
return true; |
|
|
|
// 展开的分组保持展开(unique-opened 已关闭),宽度随最深层级放宽 |
|
|
|
handleMenuOpen(index) { |
|
|
|
if (this.openMenuIndexes.indexOf(index) === -1) { |
|
|
|
this.openMenuIndexes = [...this.openMenuIndexes, index]; |
|
|
|
} |
|
|
|
return visibleChildren.some(child => this.hasVisibleNestedMenu(child)); |
|
|
|
}, |
|
|
|
handleMenuClose(index) { |
|
|
|
const prefix = index + "/"; |
|
|
|
this.openMenuIndexes = this.openMenuIndexes.filter( |
|
|
|
item => item !== index && String(item).indexOf(prefix) !== 0 |
|
|
|
); |
|
|
|
}, |
|
|
|
} |
|
|
|
}; |
|
|
|
|