mirror of
https://github.com/XShengTech/MEGREZ.git
synced 2026-01-14 00:57:17 +08:00
[Feat] ✨ Independent Admin Management Page
This commit is contained in:
parent
e6f81e19dc
commit
2a18684162
87
frontend/src/layout/AppAdminLayout.vue
Normal file
87
frontend/src/layout/AppAdminLayout.vue
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
<script setup>
|
||||||
|
import { useLayout } from '@/layout/composables/layout';
|
||||||
|
import { computed, onMounted, ref, watch } from 'vue';
|
||||||
|
import AppAdminSidebar from './AppAdminSidebar.vue';
|
||||||
|
import AppFooter from './AppFooter.vue';
|
||||||
|
import AppTopbar from './AppTopbar.vue';
|
||||||
|
|
||||||
|
import api from '@/api';
|
||||||
|
import { useProfileStore } from '@/stores/profile';
|
||||||
|
import { useToast } from 'primevue/usetoast';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const profileStore = useProfileStore()
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
|
const { layoutConfig, layoutState, isSidebarActive, resetMenu } = useLayout();
|
||||||
|
|
||||||
|
const outsideClickListener = ref(null);
|
||||||
|
|
||||||
|
watch(isSidebarActive, (newVal) => {
|
||||||
|
if (newVal) {
|
||||||
|
bindOutsideClickListener();
|
||||||
|
} else {
|
||||||
|
unbindOutsideClickListener();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const containerClass = computed(() => {
|
||||||
|
return {
|
||||||
|
'layout-overlay': layoutConfig.menuMode === 'overlay',
|
||||||
|
'layout-static': layoutConfig.menuMode === 'static',
|
||||||
|
'layout-static-inactive': layoutState.staticMenuDesktopInactive && layoutConfig.menuMode === 'static',
|
||||||
|
'layout-overlay-active': layoutState.overlayMenuActive,
|
||||||
|
'layout-mobile-active': layoutState.staticMenuMobileActive
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
function bindOutsideClickListener() {
|
||||||
|
if (!outsideClickListener.value) {
|
||||||
|
outsideClickListener.value = (event) => {
|
||||||
|
if (isOutsideClicked(event)) {
|
||||||
|
resetMenu();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
document.addEventListener('click', outsideClickListener.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function unbindOutsideClickListener() {
|
||||||
|
if (outsideClickListener.value) {
|
||||||
|
document.removeEventListener('click', outsideClickListener);
|
||||||
|
outsideClickListener.value = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isOutsideClicked(event) {
|
||||||
|
const sidebarEl = document.querySelector('.layout-sidebar');
|
||||||
|
const topbarEl = document.querySelector('.layout-menu-button');
|
||||||
|
|
||||||
|
return !(sidebarEl.isSameNode(event.target) || sidebarEl.contains(event.target) || topbarEl.isSameNode(event.target) || topbarEl.contains(event.target));
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
api.GetUserProfile().then(res => {
|
||||||
|
profileStore.setUserProfile(res.data.data.result)
|
||||||
|
}).catch(_ => {
|
||||||
|
toast.add({ severity: 'error', summary: '登录过期,请重新登录', life: 3000 })
|
||||||
|
profileStore.clearUserProfile()
|
||||||
|
router.push('/login')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="layout-wrapper" :class="containerClass">
|
||||||
|
<app-topbar></app-topbar>
|
||||||
|
<app-admin-sidebar></app-admin-sidebar>
|
||||||
|
<div class="layout-main-container">
|
||||||
|
<div class="layout-main">
|
||||||
|
<router-view></router-view>
|
||||||
|
</div>
|
||||||
|
<app-footer></app-footer>
|
||||||
|
</div>
|
||||||
|
<div class="layout-mask animate-fadein"></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
88
frontend/src/layout/AppAdminMenu.vue
Normal file
88
frontend/src/layout/AppAdminMenu.vue
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
<script setup>
|
||||||
|
import { useProfileStore } from '@/stores/profile';
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
|
||||||
|
import AppMenuItem from './AppMenuItem.vue';
|
||||||
|
|
||||||
|
const profileStore = useProfileStore();
|
||||||
|
|
||||||
|
|
||||||
|
const model = ref([]);
|
||||||
|
|
||||||
|
const defaultModel = ref({
|
||||||
|
label: '用户中心',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
label: '实例管理',
|
||||||
|
icon: 'pi pi-fw pi-desktop text-lime-500',
|
||||||
|
to: '/'
|
||||||
|
},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
const adminModel = ref({
|
||||||
|
label: '系统设置',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
label: '实例管理',
|
||||||
|
icon: 'pi pi-fw pi-desktop text-lime-500',
|
||||||
|
to: '/admin/instances'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '用户管理',
|
||||||
|
icon: 'pi pi-fw pi-users text-indigo-500',
|
||||||
|
to: '/admin/users'
|
||||||
|
},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
const superAdminModel = ref({
|
||||||
|
label: '系统设置',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
label: '节点管理',
|
||||||
|
icon: 'pi pi-fw pi-server text-yellow-400',
|
||||||
|
to: '/admin/servers'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '实例管理',
|
||||||
|
icon: 'pi pi-fw pi-desktop text-lime-500',
|
||||||
|
to: '/admin/instances'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '用户管理',
|
||||||
|
icon: 'pi pi-fw pi-users text-indigo-500',
|
||||||
|
to: '/admin/users'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '镜像管理',
|
||||||
|
icon: 'pi pi-fw pi-images text-teal-500',
|
||||||
|
to: '/admin/images'
|
||||||
|
},
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const superAdmin = profileStore.isSuperAdmin
|
||||||
|
const admin = profileStore.isAdmin
|
||||||
|
|
||||||
|
if (superAdmin) {
|
||||||
|
model.value.push(superAdminModel.value)
|
||||||
|
} else if (admin) {
|
||||||
|
model.value.push(adminModel.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
model.value.push(defaultModel.value);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ul class="layout-menu">
|
||||||
|
<template v-for="(item, i) in model" :key="item">
|
||||||
|
<app-menu-item v-if="!item.separator" :item="item" :index="i"></app-menu-item>
|
||||||
|
<li v-if="item.separator" class="menu-separator"></li>
|
||||||
|
</template>
|
||||||
|
</ul>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
11
frontend/src/layout/AppAdminSidebar.vue
Normal file
11
frontend/src/layout/AppAdminSidebar.vue
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<script setup>
|
||||||
|
import AppAdminMenu from './AppAdminMenu.vue';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="layout-sidebar">
|
||||||
|
<app-admin-menu></app-admin-menu>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
@ -67,15 +67,10 @@ const adminModel = ref({
|
|||||||
label: '系统设置',
|
label: '系统设置',
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
label: '实例管理',
|
label: '管理后台',
|
||||||
icon: 'pi pi-fw pi-desktop text-lime-500',
|
icon: 'pi pi-fw pi-sliders-h text-yellow-500',
|
||||||
to: '/admin/instances'
|
to: '/admin/instances'
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: '用户管理',
|
|
||||||
icon: 'pi pi-fw pi-users text-indigo-500',
|
|
||||||
to: '/admin/users'
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -83,25 +78,10 @@ const superAdminModel = ref({
|
|||||||
label: '系统设置',
|
label: '系统设置',
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
label: '节点管理',
|
label: '管理后台',
|
||||||
icon: 'pi pi-fw pi-server text-yellow-400',
|
icon: 'pi pi-fw pi-sliders-h text-yellow-400',
|
||||||
to: '/admin/servers'
|
to: '/admin/servers'
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: '实例管理',
|
|
||||||
icon: 'pi pi-fw pi-desktop text-lime-500',
|
|
||||||
to: '/admin/instances'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '镜像管理',
|
|
||||||
icon: 'pi pi-fw pi-images text-teal-500',
|
|
||||||
to: '/admin/images'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '用户管理',
|
|
||||||
icon: 'pi pi-fw pi-users text-indigo-500',
|
|
||||||
to: '/admin/users'
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -39,7 +39,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Menu ref="profileMenu" :model="instanceMenuItems" :popup="true" @blur="profileMenuActive = false" />
|
<Menu ref="profileMenu" :model="profileMenuItems" :popup="true" @blur="profileMenuActive = false" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
@ -61,7 +61,7 @@ const username = ref('');
|
|||||||
|
|
||||||
const profileMenu = ref(null);
|
const profileMenu = ref(null);
|
||||||
const profileMenuActive = ref(false);
|
const profileMenuActive = ref(false);
|
||||||
const instanceMenuItems = [
|
const profileMenuItems = ref([
|
||||||
{
|
{
|
||||||
label: '个人信息',
|
label: '个人信息',
|
||||||
icon: 'pi pi-user !text-cyan-500',
|
icon: 'pi pi-user !text-cyan-500',
|
||||||
@ -78,6 +78,12 @@ const instanceMenuItems = [
|
|||||||
router.push('/settings');
|
router.push('/settings');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
separator: true
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const profileExitItems = ref([
|
||||||
{
|
{
|
||||||
label: '退出登录',
|
label: '退出登录',
|
||||||
icon: 'pi pi-sign-out',
|
icon: 'pi pi-sign-out',
|
||||||
@ -86,7 +92,35 @@ const instanceMenuItems = [
|
|||||||
logout();
|
logout();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
];
|
]);
|
||||||
|
|
||||||
|
const profileAdminItems = ref([
|
||||||
|
{
|
||||||
|
label: '管理后台',
|
||||||
|
icon: 'pi pi-sliders-h !text-yellow-400',
|
||||||
|
command: () => {
|
||||||
|
profileMenu.value.hide();
|
||||||
|
router.push('/admin/instances');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
separator: true
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
const profileSuperadminItems = ref([
|
||||||
|
{
|
||||||
|
label: '管理后台',
|
||||||
|
icon: 'pi pi-sliders-h !text-yellow-400',
|
||||||
|
command: () => {
|
||||||
|
profileMenu.value.hide();
|
||||||
|
router.push('/admin/servers');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
separator: true
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
const showMenu = (event) => {
|
const showMenu = (event) => {
|
||||||
profileMenu.value.show(event);
|
profileMenu.value.show(event);
|
||||||
@ -105,6 +139,14 @@ const logout = () => {
|
|||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
username.value = profileStore.username;
|
username.value = profileStore.username;
|
||||||
|
|
||||||
|
if (profileStore.isSuperAdmin) {
|
||||||
|
profileMenuItems.value.push(...profileSuperadminItems.value);
|
||||||
|
} else if (profileStore.isAdmin) {
|
||||||
|
profileMenuItems.value.push(...profileAdminItems.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
profileMenuItems.value.push(...profileExitItems.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
@ -17,6 +17,7 @@ import Instances from '@/views/admin/Instances.vue';
|
|||||||
import Servers from '@/views/admin/Servers.vue';
|
import Servers from '@/views/admin/Servers.vue';
|
||||||
import Users from '@/views/admin/Users.vue';
|
import Users from '@/views/admin/Users.vue';
|
||||||
|
|
||||||
|
import AppAdminLayout from '@/layout/AppAdminLayout.vue';
|
||||||
import NotFound from '@/views/NotFound.vue';
|
import NotFound from '@/views/NotFound.vue';
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
@ -70,24 +71,31 @@ const router = createRouter({
|
|||||||
path: 'settings',
|
path: 'settings',
|
||||||
name: 'settings',
|
name: 'settings',
|
||||||
component: Settings
|
component: Settings
|
||||||
},
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/admin/',
|
||||||
|
name: 'admin-dashboard',
|
||||||
|
component: AppAdminLayout,
|
||||||
|
children: [
|
||||||
{
|
{
|
||||||
path: 'admin/images',
|
path: 'images',
|
||||||
name: 'admin-images',
|
name: 'admin-images',
|
||||||
component: Images
|
component: Images
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'admin/instances',
|
path: 'instances',
|
||||||
name: 'admin-instances',
|
name: 'admin-instances',
|
||||||
component: Instances
|
component: Instances
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'admin/servers',
|
path: 'servers',
|
||||||
name: 'admin-servers',
|
name: 'admin-servers',
|
||||||
component: Servers
|
component: Servers
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'admin/users',
|
path: 'users',
|
||||||
name: 'admin-users',
|
name: 'admin-users',
|
||||||
component: Users
|
component: Users
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user