Browse Source

动态路由、水文

Hwf 7 tháng trước cách đây
mục cha
commit
b77e9f02a4

+ 79 - 34
src/components/Tabbar/index.vue

@@ -15,43 +15,88 @@
 </template>
 
 <script setup lang="ts">
-import { ref, reactive, onMounted } from "vue";
+import {ref, reactive, onMounted, watch} from "vue";
+import useUserStore from "@/store/modules/user";
 
 const active = ref(0);
-const tabBarData = reactive([
-  {
-    icon: 'index',
-    iconActive: 'indexActive',
-    title: "首页",
-    to: {
-      name: "LeaderIndex"
-    }
-  },
-  {
-    icon: 'command',
-    iconActive: 'commandActive',
-    title: "移动指挥",
-    to: {
-      name: "MobileControl"
-    }
-  },
-  {
-    icon: 'contact',
-    iconActive: 'contactActive',
-    title: "通讯录",
-    to: {
-      name: "AddressBook"
-    }
-  },
-  {
-    icon: 'mine',
-    iconActive: 'mineActive',
-    title: "我的",
-    to: {
-      name: "My"
-    }
+let tabBarData = reactive([]);
+watch(() => useUserStore().roles, () => {
+  const roles = useUserStore().roles
+  if (roles.includes("leader")) {
+    tabBarData = ([
+      {
+        icon: 'index',
+        iconActive: 'indexActive',
+        title: "首页",
+        to: {
+          name: "LeaderIndex"
+        }
+      },
+      {
+        icon: 'command',
+        iconActive: 'commandActive',
+        title: "移动指挥",
+        to: {
+          name: "MobileControl"
+        }
+      },
+      {
+        icon: 'contact',
+        iconActive: 'contactActive',
+        title: "通讯录",
+        to: {
+          name: "AddressBook"
+        }
+      },
+      {
+        icon: 'mine',
+        iconActive: 'mineActive',
+        title: "我的",
+        to: {
+          name: "My"
+        }
+      }
+    ]);
+  } else if (roles.includes("worker")) {
+    tabBarData = ([
+      {
+        icon: 'index',
+        iconActive: 'indexActive',
+        title: "首页",
+        to: {
+          name: "WorkerIndex"
+        }
+      },
+      {
+        icon: 'command',
+        iconActive: 'commandActive',
+        title: "移动指挥",
+        to: {
+          name: "MobileControl"
+        }
+      },
+      {
+        icon: 'contact',
+        iconActive: 'contactActive',
+        title: "通讯录",
+        to: {
+          name: "AddressBook"
+        }
+      },
+      {
+        icon: 'mine',
+        iconActive: 'mineActive',
+        title: "我的",
+        to: {
+          name: "My"
+        }
+      }
+    ]);
   }
-]);
+}, {
+  immediate: true,
+  deep: true
+})
 
 const getImageUrl = (name) => {
   return new URL(`../../assets/images/tabBar/${name}.png`, import.meta.url).href;

+ 16 - 4
src/router/index.ts

@@ -3,14 +3,15 @@ import {
   createWebHashHistory,
   type RouteLocationNormalized
 } from "vue-router";
-import routes from "./routes";
+import { constantRoutes } from "./routes";
 import { useCachedViewStoreHook } from "@/store/modules/cachedView";
 import NProgress from "@/utils/progress";
 import setPageTitle from "@/utils/set-page-title";
+import useUserStore from "@/store/modules/user";
 
-const router = createRouter({
+let router = createRouter({
   history: createWebHashHistory(),
-  routes
+  routes: constantRoutes
 });
 
 export interface toRouteType extends RouteLocationNormalized {
@@ -19,6 +20,7 @@ export interface toRouteType extends RouteLocationNormalized {
     noCache?: boolean;
   };
 }
+// const whiteList = ['/roleSelect'];
 
 router.beforeEach((to: toRouteType, from, next) => {
   NProgress.start();
@@ -26,11 +28,21 @@ router.beforeEach((to: toRouteType, from, next) => {
   useCachedViewStoreHook().addCachedView(to);
   // 页面 title
   setPageTitle(to.meta.title);
-  next();
+  const role = localStorage.getItem('role');
+  const roles = useUserStore().roles;
+  if(to.path === '/' || (!!role && roles.includes(role))) {
+    next();
+  } else if (!!role) {
+    useUserStore().setRoles(role);
+    next({ path: to.path, replace: true, params: to.params, query: to.query, hash: to.hash, name: to.name as string }); // hack方法 确保addRoutes已完成
+  } else {
+    next({ path: '/' });
+  }
 });
 
 router.afterEach(() => {
   NProgress.done();
 });
 
+
 export default router;

+ 105 - 75
src/router/routes.ts

@@ -1,13 +1,13 @@
 import Layout from "@/layout/index.vue";
 import type { RouteRecordRaw } from "vue-router";
 
-const routes: Array<RouteRecordRaw> = [
+export const constantRoutes: Array<RouteRecordRaw> = [
   {
     path: "/",
-    name: "index",
+    name: "RoleSelect",
     component: () => import("@/views/index.vue"),
     meta: {
-      title: import.meta.env.VITE_APP_TITLE
+      title: '角色选择'
     }
   },
   {
@@ -15,77 +15,6 @@ const routes: Array<RouteRecordRaw> = [
     name: "YzyCallback",
     component: () => import("@/components/YzyCallback/index.vue")
   },
-  {
-    path: "/leader",
-    name: "Leader",
-    component: Layout,
-    redirect: { name: "LeaderIndex" },
-    children: [
-      {
-        path: "index",
-        name: "LeaderIndex",
-        component: () => import("@/views/leader/index.vue"),
-        meta: {
-          title: "首页",
-          noCache: true
-        }
-      },
-      {
-        path: "mobile_control",
-        name: "MobileControl",
-        component: () => import("@/views/mobileControl/index.vue"),
-        meta: {
-          title: "移动指挥(平时态)",
-          noCache: true
-        }
-      },
-      {
-        path: "rollCallDetails",
-        name: "RollCallDetails",
-        component: () => import("@/views/onlineRollCall/rollCallDetails.vue"),
-        meta: {
-          title: "选择抽查单位",
-          noCache: true
-        }
-      },
-      {
-        path: "rollCallRecord2",
-        name: "rollCallRecord2",
-        component: () => import("@/views/onlineRollCall/rollCallRecord2.vue"),
-        meta: {
-          title: "点名记录",
-          noCache: true
-        }
-      },
-      // {
-      //   path: "mobile_control2",
-      //   name: "MobileControl2",
-      //   component: () => import("@/views/mobileControl2/index.vue"),
-      //   meta: {
-      //     title: "移动指挥(应急态)",
-      //     noCache: true
-      //   }
-      // },
-      {
-        path: "address_book",
-        name: "AddressBook",
-        component: () => import("@/views/addressBook/index.vue"),
-        meta: {
-          title: "通讯录",
-          noCache: true
-        }
-      },
-      {
-        path: "my",
-        name: "My",
-        component: () => import("@/views/about/index.vue"),
-        meta: {
-          title: "我",
-          noCache: true
-        }
-      }
-    ]
-  },
   {
     path: "/warningSituation",
     name: "WarningSituation",
@@ -122,6 +51,24 @@ const routes: Array<RouteRecordRaw> = [
       noCache: true
     }
   },
+  {
+    path: "/hydrologicalMonitor",
+    name: "HydrologicalMonitor",
+    component: () => import("@/views/disasterRiskMonitor/hydrologicalMonitor/index.vue"),
+    meta: {
+      title: "水文监测",
+      noCache: true
+    }
+  },
+  {
+    path: "/reservoirMonitor",
+    name: "ReservoirMonitor",
+    component: () => import("@/views/disasterRiskMonitor/hydrologicalMonitor/reservoirMonitor.vue"),
+    meta: {
+      title: "水库监测",
+      noCache: true
+    }
+  },
   {
     path: "/infoReception",
     name: "InfoReception",
@@ -270,4 +217,87 @@ const routes: Array<RouteRecordRaw> = [
   }
 ];
 
-export default routes;
+// 领导端动态菜单
+export const leaderRoute: Array<RouteRecordRaw> = [
+  {
+    path: "/leader",
+    name: "Leader",
+    component: Layout,
+    children: [
+      {
+        path: "index",
+        name: "LeaderIndex",
+        component: () => import("@/views/leader/index.vue"),
+        meta: {
+          title: "首页",
+          noCache: true
+        }
+      },
+      {
+        path: "mobile_control",
+        name: "MobileControl",
+        component: () => import("@/views/mobileControl/index.vue"),
+        meta: {
+          title: "移动指挥(平时态)",
+          noCache: true
+        }
+      },
+      {
+        path: "rollCallDetails",
+        name: "RollCallDetails",
+        component: () => import("@/views/onlineRollCall/rollCallDetails.vue"),
+        meta: {
+          title: "选择抽查单位",
+          noCache: true
+        }
+      },
+      {
+        path: "rollCallRecord2",
+        name: "rollCallRecord2",
+        component: () => import("@/views/onlineRollCall/rollCallRecord2.vue"),
+        meta: {
+          title: "点名记录",
+          noCache: true
+        }
+      },
+      {
+        path: "address_book",
+        name: "AddressBook",
+        component: () => import("@/views/addressBook/index.vue"),
+        meta: {
+          title: "通讯录",
+          noCache: true
+        }
+      },
+      {
+        path: "my",
+        name: "My",
+        component: () => import("@/views/about/index.vue"),
+        meta: {
+          title: "我",
+          noCache: true
+        }
+      }
+    ]
+  }
+];
+
+export const workerRoute = [
+  {
+    path: "/worker",
+    name: 'Worker',
+    component: Layout,
+    redirect: 'Index',
+    children: [
+      {
+        path: "index",
+        name: "WorkerIndex",
+        component: () => import("@/views/worker/index.vue"),
+        meta: {
+          title: "首页",
+          noCache: true
+        }
+      }
+    ]
+  }
+];

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

@@ -4,7 +4,9 @@ import { login as loginApi, logout as logoutApi, getInfo as getUserInfo } from '
 import defAva from '@/assets/images/profile.jpg';
 import {store} from '@/store/index';
 import {defineStore} from "pinia";
+import { leaderRoute, workerRoute } from '@/router/routes';
 import {ref} from "vue";
+import router from "@/router";
 
 export const useUserStore = defineStore('user', () => {
   const token = ref(getToken());
@@ -38,7 +40,6 @@ export const useUserStore = defineStore('user', () => {
       const data = res.data;
       const user = data.user;
       const profile = user.avatar == '' || user.avatar == null ? defAva : user.avatar;
-
       if (data.roles && data.roles.length > 0) {
         // 验证返回的roles是否是一个非空数组
         roles.value = data.roles;
@@ -68,6 +69,18 @@ export const useUserStore = defineStore('user', () => {
     avatar.value = value;
   };
 
+  const setRoles = (role) => {
+    if (!!role && roles.value.includes(role)) {
+      return;
+    }
+    roles.value = [role];
+    const dynamicRoutes = role === 'leader' ? leaderRoute : (role === 'worker' ? workerRoute : [])
+    dynamicRoutes.forEach((route) => {
+      router.addRoute(route);
+    });
+    localStorage.setItem('role', role);
+  }
+
   return {
     userId,
     token,
@@ -78,7 +91,8 @@ export const useUserStore = defineStore('user', () => {
     login,
     getInfo,
     logout,
-    setAvatar
+    setAvatar,
+    setRoles
   };
 });
 

+ 1 - 1
src/utils/set-page-title.ts

@@ -2,6 +2,6 @@ import { pageDefaultTitle } from "@/settings";
 
 export default function setPageTitle(routerTitle?: string): void {
   window.document.title = routerTitle
-    ? `${routerTitle} | ${pageDefaultTitle}`
+    ? `${routerTitle}`
     : `${pageDefaultTitle}`;
 }

+ 1 - 1
src/views/disasterRiskMonitor/forestFireWarn.vue

@@ -124,7 +124,7 @@ onMounted(() => {
 
 <style lang="scss" scoped>
 .container {
-  height: calc(100vh - 55px);
+  height: 100vh;
   .map {
     width: 100%;
     height: 50%;

+ 2 - 2
src/views/disasterRiskMonitor/galeDisaster.vue

@@ -37,7 +37,7 @@
         </template>
       </el-table-column>
       <el-table-column label="站址" prop="address" align="center" width="160px" />
-      <el-table-column label="风速(m/s)" prop="speed" align="center" sort />
+      <el-table-column label="风速(m/s)" prop="speed" align="center" sortable />
     </el-table>
     <van-popup v-model:show="showLevelPicker" round position="bottom">
       <van-picker
@@ -114,7 +114,7 @@ onMounted(() => {
 
 <style lang="scss" scoped>
 .container {
-  height: calc(100vh - 55px);
+  height: 100vh;
   padding: 0;
   .box {
     background-color: #ffffff;

+ 65 - 0
src/views/disasterRiskMonitor/hydrologicalMonitor/index.vue

@@ -0,0 +1,65 @@
+<template>
+  <div class="container">
+    <div v-for="(item, index) in menuData" :key="index" class="box" @click="handleClick(item.path)">
+      <div class="box-left">
+        <i :class="item.img" />
+      </div>
+      <div class="label">{{ item.name }}</div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup name="hydrologicalMonitor">
+import {reactive} from "vue";
+import {useRouter} from "vue-router";
+
+const router = useRouter();
+const menuData = reactive([
+  { name: '水库监测', path: 'ReservoirMonitor', img: 'icon1' },
+  { name: '河道监测', key: '2', img: 'icon2' },
+])
+const handleClick = (name: string) => {
+  router.push({ name: name});
+}
+</script>
+
+<style lang="scss" scoped>
+.container {
+  display: flex;
+  flex-direction: column;
+  padding: 20px 0;
+  align-items: center;
+  width: 100%;
+  height: 100vh;
+  .box {
+    width: 358px;
+    height: 89px;
+    background: url('@/assets/images/onlineRollCall/box.png') no-repeat;
+    background-size: 100% 100%;
+    display: flex;
+    align-items: center;
+    margin-top: 16px;
+    position: relative;
+    .icon1 {
+      display: inline-block;
+      width: 92px;
+      height: 93px;
+      background: url('@/assets/images/onlineRollCall/icon1.png') no-repeat;
+      background-size: 100% 100%;
+    }
+    .icon2 {
+      display: inline-block;
+      width: 97px;
+      height: 94px;
+      background: url('@/assets/images/onlineRollCall/icon2.png') no-repeat;
+      background-size: 100% 100%;
+    }
+    .label {
+      color: #414F64;
+      font-size: 16px;
+      width: 144px;
+      text-align: center;
+    }
+  }
+}
+</style>

+ 92 - 0
src/views/disasterRiskMonitor/hydrologicalMonitor/reservoirMonitor.vue

@@ -0,0 +1,92 @@
+<template>
+  <div class="container">
+    <van-search
+        v-model="keyword"
+        class="common-search"
+        :left-icon="searchImg"
+        :right-icon="closeImg"
+        :clearable="false"
+        @search="onSearchKeyword"
+        @click-right-icon.stop="onSearchCancel"
+    />
+    <Map ref="mapRef" class="map" active-map="vectorgraph" :point-type="pointType" :event-details="eventDetails" />
+    <van-text-ellipsis class="text-box" :content="detailsData.text" rows="3" expand-text="展开" collapse-text="收起" />
+    <el-table :data="detailsData.dataList">
+      <el-table-column label="站点" prop="data1" align="center" />
+      <el-table-column label="河流" prop="data2" align="center" />
+      <el-table-column label="时间" prop="data3" align="center" />
+      <el-table-column label="水位(m)" prop="data4" align="center" />
+      <el-table-column label="超警戒(m)" prop="data5" align="center" sortable />
+    </el-table>
+  </div>
+</template>
+
+<script lang="ts" setup name="reservoirMonitor">
+import searchImg from "@/assets/images/search.png";
+import closeImg from "@/assets/images/close.png";
+import {onMounted, ref} from "vue";
+import {ElTable, ElTableColumn} from "element-plus";
+
+let pointType = ref([]);
+let eventDetails = ref({});
+let keyword = ref('');
+const detailsData = ref({
+  text: '',
+  dataList: []
+});
+
+const onSearchKeyword = (val) => {
+  keyword.value = val;
+  initData();
+}
+
+const onSearchCancel = () => {
+  keyword.value = '';
+  initData();
+}
+const initData = () => {
+  detailsData.value = {
+    text: '就诞生了附件两款发动机了解案件来看世界分手机否结束骄傲就开了房间离开家可怜见立刻就杀了对方吉林省大家逻辑了及时了解多方了解逻辑范德萨发给收到发给对方独放个',
+    dataList: [
+      { data1: '高州(四)', data2: '鉴江', data3: '10日10:00', data4: 27.82, data5: -2.68 },
+      { data1: '化州(城)', data2: '鉴江', data3: '10日10:00', data4: 26.82, data5: -2.71 },
+    ]
+  }
+}
+
+onMounted(() => {
+  initData();
+})
+</script>
+
+<style lang="scss" scoped>
+.container {
+  height: 100vh;
+  .common-search {
+    :deep(.van-field__left-icon) {
+
+      .van-icon__image {
+        width: 12px;
+        height: 12px;
+      }
+    }
+    :deep(.van-field__right-icon) {
+      width: 30px;
+      height: 30px;
+      padding: 0;
+      .van-icon__image {
+        width: 30px;
+        height: 30px;
+      }
+    }
+  }
+  .map {
+    width: 100%;
+    height: 300px;
+  }
+  .text-box {
+    padding: 10px;
+    background-color: #fff;
+  }
+}
+</style>

+ 1 - 1
src/views/disasterRiskMonitor/warningSituation.vue

@@ -135,7 +135,7 @@ onMounted(() => {
 
 <style lang="scss" scoped>
 .container {
-  height: calc(100vh - 55px);
+  height: 100vh;
   padding: 0;
   .table-line {
     display: flex;

+ 12 - 4
src/views/index.vue

@@ -1,10 +1,10 @@
 <template>
   <div class="container">
-    <div class="item" @click="handleJump('/leader/index')">
+    <div class="item" @click="handleJump('LeaderIndex', 'leader')">
       <i class="icon1" />
       <span>指挥端</span>
     </div>
-    <div class="item2" @click="handleJump('/worker')">
+    <div class="item2" @click="handleJump('WorkerIndex', 'worker')">
       <i class="icon2" />
       <span>工作人员端</span>
     </div>
@@ -13,13 +13,21 @@
 
 <script lang="ts" setup>
 import {useRouter} from "vue-router";
+import useUserStore from "@/store/modules/user";
+import {onMounted} from "vue";
 
 const router = useRouter();
+const useUser = useUserStore();
 
-const handleJump = (path: string) => {
-  router.push({ path: path });
+const handleJump = (path: string, role: string) => {
+  useUser.setRoles(role);
+  router.push({ name: path });
 }
 
+onMounted(() => {
+  useUser.setRoles('');
+  localStorage.setItem('role', '');
+})
 </script>
 
 <style lang="scss" scoped>

+ 14 - 8
src/views/leader/index.vue

@@ -36,13 +36,14 @@
     </div>
     <div class="container-content">
       <van-notice-bar
-        v-show="noticeBarState.show"
-        :left-icon="noticeImg"
-        scrollable
-        background="linear-gradient(180deg, #F3F7FF 0%, #FDFEFF 100%)"
-        mode="closeable"
-        :text="noticeBarState.event_title"
-        @click="handleNoticeBar"
+          v-show="noticeBarState.show"
+          :left-icon="noticeImg"
+          scrollable
+          background="linear-gradient(180deg, #F3F7FF 0%, #FDFEFF 100%)"
+          mode="closeable"
+          :text="noticeBarState.event_title"
+          class="notice"
+          @click="handleNoticeBar"
       />
       <div class="padding-content">
         <van-grid :column-num="2">
@@ -148,7 +149,7 @@ const menu2 = ref([
       { name: '大风灾害', icon: 'wind', url: 'GaleDisaster' },
       { name: '森林火灾', icon: 'forestFire', url: 'ForestFireWarn' },
       { name: '台风实况', icon: 'typhoon', url: 'TyphoonPath' },
-      { name: '水文监测', icon: 'hydrology', url: '' }
+      { name: '水文监测', icon: 'hydrology', url: 'HydrologicalMonitor' },
     ]
   },
   {
@@ -338,11 +339,16 @@ onUnmounted(() => {
   .search-box {
     width: 100%;
     position: relative;
+    margin-bottom: 10px;
   }
   .van-search {
     width: 100%;
   }
 }
+.notice {
+  width: 100%;
+  margin-bottom: 10px;
+}
 .container-content {
   margin-top: -70px;
   .van-notice-bar {

+ 0 - 18
src/views/onlineRollCall/spotCheckUnits.vue

@@ -108,25 +108,7 @@ onMounted(() => {
   padding: 16px;
   background-color: #F4F8FA;
 }
-.common-search {
-  margin-bottom: 16px;
-  :deep(.van-field__left-icon) {
 
-    .van-icon__image {
-      width: 12px;
-      height: 12px;
-    }
-  }
-  :deep(.van-field__right-icon) {
-    width: 30px;
-    height: 30px;
-    padding: 0;
-    .van-icon__image {
-      width: 30px;
-      height: 30px;
-    }
-  }
-}
 .content {
   height: calc(100vh - 167px);
   overflow-y: auto;