forked from drone/command-center-dashboard

shuishen
2025-04-17 55948460dd112d2357cb595b3f6d26762ca7f0a9
feat:搜索栏优化
1 files modified
442 ■■■■ changed files
src/views/Home/SearchBox.vue 442 ●●●● patch | view | raw | blame | history
src/views/Home/SearchBox.vue
@@ -1,277 +1,281 @@
<template>
  <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 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>
      <el-input v-model="searchKey"  @focus="handleFocus" placeholder="请输入搜索关键字"></el-input>
    </div>
    <div class="searchBtn" @click="searchClick"></div>
    <div class="region">
      <el-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>
            <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
                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 { getRegion } from '@/api/home';
import { searchByKeyword, selectDeviceList } from '@/api/home/common';
import { useStore } from 'vuex';
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);
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);
    areaValue.value = data.name
    store.commit('setSelectedAreaCode', data.code)
}
const props = {
  label: 'name',
  value: 'code',
  children: 'children',
};
    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 || []);
  });
};
    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 optionsValue = ref('1')
const options = [
  {
    value: '1',
    label: '机巢',
  },
  {
    value: '2',
    label: '地址',
  },
];
    {
        value: '1',
        label: '机巢',
    },
    {
        value: '2',
        label: '地址',
    },
]
const isSelectDown = ref(false);
// 下拉数据列表
const machineNestList = ref([]);
const isSelectDown = ref(false)
// 地址搜索结果
const downList = ref([]);
const position = ref({});
const downList = ref([])
const position = ref({})
// 获取机巢搜索结果
const getDeviceList = async () => {
  const res = await selectDeviceList({nickname: searchKey.value});
  if (res.data.code !== 0) return;
  machineNestList.value = res?.data?.data || [];
  downList.value = res?.data?.data || [];
};
    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 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 = [];
};
    searchKey.value = ''
    downList.value = []
    inputSelect()
}
// input对应下拉数据初始化
const inputSelect = () => {
    if (optionsValue.value === '2') {
        getAddressList()
    } else {
        getDeviceList()
    }
}
inputSelect()
// 输入框input事件
const handlerInput = _.debounce(inputSelect, 1000)
// 输入框获取焦点
const handleFocus = () => {
  if (optionsValue.value === '1') {
    isSelectDown.value = true;
    downList.value = machineNestList.value;
  }
};
    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;
};
    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] }
    }
// 监听搜索关键字变化
watch(searchKey, async (newVal) => {
  if (optionsValue.value === '2') {
    await getAddressList();
  } else {
    await getDeviceList();
  }
}, { immediate: false });
    isSelectDown.value = false
}
onMounted(() => {
  document.addEventListener('click', handleClickOutside);
});
    document.addEventListener('click', handleClickOutside)
})
onUnmounted(() => {
  document.removeEventListener('click', handleClickOutside);
});
    document.removeEventListener('click', handleClickOutside)
})
const handleClickOutside = (event) => {
  if (!searchBoxRef.value?.contains(event.target) && !selectDownRef.value?.contains(event.target)) {
    isSelectDown.value = false;
  }
};
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;
    }
  }
    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;
    width: 420px;
    height: 43px;
    position: absolute;
    top: 145px;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
  .el-select {
    width: 130px;
    height: 100%;
    .el-select {
        width: 130px;
        height: 100%;
    :deep() {
      .el-select__wrapper {
        background: transparent;
        border: none;
        box-shadow: none;
        height: 100%;
        padding-left: 20px;
      }
        :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;
      }
    }
  }
            .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;
    .searchInput {
        width: 243px;
        height: 100%;
        background: url('@/assets/images/home/searchBox/searchBg1.png') no-repeat center / 100% 100%;
        display: flex;
    .el-input {
      height: 100%;
        .el-input {
            height: 100%;
      :deep() {
        .el-input__wrapper {
          background: transparent;
          border: none;
          box-shadow: none;
          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;
        }
      }
    }
  }
                .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;
  }
    .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%;
    .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;
      }
    }
  }
        :deep() {
            .el-select__wrapper {
                width: 100px;
                padding-left: 20px;
            }
        }
    }
}
</style>