fix: 权限和显示优化
All checks were successful
continuous-integration/drone/push Build is passing

1. 侧边栏:根据角色过滤菜单,无可访问子菜单时隐藏父菜单
2. Dashboard:智能工牌分析、统计卡片、最近考试仅对学员显示
3. 快捷操作:根据角色显示不同的操作入口
4. 欢迎语:根据角色显示不同的欢迎信息
5. 学习天数:改为基于注册日期计算(至少为1天)
6. 成长路径:AI分析按钮仅对学员显示

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
yuliang_guo
2026-02-03 15:41:56 +08:00
parent 7555de2275
commit 149cc5f6b0
4 changed files with 88 additions and 23 deletions

View File

@@ -379,6 +379,8 @@ const menuConfig = [
// 获取菜单路由
const menuRoutes = computed(() => {
const userRole = authManager.getUserRole()
// 仅保留当前用户可访问的菜单项和启用的功能
const filterChildren = (children: any[] = []) =>
children.filter((child: any) => {
@@ -389,7 +391,22 @@ const menuRoutes = computed(() => {
return true
})
// 根据角色预过滤顶级菜单
const roleMenuFilter = (route: any): boolean => {
// 管理者中心:仅 admin 和 manager 可见
if (route.path === '/manager') {
return userRole === 'admin' || userRole === 'manager'
}
// 系统管理:仅 admin 可见
if (route.path === '/admin') {
return userRole === 'admin'
}
// 数据分析:所有登录用户可见(但子菜单会进一步过滤)
return true
}
return menuConfig
.filter(roleMenuFilter) // 先按角色过滤顶级菜单
.map((route: any) => {
const next = { ...route }
if (route.children && route.children.length > 0) {
@@ -398,8 +415,10 @@ const menuRoutes = computed(() => {
return next
})
.filter((route: any) => {
// 有子菜单至少一个可访问
if (route.children && route.children.length > 0) return true
// 有子菜单的必须至少一个可访问的子项
if (route.children !== undefined) {
return route.children.length > 0
}
// 无子菜单时检查自身路径
return authManager.canAccessRoute(route.path)
})

View File

@@ -4,7 +4,15 @@
<div class="welcome-card card">
<div class="welcome-content">
<h1 class="welcome-title">欢迎回来{{ userName }}</h1>
<p class="welcome-desc">今天是您学习的第 <span class="highlight">{{ learningDays }}</span> 继续加油</p>
<p class="welcome-desc" v-if="userRole === 'trainee'">
今天是您学习的第 <span class="highlight">{{ learningDays }}</span> 继续加油
</p>
<p class="welcome-desc" v-else-if="userRole === 'manager'">
管理您的团队助力成员成长
</p>
<p class="welcome-desc" v-else>
系统运行正常一切尽在掌控
</p>
</div>
<div class="welcome-image">
<el-icon :size="120" color="#667eea">
@@ -13,8 +21,8 @@
</div>
</div>
<!-- 统计卡片 -->
<div class="stats-grid">
<!-- 统计卡片 - 仅学员显示 -->
<div class="stats-grid" v-if="userRole === 'trainee'">
<div class="stat-card card" v-for="stat in stats" :key="stat.title">
<div class="stat-icon" :style="{ backgroundColor: stat.bgColor }">
<el-icon :size="24" :color="stat.color">
@@ -24,7 +32,7 @@
<div class="stat-content">
<div class="stat-value">{{ stat.value }}</div>
<div class="stat-title">{{ stat.title }}</div>
<div class="stat-trend" :class="stat.trend > 0 ? 'up' : 'down'">
<div class="stat-trend" :class="stat.trend > 0 ? 'up' : 'down'" v-if="stat.trend !== 0">
<el-icon :size="12">
<component :is="stat.trend > 0 ? 'Top' : 'Bottom'" />
</el-icon>
@@ -48,8 +56,8 @@
</div>
</div>
<!-- 最近考试 -->
<div class="recent-exams">
<!-- 最近考试 - 仅学员显示 -->
<div class="recent-exams" v-if="userRole === 'trainee'">
<h2 class="section-title">最近考试</h2>
<div v-if="recentExams.length > 0" class="exam-list">
<div class="exam-item card" v-for="exam in recentExams" :key="exam.id">
@@ -99,6 +107,7 @@ const router = useRouter()
// 获取当前用户信息
const currentUser = computed(() => authManager.getCurrentUser())
const userName = computed(() => currentUser.value?.full_name || currentUser.value?.username || '用户')
const userRole = computed(() => authManager.getUserRole())
const learningDays = ref(0)
// 统计数据
@@ -156,37 +165,64 @@ const loadStatistics = async () => {
}
}
// 快捷操作
const quickActions = ref([
// 快捷操作配置(包含角色限制)
const allQuickActions = [
{
title: '智能工牌分析',
desc: 'AI能力评估与成长路径规划',
icon: 'TrendCharts',
color: '#e6a23c',
path: '/trainee/growth-path'
path: '/trainee/growth-path',
roles: ['trainee'] // 仅学员可见
},
{
title: '课程中心',
desc: '查看可用课程',
icon: 'Collection',
color: '#67c23a',
path: '/trainee/course-center'
path: '/trainee/course-center',
roles: ['trainee', 'manager', 'admin'] // 所有角色可见
},
{
title: '查分中心',
desc: '查看成绩和分析报告',
icon: 'DataAnalysis',
color: '#409eff',
path: '/trainee/score-report'
path: '/trainee/score-report',
roles: ['trainee'] // 仅学员可见
},
{
title: 'AI陪练',
desc: '智能陪练系统',
icon: 'ChatLineRound',
color: '#f56c6c',
path: '/trainee/ai-practice-center'
path: '/trainee/ai-practice-center',
roles: ['trainee', 'manager', 'admin'] // 所有角色可见
},
{
title: '团队看板',
desc: '查看团队学习情况',
icon: 'DataBoard',
color: '#667eea',
path: '/manager/team-dashboard',
roles: ['manager', 'admin'] // 管理者和管理员可见
},
{
title: '课程管理',
desc: '管理培训课程内容',
icon: 'Notebook',
color: '#909399',
path: '/manager/course-management',
roles: ['manager', 'admin'] // 管理者和管理员可见
}
])
]
// 根据角色过滤快捷操作
const quickActions = computed(() => {
const role = userRole.value
if (!role) return []
return allQuickActions.filter(action => action.roles.includes(role))
})
// 最近考试
const recentExams = ref<any[]>([])

View File

@@ -31,7 +31,14 @@
<div class="ability-radar card">
<div class="card-header">
<h3 class="card-title">能力评估</h3>
<el-button type="primary" size="small" @click="analyzeSmartBadgeData" :loading="analyzing">
<!-- AI智能工牌分析仅对学员开放 -->
<el-button
v-if="userInfo.role === 'trainee'"
type="primary"
size="small"
@click="analyzeSmartBadgeData"
:loading="analyzing"
>
<el-icon><TrendCharts /></el-icon>
AI 分析智能工牌数据
</el-button>
@@ -302,7 +309,7 @@
<p class="empty-description">
{{ analyzing ? '正在分析您的智能工牌数据,为您推荐最适合的课程' : '暂无智能工牌数据,请先使用智能工牌记录对话' }}
</p>
<el-button v-if="!analyzing" type="primary" @click="analyzeSmartBadgeData">
<el-button v-if="!analyzing && userInfo.role === 'trainee'" type="primary" @click="analyzeSmartBadgeData">
<el-icon><Refresh /></el-icon>
重新分析
</el-button>