Ryuiso 8 bulan lalu
induk
melakukan
bb4bea11f6

+ 0 - 2
web/src/App.vue

@@ -1,7 +1,6 @@
 <script setup lang="ts">
 import { RouterView, useRoute } from 'vue-router'
 import { computed } from 'vue'
-import Header from '@/layout/Header/index.vue'
 import Footer from '@/layout/Footer/index.vue'
 import NewHeader from '@/layout/NewHeader/index.vue'
 
@@ -12,7 +11,6 @@ const showFooter = computed(() => route.name !== 'doc') // 在文档页面不显
 
 <template>
   <div class="app-container">
-    <!--<Header />-->
     <NewHeader />
 
     <!-- 主要内容区域 -->

+ 13 - 0
web/src/assets/img/Readme.md

@@ -0,0 +1,13 @@
+# 图片资源
+
+本目录包含应用中使用的素材图片资源。
+
+## Home页面列表
+
+- `api-diagram.svg` - API架构图,用于展示API的结构和关系
+- `doc-structure.svg` - 文档结构图,用于展示文档的层次结构
+- `project-structure.svg` - 项目结构图,用于展示项目的目录结构
+- `system-architecture.svg` - 系统架构图,用于展示系统的整体架构
+
+
+

+ 0 - 0
web/src/layout/Header/index.vue → web/src/layout/BackUp/index.vue


+ 125 - 50
web/src/layout/NewHeader/index.vue

@@ -7,6 +7,9 @@ import { computed, ref } from 'vue'
 const route = useRoute()
 const isDocView = computed(() => route.name === 'doc')
 
