前言:

公司之前使用的表格组件是DevExtreme,后面由于某些原因需要把表格组件替换成了Ag-grid

替换后发现Ag-grid的表格不支持搜索时匹配到的内容高亮功能。这下苦逼了,只能自己实现了。


思考:

Ag-grid已经内置了快速搜索功能,剩下的就是在执行搜索后让匹配到的文字呈现高亮效果就OK啦。于是就想到了CSS 自定义高亮 API。话不多说,直接看解决方案!


解决方案:

1、写在前面:

        项目中所使用到的依赖包版本:

     "ag-grid-community": "~31.0.0",
     "ag-grid-enterprise": "~31.0.0",
     "ag-grid-vue3": "~31.0.0",
     "element-plus": "^2.4.4",
     "vue": "^3.3.10",

2、封装QuickFilter组件

<template>
  <div class="quick-filter-wrapper">
    <el-input v-model="searchStr" clearable placeholder="输入关键字搜索" @input="debounceSearch"></el-input>
  </div>
</template>

<script setup lang="ts">
import {ref, toValue} from "vue";
import {debounce} from "@/utils";

const props = defineProps<{
  gridApi: any
}>();
// 用户输入的搜索字符
const searchStr = ref();
// 添加防抖处理,注:debounce方法可自行实现或使用第三方库
const debounceSearch = debounce(onSearch, 400);

// 监听搜索
function onSearch() {
  const gridApi = toValue(props.gridApi);
  if (!gridApi) return;
  // 调用Ag grid的快速搜索
  gridApi.setGridOption('quickFilterText', searchStr.value);
  setHighlight(gridApi.gridBodyCtrl.eBodyViewport, searchStr.value)
}

// 设置高亮效果
function setHighlight(el: HTMLElement, searchStr: string) {
  if (!CSS.highlights) {
    console.warn('CSS Custom Highlight API not supported.');
    return;
  }
  // 清除之前的高亮
  CSS.highlights.clear();
  if (!searchStr) return;
  let ranges: any = [];
  const allTextNodes = getAllTextNodes(el);
  allTextNodes.forEach((node: any) => {
    const positions = findStrPositions(node.nodeValue, searchStr);
    positions.forEach((index: number) => {
      ranges.push(createRange(node, index, index + searchStr.length));
    })
  });
  const highlight = new Highlight(...ranges);
  CSS.highlights.set('search-results', highlight);
}

// 找出所有的文本节点
function getAllTextNodes(el: HTMLElement) {
  const treeWalker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT);
  const allTextNodes: any = [];
  let currentNode = treeWalker.nextNode();
  while (currentNode) {
    allTextNodes.push(currentNode);
    currentNode = treeWalker.nextNode();
  }
  return allTextNodes;
}

// 找出字符串在文本节点中出现的所有位置
function findStrPositions(mainStr: string, searchStr: string) {
  let positions: any = [];
  let index: number = 0;
  while (index != -1) {
    index = mainStr.toLowerCase().indexOf(searchStr.toLowerCase(), index);
    if (index != -1) {
      positions.push(index);
      index += searchStr.length;
    }
  }
  return positions;
}

// 创建选区
function createRange(el: any, start: number, end: number) {
  const range = new Range();
  range.setStart(el, start);
  range.setEnd(el, end);
  return range;
}

</script>

<style lang="scss">
.quick-filter-wrapper {
  display: inline-block;

  .el-input {
    width: 200px;
  }
}

// 可根据实际应用场景调整highlight样式位置,保证highlight样式生效即可
.ag-theme-quartz .ag-center-cols-viewport .ag-center-cols-container {
  ::highlight(search-results) {
    background-color: #f29b4a;
  }
}
</style>

2、在main.ts中注册QuickFilter组件

app.component('QuickFilter', QuickFilter)

3、使用

<template>
  <div class="home">
    <div class="toolbar">
      <el-button @click="getData">刷新</el-button>
      <div class="quick-filter">
        <QuickFilter :grid-api="gridApi"></QuickFilter>
      </div>
    </div>
    <section class="grid-wrapper">
      <ag-grid-vue
          class="ag-theme-quartz"
          style="height: 100%"
          :grid-options="gridOptions"
          :defaultColDef="defaultColDef"
          :columnDefs="columnDefs"
          :rowData="rowData"
          animateRows="true"
          @grid-ready="onGridReady">
      </ag-grid-vue>
    </section>
  </div>
</template>

<script setup lang="ts">
import {getCurrentInstance, onBeforeMount, reactive, ref} from "vue";

const ctx: any = getCurrentInstance()?.appContext.config.globalProperties

const gridOptions = reactive({
  suppressContextMenu: true,
  suppressCellFocus: true,
  enableCellTextSelection: true,
})

const columnDefs = reactive([
  {field: "make",},
  {field: "model"},
  {field: "price"},
])

const defaultColDef = {
  sortable: true,
  filter: 'agSetColumnFilter',
  menuTabs: ['filterMenuTab'],
  flex: 1
};

let gridApi: any = null;
const rowData = ref();


onBeforeMount(() => {
  getData();
});

function getData() {
  fetch("https://www.ag-grid.com/example-assets/row-data.json").then(res => res.json()).then(data => {
    rowData.value = data;
  }).catch(err => {
    ctx.$message.error('获取数据失败!')
    rowData.value = []
  })
}


function onGridReady(e: any) {
  gridApi = e.api;
}


</script>

<style lang="scss" scoped>
.home {
  height: 100%;
  display: flex;
  flex-direction: column;

  .header-item {
    display: flex;
    align-items: center;
    padding: 4px;

    .quick-filter {
      margin-left: auto;
    }

  }


  .grid-wrapper {
    flex: 1;
  }
}

</style>

结语:

使用 CSS 自定义高亮 API,你可以通过编程方式创建文本范围并高亮显示它们,而不会影响页面中的 DOM 结构。可以完美解决我的需求。但要注意它的浏览器兼容性。感兴趣的小伙伴可以点此CSS 自定义高亮 API - Web API 接口参考 | MDN查看官方文档相关介绍。

Logo

前往低代码交流专区

更多推荐