Ver código fonte

简易路由

tongshangming 3 anos atrás
pai
commit
75b238cfb4

+ 3 - 0
package.json

@@ -14,13 +14,16 @@
     "@vueuse/core": "^9.1.1",
     "axios": "^0.27.2",
     "element-plus": "^2.2.15",
+    "nprogress": "^0.2.0",
     "pinia": "^2.0.17",
     "vue": "^3.2.37",
     "vue-router": "^4.1.3"
   },
   "devDependencies": {
+    "@iconify-json/ep": "^1.1.7",
     "@rushstack/eslint-patch": "^1.1.4",
     "@types/node": "^16.11.47",
+    "@types/nprogress": "^0.2.0",
     "@vitejs/plugin-vue": "^3.0.1",
     "@vitejs/plugin-vue-jsx": "^2.0.0",
     "@vue/eslint-config-prettier": "^7.0.0",

+ 20 - 0
pnpm-lock.yaml

@@ -2,8 +2,10 @@ lockfileVersion: 5.4
 
 specifiers:
   '@element-plus/icons-vue': ^2.0.9
+  '@iconify-json/ep': ^1.1.7
   '@rushstack/eslint-patch': ^1.1.4
   '@types/node': ^16.11.47
+  '@types/nprogress': ^0.2.0
   '@vitejs/plugin-vue': ^3.0.1
   '@vitejs/plugin-vue-jsx': ^2.0.0
   '@vue/eslint-config-prettier': ^7.0.0
@@ -15,6 +17,7 @@ specifiers:
   eslint: ^8.21.0
   eslint-plugin-vue: ^9.3.0
   npm-run-all: ^4.1.5
+  nprogress: ^0.2.0
   pinia: ^2.0.17
   prettier: ^2.7.1
   typescript: ~4.7.4
@@ -32,13 +35,16 @@ dependencies:
   '@vueuse/core': 9.1.1_vue@3.2.38
   axios: 0.27.2
   element-plus: 2.2.15_vue@3.2.38
+  nprogress: 0.2.0
   pinia: 2.0.21_fl6vay66s5mfyioevoftbjhvcm
   vue: 3.2.38
   vue-router: 4.1.5_vue@3.2.38
 
 devDependencies:
+  '@iconify-json/ep': 1.1.7
   '@rushstack/eslint-patch': 1.1.4
   '@types/node': 16.11.56
+  '@types/nprogress': 0.2.0
   '@vitejs/plugin-vue': 3.0.3_vite@3.0.9+vue@3.2.38
   '@vitejs/plugin-vue-jsx': 2.0.0_vite@3.0.9+vue@3.2.38
   '@vue/eslint-config-prettier': 7.0.0_bxpuzolsxufkw2ipnoyzzxnyrm
@@ -439,6 +445,12 @@ packages:
     resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==}
     dev: true
 
+  /@iconify-json/ep/1.1.7:
+    resolution: {integrity: sha512-GhXWVKalXFlrGgfrCXAgqBre5hv3pPAknuxyywmjamcrL5gl5Mq9WOZtuhb4cB6cJ5pMiKOMtegt73FheqWscA==}
+    dependencies:
+      '@iconify/types': 1.1.0
+    dev: true
+
   /@iconify/types/1.1.0:
     resolution: {integrity: sha512-Jh0llaK2LRXQoYsorIH8maClebsnzTcve+7U3rQUSnC11X4jtPnFuyatqFLvMxZ8MLG8dB4zfHsbPfuvxluONw==}
     dev: true
@@ -553,6 +565,10 @@ packages:
     resolution: {integrity: sha512-aFcUkv7EddxxOa/9f74DINReQ/celqH8DiB3fRYgVDM2Xm5QJL8sl80QKuAnGvwAsMn+H3IFA6WCrQh1CY7m1A==}
     dev: true
 
+  /@types/nprogress/0.2.0:
+    resolution: {integrity: sha512-1cYJrqq9GezNFPsWTZpFut/d4CjpZqA0vhqDUPFWYKF1oIyBz5qnoYMzR+0C/T96t3ebLAC1SSnwrVOm5/j74A==}
+    dev: true
+
   /@types/web-bluetooth/0.0.15:
     resolution: {integrity: sha512-w7hEHXnPMEZ+4nGKl/KDRVpxkwYxYExuHOYXyzIzCDzEZ9ZCGMAewulr9IqJu2LR4N37fcnb1XVeuZ09qgOxhA==}
 
