Преглед изворни кода

1、优化路由配置统一管理
2、优化字典,登录和重新访问都会刷新字典
3、增加路由守卫,配合平台路由权限(跳转无权限页面回退到有权限的主页)

liyanbo пре 3 месеци
родитељ
комит
15beb248a3

+ 10 - 0
src/api/system/role.js

@@ -0,0 +1,10 @@
+import axios from '@/utils/request';
+
+// 获取角色路由
+export function getRoleRoute (){
+    return axios({
+        url: "bjdxWeb/web/getRoleRoute",
+        method: 'get'
+    })
+}
+

+ 2 - 1
src/components/HomePage.vue

@@ -113,6 +113,7 @@ import studyImg from '@/assets/images/study.png'
 import logoutIcon from '@/assets/icon/logout.png'
 // 退出登录
 import { logoutLogic } from '@/utils/loginUtils.js'
+import {homeRoutes} from "@/router/index.js";
 
 // 平台标题响应式变量
 const platformTitle = ref(import.meta.env.VITE_APP_TITLE)
@@ -131,7 +132,7 @@ const updateUserName = () => {
 const router = useRouter()
 // 退出登录
 const LogoutClick = async () => {
-  await logoutLogic(router)
+  await logoutLogic(router, homeRoutes.login)
 }
 
 // 默认选中 AI 通识课

+ 60 - 25
src/router/index.js

@@ -1,5 +1,7 @@
-import { createRouter, createWebHashHistory } from 'vue-router'
-import App from '../App.vue'
+import {createRouter, createWebHashHistory} from 'vue-router'
+import {CONFIG} from "@/utils/roleUtils.js";
+import {Message} from "@/utils/message/Message.js";
+
 
 const routes = [
   { path: '/', component: () => import('../views/Login.vue') },
@@ -102,12 +104,12 @@ const routes = [
   { path: '/programming02', component: () => import('../views/gamepage/GameIndex.vue') },
   // 编程课列表
   {
-    path: '/programminglist',
+    path: '/programmingList',
     component: () => import('../views/programming/ProgrammingList.vue')
   },
   // 编程课列表内容
   {
-    path: '/programmingcourset',
+    path: '/programmingCourset',
     component: () => import('../views/programming/ProgrammingCourset.vue')
   },
   // 编程课视频
@@ -141,7 +143,9 @@ const routes = [
 
 // 定义主页及其子路由映射
 const homeRoutes = {
-  main: '/home',
+  home: '/home',
+  login: '/login',
+  role: 'course',
   children: [
     '/ai-general-course',
     '/ai-laboratory',
@@ -161,20 +165,24 @@ const homeRoutes = {
 }
 
 const blocklyRoutes = {
-  main: '/blocklyHome',
+  home: '/blocklyHome',
+  login: '/blockly-login',
+  role: 'blockly',
   children: [
     '/blockly',
     '/blockly2',
     '/mapGame',
     '/programming02',
-    '/programminglist',
-    '/programmingcourset',
+    '/programmingList',
+    '/programmingCourset',
     '/interface'
   ]
 }
 
 const aiCourseRoutes = {
-  main: '/aiCourseHome',
+  home: '/aiCourseHome',
+  login: '/ai-login',
+  role: 'aiCourse',
   children: [
     '/experiment-type',
     '/experimental-course',
@@ -184,13 +192,17 @@ const aiCourseRoutes = {
 
 // 定义登录页面与主页的映射关系
 const loginToHomeMap = {
-  '/login': homeRoutes.main,
-  '/quick-login': homeRoutes.main,
-  '/promotion-login': homeRoutes.main,
-  '/blockly-login': blocklyRoutes.main,
-  '/ai-login': aiCourseRoutes.main
+  '/login': homeRoutes.home,
+  '/quick-login': homeRoutes.home,
+  '/promotion-login': homeRoutes.home,
+  '/blockly-login': blocklyRoutes.home,
+  '/ai-login': aiCourseRoutes.home
 }
 
+// 从localStorage获取角色路由数据
+const roleRouteStr = localStorage.getItem(CONFIG.USER_ROLE_ROUTE_KEY)
+const roleRouteSet = roleRouteStr ? JSON.parse(roleRouteStr) : []
+
 const router = createRouter({
   history: createWebHashHistory(),
   routes
@@ -209,7 +221,7 @@ router.beforeEach((to, from, next) => {
   const loginPath = localStorage.getItem('loginPath')
 
   // 获取当前登录类型对应的主页
-  const allowedHome = loginToHomeMap[loginPath]
+  let allowedHome = loginToHomeMap[loginPath]
 
   // 如果没有登录信息,允许访问所有页面(或根据实际需求处理)
   if (!loginPath || !allowedHome) {
@@ -220,23 +232,46 @@ router.beforeEach((to, from, next) => {
   // 检查目标路由是否在允许的路由范围内
   let isAllowed = false
 
-  if (allowedHome === homeRoutes.main) {
-    // home主页允许访问所有页面
+  // 首先检查用户是否有权限访问对应的主页
+  const hasHomePermission = roleRouteSet.includes(homeRoutes.role)
+  const hasBlocklyPermission = roleRouteSet.includes(blocklyRoutes.role)
+  const hasAiCoursePermission = roleRouteSet.includes(aiCourseRoutes.role)
+
+  // 如果用户没有当前登录类型对应的权限,切换到用户有权限的主页
+  if (!((allowedHome === homeRoutes.home && hasHomePermission) ||
+      (allowedHome === blocklyRoutes.home && hasBlocklyPermission) ||
+      (allowedHome === aiCourseRoutes.home && hasAiCoursePermission))) {
+    // 找到用户有权限的主页
+    if (hasAiCoursePermission) {
+      allowedHome = aiCourseRoutes.home
+    } else if (hasBlocklyPermission) {
+      allowedHome = blocklyRoutes.home
+    } else if (hasHomePermission) {
+      allowedHome = homeRoutes.home
+    } else {
+      // 如果没有任何权限,可以考虑重定向到登录页面或其他公共页面
+      next(loginPath)
+      return
+    }
+  }
+
+  // 检查目标路由是否在允许的范围内
+  if ((to.path === homeRoutes.home || homeRoutes.children.includes(to.path)) && hasHomePermission) {
+    isAllowed = true
+  } else if ((to.path === blocklyRoutes.home || blocklyRoutes.children.includes(to.path)) && hasBlocklyPermission) {
+    isAllowed = true
+  } else if ((to.path === aiCourseRoutes.home || aiCourseRoutes.children.includes(to.path)) && hasAiCoursePermission) {
     isAllowed = true
-  } else if (allowedHome === blocklyRoutes.main) {
-    // blocklyHome主页只允许访问自己和子路由
-    isAllowed = to.path === blocklyRoutes.main || blocklyRoutes.children.includes(to.path)
-  } else if (allowedHome === aiCourseRoutes.main) {
-    // aiCourseHome主页只允许访问自己和子路由
-    isAllowed = to.path === aiCourseRoutes.main || aiCourseRoutes.children.includes(to.path)
   }
 
   if (isAllowed) {
     next()
   } else {
-    // 不允许访问时,重定向到对应主页
+    // 不允许访问时,重定向到用户有权限的主页
+    Message().warning('您没有权限访问该页面,已自动切换到您有权限的主页!', true);
     next(allowedHome)
   }
 })
+export default router;
 
-export default router
+export { homeRoutes, blocklyRoutes, aiCourseRoutes };

+ 5 - 4
src/utils/dictUtils.js

@@ -56,9 +56,10 @@ const setCacheWithExpiry = (dictType, data) => {
 /**
  * 获取单个字典数据
  * @param {string} dictType - 字典类型(使用DICT_TYPE中的常量)
+ * @param {boolean} refresh - 是否强制刷新缓存(默认false)
  * @returns {Promise<Array>} - 字典数据数组
  */
-export const getDictData = async (dictType) => {
+export const getDictData = async (dictType, refresh = false) => {
   try {
     // 检查是否为DICT_TYPE中定义的类型
     if (!ALL_DICT_TYPES.includes(dictType)) {
@@ -68,7 +69,7 @@ export const getDictData = async (dictType) => {
 
     // 检查缓存是否存在且未过期
     const cachedData = localStorage.getItem(dictType);
-    if (cachedData && !isCacheExpired(dictType)) {
+    if (!refresh && cachedData && !isCacheExpired(dictType)) {
       return JSON.parse(cachedData);
     }
 
@@ -91,10 +92,10 @@ export const getDictData = async (dictType) => {
  * 更新所有字典数据
  * @returns {Promise<Object>} - 包含所有支持的字典数据的对象
  */
-export const updateAllDictData = async () => {
+export const refreshAllDictData = async () => {
   try {
     // 并行获取所有DICT_TYPE中定义的字典数据
-    const dictPromises = ALL_DICT_TYPES.map(type => getDictData(type));
+    const dictPromises = ALL_DICT_TYPES.map(type => getDictData(type, true));
     const dictDataArray = await Promise.all(dictPromises);
 
     // 构建返回对象

+ 41 - 15
src/utils/loginUtils.js

@@ -1,11 +1,12 @@
 import { ref, computed } from 'vue'
 import { getTenantIdByName, login, smsLogin, smsCode, logout } from '@/api/login/login.js'
 import { ElLoading, ElMessage } from 'element-plus'
-import { updateAllDictData } from './dictUtils.js';
+import { refreshAllDictData } from './dictUtils.js';
 // CryptoJS 库(用于密码加密)
 import CryptoJS from 'crypto-js';
 // 消息工具
 import { Message } from './message/Message.js';
+import {CONFIG, refreshRoleRoute} from "@/utils/roleUtils.js";
 
 // 加密密钥 (从环境变量获取)
 const SECRET_KEY = import.meta.env.VITE_SECRET_KEY;
@@ -153,7 +154,7 @@ const loginLogic = async (loginForm, tenantId, isAuthorized, router, redirectPat
 
     // 校验登录状态
     if (res.code === 0) {
-      ElMessage.success('登录成功')
+      // ElMessage.success('登录成功')
       isLoggedIn.value = true
       // 存储登录状态
       localStorage.setItem('isLoggedIn', 'true')
@@ -180,7 +181,10 @@ const loginLogic = async (loginForm, tenantId, isAuthorized, router, redirectPat
       })
 
       // 获取字典数据
-      await getDictData();
+      await refreshDictData();
+
+      // 获取角色路由数据
+      await refreshRoleRouteData();
 
       // 登录成功后,跳转到指定的页面
       router.push(redirectPath)
@@ -207,16 +211,6 @@ const loginLogic = async (loginForm, tenantId, isAuthorized, router, redirectPat
   }
 }
 
-// 添加获取字典数据的函数
-const getDictData = async () => {
-  try {
-    // 使用新的字典管理工具更新所有字典
-    await updateAllDictData();
-  } catch (error) {
-    console.error('获取字典数据失败:', error);
-  }
-};
-
 // 本地存储管理
 const loadLoginData = (loginData) => {
   const storedTenantName = localStorage.getItem('tenantName')
@@ -241,6 +235,13 @@ const loadLoginData = (loginData) => {
 const checkLoginStatus = (router, redirectPath) => {
   const storedStatus = localStorage.getItem('isLoggedIn')
   if (storedStatus === 'true') {
+
+    // 获取字典数据
+    refreshDictData();
+
+    // 获取角色路由数据
+    refreshRoleRouteData();
+
     router.push(redirectPath)
     return true
   }
@@ -329,7 +330,7 @@ const autoLogin = async (tenantName, username, password, router, redirectPath) =
 }
 
 // 退出登录逻辑
-const logoutLogic = async (router) => {
+const logoutLogic = async (router, redirectPath = '/login') => {
   try {
     // 调用logout API 退出登录
     const logoutRes = await logout()
@@ -338,6 +339,8 @@ const logoutLogic = async (router) => {
     localStorage.removeItem('token')
     localStorage.removeItem('isLoggedIn')
     localStorage.removeItem('maxCourseSections')
+    localStorage.removeItem(CONFIG.USER_ROLE_ROUTE_KEY)
+    localStorage.removeItem(CONFIG.USER_ROLE_ROUTE_KEY + CONFIG.EXPIRY_SUFFIX)
     
     // 检查是否勾选了记住我
     const rememberMe = localStorage.getItem('rememberMe') === 'true'
@@ -350,7 +353,7 @@ const logoutLogic = async (router) => {
     }
 
     // 跳转到登录页面
-    router.push({ path: '/login' })
+    router.push({ path: redirectPath })
   } catch (error) {
     console.error('退出登录失败:', error)
     // API调用失败,也清空本地存储
@@ -372,6 +375,29 @@ const logoutLogic = async (router) => {
   }
 }
 
+
+// 添加获取字典数据的函数
+const refreshDictData = async () => {
+  try {
+    // 使用典管理工具更新所有字典
+    await refreshAllDictData();
+  } catch (error) {
+    console.error('获取字典数据失败:', error);
+  }
+};
+
+
+// 添加获取角色路由数据的函数
+const refreshRoleRouteData = async () => {
+  try {
+
+    // 使用角色路由工具更新角色路由
+    await refreshRoleRoute();
+  } catch (error) {
+    console.error('获取角色路由数据失败:', error);
+  }
+};
+
 export {
   createLoginData,
   createVerificationCodeLogic,

+ 73 - 0
src/utils/roleUtils.js

@@ -0,0 +1,73 @@
+import {getRoleRoute} from "@/api/system/role.js";
+
+/**
+ * 角色路由缓存配置
+ */
+const CONFIG = {
+    USER_ROLE_ROUTE_KEY: 'user_role_route',
+    EXPIRY_SUFFIX: '_expiry',
+    EXPIRY_TIME: 24 * 60 * 60 * 1000 // 24小时
+}
+
+/**
+ * 刷新角色路由缓存
+ * @returns {Promise<Array>} - 角色路由数组
+ */
+export const refreshRoleRoute = async () => {
+    try {
+
+        // 检查缓存是否存在且未过期
+        const cachedData = localStorage.getItem(CONFIG.USER_ROLE_ROUTE_KEY);
+        if (cachedData && !isCacheExpired(CONFIG.USER_ROLE_ROUTE_KEY)) {
+            return JSON.parse(cachedData);
+        }
+
+        // 缓存不存在或已过期,从服务器获取
+        const res = await getRoleRoute();
+        if (res.code === 0) {
+
+            // 存储到localStorage并设置过期时间
+            setCacheWithExpiry(CONFIG.USER_ROLE_ROUTE_KEY, res.data);
+
+            return res.data;
+        } else {
+            throw new Error('获取角色路由数据失败');
+        }
+    } catch (error) {
+        console.error(`获取角色路由数据失败:`, error);
+        return [];
+    }
+};
+
+
+
+/**
+ * 检查缓存是否过期
+ * @param {string} key - 缓存键
+ * @returns {boolean} - 是否已过期
+ */
+const isCacheExpired = (key) => {
+    const expiryKey = `${key}${CONFIG.EXPIRY_SUFFIX}`;
+    const expiryTime = localStorage.getItem(expiryKey);
+
+    if (!expiryTime) return true;
+
+    return Date.now() > parseInt(expiryTime);
+};
+
+/**
+ * 设置带过期时间的缓存
+ * @param {string} key - 缓存键
+ * @param {any} data - 要缓存的数据
+ */
+const setCacheWithExpiry = (key, data) => {
+    localStorage.setItem(key, JSON.stringify(data));
+
+    // 设置过期时间
+    const expiryKey = `${key}${CONFIG.EXPIRY_SUFFIX}`;
+    const expiryTime = Date.now() + CONFIG.EXPIRY_TIME;
+    localStorage.setItem(expiryKey, expiryTime.toString());
+};
+
+
+export { CONFIG };

+ 2 - 1
src/views/AiCourseLogin.vue

@@ -108,6 +108,7 @@ import {
   checkLoginStatus,
   generateRules
 } from '@/utils/loginUtils.js'
+import {aiCourseRoutes} from "@/router/index.js";
 
 const router = useRouter()
 
@@ -165,7 +166,7 @@ const handleLogin = async () => {
       }
 
       // 调用登录逻辑
-      await loginLogic(loginData.value.loginForm, tenantId, isAuthorized, router, '/aiCourseHome')
+      await loginLogic(loginData.value.loginForm, tenantId, isAuthorized, router, aiCourseRoutes.home)
     }
   })
 }

+ 4 - 3
src/views/BlocklyLogin.vue

@@ -109,6 +109,7 @@ import {
   checkLoginStatus,
   generateRules
 } from '@/utils/loginUtils.js'
+import {blocklyRoutes} from "@/router/index.js";
 
 const router = useRouter()
 
@@ -169,7 +170,7 @@ const handleLogin = async () => {
       }
 
       // 调用登录逻辑
-      await loginLogic(loginData.value.loginForm, tenantId, isAuthorized, router, '/blocklyHome')
+      await loginLogic(loginData.value.loginForm, tenantId, isAuthorized, router, blocklyRoutes.home)
     }
   })
 }
@@ -185,11 +186,11 @@ onMounted(() => {
     loginData.value.loginForm.tenantName = tenantNameQuery.value = tenantName
   } else if (Object.keys(router.currentRoute.value.query).length > 0) {
     // 其他参数,重定向到登录页
-    router.replace('/blockly-login')
+    router.replace(blocklyRoutes.login)
   }
 
   // 检查登录状态,如果已登录则直接跳转到首页
-  checkLoginStatus(router, '/blocklyHome')
+  checkLoginStatus(router, blocklyRoutes.home)
 
   const handleKeyPress = (event) => {
     // 检查是否按下回车键(keyCode 13)

+ 2 - 1
src/views/Login.vue

@@ -109,6 +109,7 @@ import {
   checkLoginStatus,
   generateRules
 } from '@/utils/loginUtils.js'
+import {homeRoutes} from "@/router/index.js";
 
 const router = useRouter()
 
@@ -163,7 +164,7 @@ const handleLogin = async () => {
       }
 
       // 调用登录逻辑
-      await loginLogic(loginData.value.loginForm, tenantId, isAuthorized, router, '/home')
+      await loginLogic(loginData.value.loginForm, tenantId, isAuthorized, router, homeRoutes.home)
     }
   })
 }

+ 2 - 1
src/views/laboratory/ExperimentalTheme.vue

@@ -112,6 +112,7 @@ import logoutIcon from '@/assets/icon/logout.png'
 import { logoutLogic } from '@/utils/loginUtils.js'
 // 实验课主题接口
 import { getThemeExperimentList } from '@/api/laboratory/index.js'
+import {aiCourseRoutes} from "@/router/index.js";
 
 // 常量定义
 const CONSTANTS = {
@@ -259,7 +260,7 @@ const goToHomePage = () => {
 
 // 退出登录
 const LogoutClick = async () => {
-  await logoutLogic(router)
+  await logoutLogic(router, aiCourseRoutes.login)
 }
 
 // 存储事件处理函数

+ 2 - 1
src/views/programming/ProgrammingGame.vue

@@ -92,6 +92,7 @@ import rightbtn from '@/assets/programming/rightbtn.png'
 import logoutIcon from '@/assets/icon/logout.png'
 // 退出登录
 import { logoutLogic } from '@/utils/loginUtils.js'
+import {blocklyRoutes} from "@/router/index.js";
 
 
 // 创建路由实例
@@ -223,7 +224,7 @@ const goToHomePage = () => {
 
 // 退出登录
 const LogoutClick = async () => {
-  await logoutLogic(router)
+  await logoutLogic(router, blocklyRoutes.login)
 }
 
 onMounted(() => {