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,87 @@
<template>
<el-menu-item :index="itemOrMenu.path" @click="handleLink">
<vab-icon
v-if="itemOrMenu.meta && itemOrMenu.meta.icon"
:icon="itemOrMenu.meta.icon"
:is-custom-svg="itemOrMenu.meta.isCustomSvg"
:title="translate(itemOrMenu.meta.title)"
/>
<span :title="translate(itemOrMenu.meta.title)">
{{ translate(itemOrMenu.meta.title) }}
</span>
<el-tag v-if="itemOrMenu.meta && itemOrMenu.meta.badge" effect="dark" type="danger">
{{ translate(itemOrMenu.meta.badge) }}
</el-tag>
<vab-dot v-if="itemOrMenu.meta && itemOrMenu.meta.dot" type="danger" />
</el-menu-item>
</template>
<script setup>
import { isHashRouterMode } from '/@/config'
import { translate } from '/@/i18n'
import { useSettingsStore } from '/@/store/modules/settings'
import { isExternal } from '/@/utils/validate'
defineOptions({
name: 'VabMenuItem',
})
const props = defineProps({
itemOrMenu: {
type: Object,
default: () => {},
},
})
const route = useRoute()
const router = useRouter()
const settingsStore = useSettingsStore()
const { device } = storeToRefs(settingsStore)
const { foldSideBar } = settingsStore
const { enter, exit } = useFullscreen()
const handleLink = () => {
nextTick(() => {
const routePath = props.itemOrMenu.path
const target = props.itemOrMenu.meta.target
const fullscreen = props.itemOrMenu.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) {
if (device.value === 'mobile') foldSideBar()
router.push(props.itemOrMenu.path)
}
}
setTimeout(() => {
if (fullscreen) enter()
else exit()
}, 1000)
})
}
</script>
<style lang="scss" scoped>
:deep(.el-tag) {
position: absolute;
right: 20px;
height: 18px;
padding-right: 5px;
padding-left: 5px;
font-size: var(--el-font-size-extra-small);
line-height: 18px;
}
.vab-dot {
position: absolute !important;
right: 20px;
}
</style>

View File

@@ -0,0 +1,36 @@
<template>
<template v-if="itemOrMenu.meta && itemOrMenu.meta.levelHidden">
<template v-for="route in itemOrMenu.children" :key="route.path">
<vab-menu :item="route" />
</template>
</template>
<el-sub-menu v-else :index="itemOrMenu.path">
<template #title>
<vab-icon
v-if="itemOrMenu.meta && itemOrMenu.meta.icon"
:icon="itemOrMenu.meta.icon"
:is-custom-svg="itemOrMenu.meta.isCustomSvg"
:title="translate(itemOrMenu.meta.title)"
/>
<span :title="translate(itemOrMenu.meta.title)">
{{ translate(itemOrMenu.meta.title) }}
</span>
</template>
<slot></slot>
</el-sub-menu>
</template>
<script setup>
import { translate } from '/@/i18n'
defineOptions({
name: 'VabSubMenu',
})
defineProps({
itemOrMenu: {
type: Object,
default: () => {},
},
})
</script>

View File

@@ -0,0 +1,39 @@
<template>
<component :is="menuComponent" :item-or-menu="item">
<template v-if="item.children && item.children.length > 0">
<vab-menu v-for="route in item.children" :key="route.path" :item="route" />
</template>
</component>
</template>
<script setup>
defineOptions({
name: 'VabMenu',
})
const imports = import.meta.glob('./**/*.vue', { eager: true })
const Components = {}
Object.getOwnPropertyNames(imports).forEach((key) => {
Components[key.replaceAll(/(\/|components|\.|vue)/g, '')] = imports[key].default
})
const props = defineProps({
item: {
type: Object,
required: true,
},
layout: {
type: String,
default: '',
},
})
const menuComponent = computed(() =>
props.item.children &&
props.item.children.some((route) => {
return route.meta && route.meta.hidden !== true
})
? Components['VabSubMenu']
: Components['VabMenuItem']
)
</script>