+// 计算当前激活的菜单索引
+defineOptions({ name: 'NewHeader' })
+
 // 组件属性
 defineProps({
   title: {
@@ -47,14 +50,35 @@ const handleMenuHover = (index: number) => {
   activeIndex.value = index
 }
 
-// 处理菜单离开
+// 处理整个菜单区域离开
 const handleMenuLeave = () => {
-  activeIndex.value = -1
+  // 添加一个小延迟,防止意外关闭
+  setTimeout(() => {
+    activeIndex.value = -1
+  }, 100)
+}
+
+// 处理子菜单悬停,防止子菜单消失
+const handleSubmenuHover = (index: number) => {
+  activeIndex.value = index
+}
+
+// 处理菜单项点击
+const handleMenuClick = (index: number) => {
+  if (activeIndex.value === index) {
+    activeIndex.value = -1
+  } else {
+    activeIndex.value = index
+  }
 }
 </script>
 
 <template>
-  <header class="new-header" :class="{ 'new-header-doc': isDocView }">
+  <header
+    class="new-header"
+    :class="{ 'new-header-doc': isDocView }"
+    @mouseleave="handleMenuLeave"
+  >
     <div class="header-container">
       <!-- Logo 区域 -->
       <div class="logo-section">
@@ -72,22 +96,28 @@ const handleMenuLeave = () => {
             :key="item.name"
             class="menu-item"
             @mouseenter="handleMenuHover(index)"
-            @mouseleave="handleMenuLeave"
+            @click="handleMenuClick(index)"
             :class="{ active: activeIndex === index }"
           >
-            <RouterLink :to="item.path" class="menu-link">{{ item.title }}</RouterLink>
-            <!-- 添加二级菜单 -->
-            <ul v-if="item.children && item.children.length > 0" class="submenu">
-              <li v-for="subItem in item.children" :key="subItem.name" class="submenu-item">
-                <RouterLink :to="`${item.path}/${subItem.path}`" class="submenu-link">{{ subItem.title }}</RouterLink>
-                <!-- 添加三级菜单 -->
-                <ul v-if="subItem.children && subItem.children.length > 0" class="sub-submenu">
-                  <li v-for="subSubItem in subItem.children" :key="subSubItem.name" class="sub-submenu-item">
-                    <RouterLink :to="`${item.path}/${subItem.path}/${subSubItem.path}`" class="sub-submenu-link">{{ subSubItem.title }}</RouterLink>
-                  </li>
-                </ul>
-              </li>
-            </ul>
+            <RouterLink :to="item.path" class="menu-link" @click.prevent>{{ item.title }}</RouterLink>
+            <!-- 添加二级和三级菜单 -->
+            <div
+              v-if="item.children && item.children.length > 0"
+              class="submenu"
+              @mouseenter="handleSubmenuHover(index)"
+            >
+              <div class="submenu-content">
+                <div v-for="subItem in item.children" :key="subItem.name" class="submenu-item">
+                  <RouterLink :to="`${item.path}/${subItem.path}`" class="submenu-link">{{ subItem.title }}</RouterLink>
+                  <!-- 添加三级菜单 -->
+                  <div v-if="subItem.children && subItem.children.length > 0" class="sub-submenu">
+                    <div v-for="subSubItem in subItem.children" :key="subSubItem.name" class="sub-submenu-item">
+                      <RouterLink :to="`${item.path}/${subItem.path}/${subSubItem.path}`" class="sub-submenu-link">{{ subSubItem.title }}</RouterLink>
+                    </div>
+                  </div>
+                </div>
+              </div>
+            </div>
           </li>
         </ul>
       </nav>
@@ -107,20 +137,17 @@ const handleMenuLeave = () => {
 @use "sass:color";
 
 .new-header {
-  position: fixed;
-  top: 0;
-  left: 0;
-  right: 0;
-  height: v.$header-height;
+  position: relative;
+  width: 100%;
   background-color: v.$background-light;
-  border-bottom: 1px solid rgba(0, 0, 0, 0.1);
-  z-index: v.$z-index-fixed;
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+  z-index: 1001; // 确保header在submenu之上
 }
 
 .header-container {
   @include m.container;
   @include m.flex-between;
-  height: 100%;
+  padding: 0 v.$spacing-lg;
 }
 
 // Logo 区域样式
@@ -128,16 +155,18 @@ const handleMenuLeave = () => {
   display: flex;
   align-items: center;
   gap: v.$spacing-sm;
+  //height: 100%; // 确保logo区域填满header高度
 
   a {
     display: flex;
     align-items: center;
     text-decoration: none;
     gap: v.$spacing-sm;
+    height: 100%; // 确保链接区域填满header高度
   }
 
   .logo {
-    height: 42px;
+    height: 42px; // 保持logo高度不变
     width: auto;
     transition: transform 0.3s ease;
     transform-origin: center;
@@ -159,6 +188,7 @@ const handleMenuLeave = () => {
 .nav-menu {
   flex: 1;
   margin: 0 v.$spacing-xl;
+  height: 100%; // 确保导航菜单填满header高度
 
   .menu-list {
     display: flex;
@@ -170,7 +200,7 @@ const handleMenuLeave = () => {
 
   .menu-item {
     position: relative;
-    height: 100%;
+    height: 64px; // 确保菜单项高度与header一致
     padding: 0 v.$spacing-lg;
 
     &:hover, &.active {
@@ -193,18 +223,30 @@ const handleMenuLeave = () => {
   // 添加二级菜单样式
   .submenu {
     display: none;
-    position: absolute;
-    top: 100%;
+    position: fixed;
+    top: 64px; // 直接使用固定的header高度
     left: 0;
+    width: 100%;
     background-color: v.$background-light;
-    border: 1px solid rgba(0, 0, 0, 0.1);
-    border-radius: v.$border-radius-md;
-    padding: v.$spacing-sm 0;
-    min-width: 200px;
+    border-bottom: 1px solid rgba(0, 0, 0, 0.1);
+    padding: v.$spacing-lg 0;
     box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
+    animation: slideDown 0.3s ease;
+    z-index: 1000;
+    pointer-events: auto; // 确保可以接收鼠标事件
+  }
+  @keyframes slideDown {
+    from {
+      opacity: 0;
+      transform: translateY(-10px);
+    }
+    to {
+      opacity: 1;
+      transform: translateY(0);
+    }
   }
 
-  .menu-item:hover .submenu {
+  .menu-item.active .submenu {
     display: block;
   }
 
@@ -212,41 +254,73 @@ const handleMenuLeave = () => {
     position: relative;
   }
 
+  .submenu-content {
+    max-width: v.$container-width;
+    margin: 0 auto;
+    padding: 0 v.$spacing-lg;
+    display: grid;
+    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
+    gap: v.$spacing-xl;
+  }
+
+  .submenu-item {
+    display: flex;
+    flex-direction: column;
+  }
+
   .submenu-link {
     display: block;
-    padding: v.$spacing-sm v.$spacing-lg;
+    padding: v.$spacing-sm 0;
     color: v.$text-primary;
     text-decoration: none;
-    font-size: v.$font-size-sm;
-    transition: background-color v.$transition-fast;
+    font-size: v.$font-size-md;
+    font-weight: v.$font-weight-medium;
+    border-bottom: 2px solid v.$primary-color;
+    margin-bottom: v.$spacing-md;
+    transition: all v.$transition-fast;
 
     &:hover {
-      background-color: rgba(v.$primary-color, 0.1);
       color: v.$primary-color;
     }
   }
 
   // 添加三级菜单样式
   .sub-submenu {
-    display: none;
-    position: absolute;
-    top: 0;
-    left: 100%;
-    background-color: v.$background-light;
-    border: 1px solid rgba(0, 0, 0, 0.1);
-    border-radius: v.$border-radius-md;
-    padding: v.$spacing-sm 0;
-    min-width: 200px;
-    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
+    display: flex;
+    flex-direction: column;
+    margin-top: v.$spacing-xs;
   }
 
-  .submenu-item:hover .sub-submenu {
+  .sub-submenu-item {
+    margin-bottom: v.$spacing-xs;
+  }
+
+  .sub-submenu-link {
     display: block;
+    padding: v.$spacing-xs v.$spacing-sm v.$spacing-xs v.$spacing-md;
+    color: v.$text-secondary;
+    text-decoration: none;
+    font-size: v.$font-size-sm;
+    transition: all v.$transition-fast;
+    position: relative;
+
+    &:before {
+      content: "•";
+      position: absolute;
+      left: 0;
+      color: v.$primary-color;
+      opacity: 0.7;
+    }
+
+    &:hover {
+      color: v.$primary-color;
+      transform: translateX(5px);
+    }
   }
 
   .sub-submenu-link {
     display: block;
-    padding: v.$spacing-sm v.$spacing-lg;
+    padding: v.$spacing-sm v.$spacing-lg v.$spacing-sm v.$spacing-lg;
     color: v.$text-primary;
     text-decoration: none;
     font-size: v.$font-size-sm;
@@ -264,6 +338,7 @@ const handleMenuLeave = () => {
   display: flex;
   align-items: center;
   gap: v.$spacing-md;
+  height: 100%; // 确保操作区域填满header高度
 }
 
 .action-link {

+ 0 - 332
web/src/layout/NewHeader/index_bk.vue

@@ -1,332 +0,0 @@
-<script setup lang="ts">
-import { RouterLink, useRoute } from 'vue-router'
-import { navigation } from '@/router'
-import { computed, ref } from 'vue'
-
-// 组件属性
-defineProps({
-  title: {
-    type: String,
-    default: '',
-  },
-  subtitle: {
-    type: String,
-    default: '',
-  },
-  dark: {
-    type: Boolean,
-    default: false,
-  },
-  bgImage: {
-    type: String,
-    default: '',
-  },
-  bgColor: {
-    type: String,
-    default: '',
-  },
-  heroSection: {
-    type: Boolean,
-    default: false,
-  },
-  centered: {
-    type: Boolean,
-    default: false,
-  },
-})
-
-// 菜单数据
-const menuItems = [
-  {
-    label: '行业',
-    children: [
-      {
-        title: '公共安全',
-        items: ['测绘', '电力', '石油与天然气', '水利', '林业'],
-      },
-      {
-        title: '应急救援',
-        items: ['洪涝灾害救援', '森林火灾救援', '地震与地质灾害救援', '山岳搜救'],
-      },
-      {
-        title: '执法',
-        items: ['侦查取证', '治安巡逻', '交通治理', '城管巡查'],
-      },
-      {
-        title: '安全生产',
-        items: ['安全生产部门监管', '企业安全生产自查', '安全生产事故处置'],
-      },
-    ],
-  },
-  { label: '产品' },
-  { label: '行业洞察' },
-  { label: '生态' },
-  { label: '数据安全' },
-  { label: '服务与支持' },
-]
-
-// 当前激活的菜单索引
-const activeIndex = ref(-1)
-
-// 处理菜单悬停
-const handleMenuHover = (index: number) => {
-  activeIndex.value = index
-}
-
-// 处理菜单离开
-const handleMenuLeave = (index: number) => {
-  activeIndex.value = -1
-}
-</script>
-
-<template>
-  <header class="new-header">
-    <div class="header-container">
-      <!-- Logo 区域 -->
-      <div class="logo-section">
-        <img src="@/assets/icons/logo.svg" alt="Pendulum Logo" class="logo" />
-        <span class="logo-text">Pendulum 开放平台</span>
-      </div>
-
-      <!-- 导航菜单 -->
-      <nav class="nav-menu">
-        <ul class="menu-list">
-          <li
-            v-for="(item, index) in menuItems"
-            :key="index"
-            class="menu-item"
-            @mouseenter="handleMenuHover(index)"
-            @mouseleave="handleMenuLeave(index)"
-            :class="{ active: activeIndex === index }"
-          >
-            <a href="#" class="menu-link">{{ item.label }}</a>
-
-            <!-- 子菜单 -->
-            <div v-if="item.children" class="submenu" :class="{ show: activeIndex === index }">
-              <div class="submenu-container">
-                <div
-                  v-for="(group, groupIndex) in item.children"
-                  :key="groupIndex"
-                  class="submenu-group"
-                >
-                  <h3 v-if="group.title" class="submenu-title">{{ group.title }}</h3>
-                  <ul class="submenu-list">
-                    <li
-                      v-for="(subItem, subIndex) in group.items"
-                      :key="subIndex"
-                      class="submenu-item"
-                    >
-                      <a href="#" class="submenu-link">{{ subItem }}</a>
-                    </li>
-                  </ul>
-                </div>
-              </div>
-            </div>
-          </li>
-        </ul>
-      </nav>
-
-      <!-- 右侧操作区 -->
-      <div class="action-section">
-        <a href="#" class="action-link">联系我们</a>
-        <a href="#" class="action-button">预览平台</a>
-      </div>
-    </div>
-  </header>
-</template>
-
-<style scoped lang="scss">
-@use '@/assets/styles/variables' as v;
-@use '@/assets/styles/mixins' as m;
-@use "sass:color";
-
-.new-header {
-  position: fixed;
-  top: 0;
-  left: 0;
-  right: 0;
-  height: v.$header-height;
-  background-color: v.$background-light;
-  border-bottom: 1px solid rgba(0, 0, 0, 0.1);
-  z-index: v.$z-index-fixed;
-}
-
-.header-container {
-  @include m.container;
-  @include m.flex-between;
-  height: 100%;
-}
-
-// Logo 区域样式
-.logo-section {
-  display: flex;
-  align-items: center;
-  gap: v.$spacing-sm;
-
-  .logo {
-    height: 42px;
-    width: auto;
-    transition: transform 0.3s ease;
-    transform-origin: center;
-
-    &:hover {
-      transform: scale(1.1);
-      animation-play-state: paused;
-    }
-  }
-
-  .logo-text {
-    font-size: v.$font-size-md;
-    font-weight: v.$font-weight-medium;
-    color: v.$text-primary;
-  }
-}
-
-// 导航菜单样式
-.nav-menu {
-  flex: 1;
-  margin: 0 v.$spacing-xl;
-
-  .menu-list {
-    display: flex;
-    list-style: none;
-    margin: 0;
-    padding: 0;
-    height: 100%;
-  }
-
-  .menu-item {
-    position: relative;
-    height: 100%;
-    padding: 0 v.$spacing-lg;
-
-    &:hover {
-      .menu-link {
-        color: v.$primary-color;
-      }
-    }
-
-    &.active {
-      .menu-link {
-        color: v.$primary-color;
-      }
-    }
-  }
-
-  .menu-link {
-    display: flex;
-    align-items: center;
-    height: 100%;
-    color: v.$text-primary;
-    text-decoration: none;
-    font-size: v.$font-size-sm;
-    transition: color v.$transition-fast;
-  }
-}
-
-// 子菜单样式
-.submenu {
-  position: absolute;
-  top: 100%;
-  left: 0;
-  right: 0;
-  width: 100%;
-  background: v.$background-light;
-  border-radius: 0;
-  box-shadow: 0 6px 12px rgba(0, 0, 0, 0.1);
-  opacity: 0;
-  visibility: hidden;
-  transition: all v.$transition-fast;
-  padding: 0;
-  border-top: 2px solid #000;
-  transform: translateY(-2px);
-  display: flex;
-  justify-content: center;
-
-  &.show {
-    opacity: 1;
-    visibility: visible;
-    transform: translateY(0);
-  }
-}
-.submenu-container {
-  display: flex;
-  gap: v.$spacing-xl;
-  padding: v.$spacing-lg;
-  background-color: #fff;
-  width: 100%;
-  max-width: 1200px;
-}
-
-.submenu-group {
-  min-width: 160px;
-  padding: v.$spacing-md;
-}
-
-.submenu-title {
-  font-size: v.$font-size-sm;
-  font-weight: v.$font-weight-bold;
-  color: v.$text-primary;
-  margin: 0 0 v.$spacing-md;
-  padding-bottom: v.$spacing-xs;
-  border-bottom: 1px solid rgba(0, 0, 0, 0.1);
-}
-
-.submenu-list {
-  list-style: none;
-  padding: 0;
-  margin: 0;
-}
-
-.submenu-item {
-  margin: v.$spacing-sm 0;
-}
-
-.submenu-link {
-  display: block;
-  padding: v.$spacing-xs 0;
-  color: v.$text-secondary;
-  text-decoration: none;
-  font-size: v.$font-size-sm;
-  transition: color v.$transition-fast;
-
-  &:hover {
-    color: v.$primary-color;
-  }
-}
-
-// 右侧操作区样式
-.action-section {
-  display: flex;
-  align-items: center;
-  gap: v.$spacing-md;
-}
-
-.action-link {
-  color: v.$text-primary;
-  text-decoration: none;
-  font-size: v.$font-size-sm;
-  transition: color v.$transition-fast;
-
-  &:hover {
-    color: v.$primary-color;
-  }
-}
-
-.action-button {
-  display: inline-flex;
-  align-items: center;
-  justify-content: center;
-  padding: v.$spacing-sm v.$spacing-lg;
-  background-color: v.$primary-color;
-  color: v.$text-color-light;
-  border-radius: v.$border-radius-md;
-  text-decoration: none;
-  font-size: v.$font-size-sm;
-  transition: background-color v.$transition-fast;
-
-  &:hover {
-    background-color: color.adjust(v.$primary-color, $lightness: -10%);
-  }
-}
-</style>

+ 1 - 1
web/src/views/Doc/index.vue

@@ -489,7 +489,7 @@ $even-row-bg: #f9f9f9;
 // 容器样式
 .doc-container {
   display: flex;
-  min-height: calc(100vh - 60px); // 减去顶部导航栏的高度
+  min-height: calc(100vh - 64px); // 减去顶部导航栏的高度
   position: relative;
   margin-top: 60px; // 为顶部导航栏留出空间
   max-width: 100%; // 确保容器可以占满整个屏幕宽度