# Platform 路由集成指南

## 快速开始

### TL;DR

1. **shared 提供**：路由生成工具、菜单处理、设备检测
2. **platform 定义**：布局组件映射、视图加载器
3. **platform 生成**：调用 `generateRoutes()` 生成自己的路由

```typescript
// 1. 导入工具
import { generateRoutes, type RouteGeneratorConfig } from 'shared/utils/route'
import type { SysMenu } from 'shared/types'

// 2. 配置生成器
const config: RouteGeneratorConfig = {
    layoutMap: { /* 布局映射 */ },
    viewLoader: (path) => { /* 组件加载 */ }
}

// 3. 生成路由
const routes = generateRoutes(menus, config)

// 4. 添加到路由
routes.forEach(route => router.addRoute(route))
```

## 架构说明

Platform 使用 shared 提供的路由生成工具来动态生成路由。架构如下：

```
┌─────────────────────────────────────────────────────────────┐
│                          shared                              │
│  ┌───────────────────────────────────────────────────────┐ │
│  │  utils/route/route-generator.ts                       │ │
│  │  - generateRoutes()        路由生成                   │ │
│  │  - buildMenuTree()         菜单树构建                 │ │
│  │  - filterMenusByPermissions() 权限过滤                │ │
│  │  - findMenuByPath()        路径查找                   │ │
│  │  - getMenuPath()           面包屑路径                 │ │
│  │  - getFirstAccessibleMenuUrl() 首页跳转               │ │
│  └───────────────────────────────────────────────────────┘ │
│  ┌───────────────────────────────────────────────────────┐ │
│  │  utils/device.ts                                      │ │
│  │  - getDeviceType()         设备类型检测               │ │
│  │  - isMobile()             移动端判断                  │ │
│  │  - useDevice()            响应式 Hook                 │ │
│  └───────────────────────────────────────────────────────┘ │
│  ┌───────────────────────────────────────────────────────┐ │
│  │  types/sys/menu.ts & types/enums.ts                   │ │
│  │  - SysMenu                菜单接口                    │ │
│  │  - ViewType               视图类型枚举                │ │
│  └───────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
                              ↓
        ┌─────────────────────────────────────┐
        │  Module Federation (远程模块)        │
        │  - shared/utils/route               │
        │  - shared/utils/device              │
        │  - shared/types                     │
        └─────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────┐
│                        platform                              │
│  ┌───────────────────────────────────────────────────────┐ │
│  │  src/router/index.ts                                  │ │
│  │  1. 定义布局组件映射 (platformLayoutMap)             │ │
│  │  2. 定义视图组件加载器 (viewLoader)                  │ │
│  │  3. 调用 generateRoutes() 生成动态路由               │ │
│  │  4. 添加到 Vue Router                                │ │
│  └───────────────────────────────────────────────────────┘ │
│  ┌───────────────────────────────────────────────────────┐ │
│  │  src/layouts/                                         │ │
│  │  - SidebarLayout.vue      侧边栏布局                  │ │
│  │  - NavigationLayout.vue   导航布局（如需）            │ │
│  │  - BasicLayout.vue        基础布局（如需）            │ │
│  └───────────────────────────────────────────────────────┘ │
│  ┌───────────────────────────────────────────────────────┐ │
│  │  src/views/                                           │ │
│  │  - 各种页面组件                                       │ │
│  └───────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```

## 使用步骤

### 1. 在 router/index.ts 中配置路由生成器

