104 lines
4.5 KiB
Vue
104 lines
4.5 KiB
Vue
<script setup lang="ts">
|
|
import { computed, ref, watch } from 'vue'
|
|
import { useRouter, useRoute } from 'vue-router'
|
|
import { adminAuth } from './auth'
|
|
import { DashboardOutlined, SettingOutlined, ControlOutlined, SmileOutlined,
|
|
GithubOutlined, PictureOutlined, TeamOutlined, AppstoreOutlined,
|
|
NotificationOutlined, SafetyCertificateOutlined, AuditOutlined,
|
|
LogoutOutlined, CloudServerOutlined, FundOutlined } from '@ant-design/icons-vue'
|
|
|
|
const router = useRouter()
|
|
const route = useRoute()
|
|
const collapsed = ref(false)
|
|
const selectedKeys = ref<string[]>([String(route.name)])
|
|
|
|
watch(() => route.name, (n) => { selectedKeys.value = [String(n)] })
|
|
|
|
const navGroups = computed(() => [
|
|
{ label: '概览', items: [
|
|
{ key: 'Dashboard', icon: DashboardOutlined, label: '仪表盘' },
|
|
] },
|
|
{ label: 'AI 配置', items: [
|
|
{ key: 'ModelService', icon: CloudServerOutlined, label: '模型服务' },
|
|
{ key: 'Personas', icon: SmileOutlined, label: 'AI 性格' },
|
|
{ key: 'Avatars', icon: GithubOutlined, label: 'AI 形象' },
|
|
{ key: 'Stickers', icon: PictureOutlined, label: '表情包库' },
|
|
] },
|
|
{ label: '产品配置', items: [
|
|
{ key: 'ProductBasic', icon: SettingOutlined, label: '品牌与基础设置' },
|
|
{ key: 'FeatureLimits', icon: ControlOutlined, label: '功能与额度' },
|
|
{ key: 'SysCategories', icon: AppstoreOutlined, label: '默认分类' },
|
|
] },
|
|
{ label: '运营', items: [
|
|
{ key: 'Users', icon: TeamOutlined, label: '用户管理' },
|
|
{ key: 'PushCampaigns', icon: NotificationOutlined, label: '推送管理' },
|
|
] },
|
|
...(adminAuth.identity.value?.role === 'super_admin' ? [{ label: '安全', items: [
|
|
{ key: 'AdminAccounts', icon: SafetyCertificateOutlined, label: '管理员账号' },
|
|
{ key: 'Audit', icon: AuditOutlined, label: '操作审计' },
|
|
] }] : []),
|
|
])
|
|
|
|
watch(adminAuth.identity, value => {
|
|
if (!value && !route.meta.public) router.replace('/login')
|
|
})
|
|
|
|
async function logout() {
|
|
await adminAuth.logout()
|
|
await router.replace('/login')
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<router-view v-if="route.meta.public || route.name === 'ChangePassword'" />
|
|
<a-layout v-else style="min-height: 100vh">
|
|
<a-layout-sider v-model:collapsed="collapsed" collapsible theme="light" :width="224"
|
|
breakpoint="lg" class="app-sider">
|
|
<div class="brand-lockup">
|
|
<FundOutlined />
|
|
<span>记之 Admin</span>
|
|
</div>
|
|
<a-menu v-model:selectedKeys="selectedKeys" mode="inline" :style="{ borderRight: 0 }"
|
|
@click="({key}: {key: string}) => router.push({name: key})">
|
|
<a-menu-item-group v-for="group in navGroups" :key="group.label" :title="group.label">
|
|
<a-menu-item v-for="item in group.items" :key="item.key">
|
|
<component :is="item.icon" />
|
|
<span>{{ item.label }}</span>
|
|
</a-menu-item>
|
|
</a-menu-item-group>
|
|
</a-menu>
|
|
<div class="logout-area">
|
|
<a-button type="text" block @click="logout">
|
|
<template #icon><LogoutOutlined /></template>退出登录
|
|
</a-button>
|
|
</div>
|
|
</a-layout-sider>
|
|
<a-layout>
|
|
<a-layout-header style="height:52px;padding:0 20px;background:#fff;border-bottom:1px solid #f0f0f0;display:flex;align-items:center;justify-content:flex-end">
|
|
<a-space>
|
|
<span>{{ adminAuth.identity.value?.username }}</span>
|
|
<a-tag>{{ adminAuth.identity.value?.role }}</a-tag>
|
|
</a-space>
|
|
</a-layout-header>
|
|
<a-layout-content class="app-content">
|
|
<router-view />
|
|
</a-layout-content>
|
|
</a-layout>
|
|
</a-layout>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.app-sider { position: sticky; top: 0; height: 100vh; overflow: auto; border-right: 1px solid #eef0f3; }
|
|
.brand-lockup { display: flex; align-items: center; gap: 10px; height: 60px; padding: 0 22px; overflow: hidden; color: #191f26; font-size: 16px; font-weight: 700; white-space: nowrap; }
|
|
.brand-lockup :first-child { color: #00a67d; font-size: 20px; }
|
|
.logout-area { position: sticky; bottom: 0; padding: 12px 16px 16px; background: #fff; }
|
|
.logout-area .ant-btn { text-align: left; }
|
|
.app-content { min-height: 360px; margin: 18px 20px; padding: 24px; border-radius: 14px; background: #fff; }
|
|
:deep(.ant-menu-item-group-title) { padding: 18px 24px 6px; color: #8b949e; font-size: 11px; font-weight: 600; letter-spacing: .08em; }
|
|
:deep(.ant-menu-item) { min-height: 42px; }
|
|
:deep(.ant-layout-sider-collapsed .ant-menu-item-group-title) { height: 12px; padding: 6px 0; overflow: hidden; color: transparent; }
|
|
@media (max-width: 760px) {
|
|
.app-content { margin: 10px; padding: 16px; }
|
|
}
|
|
</style>
|