@jnrs/vue-core 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1 +1 @@
1
- export * as router from './utils/router';
1
+ export * as router from './router/createVueRouter';
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- export * as router from './utils/router';
1
+ export * as router from './router/createVueRouter';
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @Author : TanRui
3
+ * @WeChat : Tan578853789
4
+ * @File : index.ts
5
+ * @Date : 2025/11/07
6
+ * @Desc. : 路由
7
+ */
8
+ import { useRoute, type Router, type RouterOptions, type RouteLocationRaw } from 'vue-router';
9
+ import type { MenuItem, ModulesFileList } from './types';
10
+ /**
11
+ * 创建 vue-router 实例
12
+ * @param options - 配置项
13
+ * @returns router 实例
14
+ */
15
+ declare const createVueRouter: (options: Partial<RouterOptions>) => Router;
16
+ declare const routerPush: (to: RouteLocationRaw, router: Router) => Promise<void | import("vue-router").NavigationFailure>;
17
+ declare const routerReplace: (to: RouteLocationRaw, router: Router) => Promise<void | import("vue-router").NavigationFailure>;
18
+ declare const getRoutes: (router: Router) => MenuItem[];
19
+ declare const setupDynamicRoutes: (router: Router, layoutName: string | undefined, userRoutes: MenuItem[], modulesFileList: ModulesFileList) => Promise<void>;
20
+ export { createVueRouter, setupDynamicRoutes, routerPush, routerReplace, getRoutes, useRoute, type ModulesFileList };
@@ -0,0 +1,146 @@
1
+ /**
2
+ * @Author : TanRui
3
+ * @WeChat : Tan578853789
4
+ * @File : index.ts
5
+ * @Date : 2025/11/07
6
+ * @Desc. : 路由
7
+ */
8
+ import { createRouter, useRoute } from 'vue-router';
9
+ import { DEFAULT_OPTIONS, LAYOUT_NAME } from './defaults';
10
+ /**
11
+ * 创建 vue-router 实例
12
+ * @param options - 配置项
13
+ * @returns router 实例
14
+ */
15
+ const createVueRouter = (options) => {
16
+ const router = createRouter({ ...DEFAULT_OPTIONS, ...options });
17
+ // 获取用户权限
18
+ // const canUserAccess = async (to: RouteLocationNormalized) => {
19
+ // const isAuthenticated = true
20
+ // return to.name === 'Login' || to.meta.noAuth || isAuthenticated
21
+ // }
22
+ // 全局前置守卫
23
+ router.beforeEach(async (to) => {
24
+ // const canAccess = await canUserAccess(to)
25
+ // if (!canAccess) {
26
+ // return {
27
+ // name: 'Login',
28
+ // query: { redirect: to.name?.toString() }
29
+ // }
30
+ // }
31
+ });
32
+ return router;
33
+ };
34
+ // 路由跳转
35
+ const routerPush = async (to, router) => {
36
+ try {
37
+ const failure = await router.push(to);
38
+ return failure;
39
+ }
40
+ catch (e) {
41
+ console.warn('[router.push] 失败', {
42
+ code: 'ROUTER_NOT_MATCH',
43
+ message: '未匹配到有效路由',
44
+ error: to || e,
45
+ timestamp: new Date().toISOString()
46
+ });
47
+ router.push({ name: '404' });
48
+ }
49
+ };
50
+ // 路由替换
51
+ const routerReplace = async (to, router) => {
52
+ try {
53
+ const failure = await router.replace(to);
54
+ return failure;
55
+ }
56
+ catch (e) {
57
+ console.warn('[router.replace] 失败', {
58
+ code: 'ROUTER_NOT_MATCH',
59
+ message: '未匹配到有效路由',
60
+ error: to || e,
61
+ timestamp: new Date().toISOString()
62
+ });
63
+ router.replace({ name: '404' });
64
+ }
65
+ };
66
+ // 获取路由
67
+ const getRoutes = (router) => {
68
+ const temp = [];
69
+ for (const route of router.getRoutes()) {
70
+ temp.push({
71
+ name: route.name,
72
+ path: route.path,
73
+ meta: route.meta
74
+ });
75
+ }
76
+ return temp;
77
+ };
78
+ // 加载动态路由
79
+ const setupDynamicRoutes = async (router, layoutName = LAYOUT_NAME, userRoutes, modulesFileList) => {
80
+ menuToRoute(router, layoutName, modulesFileList, userRoutes, undefined);
81
+ };
82
+ // 组件加载器
83
+ const getComponentLoader = (safePath, modulesFileList) => {
84
+ return modulesFileList[`/src/views${safePath}.vue`];
85
+ };
86
+ // 将菜单项转换为路由记录
87
+ const menuToRoute = async (router, layoutName, modulesFileList, menus, parent) => {
88
+ for (const menu of menus) {
89
+ menu.meta.fullPathTitle = parent
90
+ ? parent.meta.fullPathTitle + ',' + menu.meta.title
91
+ : menu.meta.title;
92
+ // 当前项若没有component,则为目录,直接递归处理
93
+ if (!menu.component) {
94
+ if (menu.children && menu.children.length > 0) {
95
+ await menuToRoute(router, layoutName, modulesFileList, menu.children, menu);
96
+ }
97
+ continue;
98
+ }
99
+ // 安全处理组件路径
100
+ const safePath = menu.component.replace(/[^a-zA-Z0-9/._-]/g, '');
101
+ if (!safePath.startsWith('/') || safePath.includes('..')) {
102
+ console.warn('[Router] 组件加载失败', {
103
+ code: 'INVALID_COMPONENT_PATH',
104
+ message: `组件路径拼写不符合规则,应以 '/' 开头且不能包含 '..',请检查 component 字段是否配置正确`,
105
+ menu: {
106
+ path: menu.path,
107
+ name: menu.name,
108
+ component: menu.component
109
+ },
110
+ timestamp: new Date().toISOString()
111
+ });
112
+ continue;
113
+ }
114
+ // 获取组件加载器
115
+ const loader = getComponentLoader(safePath, modulesFileList);
116
+ if (!loader) {
117
+ console.warn('[Router] 组件加载失败', {
118
+ code: 'COMPONENT_NOT_FOUND',
119
+ message: `组件加载器无效,请检查 component 字段是否配置正确`,
120
+ menu: {
121
+ path: menu.path,
122
+ name: menu.name,
123
+ component: menu.component
124
+ },
125
+ timestamp: new Date().toISOString()
126
+ });
127
+ continue;
128
+ }
129
+ // 构建路由记录
130
+ const routeRecord = {
131
+ name: menu.name,
132
+ path: menu.path,
133
+ meta: menu.meta,
134
+ redirect: menu.redirect,
135
+ component: () => loader()
136
+ };
137
+ // 根据 global 字段决定添加为全局路由还是布局子路由
138
+ if (menu.meta?.global) {
139
+ router.addRoute(routeRecord);
140
+ }
141
+ else {
142
+ router.addRoute(layoutName, routeRecord);
143
+ }
144
+ }
145
+ };
146
+ export { createVueRouter, setupDynamicRoutes, routerPush, routerReplace, getRoutes, useRoute };
@@ -0,0 +1,34 @@
1
+ /**
2
+ * @Author : TanRui
3
+ * @WeChat : Tan578853789
4
+ * @File : index.ts
5
+ * @Date : 2025/11/07
6
+ * @Desc. : 路由
7
+ */
8
+ import type { Router, RouteLocationRaw } from 'vue-router';
9
+ import type { CreateVueRouter, MenuItem } from './types';
10
+ /**
11
+ * 创建 vue-router 实例
12
+ * @param options 配置项
13
+ * @param fileModules 文件模块
14
+ * @param layoutName 布局组件名称
15
+ * @param globalComponent 全局 Layout 组件(mate.global=true时需传入)
16
+ * @param handleBeforeEach 路由前置守卫处理函数
17
+ * @returns router 实例
18
+ */
19
+ declare const createVueRouter: ({ options, fileModules, layoutName, globalComponent, handleBeforeEach }: CreateVueRouter) => Router;
20
+ /**
21
+ * 路由跳转或替换
22
+ * @param to 路由跳转参数
23
+ * @param type 默认为push, push | replace
24
+ * @returns Promise<boolean | void>
25
+ */
26
+ declare const handleRouter: (to: RouteLocationRaw, type?: "push" | "replace") => Promise<void | import("vue-router").NavigationFailure>;
27
+ /**
28
+ * 设置动态路由,生成路由记录
29
+ * @param menus 菜单项
30
+ * @param parent 父级菜单项
31
+ */
32
+ declare const asyncGenerateRoute: (menus: MenuItem[], parent?: MenuItem) => Promise<void>;
33
+ declare const getRoutes: () => MenuItem[];
34
+ export { createVueRouter, asyncGenerateRoute, handleRouter, getRoutes };
@@ -0,0 +1,156 @@
1
+ /**
2
+ * @Author : TanRui
3
+ * @WeChat : Tan578853789
4
+ * @File : index.ts
5
+ * @Date : 2025/11/07
6
+ * @Desc. : 路由
7
+ */
8
+ import { createRouter } from 'vue-router';
9
+ import { DEFAULT_OPTIONS } from './defaults';
10
+ let router;
11
+ let _fileModules;
12
+ let _layoutName;
13
+ let _globalComponent;
14
+ /**
15
+ * 创建 vue-router 实例
16
+ * @param options 配置项
17
+ * @param fileModules 文件模块
18
+ * @param layoutName 布局组件名称
19
+ * @param globalComponent 全局 Layout 组件(mate.global=true时需传入)
20
+ * @param handleBeforeEach 路由前置守卫处理函数
21
+ * @returns router 实例
22
+ */
23
+ const createVueRouter = ({ options, fileModules, layoutName, globalComponent, handleBeforeEach }) => {
24
+ _fileModules = fileModules;
25
+ if (layoutName) {
26
+ _layoutName = layoutName;
27
+ }
28
+ if (globalComponent) {
29
+ _globalComponent = globalComponent;
30
+ }
31
+ router = createRouter({ ...DEFAULT_OPTIONS, ...options });
32
+ router.beforeEach(async (to) => handleBeforeEach?.(to));
33
+ return router;
34
+ };
35
+ /**
36
+ * 路由跳转或替换
37
+ * @param to 路由跳转参数
38
+ * @param type 默认为push, push | replace
39
+ * @returns Promise<boolean | void>
40
+ */
41
+ const handleRouter = async (to, type = 'push') => {
42
+ try {
43
+ const failure = await router[type](to);
44
+ return failure;
45
+ }
46
+ catch (e) {
47
+ console.warn(`router.${type} 失败`, {
48
+ code: 'ROUTER_NOT_MATCH',
49
+ message: '未匹配到有效路由',
50
+ error: to || e,
51
+ timestamp: new Date().toISOString()
52
+ });
53
+ router[type]({ name: '404' });
54
+ }
55
+ };
56
+ /**
57
+ * 组件加载器
58
+ * @param safePath 安全处理后的组件路径
59
+ * @param fileModules 文件模块
60
+ * @returns
61
+ */
62
+ const getComponentLoader = (safePath) => {
63
+ return _fileModules[`/src/views${safePath}.vue`];
64
+ };
65
+ /**
66
+ * 设置动态路由,生成路由记录
67
+ * @param menus 菜单项
68
+ * @param parent 父级菜单项
69
+ */
70
+ const asyncGenerateRoute = async (menus, parent) => {
71
+ for (const menu of menus) {
72
+ menu.meta.fullPathTitle = parent
73
+ ? parent.meta.fullPathTitle + ',' + menu.meta.title
74
+ : menu.meta.title;
75
+ // 当前项若没有component,则为目录,直接递归处理
76
+ if (!menu.component) {
77
+ if (menu.children && menu.children.length > 0) {
78
+ asyncGenerateRoute(menu.children, menu);
79
+ }
80
+ continue;
81
+ }
82
+ // 安全处理组件路径
83
+ const safePath = menu.component.replace(/[^a-zA-Z0-9/._-]/g, '');
84
+ if (!safePath.startsWith('/') || safePath.includes('..')) {
85
+ console.warn('[Router] 组件加载失败', {
86
+ code: 'INVALID_COMPONENT_PATH',
87
+ message: `组件路径拼写不符合规则,应以 '/' 开头且不能包含 '..',请检查 component 字段是否配置正确`,
88
+ menu: {
89
+ path: menu.path,
90
+ name: menu.name,
91
+ component: menu.component
92
+ },
93
+ timestamp: new Date().toISOString()
94
+ });
95
+ continue;
96
+ }
97
+ // 获取组件加载器
98
+ const loader = getComponentLoader(safePath);
99
+ if (!loader) {
100
+ console.warn('[Router] 组件加载失败', {
101
+ code: 'COMPONENT_NOT_FOUND',
102
+ message: `组件加载器无效,请检查 component 字段是否配置正确`,
103
+ menu: {
104
+ path: menu.path,
105
+ name: menu.name,
106
+ component: menu.component
107
+ },
108
+ timestamp: new Date().toISOString()
109
+ });
110
+ continue;
111
+ }
112
+ // 根据 global 字段决定添加为全局路由还是布局子路由
113
+ let routeRecord = {
114
+ name: menu.name,
115
+ path: menu.path,
116
+ meta: menu.meta,
117
+ redirect: menu.redirect,
118
+ component: () => loader()
119
+ };
120
+ if (menu.meta?.global) {
121
+ if (_globalComponent !== undefined) {
122
+ routeRecord = {
123
+ path: menu.path,
124
+ meta: menu.meta,
125
+ redirect: menu.redirect,
126
+ component: () => _globalComponent,
127
+ children: [
128
+ {
129
+ path: '',
130
+ name: menu.name,
131
+ meta: menu.meta,
132
+ component: () => loader()
133
+ }
134
+ ]
135
+ };
136
+ }
137
+ router.addRoute(routeRecord);
138
+ }
139
+ else {
140
+ router.addRoute(_layoutName, routeRecord);
141
+ }
142
+ }
143
+ };
144
+ // 获取路由
145
+ const getRoutes = () => {
146
+ const temp = [];
147
+ for (const route of router.getRoutes()) {
148
+ temp.push({
149
+ name: route.name,
150
+ path: route.path,
151
+ meta: route.meta
152
+ });
153
+ }
154
+ return temp;
155
+ };
156
+ export { createVueRouter, asyncGenerateRoute, handleRouter, getRoutes };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * 默认配置项
3
+ * @property history - 路由历史模式
4
+ * @property routes - 路由记录数组
5
+ */
6
+ declare const DEFAULT_OPTIONS: {
7
+ history: import("vue-router").RouterHistory;
8
+ routes: never[];
9
+ };
10
+ export { DEFAULT_OPTIONS };
@@ -0,0 +1,11 @@
1
+ import { createWebHistory } from 'vue-router';
2
+ /**
3
+ * 默认配置项
4
+ * @property history - 路由历史模式
5
+ * @property routes - 路由记录数组
6
+ */
7
+ const DEFAULT_OPTIONS = {
8
+ history: createWebHistory(),
9
+ routes: []
10
+ };
11
+ export { DEFAULT_OPTIONS };
@@ -0,0 +1,4 @@
1
+ export type * from 'vue-router';
2
+ export type * from './types';
3
+ export { useRouter, useRoute } from 'vue-router';
4
+ export { createVueRouter, asyncGenerateRoute, handleRouter, getRoutes } from './createVueRouter';
@@ -0,0 +1,2 @@
1
+ export { useRouter, useRoute } from 'vue-router';
2
+ export { createVueRouter, asyncGenerateRoute, handleRouter, getRoutes } from './createVueRouter';
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @Author : TanRui
3
+ * @WeChat : Tan578853789
4
+ * @File : index.ts
5
+ * @Date : 2025/11/07
6
+ * @Desc. : 路由
7
+ */
8
+ import { useRoute, type Router, type RouterOptions, type RouteLocationRaw } from 'vue-router';
9
+ import type { MenuItem, ModulesFileList } from './types';
10
+ /**
11
+ * 创建 vue-router 实例
12
+ * @param options - 配置项
13
+ * @returns router 实例
14
+ */
15
+ declare const createVueRouter: (options: Partial<RouterOptions>) => Router;
16
+ declare const routerPush: (to: RouteLocationRaw, router: Router) => Promise<void | import("vue-router").NavigationFailure>;
17
+ declare const routerReplace: (to: RouteLocationRaw, router: Router) => Promise<void | import("vue-router").NavigationFailure>;
18
+ declare const getRoutes: (router: Router) => MenuItem[];
19
+ declare const setupDynamicRoutes: (router: Router, layoutName: string | undefined, userRoutes: MenuItem[], modulesFileList: ModulesFileList) => Promise<void>;
20
+ export { createVueRouter, setupDynamicRoutes, routerPush, routerReplace, getRoutes, useRoute, type ModulesFileList };
@@ -0,0 +1,146 @@
1
+ /**
2
+ * @Author : TanRui
3
+ * @WeChat : Tan578853789
4
+ * @File : index.ts
5
+ * @Date : 2025/11/07
6
+ * @Desc. : 路由
7
+ */
8
+ import { createRouter, useRoute } from 'vue-router';
9
+ import { DEFAULT_OPTIONS, LAYOUT_NAME } from './defaults';
10
+ /**
11
+ * 创建 vue-router 实例
12
+ * @param options - 配置项
13
+ * @returns router 实例
14
+ */
15
+ const createVueRouter = (options) => {
16
+ const router = createRouter({ ...DEFAULT_OPTIONS, ...options });
17
+ // 获取用户权限
18
+ // const canUserAccess = async (to: RouteLocationNormalized) => {
19
+ // const isAuthenticated = true
20
+ // return to.name === 'Login' || to.meta.noAuth || isAuthenticated
21
+ // }
22
+ // 全局前置守卫
23
+ router.beforeEach(async (to) => {
24
+ // const canAccess = await canUserAccess(to)
25
+ // if (!canAccess) {
26
+ // return {
27
+ // name: 'Login',
28
+ // query: { redirect: to.name?.toString() }
29
+ // }
30
+ // }
31
+ });
32
+ return router;
33
+ };
34
+ // 路由跳转
35
+ const routerPush = async (to, router) => {
36
+ try {
37
+ const failure = await router.push(to);
38
+ return failure;
39
+ }
40
+ catch (e) {
41
+ console.warn('[router.push] 失败', {
42
+ code: 'ROUTER_NOT_MATCH',
43
+ message: '未匹配到有效路由',
44
+ error: to || e,
45
+ timestamp: new Date().toISOString()
46
+ });
47
+ router.push({ name: '404' });
48
+ }
49
+ };
50
+ // 路由替换
51
+ const routerReplace = async (to, router) => {
52
+ try {
53
+ const failure = await router.replace(to);
54
+ return failure;
55
+ }
56
+ catch (e) {
57
+ console.warn('[router.replace] 失败', {
58
+ code: 'ROUTER_NOT_MATCH',
59
+ message: '未匹配到有效路由',
60
+ error: to || e,
61
+ timestamp: new Date().toISOString()
62
+ });
63
+ router.replace({ name: '404' });
64
+ }
65
+ };
66
+ // 获取路由
67
+ const getRoutes = (router) => {
68
+ const temp = [];
69
+ for (const route of router.getRoutes()) {
70
+ temp.push({
71
+ name: route.name,
72
+ path: route.path,
73
+ meta: route.meta
74
+ });
75
+ }
76
+ return temp;
77
+ };
78
+ // 加载动态路由
79
+ const setupDynamicRoutes = async (router, layoutName = LAYOUT_NAME, userRoutes, modulesFileList) => {
80
+ menuToRoute(router, layoutName, modulesFileList, userRoutes, undefined);
81
+ };
82
+ // 组件加载器
83
+ const getComponentLoader = (safePath, modulesFileList) => {
84
+ return modulesFileList[`/src/views${safePath}.vue`];
85
+ };
86
+ // 将菜单项转换为路由记录
87
+ const menuToRoute = async (router, layoutName, modulesFileList, menus, parent) => {
88
+ for (const menu of menus) {
89
+ menu.meta.fullPathTitle = parent
90
+ ? parent.meta.fullPathTitle + ',' + menu.meta.title
91
+ : menu.meta.title;
92
+ // 当前项若没有component,则为目录,直接递归处理
93
+ if (!menu.component) {
94
+ if (menu.children && menu.children.length > 0) {
95
+ await menuToRoute(router, layoutName, modulesFileList, menu.children, menu);
96
+ }
97
+ continue;
98
+ }
99
+ // 安全处理组件路径
100
+ const safePath = menu.component.replace(/[^a-zA-Z0-9/._-]/g, '');
101
+ if (!safePath.startsWith('/') || safePath.includes('..')) {
102
+ console.warn('[Router] 组件加载失败', {
103
+ code: 'INVALID_COMPONENT_PATH',
104
+ message: `组件路径拼写不符合规则,应以 '/' 开头且不能包含 '..',请检查 component 字段是否配置正确`,
105
+ menu: {
106
+ path: menu.path,
107
+ name: menu.name,
108
+ component: menu.component
109
+ },
110
+ timestamp: new Date().toISOString()
111
+ });
112
+ continue;
113
+ }
114
+ // 获取组件加载器
115
+ const loader = getComponentLoader(safePath, modulesFileList);
116
+ if (!loader) {
117
+ console.warn('[Router] 组件加载失败', {
118
+ code: 'COMPONENT_NOT_FOUND',
119
+ message: `组件加载器无效,请检查 component 字段是否配置正确`,
120
+ menu: {
121
+ path: menu.path,
122
+ name: menu.name,
123
+ component: menu.component
124
+ },
125
+ timestamp: new Date().toISOString()
126
+ });
127
+ continue;
128
+ }
129
+ // 构建路由记录
130
+ const routeRecord = {
131
+ name: menu.name,
132
+ path: menu.path,
133
+ meta: menu.meta,
134
+ redirect: menu.redirect,
135
+ component: () => loader()
136
+ };
137
+ // 根据 global 字段决定添加为全局路由还是布局子路由
138
+ if (menu.meta?.global) {
139
+ router.addRoute(routeRecord);
140
+ }
141
+ else {
142
+ router.addRoute(layoutName, routeRecord);
143
+ }
144
+ }
145
+ };
146
+ export { createVueRouter, setupDynamicRoutes, routerPush, routerReplace, getRoutes, useRoute };
@@ -0,0 +1,21 @@
1
+ import type { RouteRecordRaw, RouteMeta, RouterOptions, RouteLocationNormalizedGeneric } from 'vue-router';
2
+ export interface MenuItem {
3
+ meta: RouteMeta;
4
+ path: string;
5
+ name?: string;
6
+ todoCount?: number;
7
+ component?: string;
8
+ redirect?: string;
9
+ children?: MenuItem[];
10
+ }
11
+ export interface RouteModule {
12
+ default: RouteRecordRaw | RouteRecordRaw[];
13
+ }
14
+ export type FileModules = Record<string, () => Promise<RouteModule>>;
15
+ export interface CreateVueRouter<GC = unknown> {
16
+ options: Partial<RouterOptions>;
17
+ fileModules: FileModules;
18
+ layoutName?: string;
19
+ globalComponent?: Promise<GC>;
20
+ handleBeforeEach?: (to: RouteLocationNormalizedGeneric) => void;
21
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -12,14 +12,4 @@ declare const DEFAULT_OPTIONS: {
12
12
  * @description 用于标识应用的主布局组件
13
13
  */
14
14
  declare const LAYOUT_NAME = "Layout";
15
- /**
16
- * 模块基础路径
17
- * @description 用于动态导入视图组件的基础路径
18
- */
19
- declare const MODULES_BASE = "";
20
- /**
21
- * 异步获取菜单项的默认实现
22
- * @returns 空菜单项数组的 Promise
23
- */
24
- declare const asyncGetMenus: () => Promise<never[]>;
25
- export { DEFAULT_OPTIONS, LAYOUT_NAME, MODULES_BASE, asyncGetMenus };
15
+ export { DEFAULT_OPTIONS, LAYOUT_NAME };
@@ -13,14 +13,4 @@ const DEFAULT_OPTIONS = {
13
13
  * @description 用于标识应用的主布局组件
14
14
  */
15
15
  const LAYOUT_NAME = 'Layout';
16
- /**
17
- * 模块基础路径
18
- * @description 用于动态导入视图组件的基础路径
19
- */
20
- const MODULES_BASE = '';
21
- /**
22
- * 异步获取菜单项的默认实现
23
- * @returns 空菜单项数组的 Promise
24
- */
25
- const asyncGetMenus = () => Promise.resolve([]);
26
- export { DEFAULT_OPTIONS, LAYOUT_NAME, MODULES_BASE, asyncGetMenus };
16
+ export { DEFAULT_OPTIONS, LAYOUT_NAME };
@@ -2,32 +2,19 @@
2
2
  * @Author : TanRui
3
3
  * @WeChat : Tan578853789
4
4
  * @File : index.ts
5
- * @Date : 2025/10/07
6
- * @Desc. : 路由入口
5
+ * @Date : 2025/11/07
6
+ * @Desc. : 路由
7
7
  */
8
- import { useRoute, type RouteLocationRaw, type NavigationFailure } from 'vue-router';
9
- import type { MenuItem, CreateVueRouterOptions } from './types';
8
+ import { useRoute, type Router, type RouterOptions, type RouteLocationRaw } from 'vue-router';
9
+ import type { MenuItem, ModulesFileList } from './types';
10
10
  /**
11
- * 创建 Vue Router 实例
12
- * @param options - 实例的配置项
13
- * @param layoutName - 布局组件名称,默认为 'Layout'
14
- * @param modulesBase - 模块基础路径,默认为 ''
15
- * @param modulesList - 模块列表对象
16
- * @param asyncGetMenusFn - 异步获取菜单项处理函数
17
- * @returns router 路由实例
18
- * @returns setupDynamicRoutes 动态路由加载函数
19
- * @returns useRoute 路由使用函数
20
- * @returns routerPush 路由跳转函数
21
- * @returns routerReplace 路由替换函数
22
- * @returns getRoutes 获取路由函数
23
- * @example const { router, setupDynamicRoutes } = createVueRouter({})
11
+ * 创建 vue-router 实例
12
+ * @param options - 配置项
13
+ * @returns router 实例
24
14
  */
25
- declare const createVueRouter: ({ options, layoutName, modulesBase, modulesList, asyncGetMenusFn }?: CreateVueRouterOptions) => {
26
- router: import("vue-router").Router;
27
- setupDynamicRoutes: () => Promise<void>;
28
- useRoute: typeof useRoute;
29
- routerPush: (to: RouteLocationRaw) => Promise<undefined | void | NavigationFailure>;
30
- routerReplace: (to: RouteLocationRaw) => Promise<undefined | void | NavigationFailure>;
31
- getRoutes: () => MenuItem[];
32
- };
33
- export { createVueRouter };
15
+ declare const createVueRouter: (options: Partial<RouterOptions>) => Router;
16
+ declare const routerPush: (to: RouteLocationRaw, router: Router) => Promise<void | import("vue-router").NavigationFailure>;
17
+ declare const routerReplace: (to: RouteLocationRaw, router: Router) => Promise<void | import("vue-router").NavigationFailure>;
18
+ declare const getRoutes: (router: Router) => MenuItem[];
19
+ declare const setupDynamicRoutes: (router: Router, layoutName: string | undefined, userRoutes: MenuItem[], modulesFileList: ModulesFileList) => Promise<void>;
20
+ export { createVueRouter, setupDynamicRoutes, routerPush, routerReplace, getRoutes, useRoute, type ModulesFileList };
@@ -2,161 +2,145 @@
2
2
  * @Author : TanRui
3
3
  * @WeChat : Tan578853789
4
4
  * @File : index.ts
5
- * @Date : 2025/10/07
6
- * @Desc. : 路由入口
5
+ * @Date : 2025/11/07
6
+ * @Desc. : 路由
7
7
  */
8
8
  import { createRouter, useRoute } from 'vue-router';
9
- import { DEFAULT_OPTIONS, LAYOUT_NAME, MODULES_BASE, asyncGetMenus } from './defaults';
9
+ import { DEFAULT_OPTIONS, LAYOUT_NAME } from './defaults';
10
10
  /**
11
- * 创建 Vue Router 实例
12
- * @param options - 实例的配置项
13
- * @param layoutName - 布局组件名称,默认为 'Layout'
14
- * @param modulesBase - 模块基础路径,默认为 ''
15
- * @param modulesList - 模块列表对象
16
- * @param asyncGetMenusFn - 异步获取菜单项处理函数
17
- * @returns router 路由实例
18
- * @returns setupDynamicRoutes 动态路由加载函数
19
- * @returns useRoute 路由使用函数
20
- * @returns routerPush 路由跳转函数
21
- * @returns routerReplace 路由替换函数
22
- * @returns getRoutes 获取路由函数
23
- * @example const { router, setupDynamicRoutes } = createVueRouter({})
11
+ * 创建 vue-router 实例
12
+ * @param options - 配置项
13
+ * @returns router 实例
24
14
  */
25
- const createVueRouter = ({ options = {}, layoutName = LAYOUT_NAME, modulesBase = MODULES_BASE, modulesList = [], asyncGetMenusFn } = {}) => {
26
- const ops = { ...DEFAULT_OPTIONS, ...options };
27
- // 创建路由实例
28
- const router = createRouter(ops);
29
- // 组件加载器
30
- const getComponentLoader = (safePath) => {
31
- // console.log(modulesBase + `/src/views${safePath}.vue`)
32
- return modulesList[modulesBase + `/src/views${safePath}.vue`];
33
- };
34
- // 异步获取菜单项处理函数
35
- const handleAsyncGetMenus = asyncGetMenusFn ?? asyncGetMenus;
36
- // 将菜单项转换为路由记录
37
- const menuToRoute = async (menus, parent) => {
38
- for (const menu of menus) {
39
- menu.meta.fullPathTitle = parent
40
- ? parent.meta.fullPathTitle + ',' + menu.meta.title
41
- : menu.meta.title;
42
- // 当前项若没有component,则为目录,直接递归处理
43
- if (!menu.component) {
44
- if (menu.children && menu.children.length > 0) {
45
- await menuToRoute(menu.children, menu);
46
- }
47
- continue;
48
- }
49
- // 安全处理组件路径
50
- const safePath = menu.component.replace(/[^a-zA-Z0-9/._-]/g, '');
51
- if (!safePath.startsWith('/') || safePath.includes('..')) {
52
- console.warn('[Router] 组件加载失败', {
53
- code: 'INVALID_COMPONENT_PATH',
54
- message: `组件路径拼写不符合规则,应以 '/' 开头且不能包含 '..',请检查 component 字段是否配置正确`,
55
- menu: {
56
- path: menu.path,
57
- name: menu.name,
58
- component: menu.component
59
- },
60
- timestamp: new Date().toISOString()
61
- });
62
- continue;
63
- }
64
- // 获取组件加载器
65
- const loader = getComponentLoader(safePath);
66
- if (!loader) {
67
- console.warn('[Router] 组件加载失败', {
68
- code: 'COMPONENT_NOT_FOUND',
69
- message: `组件加载器无效,请检查 component 字段是否配置正确`,
70
- menu: {
71
- path: menu.path,
72
- name: menu.name,
73
- component: menu.component
74
- },
75
- timestamp: new Date().toISOString()
76
- });
77
- continue;
78
- }
79
- // 构建路由记录
80
- const routeRecord = {
81
- name: menu.name,
82
- path: menu.path,
83
- meta: menu.meta,
84
- redirect: menu.redirect,
85
- component: () => loader()
86
- };
87
- // 根据 global 字段决定添加为全局路由还是布局子路由
88
- if (menu.meta?.global) {
89
- router.addRoute(routeRecord);
90
- }
91
- else {
92
- router.addRoute(layoutName, routeRecord);
93
- }
94
- }
95
- };
96
- // 加载动态路由
97
- const setupDynamicRoutes = async () => {
98
- const userRoutes = await handleAsyncGetMenus();
99
- menuToRoute(userRoutes);
100
- };
15
+ const createVueRouter = (options) => {
16
+ const router = createRouter({ ...DEFAULT_OPTIONS, ...options });
101
17
  // 获取用户权限
102
- const canUserAccess = async (to) => {
103
- const isAuthenticated = true;
104
- return to.name === 'Login' || to.meta.noAuth || isAuthenticated;
105
- };
18
+ // const canUserAccess = async (to: RouteLocationNormalized) => {
19
+ // const isAuthenticated = true
20
+ // return to.name === 'Login' || to.meta.noAuth || isAuthenticated
21
+ // }
106
22
  // 全局前置守卫
107
23
  router.beforeEach(async (to) => {
108
- const canAccess = await canUserAccess(to);
109
- if (!canAccess) {
110
- return {
111
- name: 'Login',
112
- query: { redirect: to.name?.toString() }
113
- };
114
- }
24
+ // const canAccess = await canUserAccess(to)
25
+ // if (!canAccess) {
26
+ // return {
27
+ // name: 'Login',
28
+ // query: { redirect: to.name?.toString() }
29
+ // }
30
+ // }
115
31
  });
116
- // 路由跳转
117
- const routerPush = async (to) => {
118
- try {
119
- const failure = await router.push(to);
120
- return failure;
32
+ return router;
33
+ };
34
+ // 路由跳转
35
+ const routerPush = async (to, router) => {
36
+ try {
37
+ const failure = await router.push(to);
38
+ return failure;
39
+ }
40
+ catch (e) {
41
+ console.warn('[router.push] 失败', {
42
+ code: 'ROUTER_NOT_MATCH',
43
+ message: '未匹配到有效路由',
44
+ error: to || e,
45
+ timestamp: new Date().toISOString()
46
+ });
47
+ router.push({ name: '404' });
48
+ }
49
+ };
50
+ // 路由替换
51
+ const routerReplace = async (to, router) => {
52
+ try {
53
+ const failure = await router.replace(to);
54
+ return failure;
55
+ }
56
+ catch (e) {
57
+ console.warn('[router.replace] 失败', {
58
+ code: 'ROUTER_NOT_MATCH',
59
+ message: '未匹配到有效路由',
60
+ error: to || e,
61
+ timestamp: new Date().toISOString()
62
+ });
63
+ router.replace({ name: '404' });
64
+ }
65
+ };
66
+ // 获取路由
67
+ const getRoutes = (router) => {
68
+ const temp = [];
69
+ for (const route of router.getRoutes()) {
70
+ temp.push({
71
+ name: route.name,
72
+ path: route.path,
73
+ meta: route.meta
74
+ });
75
+ }
76
+ return temp;
77
+ };
78
+ // 加载动态路由
79
+ const setupDynamicRoutes = async (router, layoutName = LAYOUT_NAME, userRoutes, modulesFileList) => {
80
+ menuToRoute(router, layoutName, modulesFileList, userRoutes, undefined);
81
+ };
82
+ // 组件加载器
83
+ const getComponentLoader = (safePath, modulesFileList) => {
84
+ return modulesFileList[`/src/views${safePath}.vue`];
85
+ };
86
+ // 将菜单项转换为路由记录
87
+ const menuToRoute = async (router, layoutName, modulesFileList, menus, parent) => {
88
+ for (const menu of menus) {
89
+ menu.meta.fullPathTitle = parent
90
+ ? parent.meta.fullPathTitle + ',' + menu.meta.title
91
+ : menu.meta.title;
92
+ // 当前项若没有component,则为目录,直接递归处理
93
+ if (!menu.component) {
94
+ if (menu.children && menu.children.length > 0) {
95
+ await menuToRoute(router, layoutName, modulesFileList, menu.children, menu);
96
+ }
97
+ continue;
121
98
  }
122
- catch (e) {
123
- console.warn('[router.push] 失败', {
124
- code: 'ROUTER_NOT_MATCH',
125
- message: '未匹配到有效路由',
126
- error: to || e,
99
+ // 安全处理组件路径
100
+ const safePath = menu.component.replace(/[^a-zA-Z0-9/._-]/g, '');
101
+ if (!safePath.startsWith('/') || safePath.includes('..')) {
102
+ console.warn('[Router] 组件加载失败', {
103
+ code: 'INVALID_COMPONENT_PATH',
104
+ message: `组件路径拼写不符合规则,应以 '/' 开头且不能包含 '..',请检查 component 字段是否配置正确`,
105
+ menu: {
106
+ path: menu.path,
107
+ name: menu.name,
108
+ component: menu.component
109
+ },
127
110
  timestamp: new Date().toISOString()
128
111
  });
129
- router.push({ name: '404' });
130
- }
131
- };
132
- // 路由替换
133
- const routerReplace = async (to) => {
134
- try {
135
- const failure = await router.replace(to);
136
- return failure;
112
+ continue;
137
113
  }
138
- catch (e) {
139
- console.warn('[router.replace] 失败', {
140
- code: 'ROUTER_NOT_MATCH',
141
- message: '未匹配到有效路由',
142
- error: to || e,
114
+ // 获取组件加载器
115
+ const loader = getComponentLoader(safePath, modulesFileList);
116
+ if (!loader) {
117
+ console.warn('[Router] 组件加载失败', {
118
+ code: 'COMPONENT_NOT_FOUND',
119
+ message: `组件加载器无效,请检查 component 字段是否配置正确`,
120
+ menu: {
121
+ path: menu.path,
122
+ name: menu.name,
123
+ component: menu.component
124
+ },
143
125
  timestamp: new Date().toISOString()
144
126
  });
145
- router.replace({ name: '404' });
127
+ continue;
146
128
  }
147
- };
148
- // 获取路由
149
- const getRoutes = () => {
150
- const temp = [];
151
- for (const route of router.getRoutes()) {
152
- temp.push({
153
- name: route.name,
154
- path: route.path,
155
- meta: route.meta
156
- });
129
+ // 构建路由记录
130
+ const routeRecord = {
131
+ name: menu.name,
132
+ path: menu.path,
133
+ meta: menu.meta,
134
+ redirect: menu.redirect,
135
+ component: () => loader()
136
+ };
137
+ // 根据 global 字段决定添加为全局路由还是布局子路由
138
+ if (menu.meta?.global) {
139
+ router.addRoute(routeRecord);
140
+ }
141
+ else {
142
+ router.addRoute(layoutName, routeRecord);
157
143
  }
158
- return temp;
159
- };
160
- return { router, setupDynamicRoutes, useRoute, routerPush, routerReplace, getRoutes };
144
+ }
161
145
  };
162
- export { createVueRouter };
146
+ export { createVueRouter, setupDynamicRoutes, routerPush, routerReplace, getRoutes, useRoute };
@@ -0,0 +1,14 @@
1
+ import type { RouteRecordNameGeneric, RouteRecordRaw, RouteMeta } from 'vue-router';
2
+ export interface MenuItem {
3
+ name: string | RouteRecordNameGeneric;
4
+ path: string;
5
+ meta: RouteMeta;
6
+ todoCount?: number;
7
+ component?: string;
8
+ redirect?: string;
9
+ children?: MenuItem[];
10
+ }
11
+ export interface RouteModule {
12
+ default: RouteRecordRaw | RouteRecordRaw[];
13
+ }
14
+ export type ModulesFileList = Record<string, () => Promise<RouteModule>>;
@@ -0,0 +1,8 @@
1
+ // router/types.ts 或类似位置
2
+ export {};
3
+ // export interface CreateVueRouterOptions {
4
+ // options?: Partial<RouterOptions>
5
+ // layoutName?: string
6
+ // modulesBase?: string
7
+ // modulesFileList?: Record<string, () => Promise<RouteModule>>
8
+ // }
package/package.json CHANGED
@@ -1,21 +1,19 @@
1
1
  {
2
2
  "name": "@jnrs/vue-core",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "As the name suggests.",
5
5
  "author": "Talia-Tan",
6
6
  "license": "ISC",
7
7
  "publishConfig": {
8
8
  "access": "public"
9
9
  },
10
- "type": "module",
11
10
  "keywords": [
12
11
  "jnrs",
13
12
  "vue-core",
14
13
  "typescript",
15
14
  "helper"
16
15
  ],
17
- "main": "dist/index.js",
18
- "module": "dist/index.js",
16
+ "type": "module",
19
17
  "types": "dist/index.d.ts",
20
18
  "files": [
21
19
  "dist",
@@ -26,6 +24,10 @@
26
24
  ".": {
27
25
  "import": "./dist/index.js",
28
26
  "types": "./dist/index.d.ts"
27
+ },
28
+ "./router": {
29
+ "import": "./dist/router/index.js",
30
+ "types": "./dist/router/index.d.ts"
29
31
  }
30
32
  },
31
33
  "dependencies": {