瀏覽代碼

修改MenuItem组件,删除旧的,把原来的样式整合到新的中;

画安 2 天之前
父節點
當前提交
3c5a1cc65d
共有 4 個文件被更改,包括 146 次插入171 次删除
  1. 0 105
      src/components/MenuItem.vue
  2. 102 57
      src/components/ui/MenuItem.vue
  3. 35 0
      src/styles/global.css
  4. 9 9
      src/views/MainWatch.vue

+ 0 - 105
src/components/MenuItem.vue

@@ -1,105 +0,0 @@
-<template>
-  <div class="menu-item-wrapper">
-    <div 
-      class="node-title" 
-      @click="toggle"
-      :style="{ paddingLeft: level * 20 + 15 + 'px' }"
-      :class="{ 'is-active': !isFolder && isActive, 'bold': model.children && model.children.length, 'color-1': ! model.children }"
-    >
-      <span>{{ model.label }}</span>
-      <span 
-        v-if="isFolder" 
-        class="arrow-icon" 
-        :class="{ 'is-open': model.isOpen }"
-      >
-        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-up-icon lucide-chevron-up"><path d="m18 15-6-6-6 6"/></svg>
-      </span>
-    </div>
-
-    <div v-show="model.isOpen" v-if="isFolder" class="node-children">
-      <MenuItem
-        v-for="child in model.children"
-        :key="child.id"
-        :model="child"
-        :level="level + 1"
-        @node-click="$emit('node-click', $event)"
-      />
-    </div>
-  </div>
-</template>
-
-<script>
-export default {
-  name: 'MenuItem', // 必须声明 name,才能在模板中递归调用自身
-  props: {
-    model: Object,
-    level: {
-      type: Number,
-      default: 0
-    }
-  },
-  computed: {
-    // 判断是否包含子节点
-    isFolder() {
-      return this.model.children && this.model.children.length > 0;
-    },
-    // 模拟叶子节点的选中状态(实际项目中可配合 vue-router 或 vuex)
-    isActive() {
-      return false; // 这里可以根据实际选中逻辑扩展
-    }
-  },
-  methods: {
-    toggle(e) {
-      if (this.isFolder) {
-        this.$set(this.model, 'isOpen', !this.model.isOpen);
-      } else {
-        console.log('点击了具体项目:', this.model.label);
-        const rect = e.currentTarget.getBoundingClientRect();
-        this.$emit('node-click', {
-          id: this.model.id,
-          label: this.model.label,
-          rect: { x: rect.x, y: rect.y, width: rect.width, height: rect.height, right: rect.right, bottom: rect.bottom }
-        });
-      }
-    }
-  }
-}
-</script>
-
-<style scoped>
-.menu-item-wrapper {
-  color: #fff;
-  font-size: 14px;
-}
-.node-title {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  padding: 12px 20px;
-  cursor: pointer;
-  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
-  background: #081734;
-  transition: background 0.3s;
-}
-.menu-tree>.menu-item-wrapper>.node-title {
-    font-size: 18px;
-    border: 2px solid #82a9f4;
-    background: linear-gradient(180deg, rgba(15, 30, 60, 0.9) 0%,  rgba(40, 80, 150, 0.9) 100%);
-}
-.menu-tree>.menu-item-wrapper>.node-children>.menu-item-wrapper>.node-title {
-    background: #0f224350;
-}
-.arrow-icon {
-  transition: transform 0.3s ease;
-  transform: rotate(180deg); /* 默认箭头向下 */
-}
-.arrow-icon.is-open {
-  transform: rotate(0deg); /* 展开时箭头向上 */
-}
-.bold{
-  font-weight: bold;
-}
-.color-1 {
-    color: #535f79;
-}
-</style>

+ 102 - 57
src/components/ui/MenuItem.vue

@@ -1,13 +1,18 @@
 <template>
-  <div class="menu-item-wrapper">
+  <div class="menu-item-wrapper" :class="[`theme-${theme}`]">
+    
     <div 
       class="menu-row" 
-      :class="{
-        'is-root': level === 0,
-        'is-sub': level > 0,
-        'is-leaf': !hasChildren
-      }"
-      :style="{ paddingLeft: level * 20 + 20 + 'px' }"
+      :class="[
+        {
+          'is-leaf': !hasChildren,
+          'is-folder': hasChildren,
+          'is-root': level === 0,
+          'is-sub': level > 0
+        },
+        'level-' + level
+      ]"
+      :style="{ paddingLeft: level * 20 + 15 + 'px' }"
       @click="handleClick"
     >
       <i v-if="node.icon" :class="node.icon" class="node-icon"></i>
@@ -19,7 +24,7 @@
         class="arrow-icon" 
         :class="{ 'is-open': isOpen }"
       >
-        ^
+        <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m18 15-6-6-6 6"/></svg>
       </span>
     </div>
 
@@ -29,6 +34,7 @@
         :key="child.id" 
         :node="child" 
         :level="level + 1"
+        :theme="theme" 
         @node-click="passEventUp" 
       />
     </div>