```typescript
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
import { generateRoutes, type RouteGeneratorConfig } from 'shared/utils/route'
import type { SysMenu } from 'shared/types'
import { SidebarLayout } from '../layouts'

// 1. 定义布局组件映射
const platformLayoutMap: Record<string, () => Promise<any>> = {
    'SidebarLayout': () => Promise.resolve({ default: SidebarLayout }),
    'NavigationLayout': () => Promise.resolve({ default: SidebarLayout }), // 可复用或自定义
    'BasicLayout': () => Promise.resolve({ default: SidebarLayout }),
}

// 2. 定义视图组件加载器
const VIEW_MODULES = import.meta.glob('../views/**/*.vue')

function viewLoader(componentPath: string): (() => Promise<any>) | null {
    // 将后台路径转换为实际路径
    let path = componentPath
    if (!path.startsWith('../')) {
        if (!path.startsWith('/')) {
            path = '/' + path
        }
        path = '../views' + path
    }
    if (!path.endsWith('.vue')) {
        path += '.vue'
    }
    
    const loader = VIEW_MODULES[path]
    return loader ? (loader as () => Promise<any>) : null
}

// 3. 创建路由生成器配置
const routeConfig: RouteGeneratorConfig = {
    layoutMap: platformLayoutMap,
    viewLoader,
    staticRoutes: routes, // 可选：静态路由
    notFoundComponent: () => import('../views/public/404.vue') // 可选
}

// 4. 动态添加路由的函数
export function addDynamicRoutes(menus: SysMenu[]) {
    const dynamicRoutes = generateRoutes(menus, routeConfig)
    
    dynamicRoutes.forEach(route => {
        router.addRoute(route)
    })
    
    console.log('✅ 动态路由已添加', dynamicRoutes.length, '个')
}
```

### 2. 在应用初始化时调用

```typescript
// main.ts 或登录成功后
import { addDynamicRoutes } from './router'
import { menuAPI } from 'shared/api'

// 获取用户菜单
const response = await menuAPI.getUserMenus()
if (response.success && response.data) {
    // 添加动态路由
    addDynamicRoutes(response.data)
    
    // 跳转到首页或指定页面
    router.push('/home')
}
```

## 菜单数据格式

```typescript
interface SysMenu {
    menuID: string          // 菜单ID，作为路由 name
    parentID?: string       // 父菜单ID，'0' 表示根菜单
    name: string           // 菜单名称
    url?: string           // 路由路径，如 '/user/profile'
    type: ViewType         // 视图类型（0=目录 1=菜单 2=按钮 3=页面）
    icon?: string          // 图标
    component?: string     // 组件路径，如 'user/profile/ProfileView'
    layout?: string        // 布局名称，如 'SidebarLayout'
    orderNum?: number      // 排序号
    permission?: string    // 权限标识
    hidden?: boolean       // 是否隐藏
    children?: SysMenu[]   // 子菜单
}

enum ViewType {
    NAVBAR = 0,     // 导航栏/目录
    SIDEBAR = 1,    // 侧边栏/菜单
    BUTTON = 2,     // 按钮（权限控制，不生成路由）
    ROUTE = 3       // 空白页/路由页面
}
```

## 示例菜单数据

```typescript
const menus: SysMenu[] = [
    {
        menuID: 'user-center',
        parentID: '0',
        name: '用户中心',
        url: '/user',
        type: ViewType.NAVBAR,
        icon: 'User',
        layout: 'SidebarLayout',
        orderNum: 1,
        children: [
            {
                menuID: 'user-profile',
                parentID: 'user-center',
                name: '个人信息',
                url: '/user/profile',
                type: ViewType.SIDEBAR,
                component: 'user/profile/ProfileView',
                orderNum: 1
            },
            {
                menuID: 'user-settings',
                parentID: 'user-center',
                name: '账号设置',
                url: '/user/settings',
                type: ViewType.SIDEBAR,
                component: 'user/settings/SettingsView',
                orderNum: 2
            }
        ]
    }
]
```

## 布局组件要求

布局组件必须包含 `<router-view />` 用于渲染子路由：

```vue
<template>
    <div class="sidebar-layout">
        <aside class="sidebar">
            <!-- 侧边栏内容 -->
        </aside>
        <main class="content">
            <router-view />  <!-- 重要！用于渲染页面组件 -->
        </main>
    </div>
</template>
```

## 响应式布局（可选）

如果需要移动端适配，可以使用 shared 的设备检测工具：

