فهرست منبع

fix: 完成了未登录情况下顶级路由高亮显示问题

louhangfei 1 سال پیش
والد
کامیت
54eae5eada

+ 42 - 12
src/components/Nav.vue

@@ -7,7 +7,7 @@
         class="header__nav--item"
         v-for="item in NAV_LIST"
         :key="item.path"
-        :class="{ active: selectedKey === item.name }"
+        :class="{ active: selectedKey === item?.name }"
         @click="handleNavClick(item)"
       >
         <span>{{ item.meta?.title }}</span>
@@ -22,7 +22,7 @@
         </el-input>
       </div>
       <div class="platform__right__login">
-        <span @click="handleLogin('login')" v-if="!userInfo">登录</span>
+        <span @click="handleLogin('login')" v-if="!userStore.id">登录</span>
         <UserInfo
           v-else
           @switchAccount="handleLogin('switchAccount')"
@@ -31,27 +31,29 @@
       </div>
     </div>
   </header>
-  <Login v-if="showLogin" :type="loginType" @close="showLogin = false" class="fadeIn" />
+  <Login v-if="userStore.showLogin" :type="loginType" @close="userStore.showLogin = false" class="fadeIn" />
 </template>
 
 <script lang="ts" setup>
-  import { ref, computed, watch } from 'vue';
+  import { ref, computed } from 'vue';
   import { useRouter, useRoute } from 'vue-router';
   import { NAV_LIST } from '@/constant/nav';
   import logo from 'assets/images/home/comac-logo@1X.png';
   import searchIcon from 'assets/svg/search.svg';
   import Login from '@/components/Login.vue';
   import UserInfo from '@/components/UserInfo.vue';
-  import useMockUserStore from '@/store/modules/mockUser';
+  // import useMockUserStore from '@/store/modules/mockUser';
   import { useGlobSetting } from '@/hooks/setting';
 
-  import { storeToRefs } from 'pinia';
+  // import { storeToRefs } from 'pinia';
+  import { useUserStore } from '@/store/modules/user';
 
-  const mockUserStore = useMockUserStore();
-  const { userInfo } = storeToRefs(mockUserStore);
+  // const mockUserStore = useMockUserStore();
+  // const { userInfo } = storeToRefs(mockUserStore);
+  const userStore = useUserStore();
   const activeNav = ref(NAV_LIST[0].name);
   const router = useRouter();
-  const showLogin = ref(false);
+  // const showLogin = ref(false);
   const searchValue = ref('');
   const loginType = ref<'login' | 'switchAccount' | 'modifyPassword'>('login');
 
@@ -61,22 +63,50 @@
 
   const handleLogin = (type: 'login' | 'switchAccount' | 'modifyPassword') => {
     loginType.value = type;
-    showLogin.value = true;
+    userStore.showLogin = true;
   };
 
   const currentRoute = useRoute();
   const { title } = useGlobSetting();
 
