ソースを参照

主页:重构为左右弧形导航菜单 + 视频背景,菜单点击跳转对应路由;替换/清理废弃背景素材

画安 3 週間 前
コミット
f149b95633
共有3 個のファイルを変更した269 個の追加73 個の削除を含む
  1. BIN
      src/assets/images/main-background.png
  2. 0 0
      src/assets/main/main-bg.mp4
  3. 269 73
      src/views/Main.vue

BIN
src/assets/images/main-background.png


src/assets/main/main_bg.mp4 → src/assets/main/main-bg.mp4


+ 269 - 73
src/views/Main.vue

@@ -1,41 +1,148 @@
 <template>
   <LoginLayout>
-
-    <!-- 背景 -->
     <template #background>
-      <div class="login-bg"></div>
-      <!-- 漂浮闪烁点 -->
-      <div class="spark-layer" aria-hidden="true">
-        <span v-for="d in dots" :key="d.id" class="spark" :style="dotStyle(d)" />
-      </div>
+      <video class="bg-video" autoplay muted loop playsinline>
+        <source src="@/assets/main/main-bg.mp4" type="video/mp4" />
+      </video>
     </template>
 
     <template #main>
-      
-    </template>
+      <div class="menu-container">
+        
+        <div class="menu-side left-side">
+          <div 
+            v-for="(item, index) in leftMenus" 
+            :key="'left-'+index" 
+            class="menu-item"
+            @click="handleMenuClick(item)"
+          >
+            <div class="icon-container">
+              <img :src="item.halo" class="halo-img halo-normal" v-if="item.halo" />
+              <img :src="item.hoverHalo" class="halo-img halo-hover" v-if="item.hoverHalo" />
+              
+              <img :src="item.icon" class="icon-img img-normal" v-if="item.icon" />
+              <div class="icon-placeholder img-normal" v-else></div>
+              
+              <img :src="item.hoverIcon || item.icon" class="icon-img img-hover" v-if="item.icon" />
+              <div class="icon-placeholder img-hover" v-else></div>
+            </div>
+            
+            <span class="menu-text">{{ item.name }}</span>
+          </div>
+        </div>
+
+        <div class="menu-side right-side">
+          <div 
+            v-for="(item, index) in rightMenus" 
+            :key="'right-'+index" 
+            class="menu-item"
+            @click="handleMenuClick(item)"
+          >
+            <div class="icon-container">
+              <img :src="item.halo" class="halo-img halo-normal" v-if="item.halo" />
+              <img :src="item.hoverHalo" class="halo-img halo-hover" v-if="item.hoverHalo" />
+              
+              <img :src="item.icon" class="icon-img img-normal" v-if="item.icon" />
+              <div class="icon-placeholder img-normal" v-else></div>
+              
+              <img :src="item.hoverIcon || item.icon" class="icon-img img-hover" v-if="item.icon" />
+              <div class="icon-placeholder img-hover" v-else></div>
+            </div>
+            
+            <span class="menu-text">{{ item.name }}</span>
+          </div>
+        </div>
 
+      </div>
+    </template>
   </LoginLayout>
 </template>
 
 <script>
 import LoginLayout from "@/layouts/LoginLayout.vue";
