forked from drone/command-center-dashboard

罗广辉
2025-04-21 2800fa4f32f3900509cb4d6eefaf2bfaf54efdd7
src/views/Home/SearchBox.vue
@@ -1,13 +1,282 @@
<script setup>
</script>
<template>
   <div>
   <div class="searchBox" ref="searchBoxRef">
      <div class="searchInput">
         <el-select v-model="optionsValue" @change="optionChange" placeholder="请选择查询">
            <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
         </el-select>
   </div>
         <el-input
            v-model="searchKey"
            @input="handlerInput"
            @focus="handleFocus"
            placeholder="请输入搜索关键字"
         ></el-input>
      </div>
      <div class="searchBtn" @click="searchClick"></div>
      <div class="region">
         <el-tree-select
            popper-class="custom-tree-select"
            v-model="treeValue"
            check-strictly
            lazy
            :load="load"
            :props="props"
            style="width: 240px"
            @node-click="handleNodeClick"
         />
      </div>
   </div>
   <div class="select-down-list" ref="selectDownRef" v-show="isSelectDown">
      <div class="item" v-for="item in downList" @click="selectedValue(item)">{{ item.nickname || item.name }}</div>
   </div>
</template>
<script setup>
import _ from 'lodash'
import { getRegion } from '@/api/home'
import { searchByKeyword, selectDeviceList } from '@/api/home/common'
import { useStore } from 'vuex'
import cesiumOperation from '@/utils/cesium-tsa'
const { flyTo } = cesiumOperation()
const store = useStore()
const searchKey = ref('')
const userAreaCode = computed(() => store.state.user.userInfo.detail.areaCode)
const selectedAreaCode = computed(() => store.state.user.selectedAreaCode)
const areaValue = ref('江西省')
const treeValue = ref(userAreaCode.value)
let first = true
const searchBoxRef = ref(null)
const selectDownRef = ref(null)
function handleNodeClick(data) {
   areaValue.value = data.name
   store.commit('setSelectedAreaCode', data.code)
}
const props = {
   label: 'name',
   value: 'code',
   children: 'children',
}
const load = async (node, resolve) => {
   if (first) {
      first = false
      const res = await getRegion(userAreaCode.value)
      const { provinceCode, provinceName } = res?.data?.data?.[0] || {}
      resolve([{ code: provinceCode, name: provinceName }])
      return
   }
   const code = node?.data?.code || userAreaCode.value
   if ((node?.data?.regionLevel || 0) > 2) return resolve([])
   getRegion(code).then(res => {
      resolve(res?.data?.data || [])
   })
}
const optionsValue = ref('1')
const options = [
   {
      value: '1',
      label: '机巢',
   },
   {
      value: '2',
      label: '地址',
   },
]
const isSelectDown = ref(false)
// 地址搜索结果
const downList = ref([])
const position = ref({})
// 获取机巢搜索结果
const getDeviceList = async () => {
   const res = await selectDeviceList({ nickname: searchKey.value })
   if (res.data.code !== 0) return
   downList.value = res?.data?.data || []
}
// 获取地址搜索结果
const getAddressList = async () => {
   const res = await searchByKeyword(encodeURIComponent(`${areaValue.value}+${searchKey.value}`))
   if (res.data.code !== 0) return
   downList.value = res?.data?.data.tips || []
   if (downList.value.length > 0) {
      isSelectDown.value = true
   }
}
// 搜索结果
const searchClick = () => {
   const longitude = Number(position.value.longitude)
   const latitude = Number(position.value.latitude)
   flyTo({ longitude, latitude }, 1, 1000)
}
// 地址和机巢的切换
const optionChange = () => {
   searchKey.value = ''
   downList.value = []
   inputSelect()
}
// input对应下拉数据初始化
const inputSelect = () => {
   if (optionsValue.value === '2') {
      getAddressList()
   } else {
      getDeviceList()
   }
}
inputSelect()
// 输入框input事件
const handlerInput = _.debounce(inputSelect, 1000)
// 输入框获取焦点
const handleFocus = () => {
   isSelectDown.value = true
}
// 机巢下拉获取值
const selectedValue = item => {
   searchKey.value = item.nickname || item.name
   if (optionsValue.value === '1') {
      position.value = item
   } else {
      position.value = { longitude: item.location.split(',')[0], latitude: item.location.split(',')[1] }
   }
   isSelectDown.value = false
}
onMounted(() => {
   document.addEventListener('click', handleClickOutside)
})
onUnmounted(() => {
   document.removeEventListener('click', handleClickOutside)
})
const handleClickOutside = event => {
   if (!searchBoxRef.value?.contains(event.target) && !selectDownRef.value?.contains(event.target)) {
      isSelectDown.value = false
   }
}
</script>
<style scoped lang="scss">
.select-down-list {
   position: absolute;
   top: 188px;
   left: 44%;
   transform: translateX(-40%);
   width: 220px;
   height: 256px;
   overflow-y: auto;
   background: linear-gradient(180deg, #0d3556 0%, #012350 100%);
   border-radius: 0px 0px 8px 8px;
   border: 1px solid;
   border-image: linear-gradient(180deg, rgba(255, 255, 255, 0), rgba(115, 192, 255, 1)) 1 1;
   opacity: 0.8;
   &::-webkit-scrollbar {
      width: 0;
      display: none;
   }
   -ms-overflow-style: none; /* IE and Edge */
   scrollbar-width: none; /* Firefox */
   .item {
      color: #ffffff;
      height: 32px;
      line-height: 32px;
      text-align: center;
      cursor: pointer;
      &:hover {
         background: linear-gradient(90deg, rgba(0, 122, 255, 0) 0%, rgba(0, 98, 204, 0.6) 50%, rgba(0, 73, 153, 0) 100%);
         border: 1px solid;
         border-image: linear-gradient(90deg, rgba(0, 199, 190, 0), rgba(48, 176, 199, 1), rgba(0, 199, 190, 0)) 1 1;
      }
   }
}
.searchBox {
   width: 420px;
   height: 43px;
   position: absolute;
   top: 145px;
   left: 50%;
   transform: translateX(-50%);
   display: flex;
   .el-select {
      width: 130px;
      height: 100%;
      :deep() {
         .el-select__wrapper {
            background: transparent;
            border: none;
            box-shadow: none;
            height: 100%;
            padding-left: 20px;
         }
         .el-select__selected-item {
            font-family: Source Han Sans CN, Source Han Sans CN, serif;
            font-weight: 400;
            font-size: 14px;
            color: #ffffff;
            line-height: 18px;
         }
      }
   }
   .searchInput {
      width: 243px;
      height: 100%;
      background: url('@/assets/images/home/searchBox/searchBg1.png') no-repeat center / 100% 100%;
      display: flex;
      .el-input {
         height: 100%;
         :deep() {
            .el-input__wrapper {
               background: transparent;
               border: none;
               box-shadow: none;
               height: 100%;
            }
            .el-input__inner {
               font-family: Source Han Sans CN, Source Han Sans CN, serif;
               font-weight: 400;
               font-size: 14px;
               color: #ffffff;
               line-height: 18px;
            }
         }
      }
   }
   .searchBtn {
      width: 67px;
      height: 100%;
      background: url('@/assets/images/home/searchBox/searchBg2.png') no-repeat center / 100% 100%;
      cursor: pointer;
   }
   .region {
      width: 0;
      flex-grow: 1;
      background: url('@/assets/images/home/searchBox/searchBg3.png') no-repeat center / 100% 100%;
      :deep() {
         .el-select__wrapper {
            width: 100px;
            padding-left: 20px;
         }
      }
   }
}
</style>