|
|
@ -3,14 +3,17 @@ |
|
|
<ul style="margin: 0;"> |
|
|
<ul style="margin: 0;"> |
|
|
<li v-for="(item, index) in menuList" :key="item.path + random()"> |
|
|
<li v-for="(item, index) in menuList" :key="item.path + random()"> |
|
|
<div |
|
|
<div |
|
|
:class="[($route.path == item.path) || (!item.path && curIndex == index) ? 'active' : '', 'flex', ($route.path == item.path) ? curIndex = index : '']" @click="gotoPath(item, index)"> |
|
|
:class="[curIndex === index ? 'active' : '','flex']" @click="gotoPath(item, index)"> |
|
|
<SvgIcon1 :iconPath="require(`@/assets/${item.img}`)" defaultColor="#8A9099" :size="16" activeColor="#006AFF" |
|
|
<SvgIcon1 :iconPath="require(`@/assets/${item.img}`)" defaultColor="#8A9099" :size="16" activeColor="#006AFF" |
|
|
:isActive="($route.path == item.path || (!item.path && curIndex == index))" /> |
|
|
:isActive="curIndex === index" /> |
|
|
|
|
|
|
|
|
<span class="title_text">{{ item.name }}</span> |
|
|
<span class="title_text">{{ item.name }}</span> |
|
|
</div> |
|
|
</div> |
|
|
<p :class="['flex', (activeFloor && (activeFloor == item1.desc || activeFloor == item1.path)) ? 'curActive' : '']" v-for="(item1) in item.list" |
|
|
<p v-for="(item1) in item.list" :key="item1.name" :class="[ |
|
|
@click="setActiveCur(item1, item,index)" :key="item1.name">{{ item1.name }}</p> |
|
|
'flex', |
|
|
|
|
|
activeFloor === (item1.desc || item1.path || item1.componentsName) ? 'curActive' : '' |
|
|
|
|
|
]" @click="setActiveCur(item1, item, index)"> |
|
|
|
|
|
{{ item1.name }} |
|
|
|
|
|
</p> |
|
|
</li> |
|
|
</li> |
|
|
</ul> |
|
|
</ul> |
|
|
</aside> |
|
|
</aside> |
|
|
@ -18,22 +21,16 @@ |
|
|
|
|
|
|
|
|
<script> |
|
|
<script> |
|
|
import SvgIcon1 from '@/components/SvgIcon1.vue'; |
|
|
import SvgIcon1 from '@/components/SvgIcon1.vue'; |
|
|
// :class="[$route.path != item.path ? 'not-point' : '', 'flex', activeFloor == item1.desc ? 'curActive' : '']" |
|
|
|
|
|
import { mapState } from 'vuex'; |
|
|
import { mapState } from 'vuex'; |
|
|
import store from '../store'; |
|
|
import store from '../store'; |
|
|
|
|
|
|
|
|
export default { |
|
|
export default { |
|
|
name: 'Sidebar', |
|
|
name: 'Sidebar', |
|
|
props: { |
|
|
props: { |
|
|
menuList: { |
|
|
menuList: { |
|
|
type: Array, |
|
|
type: Array, |
|
|
required: true |
|
|
required: true |
|
|
}, |
|
|
} |
|
|
// activeFloor:{ |
|
|
|
|
|
// type:String |
|
|
|
|
|
// }, |
|
|
|
|
|
// curIndex:{ |
|
|
|
|
|
// type:Number |
|
|
|
|
|
// } |
|
|
|
|
|
}, |
|
|
}, |
|
|
components: { |
|
|
components: { |
|
|
SvgIcon1, |
|
|
SvgIcon1, |
|
|
@ -41,33 +38,31 @@ export default { |
|
|
data() { |
|
|
data() { |
|
|
return { |
|
|
return { |
|
|
activeFloor: null, |
|
|
activeFloor: null, |
|
|
curIndex: 0, |
|
|
curIndex: -1, // 初始化为 -1,表示未找到匹配项 |
|
|
scrollLock: false, |
|
|
scrollLock: false, |
|
|
componentsName:'' |
|
|
componentsName: '', |
|
|
|
|
|
// 记录每个子菜单项对应的父菜单索引 |
|
|
|
|
|
pathToIndexMap: {} |
|
|
} |
|
|
} |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
watch: { |
|
|
watch: { |
|
|
'$route'() { |
|
|
'$route.path': { |
|
|
// console.log(to, from); |
|
|
immediate: true, // 立即执行一次 |
|
|
// 路由变化时重新计算位置 |
|
|
handler(newPath) { |
|
|
this.$nextTick(() => { |
|
|
this.updateActiveMenu(newPath); |
|
|
this.calculateFloorOffsets(); |
|
|
} |
|
|
this.handleScroll(); // 立即检查当前位置 |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
} |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
mounted() { |
|
|
mounted() { |
|
|
let curItem = this.menuList[this.curIndex]; |
|
|
// 初始化路径映射表 |
|
|
// 增加单项初始值判断 |
|
|
this.buildPathMap(); |
|
|
if(!curItem.list || !curItem.list.length){ |
|
|
|
|
|
this.activeFloor = curItem.path |
|
|
// 根据当前路由更新激活状态 |
|
|
}else{ |
|
|
this.updateActiveMenu(this.$route.path); |
|
|
this.activeFloor = curItem?.list?.[0]?.desc || curItem?.list?.[0]?.path; |
|
|
|
|
|
} |
|
|
|
|
|
this.componentsName = curItem?.list?.[0]?.componentsName; |
|
|
|
|
|
this.$nextTick(() => { |
|
|
this.$nextTick(() => { |
|
|
// 更可靠的获取滚动容器方式 |
|
|
|
|
|
this.scrollContainer = document.querySelector('.main-content') || |
|
|
this.scrollContainer = document.querySelector('.main-content') || |
|
|
document.getElementById('main-content') || |
|
|
document.getElementById('main-content') || |
|
|
window; |
|
|
window; |
|
|
@ -80,55 +75,117 @@ export default { |
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
beforeDestroy() { |
|
|
beforeDestroy() { |
|
|
if (this.scrollContainer) { |
|
|
if (this.scrollContainer) { |
|
|
this.scrollContainer.removeEventListener('scroll', this.handleScroll); |
|
|
this.scrollContainer.removeEventListener('scroll', this.handleScroll); |
|
|
} |
|
|
} |
|
|
}, |
|
|
}, |
|
|
created() { |
|
|
|
|
|
// console.log(this.$parent.$refs.scrollContainer,'this.$refs.sc--rollContainer--'); |
|
|
|
|
|
}, |
|
|
|
|
|
computed: { |
|
|
computed: { |
|
|
...mapState(['pageTitle', 'carryParam']) // 从Vuex映射showSidebar状态到组件的计算属性中 |
|
|
...mapState(['pageTitle', 'carryParam']) |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
methods: { |
|
|
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() { |
|
|
random() { |
|
|
var randomNumber = Math.random(); |
|
|
return Math.random(); |
|
|
return randomNumber |
|
|
|
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
calculateFloorOffsets() { |
|
|
calculateFloorOffsets() { |
|
|
this.menuList.forEach(item => { |
|
|
if (this.curIndex === -1 || !this.menuList[this.curIndex]?.list) return; |
|
|
item.list?.forEach(every => { |
|
|
|
|
|
|
|
|
this.menuList[this.curIndex].list?.forEach(every => { |
|
|
const el = document.getElementById(every.desc); |
|
|
const el = document.getElementById(every.desc); |
|
|
if (el) { |
|
|
if (el) { |
|
|
// 获取元素相对于视口顶部的距离 |
|
|
|
|
|
every.offsetTop = el.getBoundingClientRect().top + window.pageYOffset; |
|
|
every.offsetTop = el.getBoundingClientRect().top + window.pageYOffset; |
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
|
}); |
|
|
|
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
handleScroll() { |
|
|
handleScroll() { |
|
|
if (this.scrollLock || !this.menuList[this.curIndex]?.list) return; |
|
|
if (this.scrollLock || this.curIndex === -1 || !this.menuList[this.curIndex]?.list) return; |
|
|
|
|
|
|
|
|
const scrollPosition = this.getScrollPosition(); |
|
|
const scrollPosition = this.getScrollPosition(); |
|
|
let activeFloor = null; |
|
|
let activeFloor = null; |
|
|
console.log(this.curIndex,scrollPosition,'this.curIndex==='); |
|
|
|
|
|
|
|
|
// 从上往下查找当前可见区域对应的楼层 |
|
|
// 从下往上查找当前可见区域对应的楼层 |
|
|
|
|
|
// for (let i = this.menuList[this.curIndex].list.length - 1; i >= 0; i--) { |
|
|
|
|
|
// const item = this.menuList[this.curIndex].list[i]; |
|
|
|
|
|
// if (scrollPosition + 100 >= (item.offsetTop || 0)) { // 100是提前量 |
|
|
|
|
|
// activeFloor = item.desc; |
|
|
|
|
|
// break; |
|
|
|
|
|
// } |
|
|
|
|
|
// } |
|
|
|
|
|
// 从上往下 |
|
|
|
|
|
for (let i = 0; i < this.menuList[this.curIndex].list.length; i++) { |
|
|
for (let i = 0; i < this.menuList[this.curIndex].list.length; i++) { |
|
|
const item = this.menuList[this.curIndex].list[i]; |
|
|
const item = this.menuList[this.curIndex].list[i]; |
|
|
if (scrollPosition + 100 >= (item.offsetTop || 0)) { // 100是提前量 |
|
|
if (item.offsetTop && scrollPosition + 100 >= item.offsetTop) { |
|
|
activeFloor = item.desc; |
|
|
activeFloor = item.desc || item.componentsName; |
|
|
break; |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -143,93 +200,89 @@ export default { |
|
|
} |
|
|
} |
|
|
return this.scrollContainer.scrollTop; |
|
|
return this.scrollContainer.scrollTop; |
|
|
}, |
|
|
}, |
|
|
// setActiveCur(dom, item) { |
|
|
|
|
|
// if (this.$route.path != item.path) { |
|
|
gotoPath(item, index) { |
|
|
// this.$router.push(item.path) |
|
|
this.curIndex = index; |
|
|
// store.commit('SET_PAGETITLE', item.name); |
|
|
|
|
|
// } |
|
|
|
|
|
// setTimeout(() => { |
|
|
|
|
|
// this.activeFloor = dom; |
|
|
|
|
|
// this.setHighActive(dom) |
|
|
|
|
|
// }, 500) |
|
|
|
|
|
// }, |
|
|
|
|
|
|
|
|
|
|
|
// setHighActive(dom) { |
|
|
|
|
|
// this.scrollLock = true; |
|
|
|
|
|
// const ele = document.getElementById(dom) |
|
|
|
|
|
// if (!ele) return |
|
|
|
|
|
// ele.classList.add('ceshi') |
|
|
|
|
|
// ele.scrollIntoView({ behavior: 'smooth', block: 'start' }) |
|
|
|
|
|
// setTimeout(() => { |
|
|
|
|
|
// ele.classList.remove('ceshi') |
|
|
|
|
|
// }, 1000) |
|
|
|
|
|
// } |
|
|
|
|
|
gotoPath(item) { |
|
|
|
|
|
let path = item.path; |
|
|
let path = item.path; |
|
|
this.activeFloor = path |
|
|
|
|
|
if (!path) { |
|
|
// 如果菜单项没有自己的path但有子菜单,默认选中第一个子项 |
|
|
this.curIndex = 0; |
|
|
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; |
|
|
return; |
|
|
} |
|
|
} |
|
|
if (this.$route.path != path) { |
|
|
} |
|
|
this.curIndex = 0; |
|
|
|
|
|
|
|
|
this.activeFloor = path; |
|
|
|
|
|
if (!path) { |
|
|
this.scrollLock = false; |
|
|
this.scrollLock = false; |
|
|
this.handleScroll(); |
|
|
this.handleScroll(); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// 携带当前所有参数跳转 |
|
|
if (this.$route.path !== path) { |
|
|
this.$router.push({ |
|
|
this.$router.push({ |
|
|
path: path, |
|
|
path: path, |
|
|
query: { ...this.$route.query }, // 携带所有查询参数 |
|
|
query: { ...this.$route.query }, |
|
|
params: { ...this.$route.params } // 携带所有路由参数 |
|
|
params: { ...this.$route.params } |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
// 重置页面滚动高度 |
|
|
|
|
|
const dom = document.getElementById('main-content'); |
|
|
const dom = document.getElementById('main-content'); |
|
|
if (dom) dom.scrollTop = 0; |
|
|
if (dom) dom.scrollTop = 0; |
|
|
} |
|
|
} |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
setActiveCur(item1, item,index) { |
|
|
setActiveCur(item1, item, index) { |
|
|
this.curIndex = index; |
|
|
this.curIndex = index; |
|
|
let componentsName = item1?.componentsName; |
|
|
let componentsName = item1?.componentsName; |
|
|
// 如果采用的是 组件切换显示的办法 |
|
|
|
|
|
if(componentsName){ |
|
|
// 组件切换模式 |
|
|
|
|
|
if (componentsName) { |
|
|
this.activeFloor = componentsName; |
|
|
this.activeFloor = componentsName; |
|
|
// 同步 当前切换的组件名称 |
|
|
|
|
|
store.commit('SET_COMPONENTS_NAME', componentsName); |
|
|
store.commit('SET_COMPONENTS_NAME', componentsName); |
|
|
return |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 如果 是路由跳转 |
|
|
// 如果 是路由跳转 |
|
|
// 如果 二级菜单同时存在路由 那么跳转以二级路由为优先 |
|
|
// 如果 二级菜单同时存在路由 那么跳转以二级路由为优先 |
|
|
// 如果 是同一页面 只是页面区域的滚动展示 那么跳转一级路由 |
|
|
// 如果 是同一页面 只是页面区域的滚动展示 那么跳转一级路由 |
|
|
let path = item1.path || item.path; |
|
|
let path = item1.path || item.path; |
|
|
if ( this.$route.path !== path) { |
|
|
if (path && this.$route.path !== path) { |
|
|
if(this.carryParam){ |
|
|
if (this.carryParam) { |
|
|
// 携带当前所有参数跳转 |
|
|
|
|
|
this.$router.push({ |
|
|
this.$router.push({ |
|
|
path: path, |
|
|
path: path, |
|
|
query: { ...this.$route.query }, // 携带所有查询参数 |
|
|
query: { ...this.$route.query }, |
|
|
params: { ...this.$route.params } // 携带所有路由参数 |
|
|
params: { ...this.$route.params } |
|
|
}); |
|
|
}); |
|
|
}else{ |
|
|
} else { |
|
|
this.$router.push(path); |
|
|
this.$router.push(path); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
store.commit('SET_PAGETITLE', item.name); |
|
|
store.commit('SET_PAGETITLE', item.name); |
|
|
|
|
|
|
|
|
|
|
|
// 如果是跳转到新页面,重置滚动 |
|
|
|
|
|
const dom = document.getElementById('main-content'); |
|
|
|
|
|
if (dom) dom.scrollTop = 0; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
this.scrollLock = true; |
|
|
this.scrollLock = true; |
|
|
if(item1.desc){ |
|
|
if (item1.desc) { |
|
|
this.activeFloor = item1.desc; |
|
|
this.activeFloor = item1.desc; |
|
|
} |
|
|
} |
|
|
// 如果 二级路由存在跳转 那么不再进行页面的滚动 |
|
|
if (item1.path) { |
|
|
if(item1.path){ |
|
|
|
|
|
this.activeFloor = item1.path; |
|
|
this.activeFloor = item1.path; |
|
|
return |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
this.$nextTick(() => { |
|
|
this.$nextTick(() => { |
|
|
|
|
|
if (item1.desc) { |
|
|
this.setHighActive(item1.desc); |
|
|
this.setHighActive(item1.desc); |
|
|
|
|
|
} |
|
|
setTimeout(() => { |
|
|
setTimeout(() => { |
|
|
this.scrollLock = false; |
|
|
this.scrollLock = false; |
|
|
}, 1000); |
|
|
}, 1000); |
|
|
@ -244,9 +297,9 @@ export default { |
|
|
eles[i].classList.remove('siteMessage-active'); |
|
|
eles[i].classList.remove('siteMessage-active'); |
|
|
} |
|
|
} |
|
|
ele.classList.add('siteMessage-active'); |
|
|
ele.classList.add('siteMessage-active'); |
|
|
setTimeout(function (){ |
|
|
setTimeout(() => { |
|
|
ele.classList.remove('siteMessage-active'); |
|
|
ele.classList.remove('siteMessage-active'); |
|
|
},1000) |
|
|
}, 1000); |
|
|
ele.scrollIntoView({ |
|
|
ele.scrollIntoView({ |
|
|
behavior: 'smooth', |
|
|
behavior: 'smooth', |
|
|
block: 'start' |
|
|
block: 'start' |
|
|
@ -265,10 +318,12 @@ export default { |
|
|
background: #FFFFFF; |
|
|
background: #FFFFFF; |
|
|
box-shadow: 0px 0px 11px 2px rgba(147, 147, 147, 0.11); |
|
|
box-shadow: 0px 0px 11px 2px rgba(147, 147, 147, 0.11); |
|
|
} |
|
|
} |
|
|
ul{ |
|
|
|
|
|
|
|
|
ul { |
|
|
height: 100%; |
|
|
height: 100%; |
|
|
overflow-y: auto; |
|
|
overflow-y: auto; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
.ceshi { |
|
|
.ceshi { |
|
|
// animation: fadeInOut 2s infinite; |
|
|
// animation: fadeInOut 2s infinite; |
|
|
} |
|
|
} |
|
|
|