Forráskód Böngészése

新增DropdownSelect组件;完成首页重置;

画安 3 nap óta
szülő
commit
95f74133e8
3 módosított fájl, 311 hozzáadás és 1 törlés
  1. 244 0
      src/components/ui/DropdownSelect.vue
  2. 36 0
      src/styles/base.css
  3. 31 1
      src/views/Home.vue

+ 244 - 0
src/components/ui/DropdownSelect.vue

@@ -0,0 +1,244 @@
+<template>
+  <div class="custom-dropdown" ref="dropdown" :class="[`theme-${theme}`]">
+    <div 
+      class="dropdown-trigger" 
+      :class="{ 'is-open': isOpen }" 
+      @click="toggleDropdown"
+    >
+      <span class="trigger-text">{{ currentLabel }}</span>
+      <i class="arrow-icon"></i>
+    </div>
+
+    <transition name="fade">
+      <div class="dropdown-menu" v-show="isOpen">
+        <div class="menu-arrow"></div>
+        
+        <div class="menu-list">
+          <div 
+            class="menu-item" 
+            v-for="item in options" 
+            :key="item.value"
+            :class="{ 'is-active': value === item.value }"
+            @click="selectOption(item)"
+          >
+            {{ item.label }}
+          </div>
+        </div>
+      </div>
+    </transition>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'DropdownSelect',
+  model: {
+    prop: 'value',
+    event: 'change'
+  },
+  props: {
+    value: {
+      type: [String, Number],
+      default: ''
+    },
+    options: {
+      type: Array,
+      default: () => []
+    },
+    placeholder: {
+      type: String,
+      default: '请选择'
+    },
+    // 【新增】主题风格:'bordered' (透明边框) | 'solid' (实心深色背景)
+    theme: {
+      type: String,
+      default: 'bordered'
+    }
+  },
+  data() {
+    return {
+      isOpen: false
+    };
+  },
+  computed: {
+    currentLabel() {
+      const selected = this.options.find(opt => opt.value === this.value);
+      return selected ? selected.label : this.placeholder;
+    }
+  },
+  mounted() {
+    document.addEventListener('click', this.handleClickOutside);
+  },
+  beforeDestroy() {
+    document.removeEventListener('click', this.handleClickOutside);
+  },
+  methods: {
+    toggleDropdown() {
+      this.isOpen = !this.isOpen;
+    },
+    selectOption(item) {
+      if (this.value !== item.value) {
+        this.$emit('change', item.value); 
+        this.$emit('select', item);       
+      }
+      this.isOpen = false;
+    },
+    handleClickOutside(event) {
+      if (this.$refs.dropdown && !this.$refs.dropdown.contains(event.target)) {
+        this.isOpen = false;
+      }
+    }
+  }
+};
+</script>
+
+<style scoped>
+/* 最外层容器 */
+.custom-dropdown {
+  position: relative;
+  display: inline-block;
+  user-select: none;
+}
+
+/* --- 触发器按钮 (基础样式) --- */
+.dropdown-trigger {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 6px 12px;
+  min-width: 80px;
+  color: #ffffff;
+  font-size: 14px;
+  cursor: pointer;
+  transition: all 0.2s;
+}
+.trigger-text {
+  margin-right: 12px;
+}
+.arrow-icon {
+  width: 0;
+  height: 0;
+  border-left: 4px solid transparent;
+  border-right: 4px solid transparent;
+  border-top: 5px solid #ffffff;
+  transition: transform 0.3s ease;
+}
+.dropdown-trigger.is-open .arrow-icon {
+  transform: rotate(180deg);
+}
+
+/* ================== 主题 1:透明边框 (默认原样式) ================== */
+.theme-bordered .dropdown-trigger {
+  background-color: transparent;
+  border: 1px solid rgba(100, 130, 190, 0.6); 
+  border-radius: 2px;
+}
+.theme-bordered .dropdown-trigger:hover,
+.theme-bordered .dropdown-trigger.is-open {
+  border-color: rgba(140, 180, 255, 0.9);
+}
+
+/* ================== 主题 2:实心深色 (还原你的最新截图) ================== */
+.theme-solid .dropdown-trigger {
+  background-color: #273444;; /* 截图中的深蓝色底色 */
+  border: 1px solid transparent; /* 占位防止跳动 */
+  border-radius: 4px; /* 稍微圆润的边角 */
+  padding: 5px 8px;
+}
+.theme-solid .dropdown-trigger:hover,
+.theme-solid .dropdown-trigger.is-open {
+  background-color: #385180; /* hover 时稍微提亮 */
+}
+
+
+/* --- 下拉菜单容器 (基础位置与动画) --- */
+.dropdown-menu {
+  position: absolute;
+  top: calc(100% + 10px);
+  left: 50%;
+  transform: translateX(-50%);
+  min-width: 100%; /* 至少与触发器同宽 */
+  border-radius: 6px;
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
+  z-index: 1000; 
+}
+.menu-arrow {
+  position: absolute;
+  top: -5px; 
+  left: 50%;
+  transform: translateX(-50%);
+  width: 0;
+  height: 0;
+  border-left: 6px solid transparent;
+  border-right: 6px solid transparent;
+}
+.menu-list {
+  padding: 6px 0;
+}
+.menu-item {
+  padding: 8px 16px;
+  font-size: 14px;
+  text-align: center;
+  cursor: pointer;
+  transition: all 0.2s;
+}
+
+/* ================== 下拉菜单配色:主题 1 (白色气泡) ================== */
+.theme-bordered .dropdown-menu {
+  background-color: #ffffff;
+}
+.theme-bordered .menu-arrow {
+  border-bottom: 6px solid #ffffff;
+}
+.theme-bordered .menu-item {
+  color: #333333;
+}
+.theme-bordered .menu-item:hover {
+  background-color: #f0f5ff;
+  color: #4da8ff;
+}
+.theme-bordered .menu-item.is-active {
+  color: #4da8ff;
+  font-weight: bold;
+}
+
+/* ================== 下拉菜单配色:主题 2 (深色科技气泡) ================== */
+.theme-solid .dropdown-menu {
+  background-color: #1e2c4a;
+  border: 1px solid rgba(100, 130, 190, 0.3);
+}
+.theme-solid .menu-arrow {
+  border-bottom: 6px solid #1e2c4a;
+}
+/* 利用伪元素单独画暗色三角形边框,防止悬浮感不足 */
+.theme-solid .menu-arrow::before {
+  content: '';
+  position: absolute;
+  top: 1px;
+  left: -6px;
+  border-left: 6px solid transparent;
+  border-right: 6px solid transparent;
+  border-bottom: 6px solid rgba(100, 130, 190, 0.3);
+  z-index: -1;
+}
+.theme-solid .menu-item {
+  color: #c4d7f0;
+}
+.theme-solid .menu-item:hover {
+  background-color: #2b3f66;
+  color: #32F6F8;
+}
+.theme-solid .menu-item.is-active {
+  color: #32F6F8;
+  font-weight: bold;
+}
+
+/* Vue 过渡动画 */
+.fade-enter-active, .fade-leave-active {
+  transition: opacity 0.2s, transform 0.2s;
+}
+.fade-enter, .fade-leave-to {
+  opacity: 0;
+  transform: translate(-50%, -5px);
+}
+</style>

