You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

408 lines
10 KiB

<template>
<aside class="sidebar">
<ul style="margin: 0;">
<li v-for="(item, index) in menuList" :key="item.path + random()">
<div
:class="[curIndex === index ? 'active' : '','flex']" @click="gotoPath(item, index)">
<SvgIcon1 :iconPath="require(`@/assets/${item.img}`)" defaultColor="#8A9099" :size="16" activeColor="#006AFF"
:isActive="curIndex === index" />
<span class="title_text">{{ item.name }}</span>
</div>
<p v-for="(item1) in item.list" :key="item1.name" :class="[
'flex',
activeFloor === (item1.desc || item1.path || item1.componentsName) ? 'curActive' : ''
]" @click="setActiveCur(item1, item, index)">
{{ item1.name }}
</p>
</li>
</ul>
</aside>
</template>
<script>
import SvgIcon1 from '@/components/SvgIcon1.vue';
import { mapState } from 'vuex';
import store from '../store';
export default {
name: 'Sidebar',
props: {
menuList: {
type: Array,
required: true
}
},
components: {
SvgIcon1,
},
data() {
return {
activeFloor: null,
curIndex: -1, // 初始化为 -1,表示未找到匹配项
scrollLock: false,
componentsName: '',
// 记录每个子菜单项对应的父菜单索引
pathToIndexMap: {}
}
},
watch: {
'$route.path': {
immediate: true, // 立即执行一次
handler(newPath) {
this.updateActiveMenu(newPath);
}
}
},
mounted() {
// 初始化路径映射表
this.buildPathMap();
// 根据当前路由更新激活状态
this.updateActiveMenu(this.$route.path);
this.$nextTick(() => {
this.scrollContainer = document.querySelector('.main-content') ||
document.getElementById('main-content') ||
window;
if (this.scrollContainer) {
this.scrollContainer.addEventListener('scroll', this.handleScroll);
this.calculateFloorOffsets();
} else {
console.error('未找到滚动容器');
}
});
},
beforeDestroy() {
if (this.scrollContainer) {
this.scrollContainer.removeEventListener('scroll', this.handleScroll);
}
},
computed: {
...mapState(['pageTitle', 'carryParam'])
},
methods: {
// 构建路径到父菜单索引的映射
buildPathMap() {
const map = {};
this.menuList.forEach((item, parentIndex) => {
if (item.list && item.list.length) {
item.list.forEach(subItem => {
if (subItem.path) {
map[subItem.path] = parentIndex;
}
});
}
});
this.pathToIndexMap = map;
},
// 根据当前路由更新激活的菜单项
updateActiveMenu(currentPath) {
// 1. 先尝试匹配二级菜单路径
const parentIndex = this.pathToIndexMap[currentPath];
if (parentIndex !== undefined) {
this.curIndex = parentIndex;
// 找到对应的二级菜单项
const parentItem = this.menuList[parentIndex];
const subItem = parentItem.list.find(item => item.path === currentPath);
if (subItem) {
// 如果是组件切换模式
if (subItem.componentsName) {
this.activeFloor = subItem.componentsName;
store.commit('SET_COMPONENTS_NAME', subItem.componentsName);
} else {
// 如果是路由模式
this.activeFloor = subItem.desc || subItem.path;
}
return;
}
}
// 2. 如果找不到匹配的二级路径,尝试匹配一级路径
for (let i = 0; i < this.menuList.length; i++) {
const item = this.menuList[i];
if (item.path && item.path === currentPath) {
this.curIndex = i;
this.activeFloor = item.path;
// 如果有子菜单,默认选中第一个
if (item.list && item.list.length) {
const firstSubItem = item.list[0];
if (firstSubItem.componentsName) {
store.commit('SET_COMPONENTS_NAME', firstSubItem.componentsName);
}
}
return;
}
}
// 3. 如果都没有匹配,默认选中第一个菜单项
if (this.curIndex === -1 && this.menuList.length > 0) {
this.curIndex = 0;
const firstItem = this.menuList[0];
if (firstItem.list && firstItem.list.length) {
const firstSubItem = firstItem.list[0];
this.activeFloor = firstSubItem.desc || firstSubItem.path || firstSubItem.componentsName;
if (firstSubItem.componentsName) {
store.commit('SET_COMPONENTS_NAME', firstSubItem.componentsName);
}
} else {
this.activeFloor = firstItem.path;
}
}
},
random() {
return Math.random();
},
calculateFloorOffsets() {
if (this.curIndex === -1 || !this.menuList[this.curIndex]?.list) return;
this.menuList[this.curIndex].list?.forEach(every => {
const el = document.getElementById(every.desc);
if (el) {
every.offsetTop = el.getBoundingClientRect().top + window.pageYOffset;
}
});
},
handleScroll() {
if (this.scrollLock || this.curIndex === -1 || !this.menuList[this.curIndex]?.list) return;
const scrollPosition = this.getScrollPosition();
let activeFloor = null;
// 从上往下查找当前可见区域对应的楼层
for (let i = 0; i < this.menuList[this.curIndex].list.length; i++) {
const item = this.menuList[this.curIndex].list[i];
if (item.offsetTop && scrollPosition + 100 >= item.offsetTop) {
activeFloor = item.desc || item.componentsName;
}
}
if (activeFloor && this.activeFloor !== activeFloor) {
this.activeFloor = activeFloor;
}
},
getScrollPosition() {
if (this.scrollContainer === window) {
return window.pageYOffset || document.documentElement.scrollTop;
}
return this.scrollContainer.scrollTop;
},
gotoPath(item, index) {
this.curIndex = index;
let path = item.path;
// 如果菜单项没有自己的path但有子菜单,默认选中第一个子项
if (!path && item.list && item.list.length) {
const firstSubItem = item.list[0];
if (firstSubItem.componentsName) {
this.activeFloor = firstSubItem.componentsName;
store.commit('SET_COMPONENTS_NAME', firstSubItem.componentsName);
} else if (firstSubItem.path) {
// 如果有二级路径,跳转到二级路径
this.setActiveCur(firstSubItem, item, index);
return;
}
}
this.activeFloor = path;
if (!path) {
this.scrollLock = false;
this.handleScroll();
return;
}
if (this.$route.path !== path) {
this.$router.push({
path: path,
query: { ...this.$route.query },
params: { ...this.$route.params }
});
const dom = document.getElementById('main-content');
if (dom) dom.scrollTop = 0;
}
},
setActiveCur(item1, item, index) {
this.curIndex = index;
let componentsName = item1?.componentsName;
// 组件切换模式
if (componentsName) {
this.activeFloor = componentsName;
store.commit('SET_COMPONENTS_NAME', componentsName);
return;
}
// 如果 是路由跳转
// 如果 二级菜单同时存在路由 那么跳转以二级路由为优先
// 如果 是同一页面 只是页面区域的滚动展示 那么跳转一级路由
let path = item1.path || item.path;
if (path && this.$route.path !== path) {
if (this.carryParam) {
this.$router.push({
path: path,
query: { ...this.$route.query },
params: { ...this.$route.params }
});
} else {
this.$router.push(path);
}
store.commit('SET_PAGETITLE', item.name);
// 如果是跳转到新页面,重置滚动
const dom = document.getElementById('main-content');
if (dom) dom.scrollTop = 0;
}
this.scrollLock = true;
if (item1.desc) {
this.activeFloor = item1.desc;
}
if (item1.path) {
this.activeFloor = item1.path;
return;
}
this.$nextTick(() => {
if (item1.desc) {
this.setHighActive(item1.desc);
}
setTimeout(() => {
this.scrollLock = false;
}, 1000);
});
},
setHighActive(dom) {
const ele = document.getElementById(dom);
if (ele) {
const eles = document.getElementsByClassName('siteMessage');
for (let i = 0; i < eles.length; i++) {
eles[i].classList.remove('siteMessage-active');
}
ele.classList.add('siteMessage-active');
setTimeout(() => {
ele.classList.remove('siteMessage-active');
}, 1000);
ele.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
}
}
}
</script>
<style scoped lang="scss">
.sidebar {
min-width: 158px;
padding: 21px;
box-sizing: border-box;
background: #FFFFFF;
box-shadow: 0px 0px 11px 2px rgba(147, 147, 147, 0.11);
}
ul {
height: 100%;
overflow-y: auto;
}
.ceshi {
// animation: fadeInOut 2s infinite;
}
.title_text {
margin-left: 6px;
}
ul {
list-style: none;
padding: 0;
}
.not-point {
pointer-events: none;
/* 阻止鼠标事件 */
opacity: 0.5;
/* 可选降低透明度以视觉上表示不可用 */
cursor: not-allowed;
/* 改变鼠标光标样式表示不可用 */
}
li {
margin-bottom: 10px;
div {
letter-spacing: 0.08em;
color: #1E2226;
// margin: 12px 0;
height: 40px;
cursor: pointer;
img {
margin-right: 6px;
}
}
p {
letter-spacing: 0.08em;
line-height: 18px;
height: 36px;
color: #8A9099;
cursor: pointer;
&:hover {
color: #006AFF;
}
}
.curActive {
color: #006AFF;
}
}
/* {
display: block;
padding: 8px 12px;
text-decoration: none;
color: #333;
border-radius: 4px;
} */
/*
:hover {
background: #e0e0e0;
} */
.active {
font-weight: bold;
letter-spacing: 0.08em;
color: #006AFF;
}
.item-active {
color: #006AFF;
}
/*
.exact-active {
background: #1976d2;
color: white;
} */
</style>