288 lines
7.0 KiB
Markdown
288 lines
7.0 KiB
Markdown
|
|
# 路由生成逻辑重构说明
|
|||
|
|
|
|||
|
|
## 重构目标
|
|||
|
|
|
|||
|
|
将 Platform 中的通用路由生成逻辑提取到 shared 包中,使其他 web 服务也可以复用。
|
|||
|
|
|
|||
|
|
## 架构设计
|
|||
|
|
|
|||
|
|
### 职责划分
|
|||
|
|
|
|||
|
|
**Shared 包(shared/utils/route)**
|
|||
|
|
- ✅ 提供通用的路由生成方法
|
|||
|
|
- ✅ 提供视图树构建方法
|
|||
|
|
- ✅ 提供 localStorage 数据加载方法
|
|||
|
|
- ✅ 不依赖特定的 router 实例
|
|||
|
|
- ✅ 不依赖特定的组件加载方式
|
|||
|
|
|
|||
|
|
**Platform 包(platform/src/router/dynamicRoute.ts)**
|
|||
|
|
- ✅ 提供 Platform 特定的布局组件映射
|
|||
|
|
- ✅ 提供 Platform 特定的组件加载器
|
|||
|
|
- ✅ 调用 shared 中的通用方法
|
|||
|
|
- ✅ 将生成的路由添加到 Platform 的 router 实例
|
|||
|
|
|
|||
|
|
## 核心方法
|
|||
|
|
|
|||
|
|
### Shared 包新增方法
|
|||
|
|
|
|||
|
|
#### 1. `generateSimpleRoutes()`
|
|||
|
|
```typescript
|
|||
|
|
export function generateSimpleRoutes(
|
|||
|
|
views: TbSysViewDTO[],
|
|||
|
|
config: RouteGeneratorConfig,
|
|||
|
|
options?: GenerateSimpleRoutesOptions
|
|||
|
|
): RouteRecordRaw[]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**功能**:生成简化的路由配置,适合直接添加到 router
|
|||
|
|
|
|||
|
|
**参数**:
|
|||
|
|
- `views` - 视图列表
|
|||
|
|
- `config` - 路由生成器配置(布局映射、组件加载器等)
|
|||
|
|
- `options` - 可选配置
|
|||
|
|
- `asRootChildren` - 是否作为根路由的子路由
|
|||
|
|
- `iframePlaceholder` - iframe 类型视图的占位组件
|
|||
|
|
- `verbose` - 是否启用详细日志
|
|||
|
|
|
|||
|
|
**返回**:路由配置数组
|
|||
|
|
|
|||
|
|
#### 2. `loadViewsFromStorage()`
|
|||
|
|
```typescript
|
|||
|
|
export function loadViewsFromStorage(
|
|||
|
|
storageKey?: string,
|
|||
|
|
viewsPath?: string
|
|||
|
|
): TbSysViewDTO[] | null
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**功能**:从 localStorage 加载视图数据
|
|||
|
|
|
|||
|
|
**参数**:
|
|||
|
|
- `storageKey` - localStorage 的 key(默认:'loginDomain')
|
|||
|
|
- `viewsPath` - 视图数据在对象中的路径(默认:'userViews',支持嵌套如 'user.views')
|
|||
|
|
|
|||
|
|
**返回**:视图列表,如果不存在返回 null
|
|||
|
|
|
|||
|
|
### Platform 包简化后的方法
|
|||
|
|
|
|||
|
|
#### 1. `addDynamicRoutes()`
|
|||
|
|
```typescript
|
|||
|
|
export function addDynamicRoutes(views: TbSysViewDTO[]) {
|
|||
|
|
// 使用 shared 中的通用方法生成路由
|
|||
|
|
const routes = generateSimpleRoutes(views, routeConfig, routeOptions)
|
|||
|
|
|
|||
|
|
// 将生成的路由添加到 Platform 的 router
|
|||
|
|
routes.forEach(route => {
|
|||
|
|
router.addRoute('Root', route)
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 2. `loadRoutesFromStorage()`
|
|||
|
|
```typescript
|
|||
|
|
export function loadRoutesFromStorage(): boolean {
|
|||
|
|
// 使用 shared 中的通用方法加载视图数据
|
|||
|
|
const views = loadViewsFromStorage('loginDomain', 'userViews')
|
|||
|
|
|
|||
|
|
if (views) {
|
|||
|
|
addDynamicRoutes(views)
|
|||
|
|
return true
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 使用示例
|
|||
|
|
|
|||
|
|
### 在 Platform 中使用
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
import { loadRoutesFromStorage, addDynamicRoutes } from '@/router/dynamicRoute'
|
|||
|
|
|
|||
|
|
// 从 localStorage 加载并添加路由
|
|||
|
|
loadRoutesFromStorage()
|
|||
|
|
|
|||
|
|
// 或者手动传入视图数据
|
|||
|
|
const views = [...] // 从 API 获取
|
|||
|
|
addDynamicRoutes(views)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 在其他 Web 服务中使用
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
import {
|
|||
|
|
generateSimpleRoutes,
|
|||
|
|
loadViewsFromStorage,
|
|||
|
|
type RouteGeneratorConfig
|
|||
|
|
} from 'shared/utils/route'
|
|||
|
|
import router from './router'
|
|||
|
|
|
|||
|
|
// 1. 配置路由生成器
|
|||
|
|
const config: RouteGeneratorConfig = {
|
|||
|
|
layoutMap: {
|
|||
|
|
'MyLayout': () => import('./layouts/MyLayout.vue')
|
|||
|
|
},
|
|||
|
|
viewLoader: (path) => {
|
|||
|
|
// 自定义组件加载逻辑
|
|||
|
|
return () => import(`./views/${path}.vue`)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 2. 加载视图数据
|
|||
|
|
const views = loadViewsFromStorage()
|
|||
|
|
|
|||
|
|
// 3. 生成路由
|
|||
|
|
if (views) {
|
|||
|
|
const routes = generateSimpleRoutes(views, config, {
|
|||
|
|
asRootChildren: true,
|
|||
|
|
verbose: true
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 4. 添加到 router
|
|||
|
|
routes.forEach(route => {
|
|||
|
|
router.addRoute('Root', route)
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 配置说明
|
|||
|
|
|
|||
|
|
### RouteGeneratorConfig
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
interface RouteGeneratorConfig {
|
|||
|
|
/** 布局组件映射表 */
|
|||
|
|
layoutMap: Record<string, () => Promise<any>>
|
|||
|
|
|
|||
|
|
/** 视图组件加载器 */
|
|||
|
|
viewLoader: (componentPath: string) => (() => Promise<any>) | null
|
|||
|
|
|
|||
|
|
/** 静态路由列表(可选) */
|
|||
|
|
staticRoutes?: RouteRecordRaw[]
|
|||
|
|
|
|||
|
|
/** 404 组件(可选) */
|
|||
|
|
notFoundComponent?: () => Promise<any>
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### GenerateSimpleRoutesOptions
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
interface GenerateSimpleRoutesOptions {
|
|||
|
|
/** 是否作为根路由的子路由(路径去掉前导 /) */
|
|||
|
|
asRootChildren?: boolean
|
|||
|
|
|
|||
|
|
/** iframe 类型视图的占位组件 */
|
|||
|
|
iframePlaceholder?: () => Promise<any>
|
|||
|
|
|
|||
|
|
/** 是否启用详细日志 */
|
|||
|
|
verbose?: boolean
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 优势
|
|||
|
|
|
|||
|
|
### 1. 代码复用
|
|||
|
|
- ✅ 通用逻辑只需维护一份
|
|||
|
|
- ✅ 其他 web 服务可以直接使用
|
|||
|
|
- ✅ 减少重复代码
|
|||
|
|
|
|||
|
|
### 2. 职责清晰
|
|||
|
|
- ✅ Shared 负责通用逻辑
|
|||
|
|
- ✅ 各个服务负责特定配置
|
|||
|
|
- ✅ 易于理解和维护
|
|||
|
|
|
|||
|
|
### 3. 灵活性
|
|||
|
|
- ✅ 通过配置注入实现定制化
|
|||
|
|
- ✅ 支持多种使用方式
|
|||
|
|
- ✅ 易于扩展
|
|||
|
|
|
|||
|
|
### 4. 可测试性
|
|||
|
|
- ✅ 通用方法独立测试
|
|||
|
|
- ✅ 配置化便于 mock
|
|||
|
|
- ✅ 减少耦合
|
|||
|
|
|
|||
|
|
## 迁移指南
|
|||
|
|
|
|||
|
|
如果其他服务想要使用 shared 中的路由生成逻辑:
|
|||
|
|
|
|||
|
|
### 步骤 1:准备配置
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
// 1. 准备布局组件映射
|
|||
|
|
const layoutMap = {
|
|||
|
|
'MainLayout': () => import('./layouts/MainLayout.vue'),
|
|||
|
|
'BlankLayout': () => import('./layouts/BlankLayout.vue')
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 2. 准备组件加载器
|
|||
|
|
const VIEW_MODULES = import.meta.glob('./views/**/*.vue')
|
|||
|
|
const viewLoader = (path: string) => {
|
|||
|
|
const fullPath = `./views/${path}.vue`
|
|||
|
|
return VIEW_MODULES[fullPath] || null
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 3. 组装配置
|
|||
|
|
const config: RouteGeneratorConfig = {
|
|||
|
|
layoutMap,
|
|||
|
|
viewLoader
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 步骤 2:加载视图数据
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
import { loadViewsFromStorage } from 'shared/utils/route'
|
|||
|
|
|
|||
|
|
// 从 localStorage 加载
|
|||
|
|
const views = loadViewsFromStorage('loginDomain', 'userViews')
|
|||
|
|
|
|||
|
|
// 或从 API 加载
|
|||
|
|
// const views = await api.getUserViews()
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 步骤 3:生成并添加路由
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
import { generateSimpleRoutes } from 'shared/utils/route'
|
|||
|
|
|
|||
|
|
if (views) {
|
|||
|
|
const routes = generateSimpleRoutes(views, config, {
|
|||
|
|
asRootChildren: true,
|
|||
|
|
verbose: process.env.NODE_ENV === 'development'
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
routes.forEach(route => {
|
|||
|
|
router.addRoute('YourRootRouteName', route)
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 注意事项
|
|||
|
|
|
|||
|
|
1. **组件加载器**:每个服务的组件路径可能不同,需要自行实现 `viewLoader`
|
|||
|
|
2. **布局组件**:需要提供服务特定的布局组件映射
|
|||
|
|
3. **路由实例**:需要自行将生成的路由添加到服务的 router 实例
|
|||
|
|
4. **iframe 支持**:如果需要支持 iframe 视图,需要提供占位组件
|
|||
|
|
|
|||
|
|
## 文件变更
|
|||
|
|
|
|||
|
|
### 新增文件
|
|||
|
|
- `shared/src/utils/route/route-generator.ts` - 新增通用方法
|
|||
|
|
|
|||
|
|
### 修改文件
|
|||
|
|
- `shared/src/utils/route/index.ts` - 新增导出
|
|||
|
|
- `shared/vite.config.ts` - 新增导出配置
|
|||
|
|
- `shared/EXPOSES.md` - 更新文档
|
|||
|
|
- `platform/src/router/dynamicRoute.ts` - 简化代码
|
|||
|
|
- `platform/src/types/shared.d.ts` - 新增类型声明
|
|||
|
|
|
|||
|
|
## 更新日志
|
|||
|
|
|
|||
|
|
### 2025-12-12
|
|||
|
|
- ✅ 将路由生成通用逻辑提取到 shared
|
|||
|
|
- ✅ 新增 `generateSimpleRoutes` 方法
|
|||
|
|
- ✅ 新增 `loadViewsFromStorage` 方法
|
|||
|
|
- ✅ 简化 Platform 的 dynamicRoute.ts
|
|||
|
|
- ✅ 更新文档和类型声明
|