Переглянути джерело

feat: 新增院区安全态势固定布局模式

bxy 6 місяців тому
батько
коміт
e8e432e56f

+ 16 - 2
src/App.vue

@@ -1,14 +1,21 @@
 <template>
   <div id="app">
-    <Nav />
-    <div class="content">
+    <Nav v-if="!isFixedScreen" />
+    <div class="content" :class="{ 'fixed-screen': isFixedScreen }">
       <router-view />
     </div>
   </div>
 </template>
 
 <script setup lang="ts">
+  import { useRoute } from 'vue-router';
+  import { computed } from 'vue';
   import Nav from '@/components/Nav.vue';
+
+  const route = useRoute();
+  const isFixedScreen = computed(
+    () => route.matched.some((r) => r.meta?.fixedScreen === true) || route.path.startsWith('/institute-safety'),
+  );
 </script>
 
 <style scoped lang="scss">
@@ -22,6 +29,13 @@
     flex: 1;
     overflow-y: auto;
     overflow-x: hidden;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+
+    &.fixed-screen {
+      overflow-y: hidden;
+    }
   }
   @font-face {
     font-family: 'YouSheBiaoTiYuan';

+ 48 - 1
src/components/Nav.vue

@@ -1,5 +1,5 @@
 <template>
-  <header class="header">
+  <header class="header" :class="{ 'use-px': usePx }">
     <img :src="logo" alt="logo" class="header__logo" />
     <span class="platform-name">{{ title }}</span>
     <nav class="header__nav">
@@ -53,6 +53,9 @@
   const router = useRouter();
   const searchValue = ref('');
 
+  const props = withDefaults(defineProps<{ usePx?: boolean }>(), { usePx: false });
+  const usePx = computed(() => props.usePx === true);
+
   const handleSearch = () => {
     console.log('searchValue', searchValue.value);
   };
@@ -161,4 +164,48 @@
       cursor: pointer;
     }
   }
+  /* 使用 px 的样式覆盖(用于固定画布布局) */
+  .header.use-px {
+    height: 78px;
+  }
+  .header.use-px .header__nav {
+    gap: 14px;
+    margin-left: 32px;
+  }
+  .header.use-px .header__nav--item {
+    height: 45px;
+    padding: 10px 20px;
+    font-size: 18px;
+    border-radius: 4px;
+  }
+  .header.use-px .header__logo {
+    width: 34px;
+    height: 34px;
+    margin-left: 38px;
+  }
+  .header.use-px .platform-name {
+    font-size: 18px;
+    margin-left: 9px;
+  }
+  .header.use-px .platform__right {
+    height: 73px;
+  }
+  .header.use-px .platform__right__search {
+    width: 210px;
+    padding: 28px 26px;
+  }
+  .header.use-px .platform__right__login {
+    gap: 10px;
+    padding: 27px 40px 26px 40px;
+    font-size: 18px;
+  }
+  .header.use-px .input-with-icon :deep(.el-input__inner) {
+    width: 120px;
+    margin-left: 5px;
+    font-size: 16px;
+  }
+  .header.use-px .input-with-icon .search-icon {
+    width: 28px;
+    height: 28px;
+  }
 </style>

+ 4 - 4
src/constant/nav.ts

