fix:调试后端菜单数据问题

This commit is contained in:
liangdong
2025-12-31 20:50:58 +08:00
parent f22b0dfcbc
commit bd2a143db8
3 changed files with 133 additions and 115 deletions

View File

@@ -35,7 +35,7 @@
@menu-select="handleTopMenuSelect" @menu-select="handleTopMenuSelect"
/> />
<!-- 右侧用户的内容 --> <!-- 右侧用户的内容 -->
<rightMenuGroup /> <rightMenuGroup @on-stage-manage="onStageManage" />
</el-header> </el-header>
<el-main> <el-main>
<!-- <card-item :list="[1,2,3,4,5,6]"/> --> <!-- <card-item :list="[1,2,3,4,5,6]"/> -->
@@ -98,9 +98,14 @@ const topLevelMenuList = computed(() => {
return menuList.value.map((item) => { return menuList.value.map((item) => {
const { children, ...rest } = item; const { children, ...rest } = item;
return rest; return rest;
}); }).filter(itv=>itv.name !== 'stage');
}); });
// 后台管理点击获取列表
const onStageManage = () =>{
selectedTopMenu.value = '/stage';
}
const topTitle = computed(() => { const topTitle = computed(() => {
return ( return (
topLevelMenuList.value.find((path) => path.path === selectedTopMenu.value) topLevelMenuList.value.find((path) => path.path === selectedTopMenu.value)

View File

@@ -3,7 +3,7 @@
<div class="user-header-container"> <div class="user-header-container">
<div class="action-group"> <div class="action-group">
<div class="action-item"> <div class="action-item">
<el-icon :size="16"><Monitor /></el-icon> <el-icon :size="16" @click="onStageManage"><Monitor /></el-icon>
</div> </div>
<div class="action-item"> <div class="action-item">
<el-badge is-dot class="notice-badge"> <el-badge is-dot class="notice-badge">
@@ -53,6 +53,8 @@ defineOptions({ name: "RightMenuGroup" });
const userStore = useUserStore(); const userStore = useUserStore();
const tokenManager = TokenManager.getInstance(); const tokenManager = TokenManager.getInstance();
const router = useRouter(); const router = useRouter();
const emits = defineEmits(["on-stage-manage"]);
const handleCommand = (command: string) => { const handleCommand = (command: string) => {
if (command === "logout") { if (command === "logout") {
// 退出登录 // 退出登录
@@ -61,6 +63,11 @@ const handleCommand = (command: string) => {
} }
}; };
const onStageManage = () =>{
emits('on-stage-manage')
}
// 获取当前的用户的数据信息 // 获取当前的用户的数据信息
const userInfo = computed(() => { const userInfo = computed(() => {
return userStore.userInfo; return userStore.userInfo;

View File

@@ -1,30 +1,34 @@
import { createWebHistory, createRouter, type RouteRecordRaw } from 'vue-router' import {
import { useUserStore } from '@/store' createWebHistory,
import { getRouteMenus } from '@/api' createRouter,
import TokenManager from '@/utils/storage'; type RouteRecordRaw,
} from "vue-router";
import { useUserStore } from "@/store";
import { getRouteMenus } from "@/api";
import TokenManager from "@/utils/storage";
import Login from '@/pages/Login/index.vue'; import Login from "@/pages/Login/index.vue";
import HomeView from '@/pages/Layout/index.vue'; import HomeView from "@/pages/Layout/index.vue";
const tokenManager = TokenManager.getInstance(); const tokenManager = TokenManager.getInstance();
// 基础路由(不需要权限验证) // 基础路由(不需要权限验证)
const constantRoutes: RouteRecordRaw[] = [ const constantRoutes: RouteRecordRaw[] = [
{ {
path: '/login', path: "/login",
name: 'Login', name: "Login",
component: Login, component: Login,
meta: { meta: {
title: '登录', title: "登录",
requiresAuth: false requiresAuth: false,
} },
}, },
] ];
// 动态路由(需要权限验证) // 动态路由(需要权限验证)
const asyncRoutes: RouteRecordRaw[] = [ const asyncRoutes: RouteRecordRaw[] = [
{ {
path: '/', path: "/",
name: 'Layout', name: "Layout",
component: HomeView, component: HomeView,
// redirect: '/home', // redirect: '/home',
meta: { meta: {
@@ -32,61 +36,81 @@ const asyncRoutes: RouteRecordRaw[] = [
}, },
children: [] as RouteRecordRaw[], children: [] as RouteRecordRaw[],
}, },
] ];
const router = createRouter({ const router = createRouter({
history: createWebHistory(), history: createWebHistory(),
routes: [...constantRoutes, ...asyncRoutes], routes: [...constantRoutes, ...asyncRoutes],
}) });
// 白名单路由(不需要登录即可访问) // 白名单路由(不需要登录即可访问)
const whiteList = ['/login'] const whiteList = ["/login"];
// 1. 预先加载所有可能的页面组件 // 1. 预先加载所有可能的页面组件
// 这里的路径模式要覆盖到你所有的业务组件 // 这里的路径模式要覆盖到你所有的业务组件
const modules = import.meta.glob('@/pages/**/*.vue') const modules = import.meta.glob("@/pages/**/*.vue");
const loadComponent = (componentPath: string) => { const loadComponent = (componentPath: string) => {
if (!componentPath) return null if (!componentPath) return null;
// 统一路径格式处理 // 统一路径格式处理
let fullPath = '' let fullPath = "";
if (componentPath.startsWith('@/')) { if (componentPath.startsWith("@/")) {
fullPath = componentPath fullPath = componentPath;
} else if (componentPath.includes('/')) { } else if (componentPath.includes("/")) {
// 补全路径,确保以 @/pages 开头 // 补全路径,确保以 @/pages 开头
fullPath = componentPath.startsWith('pages/') ? `@/${componentPath}` : `@/pages/${componentPath}/index.vue` fullPath = componentPath.startsWith("pages/")
? `@/${componentPath}`
: `@/pages/${componentPath}/index.vue`;
} else { } else {
// 补全 index.vue // 补全 index.vue
fullPath = `@/pages/${componentPath}/index.vue` fullPath = `@/pages/${componentPath}/index.vue`;
} }
// 重点:将 @/ 转换为 /src/,因为 glob 默认生成的 key 是相对于项目根目录的 // 重点:将 @/ 转换为 /src/,因为 glob 默认生成的 key 是相对于项目根目录的
const key = fullPath.replace('@/', '/src/') const key = fullPath.replace("@/", "/src/");
// 从 modules 中查找对应的导入函数 // 从 modules 中查找对应的导入函数
if (modules[key]) { if (modules[key]) {
return modules[key] return modules[key];
} else { } else {
console.error(`未找到组件文件: ${key}. 请检查路径或大小写。`) console.error(`未找到组件文件: ${key}. 请检查路径或大小写。`);
return null return null;
} }
} };
// 将后端返回的路由数据转换为 Vue Router 路由 // 将后端返回的路由数据转换为 Vue Router 路由
const transformRoutes = (routes: any[], parentCode: string = ''): RouteRecordRaw[] => { const transformRoutes = (
routes: any[],
parentCode: string = ""
): RouteRecordRaw[] => {
console.log("transformRoutes", routes);
return routes.flatMap((route) => { return routes.flatMap((route) => {
const fullCode = parentCode ? `${parentCode}/${route.code}` : route.code; const fullCode = parentCode ? `${parentCode}/${route.code}` : route.code;
// 如果当前路由有子路由,说明它是一个路由前缀,不需要组件 // 如果当前路由有子路由,说明它是一个路由前缀,不需要组件
if (route.children && route.children.length > 0) { if (route.children && route.children.length > 0) {
// 将子路由的路径加上当前路由的前缀,然后递归处理 const childRoutes = transformRoutes(route.children, fullCode);
return transformRoutes(route.children, fullCode); if (route.code) {
// 创建一个父路由,将子路由作为其 children
const parentRoute: RouteRecordRaw = {
path: `/${fullCode}`,
name: route.code,
meta: {
title: route.name,
icon: route.icon,
...route.meta,
},
children: childRoutes,
};
return parentRoute;
} else {
return childRoutes;
}
} else { } else {
// 叶子节点才需要组件和路由配置 // 叶子节点才需要组件和路由配置
const component = fullCode ? loadComponent(fullCode) : undefined; const component = fullCode ? loadComponent(fullCode) : undefined;
const routeRecord: any = { const routeRecord: any = {
path: route.code, path: route.code,
name: route.code, name: route.code,
@@ -95,7 +119,7 @@ const transformRoutes = (routes: any[], parentCode: string = ''): RouteRecordRaw
icon: route.icon, icon: route.icon,
...route.meta, ...route.meta,
}, },
} };
if (component) { if (component) {
routeRecord.component = component; routeRecord.component = component;
@@ -104,135 +128,117 @@ const transformRoutes = (routes: any[], parentCode: string = ''): RouteRecordRaw
return routeRecord as RouteRecordRaw; return routeRecord as RouteRecordRaw;
} }
}); });
} };
// 添加动态路由 // 添加动态路由
const addDynamicRoutes = async () => { const addDynamicRoutes = async () => {
const userStore = useUserStore() const userStore = useUserStore();
// 如果路由已加载,直接返回 // 如果路由已加载,直接返回
if (userStore.isRoutesLoaded) { if (userStore.isRoutesLoaded) {
return return;
} }
try { try {
// TODO:从后端获取路由菜单数据 (这边需要区分 后台的菜单 和用户的菜单) // 从后端获取路由菜单数据 (这边需要区分 后台的菜单 和用户的菜单)
let response:any; let allRoutes:any[] = [];
if (userStore.isBackendUser) { if (userStore.isBackendUser) {
const backendResponse = await getRouteMenus(); const backendResponse = await getRouteMenus();
response = [{ allRoutes = [
code: 'stage', {
name: '后台管理', code: "stage",
icon: '', name: "后台管理",
children: backendResponse, icon: "",
}]; children: backendResponse,
}else{ },
];
} else {
// TODO:获取用户端的数据信息
// response = await getUserMenus(); // response = await getUserMenus();
response = []; allRoutes = [];
} }
if (response) { if (allRoutes) {
const processRoutes = (routes: any[], prefix: string = ''): RouteRecordRaw[] => {
return routes.flatMap(route => {
const currentPath = prefix ? `${prefix}/${route.code}` : route.code;
if (route.children && route.children.length > 0) {
// 如果有子路由,递归处理并添加当前路径作为前缀
return processRoutes(route.children, currentPath);
} else {
// 叶子节点,创建路由记录
const component = loadComponent(currentPath);
return {
path: route.code,
name: route.code,
component: component || HomeView, // 使用Layout的组件
meta: {
title: route.name,
icon: route.icon,
...route.meta,
}
} as RouteRecordRaw;
}
});
};
// 转换路由数据 // 转换路由数据
// const dynamicRoutes = transformRoutes(Array.isArray(response) ? response : [response]) const dynamicRoutes = transformRoutes(
const dynamicRoutes = processRoutes(Array.isArray(response) ? response : [response]) Array.isArray(allRoutes) ? allRoutes : [allRoutes]
);
// 将动态路由添加到 Layout 的 children 中 // 将动态路由添加到 Layout 的 children 中
const layoutRoute = router.getRoutes().find(route => route.name === 'Layout') const layoutRoute = router
.getRoutes()
.find((route) => route.name === "Layout");
if (layoutRoute) { if (layoutRoute) {
dynamicRoutes.forEach(route => { dynamicRoutes.forEach((route) => {
router.addRoute('Layout', route) router.addRoute("Layout", route);
}) });
} else { } else {
// 如果找不到 Layout 路由,直接添加到根路由 // 如果找不到 Layout 路由,直接添加到根路由
dynamicRoutes.forEach(route => { dynamicRoutes.forEach((route) => {
router.addRoute(route) router.addRoute(route);
}) });
} }
console.log('Layout route:', router.getRoutes())
// 保存路由数据到 store // 保存路由数据到 store
userStore.setRoutes(response) userStore.setRoutes(dynamicRoutes);
// 标记路由已加载 // 标记路由已加载
userStore.isRoutesLoaded = true userStore.isRoutesLoaded = true;
} }
} catch (error) { } catch (error) {
// 如果获取路由失败,清除用户数据并跳转到登录页 // 如果获取路由失败,清除用户数据并跳转到登录页
userStore.clearUserData() userStore.clearUserData();
router.push('/login') router.push("/login");
} }
} };
// 路由导航守卫 // 路由导航守卫
router.beforeEach(async (to, _from, next) => { router.beforeEach(async (to, _from, next) => {
const userStore = useUserStore() const userStore = useUserStore();
const accessToken = tokenManager.getToken('accessToken'); const accessToken = tokenManager.getToken("accessToken");
// 获取 token // 获取 token
const token = accessToken || userStore.token const token = accessToken || userStore.token;
// 如果已登录,更新 store 中的 token // 如果已登录,更新 store 中的 token
if (token) { if (token) {
userStore.setToken(token) userStore.setToken(token);
} }
// 判断是否在白名单中 // 判断是否在白名单中
if (whiteList.includes(to.path)) { if (whiteList.includes(to.path)) {
// 如果在白名单中且已登录,重定向到首页 // 如果在白名单中且已登录,重定向到首页
if (token) { if (token) {
next('/') next("/");
} else { } else {
next() next();
} }
return return;
} }
// 需要登录验证 // 需要登录验证
if (!token) { if (!token) {
// 未登录,重定向到登录页 // 未登录,重定向到登录页
next({ next({
path: '/login', path: "/login",
query: { redirect: to.fullPath }, // 保存当前路径,登录后可以跳转回来 query: { redirect: to.fullPath }, // 保存当前路径,登录后可以跳转回来
}) });
return return;
} }
// 已登录,检查路由是否已加载 // 已登录,检查路由是否已加载
if (!userStore.isRoutesLoaded) { if (!userStore.isRoutesLoaded) {
try { try {
// 加载动态路由 // 加载动态路由
await addDynamicRoutes() await addDynamicRoutes();
// 路由加载完成后,重新导航到目标路由 // 路由加载完成后,重新导航到目标路由
next({ ...to, replace: true }) next({ ...to, replace: true });
} catch (error) { } catch (error) {
console.error('Route loading error:', error) console.error("Route loading error:", error);
next('/login') next("/login");
} }
} else { } else {
// 路由已加载,直接放行 // 路由已加载,直接放行
next() next();
} }
}) });
export default router export default router;