Prechádzať zdrojové kódy

feat: 完成左侧菜单高亮显示和展开

louhangfei 1 rok pred
rodič
commit
2d9a1479fc

+ 130 - 10
mock/login/routers.ts

@@ -4,9 +4,8 @@ const list = [
   {
     children: [
       {
-        children: [],
         component: '/todo/todo',
-        id: 1023,
+        id: 1025,
         meta: {
           activeMenu: null,
           alwaysShow: false,
@@ -17,17 +16,16 @@ const list = [
           isRoot: false,
           noCache: false,
           query: '',
-          title: '生产安全菜单1',
+          title: '总览',
         },
-        name: 'DeviceCamera',
+        name: '/disaster-prevention/overview',
         parentId: 1022,
-        path: 'camera',
+        path: 'overview',
         redirect: '',
       },
       {
-        children: [],
         component: '/todo/todo',
-        id: 1024,
+        id: 1026,
         meta: {
           activeMenu: null,
           alwaysShow: false,
@@ -38,11 +36,133 @@ const list = [
           isRoot: false,
           noCache: false,
           query: '',
-          title: '生产安全菜单2',
+          title: '重点区域监测',
         },
-        name: 'DeviceNVR',
+        name: '/disaster-prevention/risk-point-monitoring',
         parentId: 1022,
-        path: 'nvr',
+        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: '',
+          },
+          {
+            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: '预防检查',
+        },
+        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: '',
       },
     ],

+ 1 - 1
public/app.config.js

@@ -1,7 +1,7 @@
 window.__PRODUCTION__SKYEYEADMIN__CONF__ = {
 
   // document的title,以及显示在左侧导航栏的title,一般是项目的名称
-  VITE_GLOB_APP_TITLE: '上飞院大安全项目',
+  VITE_GLOB_APP_TITLE: '上飞院安全智能管控平台',
   // 接口前缀
   VITE_GLOB_API_URL_PREFIX: './eye_api_bak/api',
 

+ 44 - 9
src/components/Nav.vue

@@ -1,16 +1,16 @@
 <template>
   <header class="header">
     <img :src="logo" alt="logo" class="header__logo" />
-    <span class="platform-name">上飞院安全智能管控平台</span>
+    <span class="platform-name">{{ title }}</span>
     <nav class="header__nav">
       <div
         class="header__nav--item"
         v-for="item in NAV_LIST"
         :key="item.path"
-        :class="{ active: activeNav === item.name }"
+        :class="{ active: selectedKey === item.name }"
         @click="handleNavClick(item)"
       >
-        <span>{{ item.name }}</span>
+        <span>{{ item.meta.title }}</span>
       </div>
     </nav>
     <div class="platform__right">
@@ -35,15 +35,18 @@
 </template>
 
 <script lang="ts" setup>
-  import { ref } from 'vue';
-  import { useRouter } from 'vue-router';
+  import { ref, computed, watch } 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 { useGlobSetting } from '@/hooks/setting';
+
   import { storeToRefs } from 'pinia';
+
   const mockUserStore = useMockUserStore();
   const { userInfo } = storeToRefs(mockUserStore);
   const activeNav = ref(NAV_LIST[0].name);
@@ -61,17 +64,49 @@
     showLogin.value = true;
   };
 
+  const currentRoute = useRoute();
+  const { title } = useGlobSetting();
+
+  // 获取当前打开的子菜单
+  const matched = currentRoute.matched;
+  const activeMenu = (currentRoute.meta?.activeMenu as string) || null; // act
+  // const selectedKey = ref<string>((activeMenu ?? currentRoute.name) as string);
+
   const handleNavClick = (item: { name: string; path: string }) => {
     // 如果不是首页,先检查用户是否登录
-    if (item.name !== '首页' && !userInfo.value) {
-      handleLogin('login');
-      return;
-    }
+    // if (item.name !== '首页' && !userInfo.value) {
+    //   handleLogin('login');
+    //   return;
+    // }
     if (!item.path) return;
 
     activeNav.value = item.name;
     router.push(item.path);
   };
+
+  const matchedSubmenus = computed(() => {
+    return currentRoute.matched[0].children;
+  });
+
+  console.log('currentRoute', currentRoute);
+
+  const selectedKey = computed(() => {
+    const activeMenu = (currentRoute.meta?.activeMenu as string) || null; // act
+    return activeMenu ? activeMenu : (currentRoute.name as string);
+  });
+  console.log('selectedKeys', selectedKey.value);
+
+  // 跟随页面路由变化,切换菜单选中状态
+  // 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);
+  //   },
+  // );
 </script>
 
 <style lang="scss" scoped>

+ 34 - 6
src/constant/nav.ts

@@ -2,42 +2,70 @@
  * @description 导航常量
  */
 
+import { HOME_PAGE } from '@/router';
+
 // 首页导航列表 根据后端返回权限控制(首页永远存在)
 export const NAV_LIST = [
   {
-    name: '首页',
-    path: '/home',
+    ...HOME_PAGE,
   },
   {
     name: '院内安全态势',
     path: '',
+    meta: {
+      title: '院内安全态势',
+    },
   },
   {
     name: '生产安全',
     path: '',
+    meta: {
+      title: '生产安全',
+    },
   },
   {
     name: '交通安全',
     path: '',
+    meta: {
+      title: '交通安全',
+    },
   },
   {
     name: '保卫保密',
     path: '',
+    meta: {
+      title: '保卫保密',
+    },
   },
   {
-    name: '灾害防范',
     path: '/disaster-prevention',
+    redirect: '',
+    component: 'MENU_LAYOUT',
+    meta: {
+      title: '灾害防范',
+    },
+    name: 'DisasterPrevention',
+    parentId: -1,
   },
   {
-    name: '应急管理',
+    name: '',
     path: '',
+    meta: {
+      title: '应急管理',
+    },
   },
   {
-    name: '智慧视觉',
+    name: '',
     path: '',
+    meta: {
+      title: '智慧视觉',
+    },
   },
   {
-    name: '物联集成',
+    name: '',
     path: '',
+    meta: {
+      title: '物联集成',
+    },
   },
 ];

+ 2 - 32
src/hooks/setting/index.ts

@@ -4,43 +4,13 @@ import { warn } from '@/utils/log';
 import { getAppEnvConfig } from '@/utils/env';
 
 export const useGlobSetting = (): Readonly<GlobConfig> => {
-  const {
-    VITE_GLOB_APP_TITLE,
-    VITE_GLOB_API_URL,
-    VITE_GLOB_APP_SHORT_NAME,
-    VITE_GLOB_API_URL_PREFIX,
-    VITE_GLOB_UPLOAD_URL,
-    VITE_GLOB_PROD_MOCK,
-    VITE_GLOB_IMG_URL,
-    VITE_GLOB_APP_DOWNLOAD_QRCODE,
-    VITE_GLOB_LOGIN_APP,
-    VITE_GLOB_APP_PC,
-    VITE_GLOB_TENANT_CODE,
-    VITE_GLOB_QUESTION_LIST_VERSION,
-    VITE_GLOB_DISABLE_DEPARTMENT_EDIT,
-    VITE_GLOB_HIDE_REPORT_MESSAGE_TABS,
-    VITE_GLOB_NOTICE_CHANNEL,
-    ...rest
-  } = getAppEnvConfig();
+  const { VITE_GLOB_APP_TITLE, VITE_GLOB_API_URL_PREFIX } = getAppEnvConfig();
 
   // Take global configuration
   const glob: Readonly<GlobConfig> = {
     title: VITE_GLOB_APP_TITLE,
-    apiUrl: VITE_GLOB_API_URL,
-    shortName: VITE_GLOB_APP_SHORT_NAME,
+
     urlPrefix: VITE_GLOB_API_URL_PREFIX || '',
-    uploadUrl: VITE_GLOB_UPLOAD_URL,
-    prodMock: VITE_GLOB_PROD_MOCK,
-    imgUrl: VITE_GLOB_IMG_URL || '',
-    appDownloadUrl: VITE_GLOB_APP_DOWNLOAD_QRCODE,
-    loginApp: VITE_GLOB_LOGIN_APP,
-    appPCUrl: VITE_GLOB_APP_PC,
-    tenantCode: VITE_GLOB_TENANT_CODE,
-    questionListVersion: VITE_GLOB_QUESTION_LIST_VERSION,
-    disableDepartmentEdit: VITE_GLOB_DISABLE_DEPARTMENT_EDIT,
-    hideReportMessageTabs: VITE_GLOB_HIDE_REPORT_MESSAGE_TABS,
-    noticeChannel: VITE_GLOB_NOTICE_CHANNEL,
-    ...rest,
   };
   return glob as Readonly<GlobConfig>;
 };

+ 36 - 7
src/layout/MenuLayout.vue

@@ -2,16 +2,16 @@
 <template>
   <div class="component-container home-container">
     <aside class="aside">
-      <el-menu :default-active="defaultActive" router class="el-menu-vertical">
-        <template v-for="item in DISASTER_PREVENTION_MENU" :key="item.path">
+      <el-menu :default-active="selectedKeys" :default-openeds="openKeys" router class="el-menu-vertical">
+        <template v-for="item in subMenus" :key="item.path">
           <el-sub-menu v-if="item.children" :index="item.path">
-            <template #title>{{ item.name }}</template>
+            <template #title>{{ item.meta.title }}</template>
             <el-menu-item v-for="child in item.children" :key="child.path" :index="child.path">
-              {{ child.name }}
+              {{ child.meta.title }}
             </el-menu-item>
           </el-sub-menu>
           <el-menu-item v-else :index="item.path">
-            {{ item.name }}
+            {{ item.meta.title }}
           </el-menu-item>
         </template>
       </el-menu>
@@ -23,9 +23,38 @@
 </template>
 
 <script setup lang="ts">
-  import { ref } from 'vue';
+  import { ref, watch, computed } from 'vue';
+  import { useRoute, useRouter } from 'vue-router';
+
   import { DISASTER_PREVENTION_MENU } from '@/constant/disaster-prevention';
-  const defaultActive = ref(DISASTER_PREVENTION_MENU[0].path);
+  const defaultActive = ref('');
+
+  // 当前路由
+  const currentRoute = useRoute();
+  const router = useRouter();
+  // 获取当前打开的子菜单
+  const matched = currentRoute.matched;
+  const activeMenu = currentRoute.meta?.activeMenu || null; // activeMenu undefined,null 或 空字符串,统一变为 null。
+  const selectedKeys = ref<string>((activeMenu ?? currentRoute.name) as string);
+
+  const openKeys = ref<string[]>([]);
+
+  const subMenus = computed(() => {
+    return currentRoute.matched[0].children;
+  });
+
+  // 跟随页面路由变化,切换菜单选中状态
+  watch(
+    () => currentRoute.fullPath,
+    () => {
+      // updateMenu();
+      console.log('currentRoute', currentRoute);
+      const matched = currentRoute.matched;
+      openKeys.value = matched.map((item) => item.name) as string[];
+      const activeMenu: string = (currentRoute.meta?.activeMenu as string) || '';
+      selectedKeys.value = activeMenu ? activeMenu : (currentRoute.name as string);
+    },
+  );
 </script>
 
 <style scoped lang="scss">

+ 2 - 2
src/layout/components/Header/index.vue

@@ -158,7 +158,7 @@
   </div>
 
   <!-- 搜索 -->
-  <AppSearch ref="appSearchRef" />
+  <!-- <AppSearch ref="appSearchRef" /> -->
 
   <!--修改密码-->
   <AmendPwd ref="amendPwdRef" />
@@ -170,7 +170,7 @@
   import { useUserStore } from '@/store/modules/user';
   // import { useLockscreenStore } from '@/store/modules/lockscreen';
   import { useProjectSetting } from '@/hooks/setting/useProjectSetting';
-  import { AppSearch } from '@/components/Application/index';
+  // import { AppSearch } from '@/components/Application/index';
   import ProjectSetting from './ProjectSetting.vue';
   import QRcodePopover from './QRcodePopover.vue';
   import Sider from '../Sider/Sider.vue';

+ 2 - 2
src/layout/index.vue

@@ -59,7 +59,7 @@
 
     <div class="admin-layout-shade"></div>
 
-    <InvalidAuth />
+    <!-- <InvalidAuth /> -->
   </div>
 
   <!--项目配置-->
@@ -89,7 +89,7 @@
   import { useDesignSettingStore } from '@/store/modules/designSetting';
   import { SettingOutlined } from '@vicons/antd';
   import Sider from './components/Sider/Sider.vue';
-  import InvalidAuth from '@/components/InvalidAuth/InvalidAuth.vue';
+  // import InvalidAuth from '@/components/InvalidAuth/InvalidAuth.vue';
 
   const { getDarkTheme } = useDesignSetting();
   const {

+ 46 - 222
src/router/full-routes.ts

@@ -9,245 +9,69 @@ import { getTreeItem } from '@/utils';
 import { cloneDeep } from 'lodash-es';
 
 const fullRoutes: AppRouteRecordRaw[] = [
-  /**
-   * Dashboard
-   */
-  {
-    path: '/dashboard',
-    name: 'Dashboard',
-    component: 'LAYOUT',
-    meta: {
-      icon: 'DashboardOutlined',
-      title: 'Dashboard',
-    },
-    children: [
-      {
-        // 首页(原主控台)
-        path: 'console',
-        name: 'DashboardConsole',
-        component: '/dashboard/home/Home',
-        meta: {
-          icon: '',
-          title: '首页',
-        },
-      },
-    ],
-  },
-
-  /**
-   * 场景管理
-   */
   {
-    path: '/scene',
-    name: 'Scene',
-    component: 'LAYOUT',
-    meta: {
-      icon: 'ApartmentOutlined',
-      title: '场景管理',
-    },
     children: [
       {
-        // 模板管理
-        path: 'template',
-        name: 'SceneTemplate',
-        component: '/templateManage/templateManage',
-        meta: {
-          icon: '',
-          title: '模板管理',
-        },
-      },
-      {
-        // 车间管理
-        path: 'workshop',
-        name: 'SceneWorkshop',
-        component: '/system-config/scene-manage/SceneManage',
-        meta: {
-          icon: '',
-          title: '车间管理',
-        },
-      },
-      {
-        // 业务场景管理
-        path: 'business',
-        name: 'SceneBusiness',
-        component: '/system-config/business-scene/PageBusinessScene',
+        children: [],
+        component: '/todo/todo',
+        id: 1023,
         meta: {
+          activeMenu: null,
+          alwaysShow: false,
+          frameSrc: '',
+          hidden: false,
           icon: '',
-          title: '业务创建管理',
+          isFrame: 0,
+          isRoot: false,
+          noCache: false,
+          query: '',
+          title: '生产安全菜单1',
         },
-      },
-    ],
-  },
-
-  /**
-   * 设备管理
-   */
-  {
-    path: '/device',
-    name: 'Device',
-    component: 'LAYOUT',
-    meta: {
-      icon: 'CameraOutlined',
-      title: '设备管理',
-    },
-    children: [
-      {
-        // 相机设备
-        path: 'camera',
         name: 'DeviceCamera',
-        component: '/cameras/overview/CamerasOverview',
-        meta: {
-          icon: '',
-          title: '相机设备',
-        },
-      },
-      {
-        // NVR设备
-        path: 'nvr',
-        name: 'DeviceNVR',
-        component: '/cameras/nvrlist/NvrList',
-        meta: {
-          icon: '',
-          title: 'NVR设备',
-        },
-      },
-    ],
-  },
-
-  /**
-   * 布局管理
-   */
-  {
-    path: '/layout',
-    name: 'Layout',
-    component: 'LAYOUT',
-    meta: {
-      icon: 'PictureOutlined',
-      title: '布局管理',
-    },
-    children: [
-      {
-        // 公司场景布局入口页,选择PC端还是手机端
-        path: 'scene',
-        name: 'LayoutScene',
-        component: '/page-config/PageScene',
-        meta: {
-          icon: '',
-          title: '场景布局',
-        },
-      },
-      {
-        // 公司场景布局卡片列表。二级页面,菜单不可见!!!
-        path: 'scene-list',
-        name: 'LayoutSceneList',
-        component: '/page-config/PageSceneList',
-        meta: {
-          icon: '',
-          title: '场景布局列表',
-          activeMenu: 'LayoutScene',
-        },
-      },
-      {
-        // 公司场景配置页面。三级页面,菜单不可见!!!
-        path: 'scene-config',
-        name: 'LayoutSceneConfig',
-        component: '/page-config/ConfigEdit',
-        meta: {
-          icon: '',
-          title: '场景布局配置',
-          activeMenu: 'LayoutScene',
-        },
-      },
-      {
-        // 相机布局入口页,选择PC端还是手机端
+        parentId: 1022,
         path: 'camera',
-        name: 'LayoutCamera',
-        component: '/page-config/PageCamera',
-        meta: {
-          icon: '',
-          title: '相机布局',
-        },
-      },
-      {
-        // 相机布局卡片列表。二级页面, 菜单不可见!!!
-        path: 'camera-list',
-        name: 'LayoutCameraList',
-        component: '/page-config/PageCameraList',
-        meta: {
-          icon: '',
-          title: '相机布局列表',
-          activeMenu: 'LayoutCamera',
-        },
+        redirect: '',
       },
       {
-        // 相机布局配置。三级页面,菜单不可见!!!
-        path: 'camera-config',
-        name: 'LayoutCameraConfig',
-        component: '/map-config/mini-map/MiniMapConfig',
+        children: [],
+        component: '/todo/todo',
+        id: 1024,
         meta: {
+          activeMenu: null,
+          alwaysShow: false,
+          frameSrc: '',
+          hidden: false,
           icon: '',
-          title: '相机布局配置',
-          activeMenu: 'LayoutCamera',
+          isFrame: 0,
+          isRoot: false,
+          noCache: false,
+          query: '',
+          title: '生产安全菜单2',
         },
+        name: 'DeviceNVR',
+        parentId: 1022,
+        path: 'nvr',
+        redirect: '',
       },
     ],
-  },
-
-  /**
-   * 算法管理
-   */
-  {
-    path: '/algorithm',
-    name: 'Algorithm',
-    component: 'LAYOUT',
+    component: 'MENU_LAYOUT',
+    id: 1022,
     meta: {
-      icon: 'FunctionOutlined',
-      title: '算法管理',
+      activeMenu: null,
+      alwaysShow: false,
+      frameSrc: '',
+      hidden: false,
+      icon: 'CameraOutlined',
+      isFrame: 0,
+      isRoot: false,
+      noCache: false,
+      query: '',
+      title: '灾害防范',
     },
-    children: [
-      {
-        // 算法预览
-        path: 'preview',
-        name: 'AlgorithmPreview',
-        component: '/cameras/algo-management/algoManagement',
-        meta: {
-          icon: '',
-          title: '算法预览',
-        },
-      },
-      {
-        // 算法配置
-        path: 'config',
-        name: 'AlgorithmConfig',
-        component: '/cameras/preview/CameraPreview',
-        meta: {
-          ico: '',
-          title: '算法配置',
-        },
-      },
-      {
-        // 算法参数设置,也就是设置的详情页
-        path: 'params',
-        name: 'AlgorithmParamsSetting',
-        component: '/cameras/algo-params-setting/AlgoParamsSetting',
-        meta: {
-          ico: '',
-          title: '算法参数设置',
-          activeMenu: 'AlgorithmConfig',
-        },
-      },
-      {
-        // 多相机算法参数设置
-        path: 'camera-group',
-        name: 'AlgorithmCameraGroupParamsSetting',
-        component: '/cameras/camera-group-setting/CamerGroupSetting',
-        meta: {
-          ico: '',
-          title: '多相机算法参数设置',
-          activeMenu: 'AlgorithmConfig',
-        },
-      },
-    ],
+    name: 'DisasterPrevention',
+    parentId: -1,
+    path: '/disaster-prevention',
+    redirect: '',
   },
 
   /**