ソースを参照

feat: 实现动态权限路由加载,边侧栏的一级权限路由完成,增加项目初始化等待页面,增加404页面的重定向,增加测试权限相关的页面

zhusiqing 3 年 前
コミット
fa5a08e015

+ 5 - 0
CHANGELOG.md

@@ -5,6 +5,11 @@
 - ant-design-vue改为按需加载
 - 动态导入ant-design-vue和@ant-design/icons-vue相关组件
 - 顶部导航栏布局大体完成
+- 实现动态权限路由加载
+- 边侧栏的一级权限路由完成
+- 增加项目初始化等待页面
+- 增加404页面的重定向
+- 增加测试权限相关的页面
 
 ## 0.3.0(2021-12-17)
 

+ 2 - 0
components.d.ts

@@ -15,7 +15,9 @@ declare module 'vue' {
     AMenu: typeof import('ant-design-vue/es')['Menu']
     AMenuDivider: typeof import('ant-design-vue/es')['MenuDivider']
     AMenuItem: typeof import('ant-design-vue/es')['MenuItem']
+    ASpin: typeof import('ant-design-vue/es')['Spin']
     HelloWorld: typeof import('./src/components/HelloWorld.vue')['default']
+    InitLoading: typeof import('./src/components/InitLoading.vue')['default']
     UploadOutlined: typeof import('@ant-design/icons-vue')['UploadOutlined']
     UserOutlined: typeof import('@ant-design/icons-vue')['UserOutlined']
     VideoCameraOutlined: typeof import('@ant-design/icons-vue')['VideoCameraOutlined']

+ 3 - 2
log.md

@@ -3,7 +3,8 @@
 - [x] UI框架选取并引入
 - [x] 头部布局组件
 - [x] 底部布局组件
-- [ ] 边侧栏布局组件
+- [x] 边侧栏布局组件
+- [ ] 权限路由设计
 - [ ] 登录页
 - [ ] 登录态相关处理
-- [ ] 权限路由设计
+

+ 25 - 2
src/App.vue

@@ -1,11 +1,34 @@
 <script setup lang="ts">
 // This starter template is using Vue 3 <script setup> SFCs
 // Check out https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup
-  import { RouterView } from 'vue-router';
+  import { RouterView, useRouter, useRoute } from 'vue-router';
+  import { AppRoute, NotFoundRoute } from '@/router/base';
+  import { authRoutes } from '@/router/auth';
+  import { AppStore } from '@/store/modules/app';
+
+  const router = useRouter()
+  const route = useRoute()
+  const store = AppStore()
+  // TODO: 等二级路由添加好后,再增加无需权限时的判断
+  setTimeout(() => {
+    const authFilterRoutes = authRoutes.filter(route => Number(route.meta?.auth) === 1)
+    const childrenRoutes = [...authFilterRoutes, ...AppRoute.children as []]
+    router.removeRoute(AppRoute.name as string)
+    router.addRoute({
+      ...AppRoute,
+      children: childrenRoutes
+    })
+    router.addRoute(NotFoundRoute)
+    store.updateLoading(false)
+    store.updateAuthRoutes(authFilterRoutes)
+    router.replace(route.fullPath)
+  }, 2e3);
 </script>
 
 <template>
-  <RouterView />
+  <init-loading>
+    <RouterView />
+  </init-loading>
 </template>
 
 <style lang="less">

+ 29 - 0
src/components/InitLoading.vue

@@ -0,0 +1,29 @@
+<template>
+  <template v-if="isLoading">
+    <div class="init-loading">
+      <a-spin
+        tip="请稍等,加载中..."
+        size="large"
+      />
+    </div>
+  </template>
+  <template v-else>
+    <slot />
+  </template>
+</template>
+<script lang="ts" setup>
+  import { ref, computed } from 'vue'
+  import { AppStore } from '@/store/modules/app';
+  const store = AppStore()
+  const isLoading = computed(() => {
+    return store.isLoading
+  })
+</script>
+<style lang="less" scoped>
+.init-loading {
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+</style>

+ 30 - 27
src/layouts/app/Sider.vue

@@ -3,8 +3,6 @@
   <a-layout-sider
     breakpoint="lg"
     collapsed-width="80"
-    @collapse="onCollapse"
-    @breakpoint="onBreakpoint"
   >
     <div
       class="logo"
@@ -17,41 +15,46 @@
       theme="dark"
       mode="inline"
     >
-      <a-menu-item key="1">
+      <a-menu-item
+        v-for="menu in menus"
+        :key="menu.name"
+        @click="toPageHandle(menu.name)"
+      >
         <user-outlined />
-        <span class="nav-text">nav 1</span>
-      </a-menu-item>
-      <a-menu-item key="2">
-        <video-camera-outlined />
-        <span class="nav-text">nav 2</span>
-      </a-menu-item>
-      <a-menu-item key="3">
-        <upload-outlined />
-        <span class="nav-text">nav 3</span>
-      </a-menu-item>
-      <a-menu-item key="4">
-        <user-outlined />
-        <span class="nav-text">nav 4</span>
+        <span class="nav-text">{{ menu.name }}</span>
       </a-menu-item>
     </a-menu>
   </a-layout-sider>
 </template>
 <script lang="ts" setup>
-  import { ref } from 'vue';
-  import { useRouter } from 'vue-router';
-  const selectedKeys = ref<string[]>(['4'])
-  const router = useRouter()
+  import { ref, computed, watch, toRef } from 'vue';
+  import { useRouter, useRoute, onBeforeRouteUpdate } from 'vue-router';
+  import { AppStore } from '@/store/modules/app';
+  import type { RouteRecordName, RouteLocationNormalized } from 'vue-router'
 
-  const onCollapse = (collapsed: boolean, type: string) => {
-    console.log(collapsed, type);
-  };
-
-  const onBreakpoint = (broken: boolean) => {
-    console.log(broken);
-  };
+  const router = useRouter()
+  const route = useRoute()
+  const store = AppStore()
+  const selectedKeys = ref<string[]>([])
+  const menus = computed(() => store.authRoutes)
+  const matchRouteHandle = (to: RouteLocationNormalized) => {
+    menus.value.forEach(el => {
+      if (el.name === to.name) {
+        console.log(el, to);
+        selectedKeys.value = [to.name as string]
+      }
+    })
+  }
+  // onBeforeRouteUpdate(to => {
+  //   matchRouteHandle(to)
+  // })
+  matchRouteHandle(route)
   const toHomeHandle = () => {
     router.push('/')
   }
+  const toPageHandle = (name: RouteRecordName | undefined) => {
+    router.push({ name })
+  }
 </script>
 
 <style lang="less" scoped>

+ 26 - 2
src/pages/Error/index.vue

@@ -1,10 +1,34 @@
 <template>
   <div class="error">
     error page
+    <div>
+      <a-button
+        type="primary"
+        @click="toBackHandle"
+      >
+        返回
+      </a-button>
+    </div>
   </div>
 </template>
-<script setup>
-
+<script lang="ts" setup>
+import { ref } from 'vue';
+import { useRoute, useRouter } from 'vue-router';
+const route = useRoute()
+const router = useRouter()
+const text = ref('error page')
+const { redirectedFrom } = route
+if (redirectedFrom?.name === 'NotFound') {
+  text.value = '404 page'
+}
+const toBackHandle = () => {
+  // 这里判断大于2是因为window.open跳转的是长度是1,新开标签页再输入地址是长度是2
+  if (history.length > 2) {
+    router.back()
+  } else {
+    window.location.href = '/'
+  }
+}
 </script>
 <style scoped>
 

+ 0 - 9
src/pages/Home/index.vue

@@ -13,7 +13,6 @@
 <script lang="ts" setup>
 import { ref, watch } from 'vue';
 import { useClipboard } from '@vueuse/core';
-import { AppStore } from '@/store/modules/app';
 
 const str = ref('这是一段可以点击按钮复制的文字')
 const { text, copy, copied, isSupported } = useClipboard({ source: str })
@@ -30,14 +29,6 @@ watch(copied, (v) => {
   }
 })
 
-const store = AppStore()
-store.updateLoading(true)
-console.log('store >>>', store.isLoading)
-setTimeout(() => {
-  store.updateLoading(false)
-  console.log('store >>>', store.isLoading)
-}, 2e3);
-
 </script>
 <style scoped>
 .home {

+ 15 - 0
src/pages/Test/index.vue

@@ -0,0 +1,15 @@
+<template>
+  <div>{{ text }}</div>
+</template>
+<script lang="ts" setup>
+  import { ref, onUpdated } from "vue";
+  import { useRoute } from 'vue-router'
+  const route = useRoute()
+  const text = ref(route.name)
+  onUpdated(() => {
+    text.value = route.name
+  })
+</script>
+<style scoped>
+
+</style>

+ 37 - 0
src/router/auth.ts

@@ -0,0 +1,37 @@
+import { RouteRecordRaw } from 'vue-router';
+import Test from '@/pages/Test/index.vue'
+
+export const authRoutes: RouteRecordRaw[] = [
+  {
+    path: 'test1',
+    name: 'Test1',
+    component: Test,
+    meta: {
+      auth: 1
+    }
+  },
+  {
+    path: 'test2',
+    name: 'Test2',
+    component: Test,
+    meta: {
+      auth: 1
+    }
+  },
+  {
+    path: 'test3',
+    name: 'Test3',
+    component: () => import('@/pages/Test/index.vue'),
+    meta: {
+      auth: 1
+    }
+  },
+  {
+    path: 'test4',
+    name: 'Test4',
+    component: () => import('@/pages/Test/index.vue'),
+    meta: {
+      auth: 2
+    }
+  }
+]

+ 17 - 10
src/router/base.ts

@@ -2,14 +2,14 @@ import type { RouteRecordRaw } from 'vue-router';
 import ErrorPage from '@/pages/Error/index.vue';
 import AppLayout from '@/layouts/app/index.vue';
 import { EnumPage } from './constant';
-const RootRoute: RouteRecordRaw = {
+export const RootRoute: RouteRecordRaw = {
   path: '/',
   name: 'Root',
-  redirect: EnumPage.Home
+  redirect: EnumPage.App
 }
 
-const HomeRoute: RouteRecordRaw = {
-  path: '/app',
+export const AppRoute: RouteRecordRaw = {
+  path: EnumPage.App,
   name: 'App',
   redirect: '/app/home',
   component: AppLayout,
@@ -24,7 +24,7 @@ const HomeRoute: RouteRecordRaw = {
     }
   ]
 }
-const LoginRoute: RouteRecordRaw = {
+export const LoginRoute: RouteRecordRaw = {
   path: '/login',
   name: 'Login',
   component: () => import('@/pages/Login/index.vue'),
@@ -33,18 +33,25 @@ const LoginRoute: RouteRecordRaw = {
   }
 }
 
-const ErrorRoute: RouteRecordRaw = {
+export const ErrorRoute: RouteRecordRaw = {
   path: '/error',
   name: 'Error',
   component: ErrorPage,
   meta: {
-    title: '错误页'
+    title: '异常页'
   }
 }
 
-export const baseRoutes = [
+export const NotFoundRoute: RouteRecordRaw = {
+  path: '/:notFound(.*)*',
+  name: 'NotFound',
+  redirect: '/error'
+}
+
+export const baseRoutes: RouteRecordRaw[] = [
   RootRoute,
-  HomeRoute,
+  AppRoute,
   LoginRoute,
-  ErrorRoute
+  ErrorRoute,
+  // NotFoundRoute
 ]

+ 1 - 1
src/router/constant.ts

@@ -1,5 +1,5 @@
 export enum EnumPage {
-  Home = '/app',
+  App = '/app',
   Login = '/login',
   Error = 'error'
 }

+ 2 - 2
src/router/index.ts

@@ -14,14 +14,14 @@ export const router = createRouter({
   })
 })
 
-router.beforeEach(() => {
+router.beforeEach((to, from, next) => {
   nProgress.start()
+  next()
 })
 
 router.afterEach(() => {
   nProgress.done()
 })
-
 export const setupRouter = (app: App) => {
   app.use(router)
 }

+ 6 - 3
src/store/modules/app.ts

@@ -1,14 +1,17 @@
 import { defineStore } from 'pinia';
-
-
+import { RouteRecordRaw } from 'vue-router';
 
 export const AppStore = defineStore('app', {
   state: () => ({
-    isLoading: false,
+    isLoading: true,
+    authRoutes: [] as RouteRecordRaw[]
   }),
   actions: {
     updateLoading(isLoading = false) {
       this.isLoading = isLoading
+    },
+    updateAuthRoutes(routes: RouteRecordRaw[]) {
+      this.authRoutes = routes
     }
   }
 })