mirror of
https://github.com/soybeanjs/soybean-admin.git
synced 2025-09-19 01:56:38 +08:00
feat(projects): 新增静态路由
This commit is contained in:
parent
bbfdcc8276
commit
ca2dfa6185
4
.env
4
.env
@ -8,5 +8,5 @@ VITE_APP_DESC=SoybeanAdmin是一个中后台管理系统模版
|
|||||||
|
|
||||||
VITE_SERVER_PORT=3200
|
VITE_SERVER_PORT=3200
|
||||||
|
|
||||||
VITE_HTTP_PROXY=true
|
# 权限路由模式: static | dynamic
|
||||||
|
VITE_AUTH_ROUTE_MODE=dynamic
|
||||||
|
@ -9,7 +9,7 @@ type ServiceEnv = Record<
|
|||||||
}
|
}
|
||||||
>;
|
>;
|
||||||
|
|
||||||
/** 请求的环境 */
|
/** 环境配置 */
|
||||||
const serviceEnvConfig: ServiceEnv = {
|
const serviceEnvConfig: ServiceEnv = {
|
||||||
dev: {
|
dev: {
|
||||||
url: 'http://localhost:8080',
|
url: 'http://localhost:8080',
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
# 是否开启打包文件大小结果分析
|
VITE_HTTP_PROXY=true
|
||||||
|
|
||||||
VITE_VISUALIZER=false
|
VITE_VISUALIZER=false
|
||||||
|
@ -50,14 +50,15 @@ Soybean Admin 是一个基于 Vue3、Vite、TypeScript、Naive UI 的免费中
|
|||||||
|
|
||||||
## 开发计划
|
## 开发计划
|
||||||
|
|
||||||
- [ ] 添加前端静态路由
|
- [x] 添加前端静态路由
|
||||||
|
- [ ] 用户角色切换示例、按钮级别权限指令
|
||||||
- [ ] 最近功能的有关文档更新
|
- [ ] 最近功能的有关文档更新
|
||||||
|
- [ ] 引入ECharts替换AntV G2Plot
|
||||||
- [ ] 性能优化(优化递归函数)
|
- [ ] 性能优化(优化递归函数)
|
||||||
- [ ] 精简版(新分支thin)
|
- [ ] 精简版(新分支thin)
|
||||||
- [ ] 集成unocss替换windicss(新分支unocss)
|
- [ ] 集成unocss替换windicss(新分支unocss)
|
||||||
- [ ] 表单、表格示例
|
- [ ] 表单、表格示例
|
||||||
- [ ] 添加锁屏组件、全局Iframe组件
|
- [ ] 添加锁屏组件、全局Iframe组件
|
||||||
- [ ] 用户角色切换示例、按钮级别权限指令
|
|
||||||
- [ ] 示例页面完善
|
- [ ] 示例页面完善
|
||||||
- [ ] 其他UI版本
|
- [ ] 其他UI版本
|
||||||
- [ ] element-plus版本
|
- [ ] element-plus版本
|
||||||
@ -133,7 +134,7 @@ pnpm i -g commitizen
|
|||||||
|
|
||||||
- 微信交流群:
|
- 微信交流群:
|
||||||
<div style="text-align:left">
|
<div style="text-align:left">
|
||||||
<img src="https://s2.loli.net/2022/03/23/pNiaVoP6yIvtS5C.jpg" style="width:200px" />
|
<img src="https://s2.loli.net/2022/03/30/VpmnTMsgXJH72B9.jpg" style="width:200px" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
- QQ交流群 `711301266`
|
- QQ交流群 `711301266`
|
||||||
|
@ -3,7 +3,7 @@ import { getEnvConfig } from '../../.env-config';
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置网络代理
|
* 设置网络代理
|
||||||
* @param viteEnv
|
* @param viteEnv - vite环境描述
|
||||||
*/
|
*/
|
||||||
export function createViteProxy(viteEnv: ImportMetaEnv) {
|
export function createViteProxy(viteEnv: ImportMetaEnv) {
|
||||||
const isOpenProxy = viteEnv.VITE_HTTP_PROXY === 'true';
|
const isOpenProxy = viteEnv.VITE_HTTP_PROXY === 'true';
|
||||||
|
26
components.d.ts
vendored
26
components.d.ts
vendored
@ -10,33 +10,7 @@ declare module 'vue' {
|
|||||||
DarkModeSwitch: typeof import('./src/components/common/DarkModeSwitch.vue')['default']
|
DarkModeSwitch: typeof import('./src/components/common/DarkModeSwitch.vue')['default']
|
||||||
GithubLink: typeof import('./src/components/custom/GithubLink.vue')['default']
|
GithubLink: typeof import('./src/components/custom/GithubLink.vue')['default']
|
||||||
HoverContainer: typeof import('./src/components/common/HoverContainer.vue')['default']
|
HoverContainer: typeof import('./src/components/common/HoverContainer.vue')['default']
|
||||||
IconAntDesignCloseOutlined: typeof import('~icons/ant-design/close-outlined')['default']
|
|
||||||
IconAntDesignEnterOutlined: typeof import('~icons/ant-design/enter-outlined')['default']
|
|
||||||
IconAntDesignSettingOutlined: typeof import('~icons/ant-design/setting-outlined')['default']
|
|
||||||
IconCustomAvatar: typeof import('~icons/custom/avatar')['default']
|
|
||||||
IconCustomLogo: typeof import('~icons/custom/logo')['default']
|
|
||||||
IconCustomLogoFill: typeof import('~icons/custom/logo-fill')['default']
|
|
||||||
IconCustomNoPermission: typeof import('~icons/custom/no-permission')['default']
|
|
||||||
IconCustomNotFound: typeof import('~icons/custom/not-found')['default']
|
|
||||||
IconCustomServiceError: typeof import('~icons/custom/service-error')['default']
|
|
||||||
IconGridiconsFullscreen: typeof import('~icons/gridicons/fullscreen')['default']
|
|
||||||
IconGridiconsFullscreenExit: typeof import('~icons/gridicons/fullscreen-exit')['default']
|
|
||||||
IconIcOutlineCheck: typeof import('~icons/ic/outline-check')['default']
|
|
||||||
IconLineMdMenuFoldLeft: typeof import('~icons/line-md/menu-fold-left')['default']
|
|
||||||
IconLineMdMenuUnfoldLeft: typeof import('~icons/line-md/menu-unfold-left')['default']
|
|
||||||
IconMdiArrowDownThin: typeof import('~icons/mdi/arrow-down-thin')['default']
|
|
||||||
IconMdiArrowUpThin: typeof import('~icons/mdi/arrow-up-thin')['default']
|
|
||||||
IconMdiClose: typeof import('~icons/mdi/close')['default']
|
|
||||||
IconMdiGithub: typeof import('~icons/mdi/github')['default']
|
|
||||||
IconMdiMoonWaningCrescent: typeof import('~icons/mdi/moon-waning-crescent')['default']
|
|
||||||
IconMdiPin: typeof import('~icons/mdi/pin')['default']
|
|
||||||
IconMdiPinOff: typeof import('~icons/mdi/pin-off')['default']
|
|
||||||
IconMdiRefresh: typeof import('~icons/mdi/refresh')['default']
|
|
||||||
IconMdiWhiteBalanceSunny: typeof import('~icons/mdi/white-balance-sunny')['default']
|
|
||||||
IconPhCaretDoubleLeftBold: typeof import('~icons/ph/caret-double-left-bold')['default']
|
|
||||||
IconPhCaretDoubleRightBold: typeof import('~icons/ph/caret-double-right-bold')['default']
|
|
||||||
IconSelect: typeof import('./src/components/custom/IconSelect.vue')['default']
|
IconSelect: typeof import('./src/components/custom/IconSelect.vue')['default']
|
||||||
IconUilSearch: typeof import('~icons/uil/search')['default']
|
|
||||||
ImageVerify: typeof import('./src/components/custom/ImageVerify.vue')['default']
|
ImageVerify: typeof import('./src/components/custom/ImageVerify.vue')['default']
|
||||||
LoadingEmptyWrapper: typeof import('./src/components/business/LoadingEmptyWrapper.vue')['default']
|
LoadingEmptyWrapper: typeof import('./src/components/business/LoadingEmptyWrapper.vue')['default']
|
||||||
LoginAgreement: typeof import('./src/components/business/LoginAgreement.vue')['default']
|
LoginAgreement: typeof import('./src/components/business/LoginAgreement.vue')['default']
|
||||||
|
@ -69,16 +69,6 @@ const routes: AuthRoute.Route[] = [
|
|||||||
icon: 'simple-icons:vite',
|
icon: 'simple-icons:vite',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'document_naive',
|
|
||||||
path: '/document/naive',
|
|
||||||
component: 'self',
|
|
||||||
meta: {
|
|
||||||
title: 'naive文档',
|
|
||||||
requiresAuth: true,
|
|
||||||
icon: 'mdi:alpha-n-box-outline',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'document_project',
|
name: 'document_project',
|
||||||
path: '/document/project',
|
path: '/document/project',
|
||||||
|
40
package.json
40
package.json
@ -26,18 +26,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@antv/g2plot": "^2.4.10",
|
"@antv/g2plot": "^2.4.13",
|
||||||
"@better-scroll/core": "^2.4.2",
|
"@better-scroll/core": "^2.4.2",
|
||||||
"@vueuse/core": "^8.0.0",
|
"@vueuse/core": "^8.2.0",
|
||||||
"axios": "^0.26.1",
|
"axios": "^0.26.1",
|
||||||
"clipboard": "^2.0.10",
|
"clipboard": "^2.0.10",
|
||||||
"colord": "^2.9.2",
|
"colord": "^2.9.2",
|
||||||
"crypto-js": "^4.1.1",
|
"crypto-js": "^4.1.1",
|
||||||
"dayjs": "^1.10.8",
|
"dayjs": "^1.11.0",
|
||||||
"form-data": "^4.0.0",
|
"form-data": "^4.0.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"naive-ui": "^2.26.4",
|
"naive-ui": "^2.27.0",
|
||||||
"pinia": "^2.0.11",
|
"pinia": "^2.0.12",
|
||||||
"print-js": "^1.6.0",
|
"print-js": "^1.6.0",
|
||||||
"qs": "^6.10.3",
|
"qs": "^6.10.3",
|
||||||
"soybean-admin-layout": "^1.0.4",
|
"soybean-admin-layout": "^1.0.4",
|
||||||
@ -52,17 +52,17 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@amap/amap-jsapi-types": "^0.0.8",
|
"@amap/amap-jsapi-types": "^0.0.8",
|
||||||
"@commitlint/cli": "^16.2.1",
|
"@commitlint/cli": "^16.2.3",
|
||||||
"@commitlint/config-conventional": "^16.2.1",
|
"@commitlint/config-conventional": "^16.2.1",
|
||||||
"@iconify/json": "^2.1.14",
|
"@iconify/json": "^2.1.21",
|
||||||
"@iconify/vue": "^3.1.4",
|
"@iconify/vue": "^3.2.0",
|
||||||
"@types/bmapgl": "^0.0.5",
|
"@types/bmapgl": "^0.0.5",
|
||||||
"@types/crypto-js": "^4.1.1",
|
"@types/crypto-js": "^4.1.1",
|
||||||
"@types/node": "^17.0.21",
|
"@types/node": "^17.0.23",
|
||||||
"@types/qs": "^6.9.7",
|
"@types/qs": "^6.9.7",
|
||||||
"@types/ua-parser-js": "^0.7.36",
|
"@types/ua-parser-js": "^0.7.36",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.14.0",
|
"@typescript-eslint/eslint-plugin": "^5.17.0",
|
||||||
"@typescript-eslint/parser": "^5.14.0",
|
"@typescript-eslint/parser": "^5.17.0",
|
||||||
"@vitejs/plugin-vue": "^2.2.4",
|
"@vitejs/plugin-vue": "^2.2.4",
|
||||||
"@vue/eslint-config-prettier": "^7.0.0",
|
"@vue/eslint-config-prettier": "^7.0.0",
|
||||||
"@vue/eslint-config-typescript": "^10.0.0",
|
"@vue/eslint-config-typescript": "^10.0.0",
|
||||||
@ -71,30 +71,30 @@
|
|||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"cz-conventional-changelog": "^3.3.0",
|
"cz-conventional-changelog": "^3.3.0",
|
||||||
"cz-customizable": "^6.3.0",
|
"cz-customizable": "^6.3.0",
|
||||||
"eslint": "^8.11.0",
|
"eslint": "^8.12.0",
|
||||||
"eslint-config-airbnb-base": "^15.0.0",
|
"eslint-config-airbnb-base": "^15.0.0",
|
||||||
"eslint-config-prettier": "^8.5.0",
|
"eslint-config-prettier": "^8.5.0",
|
||||||
"eslint-plugin-import": "^2.25.4",
|
"eslint-plugin-import": "^2.25.4",
|
||||||
"eslint-plugin-prettier": "^4.0.0",
|
"eslint-plugin-prettier": "^4.0.0",
|
||||||
"eslint-plugin-vue": "^8.5.0",
|
"eslint-plugin-vue": "^8.5.0",
|
||||||
"husky": "^7.0.4",
|
"husky": "^7.0.4",
|
||||||
"lint-staged": "^12.3.5",
|
"lint-staged": "^12.3.7",
|
||||||
"mockjs": "^1.1.0",
|
"mockjs": "^1.1.0",
|
||||||
"patch-package": "^6.4.7",
|
"patch-package": "^6.4.7",
|
||||||
"postinstall-postinstall": "^2.1.0",
|
"postinstall-postinstall": "^2.1.0",
|
||||||
"prettier": "^2.5.1",
|
"prettier": "^2.6.1",
|
||||||
"rollup-plugin-visualizer": "^5.6.0",
|
"rollup-plugin-visualizer": "^5.6.0",
|
||||||
"sass": "^1.49.9",
|
"sass": "^1.49.9",
|
||||||
"typescript": "~4.6.2",
|
"typescript": "^4.6.3",
|
||||||
"unplugin-icons": "^0.13.3",
|
"unplugin-icons": "^0.14.1",
|
||||||
"unplugin-vue-components": "^0.18.0",
|
"unplugin-vue-components": "^0.18.5",
|
||||||
"vite": "2.8.6",
|
"vite": "2.8.6",
|
||||||
"vite-plugin-html": "^3.1.0",
|
"vite-plugin-html": "^3.2.0",
|
||||||
"vite-plugin-html-template": "^1.1.2",
|
"vite-plugin-html-template": "^1.1.2",
|
||||||
"vite-plugin-mock": "^2.9.6",
|
"vite-plugin-mock": "^2.9.6",
|
||||||
"vite-plugin-windicss": "^1.8.3",
|
"vite-plugin-windicss": "^1.8.3",
|
||||||
"vue-tsc": "^0.32.1",
|
"vue-tsc": "^0.33.9",
|
||||||
"vueuc": "^0.4.27",
|
"vueuc": "^0.4.28",
|
||||||
"windicss": "^3.5.1"
|
"windicss": "^3.5.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
2805
pnpm-lock.yaml
2805
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
16
src/directives/permission.ts
Normal file
16
src/directives/permission.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import type { App, Directive } from 'vue';
|
||||||
|
import { useAuthStore } from '@/store';
|
||||||
|
|
||||||
|
export default function setupLoginDirective(app: App) {
|
||||||
|
const auth = useAuthStore();
|
||||||
|
|
||||||
|
const loginDirective: Directive<HTMLElement, Auth.RoleType | undefined> = {
|
||||||
|
mounted(el: HTMLElement, binding) {
|
||||||
|
if (binding.value !== auth.userInfo.userRole) {
|
||||||
|
el.remove();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
app.directive('login', loginDirective);
|
||||||
|
}
|
@ -15,9 +15,9 @@ export async function createDynamicRouteGuard(
|
|||||||
const route = useRouteStore();
|
const route = useRouteStore();
|
||||||
const isLogin = Boolean(getToken());
|
const isLogin = Boolean(getToken());
|
||||||
|
|
||||||
// 初始化动态路由
|
// 初始化权限路由
|
||||||
if (!route.isAddedDynamicRoute) {
|
if (!route.isInitedAuthRoute) {
|
||||||
// 未登录情况下直接回到登录页,登录成功后再加载动态路由
|
// 未登录情况下直接回到登录页,登录成功后再加载权限路由
|
||||||
if (!isLogin) {
|
if (!isLogin) {
|
||||||
if (to.name === routeName('login')) {
|
if (to.name === routeName('login')) {
|
||||||
next();
|
next();
|
||||||
@ -28,16 +28,16 @@ export async function createDynamicRouteGuard(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
await route.initDynamicRoute(router);
|
await route.initAuthRoute(router);
|
||||||
|
|
||||||
if (to.name === routeName('not-found-page')) {
|
if (to.name === routeName('not-found-page')) {
|
||||||
// 动态路由没有加载导致被not-found-page路由捕获,等待动态路由加载好了,回到之前的路由
|
// 动态路由没有加载导致被not-found-page路由捕获,等待权限路由加载好了,回到之前的路由
|
||||||
next({ path: to.fullPath, replace: true, query: to.query });
|
next({ path: to.fullPath, replace: true, query: to.query });
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 动态路由已经加载,仍然未找到,重定向到not-found
|
// 权限路由已经加载,仍然未找到,重定向到not-found
|
||||||
if (to.name === routeName('not-found-page')) {
|
if (to.name === routeName('not-found-page')) {
|
||||||
next({ name: routeName('not-found'), replace: true });
|
next({ name: routeName('not-found'), replace: true });
|
||||||
return false;
|
return false;
|
||||||
|
@ -21,3 +21,4 @@ export async function setupRouter(app: App) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export * from './routes';
|
export * from './routes';
|
||||||
|
export * from './modules';
|
||||||
|
15
src/router/modules/about.ts
Normal file
15
src/router/modules/about.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
const about: AuthRoute.Route = {
|
||||||
|
name: 'about',
|
||||||
|
path: '/about',
|
||||||
|
component: 'self',
|
||||||
|
meta: {
|
||||||
|
title: '关于',
|
||||||
|
requiresAuth: true,
|
||||||
|
singleLayout: 'basic',
|
||||||
|
permissions: ['super', 'admin', 'test'],
|
||||||
|
icon: 'fluent:book-information-24-regular',
|
||||||
|
order: 7,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default about;
|
44
src/router/modules/component.ts
Normal file
44
src/router/modules/component.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
const component: AuthRoute.Route = {
|
||||||
|
name: 'component',
|
||||||
|
path: '/component',
|
||||||
|
component: 'basic',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'component_button',
|
||||||
|
path: '/component/button',
|
||||||
|
component: 'self',
|
||||||
|
meta: {
|
||||||
|
title: '按钮',
|
||||||
|
requiresAuth: true,
|
||||||
|
icon: 'ic:baseline-radio-button-checked',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'component_card',
|
||||||
|
path: '/component/card',
|
||||||
|
component: 'self',
|
||||||
|
meta: {
|
||||||
|
title: '卡片',
|
||||||
|
requiresAuth: true,
|
||||||
|
icon: 'mdi:card-outline',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'component_table',
|
||||||
|
path: '/component/table',
|
||||||
|
component: 'self',
|
||||||
|
meta: {
|
||||||
|
title: '表格',
|
||||||
|
requiresAuth: true,
|
||||||
|
icon: 'mdi:table-large',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
meta: {
|
||||||
|
title: '组件示例',
|
||||||
|
icon: 'fluent:app-store-24-regular',
|
||||||
|
order: 3,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default component;
|
35
src/router/modules/dashboard.ts
Normal file
35
src/router/modules/dashboard.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
const dashboard: AuthRoute.Route = {
|
||||||
|
name: 'dashboard',
|
||||||
|
path: '/dashboard',
|
||||||
|
component: 'basic',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'dashboard_analysis',
|
||||||
|
path: '/dashboard/analysis',
|
||||||
|
component: 'self',
|
||||||
|
meta: {
|
||||||
|
title: '分析页',
|
||||||
|
requiresAuth: true,
|
||||||
|
icon: 'icon-park-outline:analysis',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'dashboard_workbench',
|
||||||
|
path: '/dashboard/workbench',
|
||||||
|
component: 'self',
|
||||||
|
meta: {
|
||||||
|
title: '工作台',
|
||||||
|
requiresAuth: true,
|
||||||
|
permissions: ['super', 'admin'],
|
||||||
|
icon: 'icon-park-outline:workbench',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
meta: {
|
||||||
|
title: '仪表盘',
|
||||||
|
icon: 'carbon:dashboard',
|
||||||
|
order: 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default dashboard;
|
54
src/router/modules/document.ts
Normal file
54
src/router/modules/document.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
const document: AuthRoute.Route = {
|
||||||
|
name: 'document',
|
||||||
|
path: '/document',
|
||||||
|
component: 'basic',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'document_vue',
|
||||||
|
path: '/document/vue',
|
||||||
|
component: 'self',
|
||||||
|
meta: {
|
||||||
|
title: 'vue文档',
|
||||||
|
requiresAuth: true,
|
||||||
|
icon: 'mdi:vuejs',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'document_vue-new',
|
||||||
|
path: '/document/vue-new',
|
||||||
|
component: 'self',
|
||||||
|
meta: {
|
||||||
|
title: 'vue文档(新版)',
|
||||||
|
requiresAuth: true,
|
||||||
|
icon: 'mdi:vuejs',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'document_vite',
|
||||||
|
path: '/document/vite',
|
||||||
|
component: 'self',
|
||||||
|
meta: {
|
||||||
|
title: 'vite文档',
|
||||||
|
requiresAuth: true,
|
||||||
|
icon: 'simple-icons:vite',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'document_project',
|
||||||
|
path: '/document/project',
|
||||||
|
meta: {
|
||||||
|
title: '项目文档(外链)',
|
||||||
|
requiresAuth: true,
|
||||||
|
icon: 'mdi:file-link-outline',
|
||||||
|
href: 'https://docs.soybean.pro/',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
meta: {
|
||||||
|
title: '文档',
|
||||||
|
icon: 'carbon:document',
|
||||||
|
order: 2,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default document;
|
44
src/router/modules/exception.ts
Normal file
44
src/router/modules/exception.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
const exception: AuthRoute.Route = {
|
||||||
|
name: 'exception',
|
||||||
|
path: '/exception',
|
||||||
|
component: 'basic',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'exception_403',
|
||||||
|
path: '/exception/403',
|
||||||
|
component: 'self',
|
||||||
|
meta: {
|
||||||
|
title: '异常页403',
|
||||||
|
requiresAuth: true,
|
||||||
|
icon: 'ic:baseline-block',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'exception_404',
|
||||||
|
path: '/exception/404',
|
||||||
|
component: 'self',
|
||||||
|
meta: {
|
||||||
|
title: '异常页404',
|
||||||
|
requiresAuth: true,
|
||||||
|
icon: 'ic:baseline-web-asset-off',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'exception_500',
|
||||||
|
path: '/exception/500',
|
||||||
|
component: 'self',
|
||||||
|
meta: {
|
||||||
|
title: '异常页500',
|
||||||
|
requiresAuth: true,
|
||||||
|
icon: 'ic:baseline-wifi-off',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
meta: {
|
||||||
|
title: '异常页',
|
||||||
|
icon: 'ant-design:exception-outlined',
|
||||||
|
order: 5,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default exception;
|
5
src/router/modules/index.ts
Normal file
5
src/router/modules/index.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { handleModuleRoutes } from '@/utils';
|
||||||
|
|
||||||
|
const modules = import.meta.globEager('./**/*.ts') as AuthRoute.RouteModule;
|
||||||
|
|
||||||
|
export const routes = handleModuleRoutes(modules);
|
56
src/router/modules/multi-menu.ts
Normal file
56
src/router/modules/multi-menu.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
const multiMenu: AuthRoute.Route = {
|
||||||
|
name: 'multi-menu',
|
||||||
|
path: '/multi-menu',
|
||||||
|
component: 'basic',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'multi-menu_first',
|
||||||
|
path: '/multi-menu/first',
|
||||||
|
component: 'multi',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'multi-menu_first_second',
|
||||||
|
path: '/multi-menu/first/second',
|
||||||
|
component: 'self',
|
||||||
|
meta: {
|
||||||
|
title: '二级菜单',
|
||||||
|
requiresAuth: true,
|
||||||
|
icon: 'ic:outline-menu',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'multi-menu_first_second-new',
|
||||||
|
path: '/multi-menu/first/second-new',
|
||||||
|
component: 'multi',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'multi-menu_first_second-new_third',
|
||||||
|
path: '/multi-menu/first/second-new/third',
|
||||||
|
component: 'self',
|
||||||
|
meta: {
|
||||||
|
title: '三级菜单',
|
||||||
|
requiresAuth: true,
|
||||||
|
icon: 'ic:outline-menu',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
meta: {
|
||||||
|
title: '二级菜单(有子菜单)',
|
||||||
|
icon: 'ic:outline-menu',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
meta: {
|
||||||
|
title: '一级菜单',
|
||||||
|
icon: 'ic:outline-menu',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
meta: {
|
||||||
|
title: '多级菜单',
|
||||||
|
icon: 'carbon:menu',
|
||||||
|
order: 6,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default multiMenu;
|
105
src/router/modules/plugin.ts
Normal file
105
src/router/modules/plugin.ts
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
const plugin: AuthRoute.Route = {
|
||||||
|
name: 'plugin',
|
||||||
|
path: '/plugin',
|
||||||
|
component: 'basic',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'plugin_map',
|
||||||
|
path: '/plugin/map',
|
||||||
|
component: 'self',
|
||||||
|
meta: {
|
||||||
|
title: '地图',
|
||||||
|
requiresAuth: true,
|
||||||
|
icon: 'mdi:map',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'plugin_video',
|
||||||
|
path: '/plugin/video',
|
||||||
|
component: 'self',
|
||||||
|
meta: {
|
||||||
|
title: '视频',
|
||||||
|
requiresAuth: true,
|
||||||
|
icon: 'mdi:video',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'plugin_editor',
|
||||||
|
path: '/plugin/editor',
|
||||||
|
component: 'multi',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
name: 'plugin_editor_quill',
|
||||||
|
path: '/plugin/editor/quill',
|
||||||
|
component: 'self',
|
||||||
|
meta: {
|
||||||
|
title: '富文本编辑器',
|
||||||
|
requiresAuth: true,
|
||||||
|
icon: 'mdi:file-document-edit-outline',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'plugin_editor_markdown',
|
||||||
|
path: '/plugin/editor/markdown',
|
||||||
|
component: 'self',
|
||||||
|
meta: {
|
||||||
|
title: 'markdown编辑器',
|
||||||
|
requiresAuth: true,
|
||||||
|
icon: 'ri:markdown-line',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
meta: {
|
||||||
|
title: '编辑器',
|
||||||
|
icon: 'icon-park-outline:editor',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'plugin_swiper',
|
||||||
|
path: '/plugin/swiper',
|
||||||
|
component: 'self',
|
||||||
|
meta: {
|
||||||
|
title: 'Swiper插件',
|
||||||
|
requiresAuth: true,
|
||||||
|
icon: 'simple-icons:swiper',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'plugin_copy',
|
||||||
|
path: '/plugin/copy',
|
||||||
|
component: 'self',
|
||||||
|
meta: {
|
||||||
|
title: '剪贴板',
|
||||||
|
requiresAuth: true,
|
||||||
|
icon: 'mdi:clipboard-outline',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'plugin_icon',
|
||||||
|
path: '/plugin/icon',
|
||||||
|
component: 'self',
|
||||||
|
meta: {
|
||||||
|
title: '图标',
|
||||||
|
requiresAuth: true,
|
||||||
|
icon: 'ic:baseline-insert-emoticon',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'plugin_print',
|
||||||
|
path: '/plugin/print',
|
||||||
|
component: 'self',
|
||||||
|
meta: {
|
||||||
|
title: '打印',
|
||||||
|
requiresAuth: true,
|
||||||
|
icon: 'ic:baseline-local-printshop',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
meta: {
|
||||||
|
title: '插件示例',
|
||||||
|
icon: 'clarity:plugin-line',
|
||||||
|
order: 4,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default plugin;
|
@ -1,5 +1,6 @@
|
|||||||
import type { Router } from 'vue-router';
|
import type { Router } from 'vue-router';
|
||||||
import { defineStore } from 'pinia';
|
import { defineStore } from 'pinia';
|
||||||
|
import { routes as staticRoutes } from '@/router';
|
||||||
import { fetchUserRoutes } from '@/service';
|
import { fetchUserRoutes } from '@/service';
|
||||||
import {
|
import {
|
||||||
getUserInfo,
|
getUserInfo,
|
||||||
@ -11,9 +12,15 @@ import {
|
|||||||
import { useTabStore } from '../tab';
|
import { useTabStore } from '../tab';
|
||||||
|
|
||||||
interface RouteState {
|
interface RouteState {
|
||||||
/** 是否添加过动态路由 */
|
/**
|
||||||
isAddedDynamicRoute: boolean;
|
* 权限路由模式:
|
||||||
/** 路由首页name */
|
* - static - 前端声明的静态
|
||||||
|
* - dynamic - 后端返回的动态
|
||||||
|
*/
|
||||||
|
authRouteMode: ImportMetaEnv['VITE_AUTH_ROUTE_MODE'];
|
||||||
|
/** 是否初始化了权限路由 */
|
||||||
|
isInitedAuthRoute: boolean;
|
||||||
|
/** 路由首页name(前端静态路由时生效,后端动态路由该值会被后端返回的值覆盖) */
|
||||||
routeHomeName: AuthRoute.RouteKey;
|
routeHomeName: AuthRoute.RouteKey;
|
||||||
/** 菜单 */
|
/** 菜单 */
|
||||||
menus: GlobalMenuOption[];
|
menus: GlobalMenuOption[];
|
||||||
@ -25,38 +32,69 @@ interface RouteState {
|
|||||||
|
|
||||||
export const useRouteStore = defineStore('route-store', {
|
export const useRouteStore = defineStore('route-store', {
|
||||||
state: (): RouteState => ({
|
state: (): RouteState => ({
|
||||||
isAddedDynamicRoute: false,
|
authRouteMode: import.meta.env.VITE_AUTH_ROUTE_MODE,
|
||||||
|
isInitedAuthRoute: false,
|
||||||
routeHomeName: 'dashboard_analysis',
|
routeHomeName: 'dashboard_analysis',
|
||||||
menus: [],
|
menus: [],
|
||||||
searchMenus: [],
|
searchMenus: [],
|
||||||
cacheRoutes: [],
|
cacheRoutes: [],
|
||||||
}),
|
}),
|
||||||
actions: {
|
actions: {
|
||||||
|
/**
|
||||||
|
* 处理权限路由
|
||||||
|
* @param routes - 权限路由
|
||||||
|
* @param router - 路由实例
|
||||||
|
*/
|
||||||
|
handleAuthRoutes(routes: AuthRoute.Route[], router: Router) {
|
||||||
|
this.menus = transformAuthRouteToMenu(routes);
|
||||||
|
this.searchMenus = transformAuthRoutesToSearchMenus(routes);
|
||||||
|
|
||||||
|
const vueRoutes = transformAuthRoutesToVueRoutes(routes);
|
||||||
|
vueRoutes.forEach((route) => {
|
||||||
|
router.addRoute(route);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.cacheRoutes = getCacheRoutes(vueRoutes);
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
* 初始化动态路由
|
* 初始化动态路由
|
||||||
* @param router - 路由实例
|
* @param router - 路由实例
|
||||||
*/
|
*/
|
||||||
async initDynamicRoute(router: Router) {
|
async initDynamicRoute(router: Router) {
|
||||||
const { initHomeTab } = useTabStore();
|
|
||||||
|
|
||||||
const { userId } = getUserInfo();
|
const { userId } = getUserInfo();
|
||||||
if (!userId) return;
|
|
||||||
const { data } = await fetchUserRoutes(userId);
|
const { data } = await fetchUserRoutes(userId);
|
||||||
if (data) {
|
if (data) {
|
||||||
this.routeHomeName = data.home;
|
this.routeHomeName = data.home;
|
||||||
this.menus = transformAuthRouteToMenu(data.routes);
|
this.handleAuthRoutes(data.routes, router);
|
||||||
this.searchMenus = transformAuthRoutesToSearchMenus(data.routes);
|
|
||||||
|
|
||||||
const vueRoutes = transformAuthRoutesToVueRoutes(data.routes);
|
|
||||||
vueRoutes.forEach((route) => {
|
|
||||||
router.addRoute(route);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.cacheRoutes = getCacheRoutes(vueRoutes);
|
|
||||||
|
|
||||||
initHomeTab(data.home, router);
|
|
||||||
this.isAddedDynamicRoute = true;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* 初始化静态路由
|
||||||
|
* @param router - 路由实例
|
||||||
|
*/
|
||||||
|
async initStaticRoute(router: Router) {
|
||||||
|
// 先根据用户权限过滤一下staticRoutes
|
||||||
|
|
||||||
|
this.handleAuthRoutes(staticRoutes, router);
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 初始化权限路由
|
||||||
|
* @param router - 路由实例
|
||||||
|
*/
|
||||||
|
async initAuthRoute(router: Router) {
|
||||||
|
const { initHomeTab } = useTabStore();
|
||||||
|
const { userId } = getUserInfo();
|
||||||
|
if (!userId) return;
|
||||||
|
|
||||||
|
const isDynamicRoute = this.authRouteMode === 'dynamic';
|
||||||
|
if (isDynamicRoute) {
|
||||||
|
await this.initDynamicRoute(router);
|
||||||
|
} else {
|
||||||
|
await this.initStaticRoute(router);
|
||||||
|
}
|
||||||
|
|
||||||
|
initHomeTab(this.routeHomeName, router);
|
||||||
|
this.isInitedAuthRoute = true;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
8
src/typings/business.d.ts
vendored
8
src/typings/business.d.ts
vendored
@ -1,13 +1,13 @@
|
|||||||
/** 用户相关模块 */
|
/** 用户相关模块 */
|
||||||
declare namespace Auth {
|
declare namespace Auth {
|
||||||
/**
|
/**
|
||||||
* 用户角色类型
|
* 用户角色类型(前端静态路由用角色类型进行路由权限的控制)
|
||||||
* - super: 超级管理员
|
* - super: 超级管理员(该权限具有所有路由数据)
|
||||||
* - admin: 管理员
|
* - admin: 管理员
|
||||||
* - test: 测试
|
* - test: 测试
|
||||||
* - visitor: 游客
|
* - normal: 普通用户
|
||||||
*/
|
*/
|
||||||
type RoleType = 'super' | 'admin' | 'test' | 'visitor';
|
type RoleType = 'super' | 'admin' | 'test' | 'normal';
|
||||||
|
|
||||||
/** 用户信息 */
|
/** 用户信息 */
|
||||||
interface UserInfo {
|
interface UserInfo {
|
||||||
|
12
src/typings/env.d.ts
vendored
12
src/typings/env.d.ts
vendored
@ -7,7 +7,12 @@ declare module '*.vue' {
|
|||||||
export default component;
|
export default component;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** env环境类型 */
|
/**
|
||||||
|
* env环境类型
|
||||||
|
* - dev: 后台开发环境
|
||||||
|
* - test: 后台测试环境
|
||||||
|
* - prod: 后台生产环境
|
||||||
|
*/
|
||||||
type EnvType = 'dev' | 'test' | 'prod';
|
type EnvType = 'dev' | 'test' | 'prod';
|
||||||
|
|
||||||
interface ImportMetaEnv {
|
interface ImportMetaEnv {
|
||||||
@ -21,6 +26,11 @@ interface ImportMetaEnv {
|
|||||||
readonly VITE_APP_DESC: string;
|
readonly VITE_APP_DESC: string;
|
||||||
/** 开发启动的服务端口号 */
|
/** 开发启动的服务端口号 */
|
||||||
readonly VITE_SERVER_PORT: string;
|
readonly VITE_SERVER_PORT: string;
|
||||||
|
/**
|
||||||
|
* 权限路由模式:
|
||||||
|
* - static - 前端声明的静态
|
||||||
|
* - dynamic - 后端返回的动态 */
|
||||||
|
readonly VITE_AUTH_ROUTE_MODE: 'static' | 'dynamic';
|
||||||
/** vite环境类型 */
|
/** vite环境类型 */
|
||||||
readonly VITE_ENV_TYPE?: EnvType;
|
readonly VITE_ENV_TYPE?: EnvType;
|
||||||
/** 开启请求代理 */
|
/** 开启请求代理 */
|
||||||
|
10
src/typings/route.d.ts
vendored
10
src/typings/route.d.ts
vendored
@ -73,7 +73,10 @@ declare namespace AuthRoute {
|
|||||||
singleLayout?: Extract<RouteComponent, 'basic' | 'blank'>;
|
singleLayout?: Extract<RouteComponent, 'basic' | 'blank'>;
|
||||||
/** 需要登录权限 */
|
/** 需要登录权限 */
|
||||||
requiresAuth?: boolean;
|
requiresAuth?: boolean;
|
||||||
/** 哪些类型的用户有权限才能访问的路由(空的话则表示不需要权限) */
|
/**
|
||||||
|
* 哪些类型的用户有权限才能访问的路由(空的话则表示不需要权限)
|
||||||
|
* @description 后端动态路由数据不需要该属性,直接由后端根据用户角色返回对应权限的路由数据
|
||||||
|
*/
|
||||||
permissions?: Auth.RoleType[];
|
permissions?: Auth.RoleType[];
|
||||||
/** 缓存页面 */
|
/** 缓存页面 */
|
||||||
keepAlive?: boolean;
|
keepAlive?: boolean;
|
||||||
@ -89,7 +92,7 @@ declare namespace AuthRoute {
|
|||||||
multi?: boolean;
|
multi?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** 单个路由的类型结构(后端返回此类型结构的路由) */
|
/** 单个路由的类型结构(动态路由模式:后端返回此类型结构的路由) */
|
||||||
interface Route {
|
interface Route {
|
||||||
/** 路由名称(路由唯一标识) */
|
/** 路由名称(路由唯一标识) */
|
||||||
name: RouteKey;
|
name: RouteKey;
|
||||||
@ -113,6 +116,9 @@ declare namespace AuthRoute {
|
|||||||
props?: boolean | Record<string, any> | ((to: any) => Record<string, any>);
|
props?: boolean | Record<string, any> | ((to: any) => Record<string, any>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 前端导入的路由模块 */
|
||||||
|
type RouteModule = Record<string, { default: AuthRoute.Route }>;
|
||||||
|
|
||||||
/** 单独一级路由的key (单独路由需要添加一个父级路由用于应用布局组件) */
|
/** 单独一级路由的key (单独路由需要添加一个父级路由用于应用布局组件) */
|
||||||
type SingleRouteKey = Exclude<
|
type SingleRouteKey = Exclude<
|
||||||
GetSingleRouteKey<RouteKey>,
|
GetSingleRouteKey<RouteKey>,
|
||||||
|
@ -37,7 +37,7 @@ export function getUserInfo() {
|
|||||||
userId: '',
|
userId: '',
|
||||||
userName: '',
|
userName: '',
|
||||||
userPhone: '',
|
userPhone: '',
|
||||||
userRole: 'visitor',
|
userRole: 'test',
|
||||||
};
|
};
|
||||||
const userInfo: Auth.UserInfo = getLocal<Auth.UserInfo>(EnumStorageKey['user-info']) || emptyInfo;
|
const userInfo: Auth.UserInfo = getLocal<Auth.UserInfo>(EnumStorageKey['user-info']) || emptyInfo;
|
||||||
return userInfo;
|
return userInfo;
|
||||||
|
@ -27,7 +27,7 @@ export function transformToTimeCountDown(seconds: number) {
|
|||||||
* @param start - 开始范围
|
* @param start - 开始范围
|
||||||
* @param end - 结束范围
|
* @param end - 结束范围
|
||||||
*/
|
*/
|
||||||
export function getRandomInterger(end: number, start = 0) {
|
export function getRandomInteger(end: number, start = 0) {
|
||||||
const range = end - start;
|
const range = end - start;
|
||||||
const random = Math.floor(Math.random() * range + start);
|
const random = Math.floor(Math.random() * range + start);
|
||||||
return random;
|
return random;
|
||||||
|
@ -5,7 +5,6 @@ const CryptoSecret = '__CryptoJS_Secret__';
|
|||||||
/**
|
/**
|
||||||
* 加密数据
|
* 加密数据
|
||||||
* @param data - 数据
|
* @param data - 数据
|
||||||
* @param secret - 密钥
|
|
||||||
*/
|
*/
|
||||||
export function encrypto(data: any) {
|
export function encrypto(data: any) {
|
||||||
const newData = JSON.stringify(data);
|
const newData = JSON.stringify(data);
|
||||||
@ -14,11 +13,10 @@ export function encrypto(data: any) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 解密数据
|
* 解密数据
|
||||||
* @param ciphertext - 密文
|
* @param cipherText - 密文
|
||||||
* @param secret - 密钥
|
|
||||||
*/
|
*/
|
||||||
export function decrypto(ciphertext: string) {
|
export function decrypto(cipherText: string) {
|
||||||
const bytes = CryptoJS.AES.decrypt(ciphertext, CryptoSecret);
|
const bytes = CryptoJS.AES.decrypt(cipherText, CryptoSecret);
|
||||||
const originalText = bytes.toString(CryptoJS.enc.Utf8);
|
const originalText = bytes.toString(CryptoJS.enc.Utf8);
|
||||||
if (originalText) {
|
if (originalText) {
|
||||||
return JSON.parse(originalText);
|
return JSON.parse(originalText);
|
||||||
|
23
src/utils/router/auth.ts
Normal file
23
src/utils/router/auth.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
|
/**
|
||||||
|
* 根据用户权限过滤路由
|
||||||
|
* @param routes - 权限路由
|
||||||
|
* @param permission - 权限
|
||||||
|
*/
|
||||||
|
export function filterAuthRoutesByUserPermission(routes: AuthRoute.Route[], permission: Auth.RoleType) {
|
||||||
|
const filters: AuthRoute.Route[] = [];
|
||||||
|
|
||||||
|
routes.forEach((route) => {
|
||||||
|
filterAuthRouteByUserPermission(route, permission);
|
||||||
|
});
|
||||||
|
return filters;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据用户权限过滤单个路由
|
||||||
|
* @param route - 单个权限路由
|
||||||
|
* @param permission - 权限
|
||||||
|
*/
|
||||||
|
function filterAuthRouteByUserPermission(route: AuthRoute.Route, permission: Auth.RoleType): AuthRoute.Route[] {
|
||||||
|
return [];
|
||||||
|
}
|
@ -33,7 +33,7 @@ export function transformAuthRoutesToSearchMenus(routes: AuthRoute.Route[], tree
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 将单个权限路由转换成vue路由
|
* 将单个权限路由转换成vue路由
|
||||||
* @param route - 权限路由
|
* @param item - 单个权限路由
|
||||||
*/
|
*/
|
||||||
function transformAuthRouteToVueRoute(item: AuthRoute.Route) {
|
function transformAuthRouteToVueRoute(item: AuthRoute.Route) {
|
||||||
const resultRoute: RouteRecordRaw[] = [];
|
const resultRoute: RouteRecordRaw[] = [];
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
export * from './module';
|
||||||
export * from './helpers';
|
export * from './helpers';
|
||||||
export * from './cache';
|
export * from './cache';
|
||||||
export * from './menu';
|
export * from './menu';
|
||||||
|
28
src/utils/router/module.ts
Normal file
28
src/utils/router/module.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { consoleError } from '../common';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限路由排序
|
||||||
|
* @param routes - 权限路由
|
||||||
|
*/
|
||||||
|
function sortRoutes(routes: AuthRoute.Route[]) {
|
||||||
|
return routes.sort((next, pre) => Number(next.meta?.order) - Number(pre.meta?.order));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理全部导入的路由模块
|
||||||
|
* @param modules - 路由模块
|
||||||
|
*/
|
||||||
|
export function handleModuleRoutes(modules: AuthRoute.RouteModule) {
|
||||||
|
const routes: AuthRoute.Route[] = [];
|
||||||
|
|
||||||
|
Object.keys(modules).forEach((key) => {
|
||||||
|
const item = modules[key].default;
|
||||||
|
if (item) {
|
||||||
|
routes.push(item);
|
||||||
|
} else {
|
||||||
|
consoleError(`路由模块解析出错: key = ${key}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return sortRoutes(routes);
|
||||||
|
}
|
@ -18,7 +18,7 @@
|
|||||||
import { ref, onMounted } from 'vue';
|
import { ref, onMounted } from 'vue';
|
||||||
import type { DataTableColumn } from 'naive-ui';
|
import type { DataTableColumn } from 'naive-ui';
|
||||||
import { useLoadingEmpty } from '@/hooks';
|
import { useLoadingEmpty } from '@/hooks';
|
||||||
import { getRandomInterger } from '@/utils';
|
import { getRandomInteger } from '@/utils';
|
||||||
|
|
||||||
interface DataSource {
|
interface DataSource {
|
||||||
name: string;
|
name: string;
|
||||||
@ -52,7 +52,7 @@ function createDataSource(): DataSource[] {
|
|||||||
.map((_item, index) => {
|
.map((_item, index) => {
|
||||||
return {
|
return {
|
||||||
name: `Name${index}`,
|
name: `Name${index}`,
|
||||||
age: getRandomInterger(30, 20),
|
age: getRandomInteger(30, 20),
|
||||||
address: '中国',
|
address: '中国',
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -5,6 +5,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const src = 'https://www.naiveui.com';
|
const src = 'https://www.naiveui.com/zh-CN/os-theme/docs/introduction';
|
||||||
</script>
|
</script>
|
||||||
<style scoped></style>
|
<style scoped></style>
|
||||||
|
@ -5,8 +5,8 @@
|
|||||||
"src/**/*",
|
"src/**/*",
|
||||||
"src/**/*.vue",
|
"src/**/*.vue",
|
||||||
"vite.config.*",
|
"vite.config.*",
|
||||||
"mock/**/*.ts",
|
|
||||||
"build/**/*.ts",
|
"build/**/*.ts",
|
||||||
|
"mock/**/*.ts",
|
||||||
".env-config.ts",
|
".env-config.ts",
|
||||||
"components.d.ts"
|
"components.d.ts"
|
||||||
],
|
],
|
||||||
|
Loading…
Reference in New Issue
Block a user