Files
ebiz-ai-knowledge-manage/src/views/app/layout/components/Sidebar/Sidebar.vue
2025-05-08 16:35:26 +08:00

362 lines
8.7 KiB
Vue

<template>
<div :class="{ 'has-logo': showLogo }" class="sidebar-container">
<div class="sidebar-layout">
<!-- Left side - Primary menu -->
<div class="primary-menu">
<div>
<logo class="logo" v-if="showLogo" :collapse="isCollapse" />
<div
v-for="route in menuList"
:key="route.id"
class="primary-menu-item"
:class="{ active: activeParent === route.url }"
@click="selectParentMenu(route)"
>
<i
v-if="route.icon"
:class="[
route.icon,
{ iconfont: !route.icon.includes('el-icon') },
{ mb5: route.icon.includes('el-icon') }
]"
class=" fs22"
></i>
<img
v-else
:src="activeParent === route.url ? knowledgeActive : knowledge"
alt=""
class="mb5"
style="width: 20px"
/>
<span class="menu-title">{{ route.menuName }}</span>
</div>
</div>
<div>
<personal />
</div>
</div>
<!-- Right side - Submenu -->
<div class="submenu-container" v-if="currentSubmenu.length">
<!-- <div class="submenu-header">-->
<!-- <span>{{ currentParentTitle }}</span>-->
<!-- </div>-->
<el-scrollbar wrap-class="scrollbar-wrapper">
<div class="submenu-list">
<pre style="font-size: 12px; padding: 10px; display: none;">{{
JSON.stringify(currentSubmenu, null, 2)
}}</pre>
<div
v-for="subItem in currentSubmenu"
:key="subItem.id"
class="submenu-item"
:class="{ active: activeMenu === subItem.url }"
@click="navigateTo(subItem.url)"
>
<i
v-if="subItem.icon"
class="mr10"
:class="[
subItem.icon,
{ iconfont: !subItem.icon.includes('el-icon') }
]"
/>
<span>{{ subItem.menuName }}</span>
</div>
</div>
</el-scrollbar>
</div>
</div>
</div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex'
import Logo from './Logo'
import variables from '@/assets/sass/variables.scss'
import Personal from '@/views/app/layout/components/Sidebar/personal.vue'
import knowledge from '@/assets/images/knowledge.png'
import knowledgeActive from '@/assets/images/knowledgeActive.png'
export default {
components: { Personal, Logo },
computed: {
knowledge() {
return knowledge
},
knowledgeActive() {
return knowledgeActive
},
...mapGetters(['sidebar', 'sidebarList']),
routes() {
return this.$router.options.routes
},
activeMenu() {
const route = this.$route
const { meta, path } = route
// if set path, the sidebar will highlight the path you set
if (meta.activeMenu) {
return meta.activeMenu
}
return path
},
showLogo() {
return this.$store.state.settings.sidebarLogo
},
variables() {
return variables
},
isCollapse() {
return !this.sidebar.opened
},
currentSubmenu() {
if (!this.activeParent) return []
const parent = this.menuList.find(item => item.url === this.activeParent)
return parent ? parent.children || [] : []
},
currentParentTitle() {
if (!this.activeParent) return ''
const parent = this.menuList.find(item => item.url === this.activeParent)
return parent && parent.meta ? parent.meta.title : ''
}
},
data() {
return {
menuList: [],
activeParent: null
}
},
methods: {
...mapActions({
setSidebarList: 'app/setSidebarList'
}),
formatList(menu, state, parentPath = null) {
menu.map(item => {
if (parentPath) {
item.url = '/' + parentPath + '/' + item.url
}
if (item.otherInfo1 == 0) {
item.name = item.menuName
item.alwaysShow = state
item.meta = {
title: item.menuName,
icon: item.img
}
if (item.children != null) {
this.formatList(item.children, undefined, item.url)
} else {
item.children = []
}
} else {
item.children = []
}
})
return menu
},
selectParentMenu(route) {
this.activeParent = route.url
// this.$store.dispatch('app/toggleSideBar')
// If this parent has children, don't navigate
if (route.children && route.children.length > 0) {
// Optionally navigate to the first child
this.navigateTo(route.children[0].url)
} else {
// If no children, navigate to the parent route
this.navigateTo(route.url)
}
},
navigateTo(url) {
if (url && url !== this.$route.path) {
this.$router.push(url)
}
},
findParentForCurrentRoute() {
const currentPath = this.$route.path
// Try to find the parent menu that contains the current route
for (const parent of this.menuList) {
// Check if current route is the parent itself
if (parent.url === currentPath) {
this.activeParent = parent.url
return
}
// Check if current route is one of the children
if (parent.children && parent.children.length) {
const childMatch = parent.children.find(
child =>
child.url === currentPath ||
currentPath.startsWith(child.url + '/')
)
if (childMatch) {
this.activeParent = parent.url
return
}
}
}
// If no match found, default to first menu item if available
if (this.menuList.length > 0 && !this.activeParent) {
this.activeParent = this.menuList[0].url
}
}
},
created() {
if (sessionStorage.token !== 'MockToken') {
// 获取路由数据
this.menuList = this.$store.state.app.sidebarList
} else {
// 从路由配置中获取顶级路由
this.menuList = this.routes.filter(route => {
// 只显示有 meta 和 children 的路由,并且不是隐藏的
return route.meta && route.children && !route.hidden
})
}
// 根据当前路由设置活动的父菜单
this.$nextTick(() => {
this.findParentForCurrentRoute()
})
},
watch: {
$route() {
// Update active parent when route changes
this.findParentForCurrentRoute()
}
}
}
</script>
<style scoped lang="scss">
.sidebar-container {
height: 100%;
background-color: #fff;
border-right: 1px solid #e6e6e6;
overflow: hidden;
display: flex;
flex-direction: column;
}
.sidebar-layout {
display: flex;
flex: 1;
overflow: hidden;
background-color: #fff;
border-radius: 6px;
}
.primary-menu {
width: 95px;
height: 100%;
position: relative;
overflow-y: auto;
background-color: #fff;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
padding-bottom: 16px;
.logo {
margin-top: 30px;
margin-bottom: 40px;
}
&:after {
content: ' ';
position: absolute;
border-right: 1px solid #ebeef2;
height: 100%;
right: 0;
top: 0;
bottom: 0;
transform: scaleX(0.5);
}
}
.primary-menu-item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 58px;
width: 68px;
padding: 10px 0;
cursor: pointer;
color: #606266;
margin-top: 10px;
&:hover,
&.active {
color: #4d64ff;
background-color: #f8f8fa;
}
&.active span {
color: #000;
font-weight: bold;
}
.svg-icon {
font-size: 24px;
margin-bottom: 8px;
}
.menu-title {
font-size: 14px;
text-align: center;
}
}
.submenu-container {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
padding: 23px 10px;
width: 160px;
}
.submenu-header {
height: 50px;
line-height: 50px;
padding: 0 20px;
font-size: 16px;
font-weight: bold;
border-bottom: 1px solid #f0f0f0;
}
.submenu-list {
padding: 10px 0;
}
.submenu-item {
height: 40px;
line-height: 40px;
padding: 0 20px;
display: flex;
align-items: center;
justify-content: flex-start;
cursor: pointer;
color: #000;
margin-bottom: 5px;
border-radius: 4px;
font-size: 14px;
&:hover,
&.active {
color: #4d64ff;
background-color: #f8f8fa;
}
&.active span {
color: #000;
font-weight: bold;
}
.svg-icon {
margin-right: 10px;
}
}
.scrollbar-wrapper {
height: 100%;
}
</style>