diff --git a/backend/app/api/v1/users.py b/backend/app/api/v1/users.py index c28797a..adda791 100644 --- a/backend/app/api/v1/users.py +++ b/backend/app/api/v1/users.py @@ -47,20 +47,23 @@ async def get_current_user_statistics( 获取当前用户学习统计 返回字段: - - learningDays: 学习天数(按陪练会话开始日期去重) + - learningDays: 学习天数(从注册日期到今天的天数,至少为1) - totalHours: 学习总时长(小时,取整到1位小数) - practiceQuestions: 练习题数(答题记录条数汇总) - averageScore: 平均成绩(已提交考试的平均分,保留1位小数) - examsCompleted: 已完成考试数量 """ try: + from datetime import date user_id = current_user.id - # 学习天数:按会话开始日期去重 - learning_days_stmt = select(func.count(func.distinct(func.date(TrainingSession.start_time)))).where( - TrainingSession.user_id == user_id - ) - learning_days = (await db.scalar(learning_days_stmt)) or 0 + # 学习天数:从注册日期到今天的天数(至少为1天) + if current_user.created_at: + registration_date = current_user.created_at.date() if hasattr(current_user.created_at, 'date') else current_user.created_at + learning_days = (date.today() - registration_date).days + 1 # +1 是因为注册当天也算第1天 + learning_days = max(1, learning_days) # 确保至少为1 + else: + learning_days = 1 # 总时长(小时) total_seconds_stmt = select(func.coalesce(func.sum(TrainingSession.duration_seconds), 0)).where( diff --git a/frontend/src/layout/index.vue b/frontend/src/layout/index.vue index 7897b89..20c156b 100644 --- a/frontend/src/layout/index.vue +++ b/frontend/src/layout/index.vue @@ -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) }) diff --git a/frontend/src/views/dashboard/index.vue b/frontend/src/views/dashboard/index.vue index 2700233..3ec3e1c 100644 --- a/frontend/src/views/dashboard/index.vue +++ b/frontend/src/views/dashboard/index.vue @@ -4,7 +4,15 @@
今天是您学习的第 {{ learningDays }} 天,继续加油!
++ 今天是您学习的第 {{ learningDays }} 天,继续加油! +
++ 管理您的团队,助力成员成长 +
++ 系统运行正常,一切尽在掌控 +