mirror of
https://gitee.com/niucloud-team/niucloud.git
synced 2025-12-13 10:02:48 +00:00
362 lines
13 KiB
Vue
362 lines
13 KiB
Vue
<template>
|
|
<el-container class="w-100 h-screen">
|
|
<el-main class="flex p-0">
|
|
<div class="one-menu w-[124px] h-screen px-[8px] bg-[#1c2233]">
|
|
<el-header class="logo-wrap">
|
|
<div class="logo flex items-center m-auto h-[64px]" v-if="!systemStore.menuIsCollapse">
|
|
<el-image style="width: 40px; height: 40px" :src="img(logoUrl)" fit="contain">
|
|
<template #error>
|
|
<div class="flex justify-center items-center w-full h-[40px]">
|
|
<img class="max-w-[40px]" src="@/app/assets/images/icon-addon-one.png" alt="" object-fit="contain">
|
|
</div>
|
|
</template>
|
|
</el-image>
|
|
</div>
|
|
<div class="logo flex items-center justify-center h-[64px]" v-else>
|
|
<i class="text-3xl iconfont iconyunkongjian"></i>
|
|
</div>
|
|
</el-header>
|
|
|
|
<el-scrollbar class="h-[calc( 100vh - 64px )]">
|
|
<el-menu :default-active="oneMenuActive" :router="true" class="aside-menu" :unique-opened="true" :collapse="systemStore.menuIsCollapse">
|
|
<template v-for="(item, index) in oneMenuData" :key="index">
|
|
<el-menu-item :index="item.original_name" @click="router.push({ name: item.name })" v-if="item.meta.show">
|
|
<div v-if="item.meta.icon" class="w-[16px] h-[16px] relative flex justify-center">
|
|
<el-image class="w-[16px] h-[16px] rounded-[50%] overflow-hidden" :src="item.meta.icon" fit="fill" v-if="isUrl(item.meta.icon)"/>
|
|
<icon :name="item.meta.icon" class="absolute top-[50%] -translate-y-[50%]" v-else />
|
|
</div>
|
|
<div v-else class="w-[16px] h-[16px]"></div>
|
|
<template #title>
|
|
<div class="relative flex-1 w-0">
|
|
<span class="ml-[10px] w-full truncate">{{ item.meta.short_title || item.meta.title }}</span>
|
|
</div>
|
|
</template>
|
|
</el-menu-item>
|
|
</template>
|
|
</el-menu>
|
|
<div class="h-[48px]"></div>
|
|
</el-scrollbar>
|
|
</div>
|
|
|
|
<el-scrollbar v-if="twoMenuData.length" class="two-menu w-[132px]">
|
|
<div class="w-[132px] h-[64px] flex items-center justify-center text-[16px] border-b-[1px] border-solid border-[var(--el-border-color-lighter)]">
|
|
{{ route.matched[1].meta.title }}
|
|
</div>
|
|
|
|
<el-menu class="aside-menu" :default-active="route.name" :default-openeds="menuOption" :router="true" :collapse="systemStore.menuIsCollapse">
|
|
<menu-item v-for="(route, index) in twoMenuData" :routes="route" :key="index" />
|
|
</el-menu>
|
|
|
|
<div class="h-[48px]"></div>
|
|
</el-scrollbar>
|
|
</el-main>
|
|
</el-container>
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
import { ref, watch, computed, onMounted, watchEffect } from 'vue'
|
|
import { useRoute, useRouter } from 'vue-router'
|
|
import useSystemStore from '@/stores/modules/system'
|
|
import { getShowApp, getShowMarketing } from '@/app/api/site'
|
|
import useUserStore from '@/stores/modules/user'
|
|
import { img, isUrl } from '@/utils/common'
|
|
import { findFirstValidRoute } from '@/router/routers'
|
|
import menuItem from './menu-item.vue'
|
|
import { cloneDeep } from 'lodash-es'
|
|
|
|
const route = useRoute()
|
|
const router = useRouter()
|
|
|
|
const systemStore = useSystemStore()
|
|
const userStore = useUserStore()
|
|
|
|
const siteInfo = userStore.siteInfo
|
|
const routers = userStore.routers
|
|
const addonIndexRoute = userStore.addonIndexRoute
|
|
|
|
const oneMenuData = ref<Record<string, any>[]>([])
|
|
const twoMenuData = ref<Record<string, any>[]>([])
|
|
const addonRouters: Record<string, any> = {}
|
|
const logoUrl = computed(() => {
|
|
return userStore.siteInfo.icon ? userStore.siteInfo.icon : systemStore.website.icon
|
|
})
|
|
|
|
routers.forEach(item => {
|
|
item.original_name = item.name
|
|
if (item.meta.addon == '') {
|
|
if (item.meta.attr == '') {
|
|
if (item.children && item.children.length) {
|
|
item.name = findFirstValidRoute(item.children)
|
|
}
|
|
oneMenuData.value.push(item)
|
|
}
|
|
} else if (item.meta.addon != '' && siteInfo?.apps.length <= 1 && siteInfo?.apps[0].key == item.meta.addon && item.meta.show) {
|
|
if (item.children) {
|
|
item.children.forEach((citem: Record<string, any>) => {
|
|
citem.original_name = citem.name
|
|
if (citem.children && citem.children.length) {
|
|
citem.name = findFirstValidRoute(citem.children)
|
|
}
|
|
})
|
|
oneMenuData.value.unshift(...item.children)
|
|
} else {
|
|
oneMenuData.value.unshift(item)
|
|
}
|
|
} else {
|
|
addonRouters[item.meta.addon] = item
|
|
}
|
|
// 排序, 功能正确,改了排序后需要把菜单排序的默认值重新调整一下【多应用一级菜单,单应用二级菜单】
|
|
// oneMenuData.value.sort((a, b) => {
|
|
// if (a.meta.sort && b.meta.sort) {
|
|
// return b.meta.sort - a.meta.sort
|
|
// } else if (a.meta.sort) {
|
|
// return -1
|
|
// } else if (b.meta.sort) {
|
|
// return 1
|
|
// } else {
|
|
// return 0
|
|
// }
|
|
// })
|
|
})
|
|
// 多应用时将应用插入菜单
|
|
if (siteInfo?.apps.length > 1) {
|
|
const routers:Record<string, any>[] = []
|
|
siteInfo?.apps.forEach((item: Record<string, any>) => {
|
|
if (addonRouters[item.key]) {
|
|
addonRouters[item.key].name = addonIndexRoute[item.key]
|
|
routers.push(addonRouters[item.key])
|
|
}
|
|
})
|
|
oneMenuData.value.unshift(...routers)
|
|
// 排序, 功能正确,改了排序后需要把菜单排序的默认值重新调整一下【多应用一级菜单,单应用二级菜单】
|
|
// oneMenuData.value.sort((a, b) => {
|
|
// if (a.meta.sort && b.meta.sort) {
|
|
// return b.meta.sort - a.meta.sort
|
|
// } else if (a.meta.sort) {
|
|
// return -1
|
|
// } else if (b.meta.sort) {
|
|
// return 1
|
|
// } else {
|
|
// return 0
|
|
// }
|
|
// })
|
|
}
|
|
|
|
const appList = ref(null)
|
|
const marketingList = ref(null)
|
|
// const loading = ref(true);
|
|
const oneMenuActive = ref(route.matched[1].name)
|
|
|
|
const getAppList = async () => {
|
|
const res = await getShowApp()
|
|
appList.value = res.data
|
|
// loading.value = false;
|
|
}
|
|
const getMarketingList = async () => {
|
|
const res = await getShowMarketing()
|
|
marketingList.value = res.data
|
|
}
|
|
onMounted(async () => {
|
|
await getAppList() // 确保数据先加载
|
|
await getMarketingList()
|
|
})
|
|
|
|
// 让二级菜单默认展开
|
|
const menuOption = ref([])
|
|
const secondMenuShowWayFn = () => {
|
|
menuOption.value = []
|
|
if (oneMenuActive.value !== 'active' && oneMenuActive.value !== 'addon' && twoMenuData.value && Object.values(twoMenuData.value).length) {
|
|
const data = cloneDeep(twoMenuData.value)
|
|
for (const key in data) {
|
|
menuOption.value.push(data[key].name)
|
|
}
|
|
}
|
|
}
|
|
|
|
watchEffect(() => {
|
|
// if (!appList.value || loading.value) return; // 确保数据加载完毕
|
|
const addonKeys = appList.value?.addon?.list?.map(item => item.key) ?? []
|
|
const toolKeys = appList.value?.tool?.list?.map(item => item.key) ?? []
|
|
const allKeys = [...addonKeys, ...toolKeys]
|
|
const marketingKeys = marketingList.value?.marketing?.list?.map(item => item.key) ?? []
|
|
const matchedName = route.matched[1]?.name
|
|
if (allKeys.includes(matchedName)) {
|
|
oneMenuActive.value = 'addon'
|
|
twoMenuData.value = route.matched[1]?.children ?? []
|
|
} else if (marketingKeys.includes(matchedName)) {
|
|
oneMenuActive.value = 'active'
|
|
twoMenuData.value = route.matched[1]?.children ?? []
|
|
} else if (route.meta.attr !== '') {
|
|
oneMenuActive.value = route.matched[2]?.name
|
|
twoMenuData.value = route.matched[1]?.children ?? []
|
|
} else {
|
|
// 多应用
|
|
if (siteInfo?.apps.length > 1) {
|
|
twoMenuData.value = route.matched[1]?.children
|
|
oneMenuActive.value = route.matched[1]?.name
|
|
} else {
|
|
// 单应用
|
|
const oneMenu = route.matched[1]
|
|
if (oneMenu.meta.addon === '') {
|
|
oneMenuActive.value = route.matched[1]?.name
|
|
twoMenuData.value = route.matched[1]?.children ?? []
|
|
} else {
|
|
if (oneMenu.meta.addon === siteInfo?.apps[0]?.key) {
|
|
oneMenuActive.value = route.matched[2]?.name
|
|
twoMenuData.value = route.matched[2]?.children ?? []
|
|
} else {
|
|
oneMenuActive.value = route.matched[1]?.name
|
|
twoMenuData.value = route.matched[1]?.children ?? []
|
|
}
|
|
}
|
|
}
|
|
}
|
|
secondMenuShowWayFn()
|
|
})
|
|
|
|
// watch(route, () => {
|
|
|
|
// if (route.meta.attr != '') {
|
|
// oneMenuActive.value = route.matched[2].name
|
|
// twoMenuData.value = route.matched[1].children ?? []
|
|
// } else {
|
|
// // 多应用
|
|
// if (siteInfo?.apps.length > 1) {
|
|
// twoMenuData.value = route.matched[1].children
|
|
// oneMenuActive.value = route.matched[1].name
|
|
// } else {
|
|
// // 单应用
|
|
// const oneMenu = route.matched[1]
|
|
// if (oneMenu.meta.addon == '') {
|
|
// oneMenuActive.value = route.matched[1].name
|
|
// twoMenuData.value = route.matched[1].children ?? []
|
|
// } else {
|
|
// if (oneMenu.meta.addon == siteInfo?.apps[0].key) {
|
|
// oneMenuActive.value = route.matched[2].name
|
|
// twoMenuData.value = route.matched[2].children ?? []
|
|
// } else {
|
|
// oneMenuActive.value = route.matched[1].name
|
|
// twoMenuData.value = route.matched[1].children ?? []
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
// }, { immediate: true })
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
.one-menu {
|
|
|
|
.aside-menu:not(.el-menu--collapse) {
|
|
background-color: transparent;
|
|
|
|
.el-menu-item {
|
|
font-size: 14px;
|
|
height: 40px;
|
|
margin-bottom: 4px;
|
|
padding-left: 12px !important;
|
|
color: rgba(255,255,255,.7);
|
|
border-radius: 2px;
|
|
|
|
&:hover {
|
|
color: #ffffff;
|
|
background-color: var(--el-color-primary);
|
|
}
|
|
|
|
&.is-active {
|
|
color: #fff !important;
|
|
background-color: var(--el-color-primary) !important;
|
|
}
|
|
|
|
span {
|
|
margin-left: 8px;
|
|
font-size: 14px;
|
|
}
|
|
}
|
|
}
|
|
|
|
.el-menu {
|
|
border: 0;
|
|
}
|
|
|
|
.el-scrollbar {
|
|
height: calc(100vh - 65px);
|
|
}
|
|
}
|
|
|
|
.two-menu {
|
|
|
|
.aside-menu:not(.el-menu--collapse) {
|
|
width: 132px;
|
|
padding-top: 16px;
|
|
border: 0;
|
|
|
|
.el-menu-item {
|
|
height: 36px;
|
|
margin: 0 12px 4px;
|
|
padding: 0 !important;
|
|
border-radius: 2px;
|
|
|
|
span {
|
|
margin-left: 8px;
|
|
font-size: 14px;
|
|
}
|
|
|
|
&.is-active {
|
|
background-color: var(--el-color-primary-light-9) !important;
|
|
}
|
|
|
|
&:hover {
|
|
color: var(--el-color-primary);
|
|
background-color: var(--el-color-primary-light-9) !important;
|
|
}
|
|
}
|
|
|
|
.el-sub-menu {
|
|
margin-bottom: 8px;
|
|
|
|
.el-sub-menu__title {
|
|
height: 36px;
|
|
margin: 0 8px 4px;
|
|
padding-left: 0;
|
|
border-radius: 2px;
|
|
|
|
span {
|
|
font-size: 14px;
|
|
display: flex;
|
|
height: 36px;
|
|
align-items: center;
|
|
}
|
|
|
|
&:hover {
|
|
color: var(--el-color-primary);
|
|
background-color: var(--el-color-primary-light-9) !important;
|
|
}
|
|
.el-icon.el-sub-menu__icon-arrow {
|
|
right: 5px;
|
|
}
|
|
}
|
|
|
|
.el-menu-item {
|
|
padding-left: 20px !important;
|
|
span{
|
|
margin-left: 0 !important;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.logo-wrap {
|
|
display: flex;
|
|
padding: 0;
|
|
white-space: nowrap;
|
|
align-items: center;
|
|
|
|
.logo {
|
|
height: 100%;
|
|
box-sizing: border-box;
|
|
}
|
|
}
|
|
</style>
|