vue3+element-plus+js 实现快速搜索并且关键字高亮

问题遇到的现象和发生背景

img


点击搜索按钮,弹出模态框。
下拉框选择选项,然后进行相应查询。
查询出来的数据,点击后跳转相应页面关键字所在地。

问题相关代码,请勿粘贴截图
<template>
  <span v-if="theme.showSearch">
    <vab-icon icon="search-line" @click="openDialog" />
    <!-- 模态框 -->
    <el-dialog v-model="state.dialogVisible" :width="'40%'">
      <el-form :model="state.queryForm" @submit.prevent>
        <el-form-item label-width="0">
          <!-- <el-autocomplete
            v-model="state.queryForm.searchWord"
            v-focus
            :fetch-suggestions="querySearchAsync"
            select-when-unmatched
            @select="handleSelect"
          >
            <template #prefix><vab-icon icon="search-line" /></template> -->
          <!-- 快速搜索 -->
          <el-input
            v-model="state.queryForm.searchWord"
            v-focus
            placeholder="快速搜索"
            style="width: 100%"
          >
            <template #prepend>
              <el-select v-model="select">
                <el-option label="部件" :value="1" />
                <el-option label="文档" :value="2" />
                <el-option label="CAD文档" :value="3" />
                <el-option label="申请单" :value="4" />
                <el-option label="ECN" :value="5" />
              </el-select>
            </template>
            <template #append>
              <el-button icon="Search" @click="quickSearch" />
            </template>
          </el-input>
          <!-- </el-autocomplete> -->
        </el-form-item>
      </el-form>
    </el-dialog>
  </span>
</template>

<script>
  import { computed, defineComponent, onMounted, reactive, toRefs } from 'vue'
  import { useStore } from 'vuex'
  import { getList } from '@/api/search'

  export default defineComponent({
    name: 'VabSearch',
    directives: {
      focus: {
        mounted(el) {
          el.querySelector('input').focus()
        },
      },
    },
    setup() {
      const store = useStore()

      const theme = computed(() => store.getters['settings/theme'])

      // let timeout = null
      const state = reactive({
        input1: '',
        input2: '',
        input3: '',
        input4: '',
        input5: '',
        input6: '',
        input7: '',
        input8: '',
        input9: '',
        select: 1,
        dialogVisible: false,
        queryForm: {
          searchWord: '',
        },
        restaurants: [],
      })

      const loadAll = async () => {
        const {
          data: { list },
        } = await getList()
        state.restaurants = list
      }

      onMounted(() => {
        if (theme.value.showSearch) loadAll()
      })

      const openDialog = () => {
        state.queryForm.searchWord = ''
        state.dialogVisible = true
      }

      // 模糊查询
      const quickSearch = () => {
        console.log(quickSearch)
      }
      // const querySearchAsync = (queryString, cb) => {
      //   const restaurants = state.restaurants
      //   const results = queryString
      //     ? restaurants.filter(createFilter(queryString))
      //     : restaurants
      //   clearTimeout(timeout)
      //   timeout = setTimeout(() => {
      //     cb(results)
      //   }, 500)
      // }

      // const createFilter = (queryString) => (state) =>
      //   state.value.includes(queryString.toLowerCase())
      // const handleSelect = (item) => {
      //   if (item.url) {
      //     window.open(item.url)
      //   } else {
      //     window.open(`https://www.baidu.com/s?wd=${item.value}`)
      //   }
      // }

      return {
        theme,
        state,
        ...toRefs(state),
        openDialog,
        quickSearch,
        // createFilter,
        // handleSelect,
        // querySearchAsync,
      }
    },
  })
</script>

<style lang="scss" scoped>
  :deep() {
    .el-input {
      width: 180px;

      &:first-child {
        margin-right: 10px;
        margin-bottom: 10px;
      }

      & + .el-input {
        margin-right: 10px;
        margin-bottom: 10px;
        margin-left: 0;
      }
    }

    .el-textarea {
      width: 180px;
    }

    .el-select {
      .el-input {
        width: 100px;
        margin-bottom: 0;
      }
    }
  }
</style>

运行结果及报错内容

目前没什么思路

我的解答思路和尝试过的方法

希望可以贴代码的时候,多写点注释,或者提供思路

我想要达到的结果

下拉框选择选项,然后进行相应查询。实现快速搜索并且关键字高亮
查询出来的数据,点击后跳转相应页面关键字所在地。
由于没有后台数据,一切都是模拟,所以数据随意

用element-ui-plus的自动补全输入框,然后自定义建议内容插槽,就能实现关键词高亮了
你可以用关键词分割字符串然后显示

img


例如

这里的arr是你远程获取到的快速搜索推荐项
<template v-for="(content,contentIndex) in arr.split(keyWord)" :key="contentIndex">  
            <span class="keyword" v-if="contentIndex !== 0">{{ keyWord }}</span>
          <span>{{ content }}</span>
 </template>



如有帮助,麻烦点个【采纳此答案】 谢谢啦~

拿官网远程搜索例子稍改了一下
主要思路就是搜索的数据,匹配关键字替换成包裹span标签,再v-html渲染

img

<template>
  <div>
    <h1>test</h1>
    <el-autocomplete
      v-model="state"
      :fetch-suggestions="querySearchAsync"
      placeholder="Please input"
      @select="handleSelect">
      <template #default="{ item }">
        <div class="value" v-html="item.value"></div>
      </template>
    </el-autocomplete>
  </div>
</template>
<script lang="ts">
import { defineComponent, ref, onMounted } from 'vue'
export default defineComponent({
  setup() {
    const links = ref([])
    const loadAll = () => {
      return [
        { value: 'vue', link: 'https://github.com/vuejs/vue' },
        { value: 'element', link: 'https://github.com/ElemeFE/element' },
        { value: 'cooking', link: 'https://github.com/ElemeFE/cooking' },
        { value: 'mint-ui', link: 'https://github.com/ElemeFE/mint-ui' },
        { value: 'vuex', link: 'https://github.com/vuejs/vuex' },
        { value: 'vue-router', link: 'https://github.com/vuejs/vue-router' },
        { value: 'babel', link: 'https://github.com/babel/babel' },
      ]
    }
    let timeout
    const querySearchAsync = (queryString: string, cb: (arg: any) => void) => {
      const list = JSON.parse(JSON.stringify(links.value))
      const results = queryString
        ? list.filter(createFilter(queryString))
        : list
      
      console.log(queryString)
      if (queryString) {
        // 匹配关键字
        let replaceReg = new RegExp(queryString, 'g')
        results.forEach(item => {
            // 替换html
            let replaceString = `<span class="search-text">${queryString}</span>`
            // 开始替换
            item.value = item.value.replace(replaceReg, replaceString);
        })
      }
      clearTimeout(timeout)
      timeout = setTimeout(() => {
        cb(results)
      }, 1000 * Math.random())
    }
    const createFilter = (queryString: string) => {
      return (restaurant) => {
        return (
          restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) !== -1
        )
      }
    }
    const handleSelect = (item) => {
      console.log(item)
    }
    onMounted(() => {
      links.value = loadAll()
    })
    return {
      links,
      state: ref(''),
      querySearchAsync,
      createFilter,
      loadAll,
      handleSelect,
    }
  },
})
</script>

<style>
.search-text {
  color: red;
  font-weight: 700;
}
</style>

html也在学习

用element-ui-plus的自动补全输入框,自定义内容插槽

可以watch箭筒 select,select改变了,发起请求查询数据

书写相关css渲染和即时处理输入即可完成

我也遇到这个问题了,查了好多没解决

element UI 有相应功能解决