This commit is contained in:
liangjian
2026-01-27 16:32:15 +08:00
commit 836c61ac55
303 changed files with 34442 additions and 0 deletions

View File

@@ -0,0 +1,377 @@
<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>