@@ -2556,6 +2572,10 @@ packages:
       path-key: 3.1.1
     dev: true
 
+  /nprogress/0.2.0:
+    resolution: {integrity: sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==}
+    dev: false
+
   /nth-check/2.1.1:
     resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
     dependencies:

+ 8 - 0
src/components.d.ts

@@ -9,6 +9,7 @@ declare module '@vue/runtime-core' {
   export interface GlobalComponents {
     ElAside: typeof import('element-plus/es')['ElAside']
     ElAvatar: typeof import('element-plus/es')['ElAvatar']
+    ElButton: typeof import('element-plus/es')['ElButton']
     ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
     ElContainer: typeof import('element-plus/es')['ElContainer']
     ElDropdown: typeof import('element-plus/es')['ElDropdown']
@@ -22,6 +23,7 @@ declare module '@vue/runtime-core' {
     ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
     ElMenuItemGroup: typeof import('element-plus/es')['ElMenuItemGroup']
     ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
+    Exception: typeof import('./components/Exception.vue')['default']
     GlobalFooter: typeof import('./components/GlobalFooter.vue')['default']
     GlobalHeader: typeof import('./components/GlobalHeader.vue')['default']
     GlobalMenu: typeof import('./components/GlobalMenu.vue')['default']
@@ -31,6 +33,12 @@ declare module '@vue/runtime-core' {
     IconEcosystem: typeof import('./components/icons/IconEcosystem.vue')['default']
     IconSupport: typeof import('./components/icons/IconSupport.vue')['default']
     IconTooling: typeof import('./components/icons/IconTooling.vue')['default']
+    'IEp-Menu': typeof import('~icons/ep/-menu')['default']
+    IEpArrowDown: typeof import('~icons/ep/arrow-down')['default']
+    IEpDocument: typeof import('~icons/ep/document')['default']
+    IEpLocation: typeof import('~icons/ep/location')['default']
+    IEpMenu: typeof import('~icons/ep/menu')['default']
+    IEpSetting: typeof import('~icons/ep/setting')['default']
     RouterLink: typeof import('vue-router')['RouterLink']
     RouterView: typeof import('vue-router')['RouterView']
     TheWelcome: typeof import('./components/TheWelcome.vue')['default']

+ 9 - 0
src/components/Exception.vue

@@ -0,0 +1,9 @@
+<script setup></script>
+
+<template>
+  <div class="w-full h-full flex justify-center items-center">
+    <router-link :to="{ name: 'home' }">
+      <el-button type="primary">回到首页</el-button>
+    </router-link>
+  </div>
+</template>

+ 1 - 1
src/components/GlobalHeader.vue

@@ -9,7 +9,7 @@
         <span class="el-dropdown-link">
           Dropdown List
           <el-icon class="el-icon--right">
-            <arrow-down />
+            <i-ep-arrow-down />
           </el-icon>
         </span>
         <template #dropdown>

+ 29 - 29
src/components/GlobalMenu.vue

@@ -1,36 +1,36 @@
-<script setup></script>
+<script setup lang="ts">
+import { useRouterStore } from '@/stores/router'
+
+const routerStore = useRouterStore()
+const menuRouter = routerStore.menuRouter
+const generatorMenu = (routes: any[]) => {
+  routes.forEach(route => {
+    if (route.children && route.children.length) {
+      route.menuName = 'el-sub-menu'
+      generatorMenu(route.children)
+    } else {
+      route.menuName = 'el-menu-item'
+    }
+  })
+}
+
+generatorMenu(menuRouter)
+</script>
 
 <template>
-  <el-menu default-active="2" class="layout-menu" @open="handleOpen" @close="handleClose">
-    <el-sub-menu index="1">
+  <el-menu default-active="2" router class="layout-menu">
+    <component :is="item.menuName" :index="item.path" v-for="item in menuRouter">
       <template #title>
-        <el-icon><location /></el-icon>
-        <span>Navigator One</span>
+        <el-icon><component :is="item.meta?.icon"></component></el-icon>
+        <span>{{ item.meta.title }}</span>
       </template>
-      <el-menu-item-group title="Group One">
-        <el-menu-item index="1-1">item one</el-menu-item>
-        <el-menu-item index="1-2">item two</el-menu-item>
-      </el-menu-item-group>
-      <el-menu-item-group title="Group Two">
-        <el-menu-item index="1-3">item three</el-menu-item>
-      </el-menu-item-group>
-      <el-sub-menu index="1-4">
-        <template #title>item four</template>
-        <el-menu-item index="1-4-1">item one</el-menu-item>
-      </el-sub-menu>
-    </el-sub-menu>
-    <el-menu-item index="2">
-      <el-icon><icon-menu /></el-icon>
-      <span>Navigator Two</span>
-    </el-menu-item>
-    <el-menu-item index="3" disabled>
-      <el-icon><document /></el-icon>
-      <span>Navigator Three</span>
-    </el-menu-item>
-    <el-menu-item index="4">
-      <el-icon><setting /></el-icon>
-      <span>Navigator Four</span>
-    </el-menu-item>
+      <el-menu-item :index="subItem.path" v-for="subItem in item.children">
+        <template #title>
+          <el-icon><component :is="subItem.meta?.icon"></component></el-icon>
+          <span>{{ subItem.meta.title }}</span>
+        </template>
+      </el-menu-item>
+    </component>
   </el-menu>
 </template>
 

+ 5 - 0
src/layouts/BlankLayout.vue

@@ -0,0 +1,5 @@
+<script setup></script>
+
+<template>
+  <router-view />
+</template>

+ 57 - 0
src/router/asyncRouter.ts

@@ -0,0 +1,57 @@
+import BlankLayout from '@/layouts/BlankLayout.vue'
+import BasicLayout from '@/layouts/BasicLayout.vue'
+import type { RouteRecordRaw } from 'vue-router'
+
+const asyncRouter:RouteRecordRaw[] = [
+  {
+    path: 'home',
+    name: 'home',
+    component: () => import('@/views/exception/403.vue'),
+    meta: {
+      title: '首页'
+    }
+  },
+  {
+    path: 'setting',
+    name: 'setting',
+    component: () => import('@/views/exception/403.vue'),
+    meta: {
+      title: '设置'
+    }
+  },
+  {
+    path: '/exception',
+    name: 'exception',
+    meta: {
+      title: '异常页'
+    },
+    children: [
+      {
+        path: '403',
+        name: '403',
+        component: () => import('@/views/exception/403.vue'),
+        meta: {
+          title: '403'
+        }
+      },
+      {
+        path: '404',
+        name: '404',
+        component: () => import('@/views/exception/404.vue'),
+        meta: {
+          title: '404'
+        }
+      },
+      {
+        path: '500',
+        name: '500',
+        component: () => import('@/views/exception/404.vue'),
+        meta: {
+          title: '500'
+        }
+      },
+    ]
+  },
+]
+
+export default asyncRouter

+ 50 - 0
src/router/constantRouter.ts

@@ -0,0 +1,50 @@
+import BlankLayout from '@/layouts/BlankLayout.vue'
+import BasicLayout from '@/layouts/BasicLayout.vue'
+import type { RouteRecordRaw } from 'vue-router'
+
+const constantRouter:RouteRecordRaw[] = [
+  {
+    path: '/',
+    name: 'layout',
+    component: BasicLayout,
+    children: []
+  },
+  {
+    path: '/login',
+    name: 'login',
+    component: () => import('@/views/user/Login.vue'),
+  },
+  // {
+  //   path: '/exception',
+  //   name: 'exception',
+  //   component: BasicLayout,
+  //   meta: {
+  //     title: '异常页'
+  //   },
+  //   children: [
+  //     {
+  //       path: '403',
+  //       name: '403',
+  //       component: () => import('@/views/exception/403.vue'),
+  //       meta: {
+  //         title: '403'
+  //       }
+  //     },
+  //     {
+  //       path: '404',
+  //       name: '404',
+  //       component: () => import('@/views/exception/404.vue'),
+  //       meta: {
+  //         title: '404'
+  //       }
+  //     },
+  //     {
+  //       path: '500',
+  //       name: '500',
+  //       component: () => import('@/views/exception/404.vue'),
+  //     },
+  //   ]
+  // },
+]
+
+export default constantRouter

+ 3 - 0
src/router/generator.ts

@@ -0,0 +1,3 @@
+// export const hasRole = (role, route) => {
+
+// }

+ 50 - 0
src/router/guard.ts

@@ -0,0 +1,50 @@
+import type { Router } from 'vue-router'
+import { useRouterStore } from '@/stores/router'
+import { useUserStore } from '@/stores/user'
+
+import NProgress from 'nprogress'
+import 'nprogress/nprogress.css'
+
+const title = useTitle()
+
+
+const createRouterGuard = (router:Router) => {
+  
+
+  router.beforeEach(async (to, from) => {
+    NProgress.start()
+    if (to?.meta?.title) {
+      title.value = to?.meta?.title
+    }
+
+    const token = 'test'
+
+    if (token) {
+      if (to.path === '/login') {
+        return '/'
+      } else {
+        const userStore = useUserStore()
+        const routerStore = useRouterStore()
+
+        if (!userStore.flag) {
+          userStore.getUserInfo()
+          
+          const accessedRouters = await routerStore.generatorRouter('')
+          accessedRouters.forEach(route => {
+            router.addRoute('layout',route)
+          })
+          console.log(router.getRoutes());
+        }
+        return true
+      }
+    } else {
+      
+    }
+  })
+
+  router.afterEach(to => {
+    NProgress.done()
+  })
+}
+
+export default createRouterGuard

+ 8 - 14
src/router/index.ts

@@ -1,24 +1,18 @@
+import type { App } from 'vue'
 import { createRouter, createWebHistory } from 'vue-router'
-import HomeView from '../views/HomeView.vue'
 import BasicLayout from '@/layouts/BasicLayout.vue'
+import constantRouter from './constantRouter'
+import createRouterGuard from './guard'
+export { default as asyncRouter } from './asyncRouter'
 
 const router = createRouter({
   history: createWebHistory(import.meta.env.BASE_URL),
   routes: [
-    {
-      path: '/',
-      name: 'home',
-      component: BasicLayout
-    },
-    {
-      path: '/about',
-      name: 'about',
-      // route level code-splitting
-      // this generates a separate chunk (About.[hash].js) for this route
-      // which is lazy-loaded when the route is visited.
-      component: () => import('../views/AboutView.vue')
-    }
+    ...constantRouter
   ]
 })
 
+createRouterGuard(router)
+
+export { constantRouter }
 export default router

+ 17 - 0
src/router/router.d.ts

@@ -0,0 +1,17 @@
+import 'vue-router'
+
+declare module 'vue-router' {
+  interface RouteMeta {
+    permission?: any[],
+    role?: any[],
+    title: string,
+    icon?: string
+  }
+}
+
+declare module 'vue-router' {
+  interface RouteRecordRaw {
+    hidden?: boolean,
+    menuName?: string
+  }
+}

+ 47 - 0
src/stores/router.ts

@@ -0,0 +1,47 @@
+import type { RouteRecordRaw } from 'vue-router'
+import {constantRouter, asyncRouter} from '@/router'
+
+function hasPermission (permission:any[], route:RouteRecordRaw) {
+  if (route.meta && route.meta.permission) {
+    if (permission === undefined) {
+      return false
+    }
+    let flag = false
+    for (let i = 0, len = permission.length; i < len; i++) {
+      flag = route.meta.permission.includes(permission[i])
+      if (flag) {
+        return true
+      }
+    }
+    return false
+  }
+  return true
+}
+
+function filterAsyncRouter (routerMap:RouteRecordRaw[], role:any) {
+  const accessedRouters = routerMap.filter(route => {
+    if (hasPermission(role.permissionList, route)) {
+      if (route.children && route.children.length) {
+        route.children = filterAsyncRouter(route.children, role)
+      }
+      return true
+    }
+    return false
+  })
+  return accessedRouters
+}
+
+export const useRouterStore = defineStore({
+  id: 'router',
+  state: () => ({
+    menuRouter: <any>[]
+  }),
+  actions: {
+    generatorRouter(role: any):Promise<RouteRecordRaw[]> {
+      return new Promise(resolve => {
+        this.menuRouter = filterAsyncRouter(asyncRouter, role)
+        resolve(this.menuRouter)
+      })
+    }
+  }
+})

+ 13 - 0
src/stores/user.ts

@@ -0,0 +1,13 @@
+
+export const useUserStore = defineStore({
+  id: 'user',
+  state: () => ({
+    user: {},
+    flag: false
+  }),
+  actions: {
+    getUserInfo() {
+      this.flag = true
+    }
+  }
+})

+ 5 - 0
src/views/exception/403.vue

@@ -0,0 +1,5 @@
+<script setup lang="ts"></script>
+
+<template>
+  <exception></exception>
+</template>

+ 3 - 0
src/views/exception/404.vue

@@ -0,0 +1,3 @@
+<template>
+  <exception></exception>
+</template>

+ 3 - 0
src/views/exception/500.vue

@@ -0,0 +1,3 @@
+<template>
+  <exception></exception>
+</template>

+ 1 - 0
src/views/user/Login.vue

@@ -0,0 +1 @@
+<template>login</template>