+ 36 - 0
src/styles/base.css

@@ -52,6 +52,42 @@ html, body {
   z-index: 1;
 }
 
+/* 通用按钮样式清除 */
+.btn {
+    background: transparent;
+    border: none;
+    outline: none;
+    cursor: pointer;
+    font-size: 14px;
+    padding: 5px 8px;
+    border-radius: 4px;
+    transition: all 0.2s;
+}
+
+/* 忽略按钮:低调的文字按钮 */
+.btn-ignore {
+    background-color: #0D2244;
+    color: #ffffff;
+}
+
+.btn-ignore:hover {
+    color: #ffffff;
+    background: rgba(255, 255, 255, 0.1);
+}
+
+/* 查看按钮:科技感蓝色渐变边框按钮 */
+.btn-view {
+    color: #ffffff;
+    background: linear-gradient(180deg, rgba(24, 144, 255, 0.1) 0%, rgba(24, 144, 255, 0.3) 100%);
+    box-shadow: inset 0 0 8px rgba(24, 144, 255, 0.2);
+}
+
+.btn-view:hover {
+    background: linear-gradient(180deg, rgba(24, 144, 255, 0.2) 0%, rgba(24, 144, 255, 0.5) 100%);
+    box-shadow: inset 0 0 12px rgba(24, 144, 255, 0.4);
+    border-color: #32F6F8;
+}
+
 /* ==========================================
    全局滚动条美化 (大屏科技感风格)
 ========================================== */

+ 31 - 1
src/views/Home.vue

@@ -120,6 +120,15 @@
     </template>
 
     <template #center>
+      <div class="top-search-pos">
+        <button class="btn btn-view" @click="handleSearch()">查询</button>
+        <DropdownSelect 
+          placeholder="请选择"
+          v-model="currentMapSearch" 
+          :options="mapSearchOptions" 
+          @click.native.prevent 
+          theme="solid" />
+      </div>
       <div class="map-legend-pos">
         <MapLegend></MapLegend>
       </div>
@@ -140,6 +149,7 @@ import TickDonutChart from '@/components/ui/TickDonutChart.vue';
 import AlarmMessageList from '@/components/ui/AlarmMessageList.vue';
 import TechTable from '@/components/ui/TechTable.vue';
 import MapLegend from '@/components/ui/MapLegend.vue';
+import DropdownSelect from '@/components/ui/DropdownSelect.vue';
 
 
 const mockDeviceData = {
@@ -183,7 +193,8 @@ export default {
     TickDonutChart,
     AlarmMessageList,
     TechTable,
-    MapLegend
+    MapLegend,
+    DropdownSelect
   },
   data() {
     return {
@@ -252,6 +263,13 @@ export default {
         { intersection: '实行东街双园路交叉路口', mode: '定周期控制', plan: '4' },
         { intersection: '实行东街双园路交叉路口', mode: '自适应控制', plan: '1' },
         { intersection: '实行东街双园路交叉路口', mode: '感应控制', plan: '5' }
+      ],
+      // 搜索数据
+      currentMapSearch: 'all',
+      mapSearchOptions: [
+        {label: '全部', value: 'all' },
+        {label: '选项2', value: '1' },
+        {label: '选项3', value: '2' },
       ]
     };
   },
@@ -286,6 +304,10 @@ export default {
     onIntersectionRowClick({ row, index }) {
       console.log(`用户点击了第 ${index + 1} 行,路口名称是:`, row.intersection);
       
+    },
+    // 处理搜索
+    handleSearch() {
+      console.log('搜索', this.currentMapSearch);
     }
   }
 }
@@ -317,4 +339,12 @@ export default {
   bottom: 100px;
   right: 0;
 }
+.top-search-pos {
+  position: absolute;
+  top: 0;
+  right: 0;
+  display: flex;
+  flex-direction: row;
+  column-gap: 9px;
+}
 </style>