# 动态路由代码示例

## 📦 简化版代码示例

### 1. 路由守卫（简化版）

```typescript
// src/permission.ts (简化版)

router.beforeEach(async (to, from, next) => {
  // 1. 检查是否已登录
  if (getToken()) {
    // 2. 检查用户信息是否已加载
    if (useUserStore().roles.length === 0) {
      // 3. 获取用户信息
      await useUserStore().getInfo();
      
      // 4. 生成动态路由
      const routes = await usePermissionStore().generateRoutes();
      
      // 5. 注册路由
      routes.forEach(route => {
        router.addRoute(route);
      });
      
      // 6. 重新跳转
      next({ ...to, replace: true });
    } else {
      next();
    }
  } else {
    next('/login');
  }
});
```

---

### 2. 生成路由（简化版）

```typescript
// src/store/modules/permission.ts (简化版)

const generateRoutes = async () => {
  // 步骤1: 调用后端接口
  const res = await getRouters();
  const menuData = res.data;
  
  // 步骤2: 转换路由格式
  const routes = transformRoutes(menuData);
  
  // 步骤3: 保存到 Store
  setSidebarRouters(routes);
  
  return routes;
};

// 转换函数（简化版）
const transformRoutes = (menuData) => {
  return menuData.map(menu => {
    const route = {
      name: menu.name,
      path: menu.path,
      component: loadComponent(menu.component), // 动态加载组件
      meta: menu.meta,
      children: menu.children ? transformRoutes(menu.children) : undefined
    };
    
    // 处理特殊组件
    if (menu.component === 'Layout') {
      route.component = Layout;
    }
    
    return route;
  });
};

// 加载组件（简化版）
const loadComponent = (componentPath) => {
  // componentPath: 'system/user/index'
  // 返回: () => import('@/views/system/user/index.vue')
  return () => import(`@/views/${componentPath}.vue`);
};
```

---

### 3. 后端返回数据示例

```json
// GET /system/menu/getRouters 返回的数据

[
  {
    "name": "System",
    "path": "/system",
    "component": "Layout",
    "redirect": "/system/user",
    "alwaysShow": true,
    "meta": {
      "title": "系统管理",
      "icon": "system"
    },
    "children": [
      {
        "name": "User",
        "path": "user",
        "component": "system/user/index",
        "meta": {
          "title": "用户管理",
          "icon": "user"
        }
      }
    ]
  },
  {
    "name": "Platform",
    "path": "/platform",
    "component": "Layout",
    "redirect": "/platform/carousel",
    "alwaysShow": true,
    "meta": {
      "title": "平台装修",
      "icon": "system"
    },
    "children": [
      {
        "name": "Carousel",
        "path": "carousel",
        "component": "platform/decoration/carousel/index",
        "meta": {
          "title": "轮播广告",
          "icon": "component"
        }
      }
    ]
  }
]
```

---

### 4. 转换后的路由对象示例

```typescript
// 转换后的路由对象（Vue Router 格式）

[
  {
    name: "System",
    path: "/system",
    component: Layout, // 已转换为组件对象
    redirect: "/system/user",
    alwaysShow: true,
    meta: {
      title: "系统管理",
      icon: "system"
    },
    children: [
      {
        name: "User",
        path: "user",
        component: () => import('@/views/system/user/index.vue'), // 动态导入
        meta: {
          title: "用户管理",
          icon: "user"
        }
      }
    ]
  },
  {
    name: "Platform",
    path: "/platform",
    component: Layout,
    redirect: "/platform/carousel",
    alwaysShow: true,
    meta: {
      title: "平台装修",
      icon: "system"
    },
    children: [
      {
        name: "Carousel",
        path: "carousel",
        component: () => import('@/views/platform/decoration/carousel/index.vue'),
        meta: {
          title: "轮播广告",
          icon: "component"
        }
      }
    ]
  }
]
```

---

### 5. 注册路由示例

```typescript
// 在路由守卫中注册路由

const routes = await generateRoutes();

routes.forEach(route => {
  // 将路由添加到 Vue Router
  router.addRoute(route);
  
  // 现在可以通过以下方式访问：
  // router.push('/system/user')
  // router.push('/platform/carousel')
});
```

---

### 6. 侧边栏使用路由示例

