Files
liangjian 836c61ac55 @@@@@@
2026-01-27 16:32:15 +08:00

378 lines
9.5 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div
class="vab-fall-bar"
:class="{
'is-collapse': collapse,
}"
>
<vab-logo style="z-index: 999" />
<fall-menu :data="handleRoutes">
<template #level1="{ slotScope }">
<a :title="translate(slotScope.meta.title)" @click="handleLink(slotScope)">
<vab-icon :icon="slotScope.meta && slotScope.meta.icon" />
<span>{{ translate(slotScope.meta.title) }}</span>
<vab-icon v-if="slotScope.children" class="fall-icon-right" icon="arrow-right-s-line" />
</a>
</template>
<template #level2="{ slotScope }">
<span style="cursor: pointer" @click="handleLink(slotScope)">
<vab-icon :icon="slotScope.meta && slotScope.meta.icon" />
<span>{{ translate(slotScope.meta.title) }}</span>
</span>
</template>
<template #level3="{ slotScope }">
<a v-for="(level3, index) in slotScope" :key="index" :href="level3.url" @click="handleLink(level3)">
- {{ translate(level3.meta.title) }}
<el-tag v-if="level3.meta && level3.meta.badge" effect="dark" size="small" type="danger">
{{ level3.meta.badge }}
</el-tag>
<vab-dot v-if="level3.meta && level3.meta.dot" type="danger" />
</a>
</template>
</fall-menu>
</div>
</template>
<script setup>
/**
* @author:zxwk-sundan
* @description:瀑布菜单最多支持到三级菜单不支持左侧点击选中且非element-plus官方组件生产环境请谨慎使用
*/
import { FallMenu } from '@opentiny/vue'
import { isHashRouterMode } from '/@/config'
import { translate } from '/@/i18n'
import { useRoutesStore } from '/@/store/modules/routes'
import { useSettingsStore } from '/@/store/modules/settings'
import { isExternal } from '/@/utils/validate'
defineOptions({
name: 'VabFallBar',
})
const settingsStore = useSettingsStore()
const routesStore = useRoutesStore()
const { getRoutes: routes } = storeToRefs(routesStore)
const route = useRoute()
const router = useRouter()
const { device, collapse } = storeToRefs(settingsStore)
const { foldSideBar } = settingsStore
const { enter, exit } = useFullscreen()
const mousePosition = ref({ x: 0, y: 0 })
const handleRoutes = computed(() =>
routes.value.flatMap((route) => (route.meta.levelHidden && route.children ? [...route.children] : route))
)
const handleLink = (slotScope) => {
nextTick(() => {
const routePath = slotScope.path
const target = slotScope.meta.target
const fullscreen = slotScope.meta.fullscreen
if (target === '_blank') {
if (isExternal(routePath)) {
window.open(routePath)
router.push('/redirect')
} else if (route.path !== routePath) isHashRouterMode ? window.open(`#${routePath}`) : window.open(routePath)
router.push('/redirect')
} else {
if (isExternal(routePath)) window.location.href = routePath
else if (route.path === routePath) {
$pub('reload-router-view')
} else {
if (device.value === 'mobile') foldSideBar()
if (slotScope.children) router.push(slotScope.redirect)
else router.push(slotScope.path)
}
}
setTimeout(() => {
if (fullscreen) enter()
else exit()
}, 1000)
})
}
useEventListener('mousemove', (e) => {
mousePosition.value = {
x: e.clientX,
y: e.clientY,
}
if ((mousePosition.value.x < 265 && !collapse.value) || (mousePosition.value.x < 65 && collapse.value)) {
const element = document.querySelector('.vab-fall-bar .tiny-fall-menu__box')
const base = 60
const intervalSize = 48
for (let i = 0; i < 10; i++) {
const lowerBound = base + i * intervalSize
const upperBound = lowerBound + intervalSize
if (mousePosition.value.y > lowerBound && mousePosition.value.y < upperBound) {
mousePosition.value.y = lowerBound - 60
break
}
}
element.style.top = `${mousePosition.value.y}px`
}
})
</script>
<style lang="scss" scoped>
.vab-fall-bar {
position: fixed;
top: 0;
bottom: 0;
left: 0;
z-index: var(--el-z-index);
width: calc(var(--el-left-menu-width) - 1px);
background: var(--el-menu-background-color);
border-right: 1px solid var(--el-border-color);
.fall-icon-right {
float: right;
}
:deep() {
.tiny-fall-menu {
--ti-fall-menu-bg-color-normal: var(--el-menu-background-color);
--ti-fall-menu-bg-color-hover: var(--el-color-primary);
--ti-fall-menu-slot-bg-color: var(--el-menu-background-color);
--ti-fall-menu-box-text-color: var(--el-color-white);
--ti-fall-menu-slot-text-color: var(--el-color-white);
--ti-common-font-size-base: var(--el-font-size-base);
--ti-fall-menu-title-font-size: var(--el-font-size-base);
--ti-fall-menu-box-width: 560px;
&__nav {
height: calc(var(--vh, 1vh) * 100);
}
&__wrap {
padding: 0;
background: var(--ti-fall-menu-bg-color-normal);
}
&__subnav {
.icon-slot-left,
.icon-slot-right {
opacity: 0;
}
}
&__list {
right: 0 !important;
left: 0 !important;
min-width: 100%;
li {
display: block;
float: none;
width: 100%;
a {
display: block;
width: calc(100% - 20px);
margin: 0 10px 0px 10px !important;
text-align: left;
border-radius: var(--el-border-radius-base);
[class*='ri-'] {
margin-left: 1.5px;
}
[class*='ri-'] + span {
padding-left: 3px;
}
}
}
.fall-hide {
opacity: 1;
}
}
&__box {
top: 5px;
left: var(--el-left-menu-width);
min-width: var(--ti-fall-menu-box-width);
padding: var(--el-padding);
overflow-y: auto;
border: 0;
border-radius: var(--el-border-radius-base);
box-shadow: none;
transition:
all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1),
top 0.1s !important;
.cont {
padding: 0;
}
.sublist {
li {
h3.mcate-item-hd {
color: var(--ti-fall-menu-box-title-text-color);
[class*='ri-'] + span {
padding-left: 3px;
}
&:hover {
color: var(--el-color-primary) !important;
}
}
p.mcate-item-bd {
a {
font-size: 13px;
color: var(--ti-fall-menu-box-text-color);
&:hover {
color: var(--el-color-primary) !important;
}
.el-tag {
height: 16px;
padding: 0 5px 0 5px;
}
}
}
}
}
}
}
}
&.is-collapse {
width: 65px;
:deep() {
.tiny-fall-menu {
&__list {
li {
a {
[class*='ri-'] + span {
display: none;
}
}
}
.fall-hide {
opacity: 1;
}
}
&__box {
left: calc(var(--el-left-menu-width-min) + 2px);
padding-right: 0;
}
}
}
}
}
</style>
<style lang="scss">
.vab-theme-technology {
.tiny-fall-menu {
--ti-fall-menu-bg-color-normal: var(--el-menu-background-color);
--ti-fall-menu-bg-color-hover: var(--el-color-primary);
--ti-fall-menu-slot-bg-color: var(--el-menu-background-color);
--ti-fall-menu-box-text-color: #fff !important;
--ti-fall-menu-slot-text-color: var(--el-color-white);
&__list {
a {
color: #fff;
}
}
&__box {
border: 1px solid var(--el-border-color) !important;
.sublist {
li {
h3.mcate-item-hd {
color: var(--ti-fall-menu-box-title-text-color);
}
p.mcate-item-bd {
a {
color: var(--ti-fall-menu-box-text-color);
}
}
}
}
}
}
}
.vab-theme-plain {
.tiny-fall-menu {
--ti-fall-menu-bg-color-normal: var(--el-menu-background-color);
--ti-fall-menu-bg-color-hover: var(--el-color-primary);
--ti-fall-menu-slot-bg-color: var(--el-menu-background-color);
--ti-fall-menu-box-text-color: var(--el-color-grey) !important;
--ti-fall-menu-slot-text-color: var(--el-color-grey) !important;
&__box {
border: 1px solid var(--el-border-color) !important;
.sublist {
li {
h3.mcate-item-hd {
color: #515a6e !important;
}
p.mcate-item-bd {
a {
color: var(--ti-fall-menu-box-text-color);
}
}
}
}
}
&__list {
a:hover {
color: #fff !important;
}
}
}
}
.dark {
.tiny-fall-menu {
--ti-fall-menu-bg-color-normal: var(--el-menu-background-color);
--ti-fall-menu-bg-color-hover: var(--el-color-primary);
--ti-fall-menu-slot-bg-color: var(--el-menu-background-color);
--ti-fall-menu-box-text-color: var(--el-color-grey) !important;
--ti-fall-menu-slot-text-color: var(--el-color-grey) !important;
&__box {
border: 1px solid var(--el-border-color) !important;
.sublist {
li {
h3.mcate-item-hd {
color: #fff !important;
}
p.mcate-item-bd {
a {
color: var(--ti-fall-menu-box-text-color);
}
}
}
}
}
&__list {
a:hover {
color: #fff !important;
}
}
}
}
</style>