feat: 用户与权限管理

新增 PermissionCode 权限类型与用户/权限 API,路由注册用户设置/创建/权限页与无权访问页并接入权限守卫,AppSidebar 按权限过滤菜单并新增用户设置入口,auth store 与 mock 适配器同步支持用户管理与新登录认证,回归脚本与 npm 脚本注册。
This commit is contained in:
caoxiaozhu
2026-07-14 16:10:50 +08:00
parent 2bceb686c6
commit e09c6e81df
8 changed files with 296 additions and 28 deletions

View File

@@ -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">