feat: scaffold mediai admin

This commit is contained in:
王天骄
2026-06-03 15:21:07 +08:00
parent 7da12b0c07
commit 875bf1f098
665 changed files with 4345 additions and 73920 deletions
+120
View File
@@ -0,0 +1,120 @@
<template>
<div class="admin-shell" :class="{ collapsed: appStore.collapsed, dark: appStore.darkMode }">
<aside class="sidebar">
<div class="sidebar-header">
<div class="brand-mark">M</div>
<div class="brand-copy">
<strong>MediAI</strong>
<span>医疗AI平台</span>
</div>
</div>
<el-scrollbar class="sidebar-scroll">
<nav class="nav-list">
<section v-for="section in visibleSections" :key="section.title" class="nav-section">
<div class="nav-section-title">{{ section.title }}</div>
<router-link v-for="item in section.items" :key="item.page" :to="getPagePath(item.page)" class="nav-item">
<el-icon><component :is="item.icon" /></el-icon>
<span>{{ item.title }}</span>
</router-link>
</section>
</nav>
</el-scrollbar>
<div class="sidebar-footer">
<div class="user-card">
<el-avatar :size="38">{{ appStore.user.avatarText }}</el-avatar>
<div class="user-meta">
<strong>{{ appStore.user.name }}</strong>
<span>{{ appStore.roleLabel }}</span>
</div>
</div>
<el-select v-model="role" class="role-select" size="small" @change="handleRoleChange">
<el-option v-for="item in roleOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div>
</aside>
<section class="main-panel">
<header class="top-header">
<div class="header-left">
<el-tooltip content="折叠菜单" placement="bottom">
<el-button :icon="Fold" circle @click="appStore.collapsed = !appStore.collapsed" />
</el-tooltip>
<el-breadcrumb separator="/">
<el-breadcrumb-item>首页</el-breadcrumb-item>
<el-breadcrumb-item>{{ pageTitle }}</el-breadcrumb-item>
</el-breadcrumb>
</div>
<div class="header-right">
<el-input v-model="keyword" class="global-search" :prefix-icon="Search" clearable placeholder="全局搜索..." />
<el-badge :value="3" class="header-badge">
<el-button :icon="Bell" circle />
</el-badge>
<el-tooltip content="切换主题" placement="bottom">
<el-button :icon="appStore.darkMode ? Sunny : Moon" circle @click="appStore.darkMode = !appStore.darkMode" />
</el-tooltip>
<el-button :icon="QuestionFilled" circle />
<div class="header-user">
<strong>{{ appStore.user.name }}</strong>
<span>{{ appStore.roleLabel }}</span>
</div>
<el-dropdown @command="handleCommand">
<el-button :icon="ArrowDown" circle />
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="profile">个人中心</el-dropdown-item>
<el-dropdown-item command="logout" divided>退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</header>
<main class="content-area">
<router-view />
</main>
</section>
</div>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { ArrowDown, Bell, Fold, Moon, QuestionFilled, Search, Sunny } from '@element-plus/icons-vue'
import { roleOptions } from '@/mock/dashboard'
import { getFirstPage, getPagePath, pageTitles, roleMenus } from '@/mock/navigation'
import { useAppStore } from '@/stores/app'
import type { RoleKey } from '@/types'
const route = useRoute()
const router = useRouter()
const appStore = useAppStore()
const keyword = ref('')
const role = computed({
get: () => appStore.user.role,
set: value => appStore.switchRole(value)
})
const visibleSections = computed(() => roleMenus[appStore.user.role])
const pageTitle = computed(() => {
const page = route.params.page
if (typeof page === 'string') {
return pageTitles[page] || '业务模块'
}
return String(route.meta.title || '数据驾驶舱')
})
function handleRoleChange(value: RoleKey) {
appStore.switchRole(value)
router.push(getPagePath(getFirstPage(value)))
}
function handleCommand(command: string) {
if (command === 'logout') {
appStore.logout()
router.push('/login')
}
}
</script>