```vue
<!-- src/layout/components/Sidebar/index.vue -->

<template>
  <el-menu :default-active="activeMenu">
    <template v-for="route in sidebarRouters" :key="route.path">
      <!-- 有子菜单 -->
      <el-sub-menu v-if="route.children" :index="route.path">
        <template #title>
          <el-icon><component :is="route.meta.icon" /></el-icon>
          <span>{{ route.meta.title }}</span>
        </template>
        <el-menu-item 
          v-for="child in route.children" 
          :key="child.path"
          :index="child.path"
          @click="handleMenuClick(child)"
        >
          {{ child.meta.title }}
        </el-menu-item>
      </el-sub-menu>
      
      <!-- 无子菜单 -->
      <el-menu-item v-else :index="route.path" @click="handleMenuClick(route)">
        <el-icon><component :is="route.meta.icon" /></el-icon>
        <span>{{ route.meta.title }}</span>
      </el-menu-item>
    </template>
  </el-menu>
</template>

<script setup>
import { usePermissionStore } from '@/store/modules/permission';
import { useRouter } from 'vue-router';

const permissionStore = usePermissionStore();
const router = useRouter();

// 获取侧边栏路由
const sidebarRouters = computed(() => {
  return permissionStore.getSidebarRoutes();
});

// 菜单点击事件
const handleMenuClick = (route) => {
  router.push(route.path);
};
</script>
```

---

## 🔄 完整流程示例

### 场景：用户登录后访问 `/platform/carousel`

```typescript
// 步骤1: 用户访问 /platform/carousel
router.push('/platform/carousel');

// 步骤2: 路由守卫拦截
router.beforeEach(async (to, from, next) => {
  // to.path = '/platform/carousel'
  
  // 步骤3: 检查 Token
  if (getToken()) {
    // 步骤4: 检查用户信息
    if (useUserStore().roles.length === 0) {
      // 步骤5: 获取用户信息
      await useUserStore().getInfo();
      
      // 步骤6: 调用后端接口
      const res = await getRouters();
      // res.data = [{ name: "Platform", path: "/platform", ... }]
      
      // 步骤7: 转换路由
      const routes = await generateRoutes();
      // routes = [{ name: "Platform", component: Layout, ... }]
      
      // 步骤8: 注册路由
      routes.forEach(route => {
        router.addRoute(route);
      });
      
      // 步骤9: 重新跳转
      next({ path: '/platform/carousel', replace: true });
    } else {
      // 用户信息已加载，直接放行
      next();
    }
  }
});

// 步骤10: 路由匹配成功，显示页面
// <router-view> 渲染 Carousel 组件
```

---

## 📊 数据流转图

```
后端数据库 (sys_menu)
    ↓
后端接口 (/system/menu/getRouters)
    ↓
前端 API (getRouters())
    ↓
路由转换 (filterAsyncRouter)
    ↓
组件加载 (loadView)
    ↓
路由注册 (router.addRoute)
    ↓
侧边栏显示 (sidebarRouters)
    ↓
页面渲染 (<router-view>)
```

---

## 🎯 关键函数说明

### `generateRoutes()`
- **作用**：生成动态路由的主函数
- **输入**：无（内部调用后端接口）
- **输出**：路由对象数组
- **流程**：获取数据 → 转换格式 → 保存到 Store → 返回路由

### `filterAsyncRouter()`
- **作用**：将后端 JSON 转换为 Vue Router 格式
- **输入**：后端返回的路由 JSON 数组
- **输出**：Vue Router 路由对象数组
- **关键**：处理组件加载、递归处理子路由

### `loadView()`
- **作用**：根据路径字符串动态加载 Vue 组件
- **输入**：组件路径字符串（如 `'system/user/index'`）
- **输出**：组件导入函数（如 `() => import('@/views/system/user/index.vue')`）
- **原理**：使用 `import.meta.glob()` 预扫描所有组件文件

### `router.addRoute()`
- **作用**：将路由添加到 Vue Router 实例
- **输入**：路由对象
- **输出**：无
- **效果**：路由立即可用，可以通过 `router.push()` 访问

---

## 💡 常见问题

### Q1: 为什么需要重新跳转？
**A**: 因为路由是异步注册的，需要确保路由已注册后再跳转，否则会 404。

### Q2: 组件路径如何匹配？
**A**: 后端返回的 `component` 路径（如 `'system/user/index'`）必须对应 `views` 目录下的文件路径（`views/system/user/index.vue`）。

### Q3: 路由名称重复会怎样？
**A**: `duplicateRouteChecker` 会检查并报错，可能导致路由冲突和 404。

### Q4: 如何实现权限控制？
**A**: 后端根据用户角色和权限过滤菜单数据，前端只显示后端返回的菜单。

### Q5: 静态路由和动态路由的区别？
**A**: 
- **静态路由**：写在 `router/index.ts` 中，所有用户都能访问
- **动态路由**：从后端获取，根据用户权限动态生成

