| name | route-migration |
| description | 专注于 Vue2 传统路由配置到 Vue3 约定式路由的迁移。
触发条件(满足任意一项即触发):
- 任务包含"路由迁移"、"pages.json"、"约定式路由"、"路由配置"等关键词
- 需要从 pages.json 迁移到文件系统路由
- 需要添加 definePage 页面配置
- 需要配置强类型路由系统(TypedRouter)
- 需要更新路由跳转代码(uni.navigateTo → TypedRouter)
- 需要处理多平台路由适配
- 需要查阅路由迁移映射表(docs/prompts/route-migration-map.yml)
- 从 Vue2 项目迁移页面路由
必须协同的技能:
- code-migration(代码迁移)- Vue2 → Vue3 代码写法
- component-migration(组件迁移)- ColorUI → wot-design-uni
- style-migration(样式迁移)- ColorUI 类名 → UnoCSS 原子类
禁止事项:
- 禁止自行决定路由路径(必须查阅映射表)
- 禁止使用 uni.navigateTo 字符串拼接(必须使用 TypedRouter)
- 禁止在 definePage 中添加 name 字段(只使用 style)
- 禁止跳过 definePage 配置(会导致标题显示为 "unibest")
- 禁止不更新映射表状态(完成后必须添加 ✅ 标记)
覆盖场景:几乎所有从 Vue2 迁移到 Vue3 的页面都需要此技能,包括路由配置、页面跳转、参数传递等。
|
| context | fork |
路由系统迁移专家
从 Vue2 项目的 传统 pages.json 路由配置 迁移到 Vue3 项目的 约定式路由系统 + 自动路由生成 现代化路由管理模式。
⚠️ 多技能协同
完整页面迁移:
code-migration + component-migration + style-migration
参阅 .claude/skills/check-trigger.md 了解完整的技能触发检查流程。
核心文档与规范
必读文件:
docs/prompts/route-migration-map.yml - 路由映射表(强制查阅)
src/router/index.ts - 强类型路由工具函数
关键要求:
- 所有路由迁移严格按照映射表执行
- 禁止自行决定路由路径,必须查表
- 完成后在映射表添加 ✅ 标记
常见错误
| ❌ 错误写法 | ✅ 正确写法 | 说明 |
|---|
| 自行决定路由路径 | 查阅映射表执行 | 必须严格按照映射表 |
uni.navigateTo({ url: '/pages/...' }) | TypedRouter.toXxx() | 使用强类型路由 |
| 不看映射表直接迁移 | 先读映射表再迁移 | 映射表是唯一标准 |
| 不标记完成状态 | 完成后添加 ✅ | 必须追踪进度 |
| 页面缺少 definePage 配置 | 所有页面必须添加 definePage | 页面标题和配置必需 |
| 页面标题显示 "unibest" 或空 | 使用 definePage 设置标题 | 影响用户体验 |
definePage({ name: ... }) | 删除 name 字段 | name 字段是非法配置 |
⚠️ 重要工作原则
必须严格遵照 Vue2 到 Vue3 uni-app 路由迁移映射表 执行所有路由迁移任务
映射表文件位置
docs\prompts\route-migration-map.yml
工作流程
- 任务开始前: 必须首先读取完整的路由迁移映射表
- 路径查询: 根据旧路径在映射表中查找对应的新路径
- 严格执行: 所有迁移必须按照映射表的路径执行,不允许自行决定路径
- 进度追踪: 映射表文件本身作为进度表,完成迁移后需要标记状态
- 映射表优先: 如有冲突,一切以映射表为准
映射表使用方法
Read: docs\prompts\route-migration-map.yml
路由架构对比
Vue2 项目路由架构
传统路由配置模式 (pages.json)
├── pages.json # 手动维护的集中式路由配置
│ ├── pages[] # 主包页面配置数组
│ ├── subPackages[] # 分包配置
│ ├── globalStyle{} # 全局样式配置
│ ├── tabBar{} # 底部导航配置
│ └── networkTimeout{} # 网络超时配置
├── 页面文件 # 页面文件与路由配置分离
└── 手动同步 # 需要手动保持文件与配置同步
特点:
- 集中式配置: 所有路由在 pages.json 中手动维护
- 手动同步: 新增页面需同时修改文件和配置
- 配置冗余: 页面路径和标题分散配置
- 维护成本高: 大项目中配置文件过长难以维护
Vue3 项目路由架构
约定式路由系统 (文件系统路由)
├── pages.config.ts # 全局配置和组件自动导入
├── src/pages/ # 页面目录结构即路由结构
│ ├── index/ # /pages/index/index
│ │ └── index.vue # 页面文件
│ ├── login/ # /pages/login/
│ │ ├── login.vue # 登录页面
│ │ └── register.vue # 注册页面
│ └── about/ # /pages/about/
│ ├── about.vue # 关于页面
│ └── components/ # 页面级组件
├── src/pages-sub/ # 分包页面 (自动识别为分包)
├── src/tabbar/ # 底部导航配置
│ └── config.ts # TabBar 配置
└── 自动生成 # 路由配置自动生成到 pages.json
特点:
- 约定优于配置: 文件路径即路由路径
- 自动生成: 路由配置自动从文件结构生成
- definePage: 页面级配置直接写在 Vue 文件中
- TypeScript 支持: 完整的类型检查和智能提示
路由配置差异分析
1. 页面路由定义方式对比
Vue2 项目 - 集中式配置:
{
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "首页"
}
},
{
"path": "pages/login/login",
"style": {
"navigationBarTitleText": "登录",
"navigationStyle": "custom"
}
}
]
}
Vue3 项目 - 约定式路由:
<!-- src/pages/index/index.vue -->
<script setup lang="ts">
// 使用 definePage API
definePage({
style: {
navigationBarTitleText: "首页",
},
});
</script>
<template>
<view>首页内容</view>
</template>
2. 分包配置迁移对比
Vue2 项目 - 手动分包配置:
{
"subPackages": [
{
"root": "pages-sub/maintenance",
"pages": [
{
"path": "maintainance",
"style": {
"navigationBarTitleText": "设备保养"
}
}
]
}
]
}
Vue3 项目 - 自动分包识别:
src/pages-sub/ # 自动识别为分包目录
├── maintenance/ # 分包名称
│ ├── maintainance.vue # 自动生成路径: pages-sub/maintenance/maintainance
│ └── excuteMaintainance.vue # 自动生成路径: pages-sub/maintenance/excuteMaintainance
└── complaint/ # 其他分包
├── complaint.vue
└── detail.vue
完整迁移工作流程(⭐ 重要)
当接到路由迁移任务时,必须按照以下完整流程执行:
阶段 1:前期准备
1.1 读取路由迁移映射表
Read: docs\prompts\route-migration-map.yml
1.2 确认迁移目标
- 确定要迁移的页面路径(旧项目路径)
- 在映射表中查找对应的新项目路径
- 确认该页面是否已经迁移完成(检查映射表标记)
阶段 2:搜索旧项目中的路由跳转
在开始迁移页面之前,必须先搜索旧项目中所有跳转到该页面的代码:
2.1 搜索旧路由路径
Grep: pattern="pages/repairOrder/repairOrder" path="../../examples/gitee-exampl-app/" output_mode="content"
2.2 记录所有跳转点
记录以下信息:
- 跳转的源文件路径
- 跳转代码所在行号
- 跳转时传递的参数
- 跳转方式(navigateTo/redirectTo/switchTab)
阶段 3:页面迁移实施
3.1 创建新页面文件
Write: src/pages-sub/repair/order-list.vue
3.2 迁移页面内容
按照其他技能的要求迁移:
- 使用
code-migration 技能迁移 Vue 代码
- 使用
component-migration 技能迁移组件
- 使用
style-migration 技能迁移样式
- 使用
api-migration 技能迁移接口调用
3.3 添加 definePage 配置(⚠️ 必须执行)
重要性: definePage 是页面配置的核心,缺少它会导致页面标题显示为 "unibest" 或空白,严重影响用户体验。
执行时机: 在创建页面文件后,必须立即添加 definePage 配置,这是不可跳过的强制步骤。
最小配置示例:
<script setup lang="ts">
// ✅ 所有页面必须添加 definePage,至少包含页面标题
definePage({
style: {
navigationBarTitleText: "维修工单池", // 导航栏标题(必需)
enablePullDownRefresh: false, // 是否启用下拉刷新
},
});
</script>
常见页面配置:
<script setup lang="ts">
definePage({
style: {
navigationBarTitleText: "维修工单池", // 导航栏标题
enablePullDownRefresh: true, // 启用下拉刷新
onReachBottomDistance: 50, // 触底距离
backgroundColor: "#f5f5f5", // 背景色
navigationBarBackgroundColor: "#368CFE", // 导航栏背景色
navigationBarTextStyle: "white", // 导航栏文字颜色
},
});
</script>
阶段 4:强类型路由配置
4.1 添加路由类型定义
在 src/types/routes.ts 中添加:
export type PageRoute = "/pages/index/index" | "/pages-sub/repair/order-list";
export interface PageParams {
"/pages-sub/repair/order-list": {
status?: string;
page?: number;
};
}
4.2 在 TypedRouter 中添加跳转方法
在 src/router/helpers.ts 中添加:
export class TypedRouter {
static toRepairList(params?: PageParams["/pages-sub/repair/order-list"]) {
return navigateToTyped("/pages-sub/repair/order-list", params);
}
}
4.3 导出新方法
在 src/router/index.ts 中导出:
export const {
toRepairList,
} = TypedRouter;
4.4 更新路由验证函数
在 src/router/helpers.ts 的 isValidRoute 中添加:
const validRoutes: PageRoute[] = [
"/pages-sub/repair/order-list",
];
阶段 5:更新所有跳转代码
根据阶段 2 记录的跳转点,逐一更新:
5.1 在新项目中搜索跳转代码
Grep: pattern="pages/repairOrder/repairOrder" path="src/" output_mode="content"
Grep: pattern="pages-sub/repair/order-list" path="src/" output_mode="content"
5.2 替换跳转代码
迁移前(Vue2 旧代码):
uni.navigateTo({
url: `/pages/repairOrder/repairOrder?status=${status}&page=${page}`,
});
迁移后(Vue3 新代码):
import { TypedRouter } from "@/router";
TypedRouter.toRepairList({ status, page });
5.3 处理不同的跳转方式
TypedRouter.toRepairList(params);
redirectToTyped("/pages-sub/repair/order-list", params);
switchTabTyped("/pages/index/index");
阶段 6:验证与测试
6.1 TypeScript 编译检查
pnpm type-check
6.2 搜索验证
Grep: pattern="pages/repairOrder" path="src/" output_mode="files_with_matches"
Grep: pattern="pages-sub/repair/order-list" path="src/" glob="*.vue" output_mode="content"
6.3 功能测试
阶段 7:更新映射表状态
完成迁移后,更新映射表:
route_mappings:
repair_module:
- old: examples/gitee-exampl-app/pages/repairOrder/repairOrder.vue
new: src/pages-sub/repair/order-list.vue
status: ✅
实际操作示例
示例:迁移维修工单详情页
步骤 1:搜索旧项目跳转
Grep: pattern="pages/repairDispatch/repairDispatch" path="../../examples/gitee-exampl-app/" output_mode="content"
搜索结果:
../../examples/gitee-exampl-app/pages/repairOrder/repairOrder.vue:120: uni.navigateTo({ url: '/pages/repairDispatch/repairDispatch?id=' + item.id })
../../examples/gitee-exampl-app/pages/index/index.vue:45: uni.navigateTo({ url: '/pages/repairDispatch/repairDispatch' })
步骤 2:分析参数
从搜索结果分析:
- 页面需要接收
id 参数(有时没有参数)
- 使用
navigateTo 跳转
步骤 3:定义类型
export interface PageParams {
"/pages-sub/repair/dispatch": {
id?: string;
};
}
步骤 4:创建 TypedRouter 方法
static toRepairDispatch(id?: string) {
return navigateToTyped('/pages-sub/repair/dispatch', { id })
}
步骤 5:替换跳转代码
在新项目中找到对应位置,替换为:
TypedRouter.toRepairDispatch(item.id);
常见错误与避免方法
错误 1:忘记读取映射表
❌ 错误做法:直接按照自己的理解创建路径
✅ 正确做法:必须先读取映射表,严格按照映射表执行
错误 2:只迁移页面,不更新跳转代码
❌ 错误做法:只创建了新页面,但忘记更新其他页面中的跳转代码
✅ 正确做法:迁移前先搜索所有跳转点,迁移后逐一更新
错误 3:未添加强类型路由支持
❌ 错误做法:直接使用 uni.navigateTo 跳转到新路径
✅ 正确做法:完整配置强类型路由系统(类型定义 + TypedRouter 方法)
错误 4:参数类型定义不准确
❌ 错误做法:所有参数都定义为可选
✅ 正确做法:根据实际使用情况,正确区分必填和可选参数
错误 5:未验证迁移完整性
❌ 错误做法:迁移完就认为完成了
✅ 正确做法:搜索验证,确保没有遗漏的旧路径引用
迁移步骤
步骤 1: 创建页面文件
根据映射表,在新项目中创建对应的页面文件:
Write: src/pages-sub/repair/order-list.vue
步骤 2: 添加 definePage 配置
在页面文件中使用 definePage 定义页面配置:
<script setup lang="ts">
definePage({
style: {
navigationBarTitleText: "维修工单池",
enablePullDownRefresh: true,
onReachBottomDistance: 50,
},
});
</script>
步骤 3: 迁移页面内容
迁移页面的模板、脚本和样式:
- 使用 Vue3 Composition API
- 使用 TypeScript
- 使用 UnoCSS 样式
- 使用 wot-design-uni 组件
步骤 4: 更新路由跳转
更新所有跳转到该页面的路由路径:
uni.navigateTo({
url: "/pages/repairOrder/repairOrder",
});
uni.navigateTo({
url: "/pages-sub/repair/order-list",
});
常用 definePage 配置
⚠️ definePage 字段规范(严禁使用 name 字段)
definePage 不支持 name 字段。使用 name 字段会导致类型错误或运行时警告。
❌ 错误示例:
definePage({
name: "test-z-paging-loading",
style: {
navigationBarTitleText: "z-paging-loading 组件测试",
},
});
✅ 有效配置项:
style: 页面样式配置 (对象)
navigationBarTitleText: 导航栏标题
enablePullDownRefresh: 是否启用下拉刷新
onReachBottomDistance: 触底距离
navigationBarBackgroundColor: 导航栏背景色
navigationBarTextStyle: 导航栏文字颜色 (black/white)
navigationStyle: 导航栏样式 (default/custom)
backgroundColor: 背景色
middlewares: 中间件 (数组)
典型配置示例
<script setup lang="ts">
definePage({
style: {
// 导航栏标题(必需)
navigationBarTitleText: "维修工单",
// 下拉刷新
enablePullDownRefresh: true,
// 触底距离
onReachBottomDistance: 50,
// 导航栏背景色
navigationBarBackgroundColor: "#368CFE",
// 导航栏标题颜色
navigationBarTextStyle: "white",
// 自定义导航栏(谨慎使用)
navigationStyle: "custom",
// 背景色
backgroundColor: "#f5f5f5",
},
});
</script>
强类型路由跳转系统(⭐ 重点)
Vue3 项目采用了完整的强类型路由跳转系统,通过 TypeScript 提供编译时类型检查,避免路由错误和参数错误。
1. 核心文件结构
src/
├── types/
│ └── routes.ts # 路由类型定义文件
│ ├── PageRoute # 所有页面路由的联合类型
│ ├── TabRoute # Tab页面路由类型
│ └── PageParams # 页面参数类型映射
├── router/
│ ├── index.ts # 路由管理中心(导出所有工具)
│ ├── helpers.ts # 强类型路由跳转工具实现
│ │ ├── navigateToTyped() # 类型安全的页面跳转
│ │ ├── redirectToTyped() # 类型安全的重定向
│ │ ├── switchTabTyped() # 类型安全的Tab切换
│ │ └── TypedRouter # 封装业务逻辑的路由类
│ ├── examples.ts # 路由使用示例代码
│ ├── guards.ts # 路由守卫
│ └── interceptor.ts # 路由拦截器
2. 路由类型定义系统
2.1 定义页面路由类型 (src/types/routes.ts)
export type PageRoute =
| "/pages/index/index"
| "/pages/about/about"
| "/pages/me/me"
| "/pages-sub/repair/order-list"
| "/pages-sub/repair/dispatch"
| "/pages-sub/repair/finish"
| "/pages-sub/repair/order-detail"
| "/pages-sub/repair/add-order"
| "/pages-sub/repair/handle";
export type TabRoute = "/pages/index/index" | "/pages/address/list" | "/pages/me/me";
2.2 定义页面参数类型映射
export interface PageParams {
"/pages/index/index": {};
"/pages/login/login": {
redirect?: string;
};
"/pages-sub/repair/order-detail": {
repairId: string;
storeId: string;
};
"/pages-sub/repair/order-list": {
status?: string;
page?: number;
row?: number;
repairName?: string;
state?: string;
};
"/pages-sub/repair/handle": {
action: "DISPATCH" | "TRANSFER" | "BACK" | "FINISH";
repairId: string;
repairType: string;
preStaffId?: string;
preStaffName?: string;
repairObjType?: string;
publicArea?: string;
repairChannel?: string;
};
}
3. 强类型跳转工具使用
3.1 TypedRouter 类(推荐使用)
TypedRouter 是封装了业务逻辑的路由工具类,提供语义化的跳转方法:
import { TypedRouter } from "@/router";
TypedRouter.toRepairDetail("repair123", "store456");
TypedRouter.toRepairList({ status: "pending", page: 1 });
TypedRouter.toRepairHandle({
action: "DISPATCH",
repairId: "R001",
repairType: "TYPE_01",
preStaffId: "STAFF_123",
});
TypedRouter.toHome();
TypedRouter.toSelectFloor();
TypedRouter.toSelectUnit("F001");
TypedRouter.toSelectRoom("F001", "U001");
3.2 基础类型安全函数
import { navigateToTyped, redirectToTyped, switchTabTyped } from "@/router";
navigateToTyped("/pages-sub/repair/order-detail", {
repairId: "repair123",
storeId: "store456",
});
redirectToTyped("/pages/login/login", {
redirect: "/pages/me/me",
});
switchTabTyped("/pages/index/index");
4. 迁移旧路由跳转代码
4.1 基础跳转迁移
Vue2 旧代码:
uni.navigateTo({
url: `/pages/repairOrder/repairOrder?repairId=${repairId}&status=${status}`,
});
Vue3 新代码:
import { TypedRouter } from "@/router";
TypedRouter.toRepairDetail(repairId, storeId);
4.2 带参数跳转迁移
Vue2 旧代码:
const url = `/pages/repairDispatch/repairDispatch?action=DISPATCH&repairId=${id}`;
uni.navigateTo({ url });
Vue3 新代码:
TypedRouter.toRepairHandle({
action: "DISPATCH",
repairId: id,
repairType: type,
});
5. 为新页面添加强类型路由支持
当迁移完成一个新页面后,需要将其加入强类型路由系统。
5.1 步骤 1:添加路由类型定义
在 src/types/routes.ts 中添加页面路由和参数类型:
export type PageRoute = "/pages/index/index" | "/pages-sub/repair/new-page";
export interface PageParams {
"/pages-sub/repair/new-page": {
repairId: string;
status?: string;
};
}
5.2 步骤 2:在 TypedRouter 中添加跳转方法
在 src/router/helpers.ts 的 TypedRouter 类中添加对应方法:
export class TypedRouter {
static toNewPage(repairId: string, status?: string) {
return navigateToTyped("/pages-sub/repair/new-page", { repairId, status });
}
}
5.3 步骤 3:导出新方法(可选)
在 src/router/index.ts 中导出新方法(便于外部使用):
export const {
toRepairList,
toRepairDetail,
toNewPage,
} = TypedRouter;
5.4 步骤 4:更新 isValidRoute 验证函数
在 src/router/helpers.ts 的 isValidRoute 函数中添加新路由:
export function isValidRoute(path: string): path is PageRoute {
const validRoutes: PageRoute[] = [
"/pages/index/index",
"/pages-sub/repair/new-page",
];
return validRoutes.includes(path as PageRoute);
}
6. 实际使用示例(repair 模块参考)
6.1 维修工单列表页跳转示例
<script setup lang="ts">
import { TypedRouter } from "@/router";
import type { RepairOrder } from "@/types/repair";
/** 查看工单详情 */
function handleViewDetail(item: RepairOrder) {
// ✅ 类型安全的跳转,参数自动推断和检查
TypedRouter.toRepairDetail(item.repairId!, userInfo.storeId);
}
/** 派单 */
function handleDispatch(item: RepairOrder) {
TypedRouter.toRepairHandle({
action: "DISPATCH", // 类型约束为 'DISPATCH' | 'TRANSFER' | 'BACK' | 'FINISH'
repairId: item.repairId!,
repairType: item.repairType,
});
}
/** 结束工单 */
function handleEndOrder(item: RepairOrder) {
TypedRouter.toEndRepair(item.repairId!, item.communityId);
}
</script>
6.2 选择器级联跳转示例
<script setup lang="ts">
import { TypedRouter } from "@/router";
import { useSelectorStore } from "@/stores/useSelectorStore";
const selectorStore = useSelectorStore();
/** 选择楼栋 */
function handleSelectFloor() {
TypedRouter.toSelectFloor(); // 无参数
}
/** 选择单元 */
function handleSelectUnit() {
// ✅ 参数类型自动检查
TypedRouter.toSelectUnit(selectorStore.selectedFloor.floorId);
}
/** 选择房屋 */
function handleSelectRoom() {
// ✅ 多个参数的类型安全
TypedRouter.toSelectRoom(selectorStore.selectedFloor.floorId, selectorStore.selectedUnit.unitId);
}
</script>
7. 类型安全的好处
7.1 编译时错误检查
TypedRouter.toRepairDetial("R001", "S001");
TypedRouter.toRepairDetail("R001", 123);
TypedRouter.toRepairHandle({
action: "DISPATCH",
});
TypedRouter.toRepairHandle({
action: "INVALID_ACTION",
repairId: "R001",
repairType: "TYPE_01",
});
7.2 智能代码提示
TypedRouter.to;
TypedRouter.toRepairHandle({
action: "",
});
8. 迁移检查清单(强类型路由部分)
完成页面迁移后,必须完成以下强类型路由相关的配置:
9. 常见问题与解决方案
问题 1:新页面如何快速添加路由支持?
解决方案:按照"步骤 5"依次完成四步配置即可。
问题 2:参数太多,TypedRouter 方法签名太长怎么办?
解决方案:使用参数对象形式:
static toRepairHandle(action, repairId, repairType, preStaffId, preStaffName, ...)
static toRepairHandle(params: PageParams['/pages-sub/repair/handle'])
问题 3:如何处理动态路由参数?
解决方案:在类型定义中使用可选参数:
export interface PageParams {
"/pages-sub/repair/order-list": {
status?: string;
page?: number;
};
}
路由跳转方式
普通跳转
uni.navigateTo({
url: "/pages-sub/repair/order-list",
});
TypedRouter.toRepairList();
TypedRouter.toRepairDetail("repair123", "store456");
接收路由参数
<script setup lang="ts">
import { onLoad } from "@dcloudio/uni-app";
const orderId = ref("");
const status = ref("");
onLoad((options) => {
orderId.value = options.orderId as string;
status.value = options.status as string;
});
</script>
迁移检查清单
通过约定式路由系统,实现路由配置的自动化管理,提升开发效率和代码可维护性!