-import BottomDock from "@/components/ui/BottomDock.vue";
 
 export default {
   name: "MainPage",
   components: { 
     LoginLayout, 
-    BottomDock 
-  },
-  created() {
-
   },
   data() {
+    // 假设公共的光圈图片路径(按需修改为你实际的文件名)
+    const defaultHalo = require('@/assets/main/main-menu-bg.png');
+    const defaultHoverHalo = require('@/assets/main/main-menu-bg-hover.png');
+
     return {
-      // 亮点
+      baseW: 1920,
+      baseH: 1080,
+      scale: 1,
       dots: [],
+      // 菜单配置数据
+      menuList: [
+        { 
+          name: '首页', 
+          icon: require('@/assets/main/main-home.png'),
+          hoverIcon: require('@/assets/main/main-home-hover.png'),
+          halo: defaultHalo,             // 引入光圈图片
+          hoverHalo: defaultHoverHalo,   // 引入高亮光圈图片
+          side: 'left', 
+          path: '/home' 
+        },
+        { 
+          name: '状态监测', 
+          icon: require('@/assets/main/main-surve.png'),
+          hoverIcon: require('@/assets/main/main-surve-hover.png'),
+          halo: defaultHalo,
+          hoverHalo: defaultHoverHalo,
+          side: 'left', 
+          path: '/surve' 
+        },
+        { 
+          name: '勤务管理', 
+          icon: require('@/assets/main/main-security.png'),
+          hoverIcon: require('@/assets/main/main-security-hover.png'),
+          halo: defaultHalo,
+          hoverHalo: defaultHoverHalo,
+          side: 'left', 
+          path: '/security' 
+        },
+        { 
+          name: '干线协调', 
+          icon: require('@/assets/main/main-coor.png'),
+          hoverIcon: require('@/assets/main/main-coor-hover.png'),
+          halo: defaultHalo,
+          hoverHalo: defaultHoverHalo,
+          side: 'right',
+          path: '/trunk'
+        },
+        { 
+          name: '数据分析', 
+          icon: require('@/assets/main/main-watch.png'),
+          hoverIcon: require('@/assets/main/main-watch-hover.png'),
+          halo: defaultHalo,
+          hoverHalo: defaultHoverHalo,
+          side: 'right', 
+          path: '/watch' 
+        },
+        { 
+          name: '系统设置', 
+          icon: require('@/assets/main/main-setting.png'),
+          hoverIcon: require('@/assets/main/main-setting-hover.png'),
+          halo: defaultHalo,
+          hoverHalo: defaultHoverHalo,
+          side: 'right', 
+          path: '/setting' 
+        },
+      ]
     };
   },
+  computed: {
+    leftMenus() {
+      return this.menuList.filter(item => item.side === 'left');
+    },
+    rightMenus() {
+      return this.menuList.filter(item => item.side === 'right');
+    }
+  },
   mounted() {
     this.updateScale();
     window.addEventListener("resize", this.updateScale, { passive: true });
@@ -48,24 +155,21 @@ export default {
     updateScale() {
       const w = window.innerWidth || this.baseW;
       const h = window.innerHeight || this.baseH;
-      // 取 min 保证不裁切(大屏常见)
       this.scale = Math.min(w / this.baseW, h / this.baseH);
-      // 写到 css 变量,方便 calc(var(--s) * xxxpx)
       this.$el && this.$el.style.setProperty("--s", this.scale.toFixed(6));
     },
-    // ---------- 闪烁点 ----------
     initDots() {
       const count = 50;
       const arr = [];
       for (let i = 0; i < count; i++) {
         arr.push({
           id: i,
-          x: 18 + Math.random() * 64, // 百分比布局,适配任意屏幕
+          x: 18 + Math.random() * 64,
           y: 22 + Math.random() * 56,
-          r: 1.2 + Math.random() * 2.8, // 半径
+          r: 1.2 + Math.random() * 2.8,
           a: 0.45 + Math.random() * 0.55,
-          d: Math.random() * 2.8, // delay
-          t: 1.8 + Math.random() * 2.6, // duration
+          d: Math.random() * 2.8,
+          t: 1.8 + Math.random() * 2.6,
         });
       }
       this.dots = arr;
@@ -81,78 +185,170 @@ export default {
         animationDuration: `${d.t}s`,
       };
     },
+    handleMenuClick(item) {
+      if (!item.path) return;
+      this.$router.push(item.path).catch(err => {
+        if (err.name !== 'NavigationDuplicated') {
+          console.error('路由跳转失败:', err);
+        }
+      });
+    }
   }
-
 }
 </script>
 
 <style scoped>
-.dock-style {
+/* ================= 全局与背景层 ================= */
+.bg-video {
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+  width: 100%;
+  height: 100%;
+  object-fit: cover;
+  z-index: 0;
+}
+
+/* ================= 菜单整体布局 ================= */
+.menu-container {
+  position: absolute;
+  top: 0;
   left: 0;
-  bottom: unset !important;
+  width: 100%;
   height: 100%;
-  margin: auto 0;
-  border-radius: calc(var(--s) * 1.125rem);
-  background: linear-gradient(180deg, rgba(10, 35, 70, .18), rgba(0, 0, 0, .08));
-  box-shadow: 0 0 calc(var(--s) * 1.625rem) rgba(0, 140, 255, .08) inset;
-  backdrop-filter: blur(.125rem);
+  pointer-events: none;
+  z-index: 10;
 }
-.dock-style ::v-deep .right-arrow {
-  margin-left: 100px;
+
+.menu-side {
+  position: absolute;
+  top: 50%;
+  transform: translateY(-50%);
+  display: flex;
+  flex-direction: column;
+  gap: calc(var(--s) * 70px);
+  pointer-events: auto;
 }
-.dock-style ::v-deep .left-arrow {
-  margin-right: 100px;
+
+.left-side { left: calc(var(--s) * 180px); }
+.right-side { right: calc(var(--s) * 180px); }
+
+/* ================= 单个菜单项 ================= */
+.menu-item {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  cursor: pointer;
 }
-.main-page {
+
+/* --- 图标复合容器 --- */
+.icon-container {
   position: relative;
-  width: 100vw;
-  height: 100vh;
-  overflow: hidden;
-  --s: 1;
-  color: #d9efff;
-  user-select: none;
+  width: calc(var(--s) * 130px);
+  height: calc(var(--s) * 100px);
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  margin-bottom: calc(var(--s) * 12px);
 }
 
-.login-bg {
-  background: url('@/assets/images/main-background.png') no-repeat center/cover;
-  width: 100%;
-  height: 100%;
-  filter: saturate(1.05) contrast(1.03);
+/* --- 1. 光圈底座样式 (图片) --- */
+.halo-img {
+  position: absolute;
+  bottom: 0; /* 根据你切图的空白区域自行微调,比如 calc(var(--s) * 5px) */
+  left: 50%;
+  transform: translateX(-50%);
+  width: 90%; /* 根据切图实际比例微调大小 */
+  height: auto;
+  object-fit: contain;
+  transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
+  z-index: 1;
 }
 
-/* 闪烁亮点 */
-.spark-layer {
+/* 光圈默认展示 normal,隐藏 hover */
+.halo-normal { opacity: 1; }
+.halo-hover { opacity: 0; }
+
+/* --- 2. 顶部图标样式 (图片) --- */
+.icon-img {
   position: absolute;
-  inset: 0;
-  z-index: 4;
-  pointer-events: none;
-  width: 100%;
-  height: 100%;
+  bottom: calc(var(--s) * 50px); /* 浮在光圈上方,根据切图微调 */
+  width: 80%;
+  height: 80%;
+  object-fit: contain;
+  transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
+  z-index: 2;
 }
 
-.spark {
+.icon-placeholder {
   position: absolute;
-  border-radius: 999px;
-  background: radial-gradient(circle, rgba(140, 235, 255, .95), rgba(140, 235, 255, 0) 70%);
-  animation-name: twinkle;
-  animation-timing-function: ease-in-out;
-  animation-iteration-count: infinite;
+  bottom: calc(var(--s) * 50px);
+  width: 40%;
+  height: 40%;
+  border-radius: 10px;
+  transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
+  z-index: 2;
 }
 
-@keyframes twinkle {
-  0% {
-    transform: translateZ(0) scale(.7);
-    opacity: .22;
-  }
+/* 图标默认展示 normal,隐藏 hover */
+.img-normal { opacity: 1; transform: translateY(0); }
+.img-hover { opacity: 0; transform: translateY(0); }
 
-  50% {
-    transform: translateZ(0) scale(1.25);
-    opacity: .9;
-  }
+/* ================= Hover 联动动效 ================= */
 
-  100% {
-    transform: translateZ(0) scale(.7);
-    opacity: .22;
-  }
+/* A. 光圈交叉淡入淡出,并可加入轻微放大效果增强动感 */
+.menu-item:hover .halo-normal {
+  opacity: 0;
+}
+.menu-item:hover .halo-hover {
+  opacity: 1;
+  transform: translateX(-50%) scale(1.08); /* 高亮时底座稍微扩散放大 */
+}
+
+/* B. 顶部图标交叉淡入淡出,并上浮 */
+.menu-item:hover .img-normal {
+  opacity: 0;
+  transform: translateY(calc(var(--s) * -8px));
+}
+.menu-item:hover .img-hover {
+  opacity: 1;
+  transform: translateY(calc(var(--s) * -8px));
+}
+
+/* C. 文字点亮并微放 */
+.menu-text {
+  color: #a6c4eb;
+  font-size: calc(var(--s) * 18px);
+  font-weight: 500;
+  transition: all 0.3s ease;
+}
+.menu-item:hover .menu-text {
+  color: #ffffff;
+  transform: scale(1.05);
+}
+
+/* ================= 菜单弧度调整 (贴合地球) ================= */
+
+/* 左侧菜单:第1个(顶部)和第3个(底部)向右(向中间)偏移,形成 ) 弧度 */
+.left-side .menu-item:nth-child(1),
+.left-side .menu-item:nth-child(3) {
+  transform: translateX(calc(var(--s) * 70px)); /* 数值越大,弧度越深 */
+}
+
+/* 左侧菜单:第2个(中间)保持最靠外,如果需要可微调 */
+.left-side .menu-item:nth-child(2) {
+  transform: translateX(0); 
+}
+
+/* 右侧菜单:第1个(顶部)和第3个(底部)向左(向中间)偏移,形成 ( 弧度 */
+.right-side .menu-item:nth-child(1),
+.right-side .menu-item:nth-child(3) {
+  transform: translateX(calc(var(--s) * -70px)); /* 数值越大,弧度越深 */
+}
+
+/* 右侧菜单:第2个(中间)保持最靠外 */
+.right-side .menu-item:nth-child(2) {
+  transform: translateX(0);
 }
-</style>
+</style>