```typescript
import { getDeviceType, DeviceType } from 'shared/utils/device'

const deviceType = getDeviceType()
if (deviceType === DeviceType.MOBILE) {
    // 移动端逻辑
}
```

## 工具方法说明

所有工具方法从 `shared/utils/route` 导入：

```typescript
import { 
    generateRoutes,
    buildMenuTree,
    filterMenusByPermissions,
    findMenuByPath,
    getMenuPath,
    getFirstAccessibleMenuUrl,
    type RouteGeneratorConfig
} from 'shared/utils/route'

import type { SysMenu } from 'shared/types'
import { ViewType } from 'shared/types/enums'
```

### generateRoutes(menus, config)
根据菜单生成路由配置数组

**参数：**
- `menus: SysMenu[]` - 菜单列表
- `config: RouteGeneratorConfig` - 路由生成器配置

**返回：** `RouteRecordRaw[]`

### buildMenuTree(menus, staticRoutes?)
将扁平菜单列表转换为树形结构

**参数：**
- `menus: SysMenu[]` - 菜单列表
- `staticRoutes?: RouteRecordRaw[]` - 静态路由（可选）

**返回：** `SysMenu[]`

### filterMenusByPermissions(menus, permissions)
根据权限过滤菜单

**参数：**
- `menus: SysMenu[]` - 菜单列表
- `permissions: string[]` - 权限列表

**返回：** `SysMenu[]`

### findMenuByPath(menus, path)
根据路径查找菜单项

**参数：**
- `menus: SysMenu[]` - 菜单列表
- `path: string` - 路由路径

**返回：** `SysMenu | null`

### getMenuPath(menus, targetMenuId)
获取菜单路径数组（用于面包屑导航）

**参数：**
- `menus: SysMenu[]` - 菜单列表
- `targetMenuId: string` - 目标菜单ID

**返回：** `SysMenu[]`

### getFirstAccessibleMenuUrl(menus)
获取第一个可访问的菜单URL（用于登录后跳转）

**参数：**
- `menus: SysMenu[]` - 菜单列表

**返回：** `string | null`

## 注意事项

1. **shared 服务必须先启动**：因为使用 Module Federation，platform 依赖 shared 的远程模块
2. **布局组件必须包含 router-view**：否则子路由无法渲染
3. **组件路径映射**：确保 `viewLoader` 能正确加载组件
4. **静态路由优先**：如果菜单标记为 `__STATIC_ROUTE__`，不会重复生成路由
5. **路由守卫**：记得在 `router.beforeEach` 中添加权限检查

## 调试技巧

1. **查看生成的路由**：
```typescript
console.log('所有路由:', router.getRoutes())
console.log('路由数量:', router.getRoutes().length)
```

2. **查看菜单树结构**：
```typescript
import { buildMenuTree } from 'shared/utils/route'

const tree = buildMenuTree(menus)
console.log('菜单树:', JSON.stringify(tree, null, 2))
```

3. **查看当前路由信息**：
```typescript
console.log('当前路由:', router.currentRoute.value)
console.log('路由路径:', router.currentRoute.value.path)
console.log('路由参数:', router.currentRoute.value.params)
console.log('路由元数据:', router.currentRoute.value.meta)
```

4. **测试菜单查找**：
```typescript
import { findMenuByPath, getMenuPath } from 'shared/utils/route'

// 根据路径查找菜单
const menu = findMenuByPath(menus, '/user/profile')
console.log('找到的菜单:', menu)

// 获取面包屑路径
const breadcrumb = getMenuPath(menus, 'user-profile')
console.log('面包屑:', breadcrumb.map(m => m.name).join(' > '))
```

5. **检查视图组件加载**：
```typescript
// 在 viewLoader 中添加日志
function viewLoader(componentPath: string) {
    console.log('尝试加载组件:', componentPath)
    const path = /* 转换逻辑 */
    const loader = VIEW_MODULES[path]
    console.log('找到的加载器:', loader ? '✅' : '❌')
    return loader ? (loader as () => Promise<any>) : null
}
```
