feat: 用户与权限管理
新增 PermissionCode 权限类型与用户/权限 API,路由注册用户设置/创建/权限页与无权访问页并接入权限守卫,AppSidebar 按权限过滤菜单并新增用户设置入口,auth store 与 mock 适配器同步支持用户管理与新登录认证,回归脚本与 npm 脚本注册。
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
import { computed, nextTick, ref, watch } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import type { PermissionCode } from '@/types'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
@@ -27,41 +28,65 @@ const activeMenu = computed(() => {
|
||||
return seg
|
||||
})
|
||||
|
||||
const menuGroups = [
|
||||
interface MenuItem {
|
||||
key: string
|
||||
label: string
|
||||
icon: string
|
||||
to: string
|
||||
permission: PermissionCode
|
||||
}
|
||||
|
||||
interface MenuGroup {
|
||||
title?: string
|
||||
showTitle?: boolean
|
||||
items: MenuItem[]
|
||||
}
|
||||
|
||||
const menuGroups: MenuGroup[] = [
|
||||
{
|
||||
// 服务看板:独立入口,不显示分类小标题
|
||||
showTitle: false,
|
||||
items: [{ key: 'dashboard', label: '服务看板', icon: 'fa-tachometer', to: '/dashboard' }],
|
||||
items: [{ key: 'dashboard', label: '服务看板', icon: 'fa-tachometer', to: '/dashboard', permission: 'dashboard' }],
|
||||
},
|
||||
{
|
||||
title: '模型服务',
|
||||
items: [
|
||||
{ key: 'fine-tune', label: '模型训练', icon: 'fa-cogs', to: '/fine-tune' },
|
||||
{ key: 'model-eval', label: '模型评测', icon: 'fa-line-chart', to: '/model-eval' },
|
||||
{ key: 'model-inference', label: '模型推理', icon: 'fa-server', to: '/model-inference' },
|
||||
{ key: 'model-manage', label: '模型管理', icon: 'fa-cube', to: '/model-manage' },
|
||||
{ key: 'fine-tune', label: '模型训练', icon: 'fa-cogs', to: '/fine-tune', permission: 'fine-tune' },
|
||||
{ key: 'model-eval', label: '模型评测', icon: 'fa-line-chart', to: '/model-eval', permission: 'model-eval' },
|
||||
{ key: 'model-inference', label: '模型推理', icon: 'fa-server', to: '/model-inference', permission: 'model-inference' },
|
||||
{ key: 'model-manage', label: '模型管理', icon: 'fa-cube', to: '/model-manage', permission: 'model-manage' },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '数据治理',
|
||||
items: [
|
||||
{ key: 'dataset', label: '数据集管理', icon: 'fa-file-text', to: '/dataset' },
|
||||
{ key: 'data-process', label: '数据处理', icon: 'fa-filter', to: '/data-process' },
|
||||
{ key: 'dataset', label: '数据集管理', icon: 'fa-file-text', to: '/dataset', permission: 'dataset' },
|
||||
{ key: 'data-process', label: '数据处理', icon: 'fa-filter', to: '/data-process', permission: 'data-process' },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '其他工具',
|
||||
items: [{ key: 'data-convert', label: '数据类型转换', icon: 'fa-exchange', to: '/data-convert' }],
|
||||
items: [{ key: 'data-convert', label: '数据类型转换', icon: 'fa-exchange', to: '/data-convert', permission: 'data-convert' }],
|
||||
},
|
||||
{
|
||||
title: '系统设置',
|
||||
items: [
|
||||
{ key: 'hardware', label: '平台性能', icon: 'fa-bar-chart', to: '/hardware' },
|
||||
{ key: 'logs', label: '查看日志', icon: 'fa-file-text', to: '/logs' },
|
||||
{ key: 'user-settings', label: '用户设置', icon: 'fa-users', to: '/user-settings', permission: 'user-settings' },
|
||||
{ key: 'hardware', label: '平台性能', icon: 'fa-bar-chart', to: '/hardware', permission: 'hardware' },
|
||||
{ key: 'logs', label: '查看日志', icon: 'fa-file-text', to: '/logs', permission: 'logs' },
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
const visibleMenuGroups = computed(() =>
|
||||
menuGroups
|
||||
.map((group) => ({
|
||||
...group,
|
||||
items: group.items.filter((item) => auth.hasPermission(item.permission)),
|
||||
}))
|
||||
.filter((group) => group.items.length > 0),
|
||||
)
|
||||
|
||||
/**
|
||||
* 传给 el-menu 的激活值。
|
||||
*
|
||||
@@ -78,7 +103,7 @@ watch(activeMenu, (value) => {
|
||||
})
|
||||
|
||||
async function handleSelect(key: string) {
|
||||
const all = menuGroups.flatMap((g) => g.items)
|
||||
const all = visibleMenuGroups.value.flatMap((g) => g.items)
|
||||
const target = all.find((i) => i.key === key)
|
||||
if (!target) return
|
||||
|
||||
@@ -115,7 +140,7 @@ function handleLogout() {
|
||||
active-text-color="var(--sidebar-active-text)"
|
||||
@select="handleSelect"
|
||||
>
|
||||
<template v-for="group in menuGroups" :key="group.title || group.items[0].key">
|
||||
<template v-for="group in visibleMenuGroups" :key="group.title || group.items[0].key">
|
||||
<div v-if="group.title" class="sidebar-section-title">{{ group.title }}</div>
|
||||
<el-menu-item
|
||||
v-for="item in group.items"
|
||||
@@ -134,8 +159,8 @@ function handleLogout() {
|
||||
<div class="footer-user" v-if="auth.username">
|
||||
<el-avatar :size="36" src="https://picsum.photos/id/1005/36/36" class="user-avatar" />
|
||||
<div class="user-info">
|
||||
<span class="user-name">{{ auth.username }}</span>
|
||||
<span class="user-role">Administrator</span>
|
||||
<span class="user-name">{{ auth.displayName }}</span>
|
||||
<span class="user-role">{{ auth.roleLabel }}</span>
|
||||
</div>
|
||||
<el-tooltip content="退出登录" placement="top">
|
||||
<div class="logout-btn" @click="handleLogout">
|
||||
|
||||
Reference in New Issue
Block a user