+<<<<<<< HEAD
   const handleNavClick = (item: any) => {
+=======
+  const handleNavClick = (item: { name: string; path: string }) => {
+    // 如果不是首页,先检查用户是否登录
+    // if (item.name !== '首页' && !userInfo.value) {
+    //   handleLogin('login');
+    //   return;
+    // }
+>>>>>>> 9bcd636 (fix: 完成了未登录情况下顶级路由高亮显示问题)
     if (!item.path) return;
     activeNav.value = item.name;
     router.push(item.path);
   };
 
+<<<<<<< HEAD
+=======
+  console.log('currentRoute', currentRoute);
+
+>>>>>>> 9bcd636 (fix: 完成了未登录情况下顶级路由高亮显示问题)
   const selectedKey = computed(() => {
-    const activeMenu = (currentRoute.meta?.activeMenu as string) || null; // act
-    return activeMenu ? activeMenu : (currentRoute.name as string);
+    return currentRoute.matched[0]?.name;
   });
+<<<<<<< HEAD
+=======
+
+  // 跟随页面路由变化,切换菜单选中状态
+  // watch(
+  //   () => currentRoute.fullPath,
+  //   () => {
+  //     updateMenu();
+  //     const matched = currentRoute.matched;
+  //     openKeys.value = matched.map((item) => item.name);
+  //     const activeMenu: string = (currentRoute.meta?.activeMenu as string) || '';
+  //     selectedKeys.value = activeMenu ? activeMenu : (currentRoute.name as string);
+  //   },
+  // );
+>>>>>>> 9bcd636 (fix: 完成了未登录情况下顶级路由高亮显示问题)
 </script>
 
 <style lang="scss" scoped>

+ 0 - 1
src/constant/nav.ts

@@ -39,7 +39,6 @@ export const NAV_LIST = [
   },
   {
     path: '/disaster-prevention',
-    redirect: '',
     component: 'MENU_LAYOUT',
     meta: {
       title: '灾害防范',

+ 184 - 60
src/router/full-routes.ts

@@ -8,71 +8,195 @@ import { AppRouteRecordRaw } from './types';
 import { getTreeItem } from '@/utils';
 import { cloneDeep } from 'lodash-es';
 
-const fullRoutes: AppRouteRecordRaw[] = [
-  {
-    children: [
-      {
-        children: [],
-        component: '/todo/todo',
-        id: 1023,
-        meta: {
-          activeMenu: null,
-          alwaysShow: false,
-          frameSrc: '',
-          hidden: false,
-          icon: '',
-          isFrame: 0,
-          isRoot: false,
-          noCache: false,
-          query: '',
-          title: '生产安全菜单1',
-        },
-        name: 'DeviceCamera',
-        parentId: 1022,
-        path: 'camera',
-        redirect: '',
+/** 灾害防范的路由 */
+export const disasterPreventionRoute = {
+  children: [
+    {
+      component: '/todo/todo',
+      id: 1025,
+      meta: {
+        activeMenu: null,
+        alwaysShow: false,
+        frameSrc: '',
+        hidden: false,
+        icon: '',
+        isFrame: 0,
+        isRoot: false,
+        noCache: false,
+        query: '',
+        title: '总览',
       },
-      {
-        children: [],
-        component: '/todo/todo',
-        id: 1024,
-        meta: {
-          activeMenu: null,
-          alwaysShow: false,
-          frameSrc: '',
-          hidden: false,
-          icon: '',
-          isFrame: 0,
-          isRoot: false,
-          noCache: false,
-          query: '',
-          title: '生产安全菜单2',
+      name: '/disaster-prevention/overview',
+      parentId: 1022,
+      path: 'overview',
+      redirect: '',
+    },
+    {
+      component: '/todo/todo',
+      id: 1026,
+      meta: {
+        activeMenu: null,
+        alwaysShow: false,
+        frameSrc: '',
+        hidden: false,
+        icon: '',
+        isFrame: 0,
+        isRoot: false,
+        noCache: false,
+        query: '',
+        title: '重点区域监测',
+      },
+      name: '/disaster-prevention/risk-point-monitoring',
+      parentId: 1022,
+      path: 'risk-point-monitoring',
+      redirect: '',
+    },
+    {
+      component: '/todo/todo',
+      id: 1027,
+      meta: {
+        activeMenu: null,
+        alwaysShow: false,
+        frameSrc: '',
+        hidden: false,
+        icon: '',
+        isFrame: 0,
+        isRoot: false,
+        noCache: false,
+        query: '',
+        title: '灾害预警',
+      },
+      name: '/disaster-prevention/disaster-warning',
+      parentId: 1022,
+      path: 'disaster-warning',
+      redirect: '',
+    },
+    {
+      children: [
+        {
+          component: '/todo/todo',
+          id: 1029,
+          meta: {
+            activeMenu: null,
+            alwaysShow: false,
+            frameSrc: '',
+            hidden: false,
+            icon: '',
+            isFrame: 0,
+            isRoot: false,
+            noCache: false,
+            query: '',
+            title: '任务管理',
+          },
+          name: '/disaster-prevention/disaster-precaution/task-management',
+          parentId: 1028,
+          path: 'task-management',
+          redirect: '',
         },
-        name: 'DeviceNVR',
-        parentId: 1022,
-        path: 'nvr',
-        redirect: '',
+        {
+          component: '/todo/todo',
+          id: 1030,
+          meta: {
+            activeMenu: null,
+            alwaysShow: false,
+            frameSrc: '',
+            hidden: false,
+            icon: '',
+            isFrame: 0,
+            isRoot: false,
+            noCache: false,
+            query: '',
+            title: '任务执行',
+          },
+          name: '/disaster-prevention/disaster-precaution/task-execution',
+          parentId: 1028,
+          path: 'task-execution',
+          redirect: '',
+        },
+        {
+          component: '/todo/todo',
+          id: 1031,
+          meta: {
+            activeMenu: null,
+            alwaysShow: false,
+            frameSrc: '',
+            hidden: false,
+            icon: '',
+            isFrame: 0,
+            isRoot: false,
+            noCache: false,
+            query: '',
+            title: '任务模板',
+          },
+          name: '/disaster-prevention/disaster-precaution/task-template',
+          parentId: 1028,
+          path: 'task-template',
+          redirect: '',
+        },
+      ],
+      component: '/todo/todo',
+      id: 1028,
+      meta: {
+        activeMenu: null,
+        alwaysShow: false,
+        frameSrc: '',
+        hidden: false,
+        icon: '',
+        isFrame: 0,
+        isRoot: false,
+        noCache: false,
+        query: '',
+        title: '预防检查',
       },
-    ],
-    component: 'MENU_LAYOUT',
-    id: 1022,
-    meta: {
-      activeMenu: null,
-      alwaysShow: false,
-      frameSrc: '',
-      hidden: false,
-      icon: 'CameraOutlined',
-      isFrame: 0,
-      isRoot: false,
-      noCache: false,
-      query: '',
-      title: '灾害防范',
+      name: '/disaster-prevention/disaster-precaution',
+      parentId: 1022,
+      path: 'disaster-precaution',
+      redirect: '',
+    },
+    {
+      component: '/todo/todo',
+      id: 1032,
+      meta: {
+        activeMenu: null,
+        alwaysShow: false,
+        frameSrc: '',
+        hidden: false,
+        icon: '',
+        isFrame: 0,
+        isRoot: false,
+        noCache: false,
+        query: '',
+        title: '灾害处置',
+      },
+      name: '/disaster-prevention/disaster-control',
+      parentId: 1022,
+      path: 'disaster-control',
+      redirect: '',
     },
-    name: 'DisasterPrevention',
-    parentId: -1,
-    path: '/disaster-prevention',
-    redirect: '',
+  ],
+  component: 'MENU_LAYOUT',
+  id: 1022,
+  meta: {
+    activeMenu: null,
+    alwaysShow: false,
+    frameSrc: '',
+    hidden: false,
+    icon: 'CameraOutlined',
+    isFrame: 0,
+    isRoot: false,
+    noCache: false,
+    query: '',
+    title: '灾害防范',
+    ignoreAuth: false,
   },
+  name: 'DisasterPrevention',
+  parentId: -1,
+  path: '/disaster-prevention',
+  redirect: '',
+};
+
+const fullRoutes: AppRouteRecordRaw[] = [
+  disasterPreventionRoute,
 
   /**
    * 用户管理

+ 10 - 2
src/router/index.ts

@@ -3,6 +3,8 @@ import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';
 import { RedirectRoute } from '@/router/base';
 import { PageEnum } from '@/enums/pageEnum';
 import { createRouterGuards } from './router-guards';
+import { disasterPreventionRoute } from './full-routes';
+import { asyncImportRoute, routerGenerator } from './generator-routers';
 
 const modules: any = import.meta.glob('./modules/**/*.ts', { eager: true });
 
@@ -33,18 +35,24 @@ export const HOME_PAGE: RouteRecordRaw = {
   // 模板管理
   path: '/home',
   name: 'HomePage',
-  component: () => import('@/views/home/PageHome.vue'),
+  component: '/home/PageHome.vue',
   meta: {
     icon: '',
     title: '公司主页',
   },
 };
 
+const { children, ...disasterPreventionTopRoute } = disasterPreventionRoute;
+const navRoutes = [HOME_PAGE, disasterPreventionTopRoute, RedirectRoute];
+
 //需要验证权限
 export const asyncRoutes = [...routeModuleList];
 
+const routeList = routerGenerator(navRoutes);
+asyncImportRoute(routeList);
+
 //普通路由 无需验证权限
-export const constantRouter: any[] = [RootRoute, HOME_PAGE, RedirectRoute];
+export const constantRouter: any[] = routeList;
 
 export const router = createRouter({
   history: createWebHashHistory(),

+ 44 - 34
src/router/router-guards.ts

@@ -7,12 +7,36 @@ import { storage } from '@/utils/Storage';
 import { PageEnum } from '@/enums/pageEnum';
 import { ErrorPageRoute } from '@/router/base';
 import NProgress from 'nprogress';
+import { ElMessage } from 'element-plus';
 import { getRedirectUrl } from '@/utils/getRedirectUrl';
 
 const LOGIN_PATH = PageEnum.BASE_LOGIN;
 
 const whitePathList = [LOGIN_PATH, PageEnum.BASE_HOME]; // no redirect whitelist
 
+const setDynamicRoute = async (router) => {
+  const userStore = useUserStoreWidthOut();
+  const asyncRouteStore = useAsyncRouteStoreWidthOut();
+  try {
+    const userInfo = await userStore.GetInfo();
+    const routes = await asyncRouteStore.generateRoutes(userInfo);
+    // 动态添加可访问路由表
+    routes.forEach((item) => {
+      router.addRoute(item as unknown as RouteRecordRaw);
+    });
+    //添加404
+    const isErrorPage = router.getRoutes().findIndex((item) => item.name === ErrorPageRoute.name);
+    if (isErrorPage === -1) {
+      router.addRoute(ErrorPageRoute as unknown as RouteRecordRaw);
+    }
+    asyncRouteStore.setDynamicAddedRoute(true);
+  } catch (error) {
+    ElMessage.error('请先登录');
+    userStore.showLogin = true;
+    return;
+  }
+};
+
 export function createRouterGuards(router: Router) {
   const userStore = useUserStoreWidthOut();
   const asyncRouteStore = useAsyncRouteStoreWidthOut();
@@ -22,15 +46,17 @@ export function createRouterGuards(router: Router) {
       next(PageEnum.BASE_HOME);
       return;
     }
+    const token = storage.get(ACCESS_TOKEN);
 
     // Whitelist can be directly entered
     if (whitePathList.includes(to.path as PageEnum)) {
       next();
+      if (token && !asyncRouteStore.getIsDynamicAddedRoute) {
+        setDynamicRoute(router);
+      }
       return;
     }
 
-    const token = storage.get(ACCESS_TOKEN);
-
     if (!token) {
       // You can access without permissions. You need to set the routing meta.ignoreAuth to true
       if (to.meta.ignoreAuth) {
@@ -39,20 +65,22 @@ export function createRouterGuards(router: Router) {
       }
       // redirect login page
       // 这里登录要改造
-      // const redirectData: { path: string; replace: boolean; query?: Recordable<string> } = {
-      //   path: LOGIN_PATH,
-      //   replace: true,
-      // };
-      // if (to.path) {
-      //   redirectData.query = {
-      //     ...redirectData.query,
-      //     redirect: to.path,
-      //   };
-      // }
+      const redirectData: { path: string; replace: boolean; query?: Recordable<string> } = {
+        path: LOGIN_PATH,
+        replace: true,
+      };
+      if (to.path) {
+        redirectData.query = {
+          ...redirectData.query,
+          redirect: to.path,
+        };
+      }
+      // ElMessage.error('请先登录');
+      userStore.showLogin = true;
       // next(redirectData);
       // window.location.href = getRedirectUrl();
-      // next();
-      // return;
+      next();
+      return;
     }
 
     if (asyncRouteStore.getIsDynamicAddedRoute) {
@@ -60,29 +88,11 @@ export function createRouterGuards(router: Router) {
       return;
     }
 
-    try {
-      const userInfo = await userStore.GetInfo();
-      const routes = await asyncRouteStore.generateRoutes(userInfo);
-      // 动态添加可访问路由表
-      routes.forEach((item) => {
-        router.addRoute(item as unknown as RouteRecordRaw);
-      });
-    } catch (err) {
-      console.log('login error', err);
-      // window.location.href = getRedirectUrl();
-    }
-
-    //添加404
-    const isErrorPage = router.getRoutes().findIndex((item) => item.name === ErrorPageRoute.name);
-    if (isErrorPage === -1) {
-      router.addRoute(ErrorPageRoute as unknown as RouteRecordRaw);
-    }
-
+    await setDynamicRoute(router);
+    NProgress.done();
     const redirectPath = (from.query.redirect || to.path) as string;
     const redirect = decodeURIComponent(redirectPath);
     const nextData = to.path === redirect ? { ...to, replace: true } : { path: redirect };
-    asyncRouteStore.setDynamicAddedRoute(true);
-    NProgress.done();
     next(nextData);
   });
 

+ 2 - 0
src/store/modules/user.ts

@@ -35,6 +35,7 @@ export interface IUserState {
     tenantId: number;
   };
   tenantId: number;
+  showLogin: boolean;
 }
 
 export const useUserStore = defineStore({
@@ -47,6 +48,7 @@ export const useUserStore = defineStore({
     avatar: '',
     permissions: [],
     info: Storage.get(CURRENT_USER, {}),
+    showLogin: false,
   }),
   getters: {
     getToken(): string {

+ 6 - 1
src/views/home/src/components/IntroductionCard.vue

@@ -1,5 +1,10 @@
 <template>
-  <div class="introduction-card" v-for="item in COMPANY_INTRODUCTION_CARD_LIST" :key="item.title" :style="{ backgroundImage: `url(${item.backgroundImg})` }">
+  <div
+    class="introduction-card"
+    v-for="item in COMPANY_INTRODUCTION_CARD_LIST"
+    :key="item.title"
+    :style="{ backgroundImage: `url(${item.backgroundImg})` }"
+  >
     <div class="introduction-card__title">{{ item.title }}</div>
     <div class="introduction-card__content">
       <div class="introduction-card__content-item" v-for="content in item.content" :key="content">