@@ -10,10 +10,10 @@ export const NAV_LIST = [
     ...HOME_PAGE,
   },
   {
-    name: '院内安全态势',
-    path: '',
+    name: 'InstituteSafety',
+    path: '/institute-safety',
     meta: {
-      title: '院安全态势',
+      title: '院安全态势',
     },
   },
   {
@@ -38,11 +38,11 @@ export const NAV_LIST = [
     },
   },
   {
+    name: 'DisasterPrevention',
     path: '/disaster-prevention',
     meta: {
       title: '灾害防范',
     },
-    name: 'DisasterPrevention',
   },
   {
     name: 'EmergencyManagement',

+ 33 - 0
src/hooks/usePageSizeStore.ts

@@ -0,0 +1,33 @@
+import { ref } from 'vue';
+import { defineStore } from 'pinia';
+
+export const DEFAULT_WIDTH = 1920;
+export const DEFAULT_HEIGHT = 1080;
+export const usePageSizeStore = defineStore('pageSize', () => {
+  // 缩放比例
+  const minRatio = ref(1);
+  // 旋转的角度
+  const rotate = ref('0deg');
+
+  const computePageSize = () => {
+    //获取屏幕尺寸
+    const windowWidth = document.body.clientWidth;
+    const windowHeight = document.body.clientHeight;
+
+    // //最小边适配
+    const ratioWidth = windowWidth / DEFAULT_WIDTH;
+    const ratioHeight = windowHeight / DEFAULT_HEIGHT;
+
+    console.log({ ratioWidth, ratioHeight });
+
+    if (ratioWidth < ratioHeight) {
+      minRatio.value = ratioWidth;
+    } else {
+      minRatio.value = ratioHeight;
+    }
+  };
+
+  return { computePageSize, ratio: minRatio, rotate };
+});
+
+export default usePageSizeStore;

+ 45 - 0
src/layout/FixedScreenLayout.vue

@@ -0,0 +1,45 @@
+<template>
+  <div class="fixed-screen-wrapper">
+    <div id="init-box" class="init-box">
+      <Nav :usePx="true" />
+      <router-view />
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { onMounted, onUnmounted } from 'vue';
+  import { storeToRefs } from 'pinia';
+  import usePageSizeStore from '@/hooks/usePageSizeStore';
+  import Nav from '@/components/Nav.vue';
+
+  const pageSize = usePageSizeStore();
+  const { ratio, rotate } = storeToRefs(pageSize);
+  const { computePageSize } = pageSize;
+
+  onMounted(() => {
+    computePageSize();
+    window.addEventListener('resize', computePageSize);
+  });
+
+  onUnmounted(() => {
+    window.removeEventListener('resize', computePageSize);
+  });
+</script>
+
+<style scoped lang="scss">
+  .fixed-screen-wrapper {
+    position: relative;
+    overflow: hidden;
+    flex-shrink: 0;
+    transform: scale(v-bind('ratio')) rotate(v-bind(rotate));
+  }
+
+  .init-box {
+    position: relative;
+    width: 1920px;
+    height: 1080px;
+    background-color: #e7f1ff;
+    overflow: hidden;
+  }
+</style>

+ 2 - 0
src/router/constant.ts

@@ -11,3 +11,5 @@ export const ParentLayout = () => import('@/layout/parentLayout.vue');
 export const HomeLayout = () => import('@/layout/HomeLayout.vue');
 /** 带有子菜单的layout */
 export const MenuLayout = () => import('@/layout/MenuLayout.vue');
+/** 固定1920x1080并整体缩放居中布局 */
+export const FixedScreenLayout = () => import('@/layout/FixedScreenLayout.vue');

+ 2 - 0
src/router/full-routes.ts

@@ -15,6 +15,7 @@ import {
   largeScreenRoutes,
   trafficRoutes,
   securityConfidentialityRoutes,
+  campusRoutes,
 } from './routers';
 
 type RouteRecordString = Omit<AppRouteRecordRaw, 'component'> & { component?: string };
@@ -46,6 +47,7 @@ export const fullRoutes: AppRouteRecordRaw[] = [
   largeScreenRoutes,
   trafficRoutes,
   securityConfidentialityRoutes,
+  campusRoutes,
 ] as const;
 
 /**

+ 2 - 1
src/router/generator-routers.ts

@@ -1,7 +1,7 @@
 import { getRouters } from '@/api/system/menu';
 import { constantRouterIcon } from './router-icons';
 import { RouteRecordRaw } from 'vue-router';
-import { Layout, ParentLayout, HomeLayout, MenuLayout } from '@/router/constant';
+import { Layout, ParentLayout, HomeLayout, MenuLayout, FixedScreenLayout } from '@/router/constant';
 import type { AppRouteRecordRaw } from '@/router/types';
 
 const Iframe = () => import('@/views/iframe/index.vue');
@@ -11,6 +11,7 @@ LayoutMap.set('LAYOUT', Layout);
 LayoutMap.set('IFRAME', Iframe);
 LayoutMap.set('HOME_LAYOUT', HomeLayout);
 LayoutMap.set('MENU_LAYOUT', MenuLayout);
+LayoutMap.set('FIXED_SCREEN_LAYOUT', FixedScreenLayout);
 
 /**
  * 格式化 后端 结构信息并递归生成层级路由表

+ 38 - 0
src/router/routers/campus.ts

@@ -0,0 +1,38 @@
+/** 院内安全态势(固定1920x1080缩放居中布局) */
+const campusRoutes = {
+  id: 6000,
+  parentId: -1,
+  name: 'InstituteSafety',
+  path: '/institute-safety',
+  component: 'FIXED_SCREEN_LAYOUT',
+  redirect: '',
+  meta: {
+    title: '院区安全态势',
+    fixedScreen: true,
+    icon: '',
+  },
+  children: [
+    {
+      id: 6001,
+      parentId: 6000,
+      name: 'overview',
+      path: 'overview',
+      component: '/institute-safety/PageInstituteSafety',
+      redirect: '',
+      meta: {
+        activeMenu: null,
+        alwaysShow: false,
+        frameSrc: '',
+        hidden: false,
+        icon: '',
+        isFrame: 0,
+        isRoot: false,
+        noCache: false,
+        query: '',
+        title: '院区安全态势',
+      },
+    },
+  ],
+};
+
+export default campusRoutes;

+ 2 - 0
src/router/routers/index.ts

@@ -5,6 +5,7 @@ import exceptionRouters from './exception';
 import largeScreenRoutes from './large-screen';
 import trafficRoutes from './traffic';
 import securityConfidentialityRoutes from './security-confidentiality';
+import campusRoutes from './campus';
 
 export {
   platformRoutes,
@@ -14,4 +15,5 @@ export {
   largeScreenRoutes,
   trafficRoutes,
   securityConfidentialityRoutes,
+  campusRoutes,
 };

+ 2 - 1
src/styles/variables.scss

@@ -1,6 +1,7 @@
 // 主题颜色
 $primary-color: #1777ff;
-$background-color: rgba(#1777ff, 0.1);
+// $background-color: rgba(#1777ff, 0.1);
+$background-color: #f1f7ff;
 $error-color: #e74c3c;
 $text-color: #000;
 $white-color: #fff;

+ 92 - 0
src/views/institute-safety/PageInstituteSafety.vue

@@ -0,0 +1,92 @@
+<template>
+  <div class="page">
+    <div class="left-section">
+      <CardWeather class="card-weather" />
+      <CardProductionSafety class="card-production-safety" />
+    </div>
+    <CardMapAndAlert class="card-map-and-alert" />
+    <div class="right-section">
+      <CardDisasterPrevention class="card-disaster-prevention" />
+      <CardEmergencyManage class="card-emergency-manage" />
+      <CardTrafficSafety class="card-traffic-safety" />
+      <CardSecurityConfidentiality class="card-security-confidentiality" />
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+  import CardWeather from './components/CardWeather.vue';
+  import CardProductionSafety from './components/CardProductionSafety.vue';
+  import CardMapAndAlert from './components/CardMapAndAlert.vue';
+  import CardDisasterPrevention from './components/CardDisasterPrevention.vue';
+  import CardEmergencyManage from './components/CardEmergencyManage.vue';
+  import CardTrafficSafety from './components/CardTrafficSafety.vue';
+  import CardSecurityConfidentiality from './components/CardSecurityConfidentiality.vue';
+</script>
+
+<style scoped lang="scss">
+  .page {
+    width: 100%;
+    height: 1002px;
+    padding: 10px;
+    display: flex;
+    gap: 10px;
+    // background-color: #f1f7ff;
+  }
+
+  .left-section,
+  .right-section {
+    display: flex;
+    flex-direction: column;
+    gap: 10px;
+  }
+
+  .card-weather {
+    width: 330px;
+    height: 196px;
+    background: linear-gradient(90deg, #b4ccff 0%, #e4fbf9 100%);
+    border-radius: 8px;
+  }
+
+  .card-production-safety {
+    width: 330px;
+    height: 776px;
+    background: #ffffff;
+    border-radius: 8px;
+  }
+
+  .card-map-and-alert {
+    width: 1220px;
+    height: 982px;
+    background: #ffffff;
+    border-radius: 8px;
+  }
+
+  .card-disaster-prevention {
+    width: 330px;
+    height: 159px;
+    background: #ffffff;
+    border-radius: 8px;
+  }
+
+  .card-emergency-manage {
+    width: 330px;
+    height: 357px;
+    background: #ffffff;
+    border-radius: 8px;
+  }
+
+  .card-traffic-safety {
+    width: 330px;
+    height: 218px;
+    background: #ffffff;
+    border-radius: 8px;
+  }
+
+  .card-security-confidentiality {
+    width: 330px;
+    height: 218px;
+    background: #ffffff;
+    border-radius: 8px;
+  }
+</style>

+ 7 - 0
src/views/institute-safety/components/CardDisasterPrevention.vue

@@ -0,0 +1,7 @@
+<template>
+  <div> </div>
+</template>
+
+<script setup lang="ts"></script>
+
+<style scoped lang="scss"></style>

+ 7 - 0
src/views/institute-safety/components/CardEmergencyManage.vue

@@ -0,0 +1,7 @@
+<template>
+  <div> </div>
+</template>
+
+<script setup lang="ts"></script>
+
+<style scoped lang="scss"></style>

+ 7 - 0
src/views/institute-safety/components/CardMapAndAlert.vue

@@ -0,0 +1,7 @@
+<template>
+  <div> </div>
+</template>
+
+<script setup lang="ts"></script>
+
+<style scoped lang="scss"></style>

+ 7 - 0
src/views/institute-safety/components/CardProductionSafety.vue

@@ -0,0 +1,7 @@
+<template>
+  <div> </div>
+</template>
+
+<script setup lang="ts"></script>
+
+<style scoped lang="scss"></style>

+ 7 - 0
src/views/institute-safety/components/CardSecurityConfidentiality.vue

@@ -0,0 +1,7 @@
+<template>
+  <div> </div>
+</template>
+
+<script setup lang="ts"></script>
+
+<style scoped lang="scss"></style>

+ 7 - 0
src/views/institute-safety/components/CardTrafficSafety.vue

@@ -0,0 +1,7 @@
+<template>
+  <div> </div>
+</template>
+
+<script setup lang="ts"></script>
+
+<style scoped lang="scss"></style>

+ 7 - 0
src/views/institute-safety/components/CardWeather.vue

@@ -0,0 +1,7 @@
+<template>
+  <div> </div>
+</template>
+
+<script setup lang="ts"></script>
+
+<style scoped lang="scss"></style>