@@ -37,27 +43,28 @@
 
 <script>
 export default {
-  // 组件必须有 name 才能进行递归调用!
   name: 'MenuItem', 
   props: {
     node: {
       type: Object,
       required: true
     },
-    // 当前节点的层级,默认从 0 开始
     level: {
       type: Number,
       default: 0
+    },
+    // 可选值:'tech' (科技渐变) | 'dark' (极简暗色)
+    theme: {
+      type: String,
+      default: 'tech' 
     }
   },
   data() {
     return {
-      // 默认展开所有节点(你可以根据需求改为 false)
       isOpen: true 
     };
   },
   computed: {
-    // 判断是否有子节点
     hasChildren() {
       return this.node.children && this.node.children.length > 0;
     }
@@ -65,14 +72,11 @@ export default {
   methods: {
     handleClick() {
       if (this.hasChildren) {
-        // 如果有子节点,点击则切换展开/折叠状态
         this.isOpen = !this.isOpen;
       } else {
-        // 如果是叶子节点(最底层路口),触发点击事件,并把当前节点数据传出去
         this.$emit('node-click', this.node);
       }
     },
-    // 递归组件极其关键的一步:子组件触发了事件,父组件要继续往上抛,直到最外层
     passEventUp(nodeData) {
       this.$emit('node-click', nodeData);
     }
@@ -81,74 +85,115 @@ export default {
 </script>
 
 <style scoped>
-/* 基础行样式 */
+.menu-item-wrapper {
+  color: #fff;
+  font-size: 14px;
+}
+
+/* ================== 公共基础样式 ================== */
 .menu-row {
   display: flex;
   align-items: center;
-  height: 44px;
-  font-size: 14px;
+  min-height: 44px;
+  max-height: 56px;
+  padding: 12px 20px;
   cursor: pointer;
+  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
+  background: #081734;
   transition: all 0.3s ease;
   user-select: none;
 }
 
-/* ================= 核心颜色配置区 ================= */
+.node-icon { margin-right: 8px; font-size: 16px; }
+.node-label { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
+.arrow-icon { margin-left: 10px; display: flex; justify-content: center; align-items: center; color: #909399; transform: rotate(180deg); transition: transform 0.3s ease; }
+.arrow-icon.is-open { transform: rotate(0deg); }
+
 
-/* 1. 主控中心标题 (顶级菜单 level: 0) 的背景颜色 */
-.menu-row.is-root {
-  background-color: #112445; /* 偏亮的深蓝色背景 */
-  color: #ffffff;            /* 白色字体 */
-  font-weight: bold;         /* 标题可以稍微加粗 */
+/* ================== 风格 A: 科技渐变风 (theme-tech) ================== */
+
+/* 1. 最外层父节点 (level: 0) */
+.theme-tech.menu-item-wrapper {
+  background: linear-gradient( 180deg, rgba(5,20,46,0) 0%, #05142E 100%);
+}
+.theme-tech .menu-row.level-0 {
+  font-size: 20px;
+  line-height: 28px;
+  background: linear-gradient( 180deg, rgba(119,161,255,0) 0%, #77A1FF 100%);
+  border-radius: 2px 0px 0px 2px;
+  border: 1px solid rgba(161,190,255,0.7);
+  font-weight: bold;
 }
 
-/* 2. 其他子菜单 (level > 0) 的背景颜色 */
-.menu-row.is-sub {
-  background-color: #0b1a37; /* 更深的暗色背景 */
-  color: #ffffff ;            /* 浅灰色字体 */
+/* 2. 第二层子节点 (level: 1) */
+.theme-tech .menu-row.level-1 {
+  background: #0f224350;
+  font-size: 18px;
+  line-height: 25px;
+  color: #ffffff;
 }
 
-/* 3. 最后一级菜单 (叶子节点) 的特殊样式 */
-.menu-row.is-leaf {
-  color: #6b7280; /* 深灰色字体 (覆盖掉上面的浅灰色) */
+/* 3. 包含子节点的目录加粗 */
+.theme-tech .menu-row.is-folder {
+  font-weight: bold;
+  font-size: 18px;
+  line-height: 25px;
+  color: #ffffff;
 }
 
-/* ================================================== */
+.theme-tech .menu-row.level-2 {
+  font-size: 18px;
+  line-height: 25px;
+  color: rgba(255,255,255, 0.8);
+}
 
-/* 统一的悬浮效果 */
-.menu-row:hover {
-  background-color: rgba(0, 229, 255, 0.1); /* 鼠标放上去给一点科技蓝的反馈 */
-  color: #00e5ff; /* 悬浮时文字变亮蓝 */
+/* 4. 叶子节点字体颜色 */
+.theme-tech .menu-row.is-leaf {
+  font-size: 18px;
+  line-height: 25px; 
+  font-weight: normal;
+  color: #ffffff;
+  background: rgba(8,23,51,0.9);
+  color: rgba(255,255,255, 0.5);
 }
 
-/* 最后一级菜单悬浮时,字体也要变亮,否则看不清 */
-.menu-row.is-leaf:hover {
+/* 5. 悬浮交互 */
+.theme-tech .menu-row:hover {
+  background: #3760A9;
   color: #ffffff; 
 }
+.theme-tech .menu-row.is-leaf:hover { color: #ffffff; }
+
+
+/* ================== 风格 B: 极简暗色风 (theme-dark) ================== */
 
-.node-icon {
-  margin-right: 8px;
-  font-size: 16px;
+/* 1. 主控中心标题 (顶级菜单 level: 0) */
+.theme-dark .menu-row.is-root {
+  background-color: #112445; 
+  color: #ffffff;            
+  font-weight: bold;
+  font-size: 20px;
+  line-height: 28px;    
 }
 
-.node-label {
-  flex: 1; /* 占据剩余空间,把箭头挤到最右边 */
-  white-space: nowrap;
-  overflow: hidden;
-  text-overflow: ellipsis;
+/* 2. 其他子菜单 (level > 0) */
+.theme-dark .menu-row.is-sub {
+  background-color: #0b1a37; 
+  color: #ffffff ;
+  font-size: 18px;
+  line-height: 25px;            
 }
 
-/* 右侧箭头样式与动画 */
-.arrow-icon {
-  margin-right: 20px;
-  font-size: 12px;
-  color: #909399;
-  /* 默认箭头朝下 (通过旋转实现) */
-  transform: rotate(180deg);
-  transition: transform 0.3s ease;
+/* 3. 叶子节点颜色 */
+.theme-dark .menu-row.is-leaf {
+  color: #6b7280; 
+  font-weight: normal;
 }
 
-/* 展开状态时,箭头朝上 */
-.arrow-icon.is-open {
-  transform: rotate(0deg);
+/* 4. 悬浮交互 */
+.theme-dark .menu-row:hover {
+  background-color: rgba(0, 229, 255, 0.1); 
+  color: #00e5ff; 
 }
+.theme-dark .menu-row.is-leaf:hover { color: #ffffff; }
 </style>

+ 35 - 0
src/styles/global.css

@@ -133,3 +133,38 @@
   height: 100%;
 }
 
+.menu-scroll-view {
+  height: 844px !important;
+  overflow: auto;
+}
+
+/* 整体滚动条 */
+::-webkit-scrollbar {
+  width: 2px; 
+  height: 2px; 
+}
+
+::-webkit-scrollbar-track {
+  background: rgba(15, 34, 67, 0.3);
+  box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.3); 
+}
+
+::-webkit-scrollbar-thumb {
+  background: linear-gradient(180deg, rgba(31, 206, 251, 0.2) 0%, rgba(0, 229, 255, 0.6) 100%);
+  border: 1px solid rgba(31, 206, 251, 0.4); 
+}
+
+::-webkit-scrollbar-thumb:hover {
+  background: linear-gradient(180deg, rgba(31, 206, 251, 0.5) 0%, rgba(0, 229, 255, 0.9) 100%);
+  box-shadow: 0 0 10px rgba(0, 229, 255, 0.8); 
+  border-color: rgba(31, 206, 251, 0.8);
+}
+
+::-webkit-scrollbar-button {
+  display: none; 
+}
+
+::-webkit-scrollbar-corner {
+  background: transparent; 
+}
+

+ 9 - 9
src/views/MainWatch.vue

@@ -35,20 +35,20 @@
 
         <!-- 左侧Tab菜单栏 -->
         <TechTabs v-model="activeOuterTab" type="underline">
-          <TechTabPane label="总览" name="overview">
-            <MenuItem 
+          <TechTabPane label="总览" name="overview" class="menu-scroll-view">
+            <MenuItem theme="tech"
               v-for="item in menuData" 
               :key="item.id" 
-              :model="item" 
+              :node="item" 
               :level="0"
               @node-click="handleMenuClick"
             />
           </TechTabPane>
-          <TechTabPane label="路口" name="crossing">
-            <MenuItem 
+          <TechTabPane label="路口" name="crossing" class="menu-scroll-view">
+            <MenuItem theme="dark"
               v-for="item in menuData" 
               :key="item.id" 
-              :model="item" 
+              :node="item" 
               :level="0"
               @node-click="handleMenuClick"
             />
@@ -57,7 +57,7 @@
             <MenuItem 
               v-for="item in menuData" 
               :key="item.id" 
-              :model="item" 
+              :node="item" 
               :level="0"
               @node-click="handleMenuClick"
             />
@@ -66,7 +66,7 @@
             <MenuItem 
               v-for="item in menuData" 
               :key="item.id" 
-              :model="item" 
+              :node="item" 
               :level="0"
               @node-click="handleMenuClick"
             />
@@ -239,7 +239,7 @@
 </template>
 
 <script>
-import MenuItem from '@/components/MenuItem.vue';
+import MenuItem from '@/components/ui/MenuItem.vue';
 import SmartDialog from '@/components/SmartDialog.vue';
 import TrafficTimeSpace from '@/components/ui/TrafficTimeSpace.vue';
 import IntersectionSignalMonitoring from '@/components/IntersectionSignalMonitoring.vue';