StarFire_xm
  • 文章
  • 粉丝
  • 评论

el-tree-select搜索优化分词检索

2025-06-26 08:54:290 次浏览0 次评论技能类型: element

组件自带的搜索filter-node-method不能满足大数据的检索,具体修改如下:

<el-tree-select
              v-model="user.productCode"
              clearable
              filterable
              :data="categoryOptions"
              :render-after-expand="false"
              style="width: 100%"
              placeholder="请选择商品类目"
              node-key="value"
              :props="{ label: 'label', value: 'value', children: 'children', deep: 'deep' }"
              :filter-node-method="filterNodeMethod"
              @node-click="getNodes"
              @node-expand="expandOnClickNode"
              @input="(e)=>changeTreeCons(e)"
              :disabled="!isSaveButtonVisible"
              ref="treeSelectRef"
            />
            
            

数据结构:都是一维数组的数据结构
[
    {
        "id": "4684037542992674816",
        "categoryCode": "PC4684037542992674817",
        "categoryName": "家用电器",
        "deep": 0,
        "parentId": 0,
        "parentName": null,
        "erpCategoryId": 972581949
    },
    {
        "id": "4684037542992674818",
        "categoryCode": "PC4684037542992674819",
        "categoryName": "3C数码",
        "deep": 0,
        "parentId": 0,
        "parentName": null,
        "erpCategoryId": 420589391
    }
]


//将一维数组转化成el-tree组件需要的数据结构,接口请求结束后就调用获取数据结构
const convertToTree = (data: any[]) => {
  if (!Array.isArray(data)) {
    console.error('数据格式错误,data 不是数组:', data);
    return [];
  }
  const tree = [];
  const map = new Map();
  data.forEach((item) => {
    map.set(item.id, {
      label: item.categoryName,
      value: item.id, // 确保 value 是唯一的
      children: [],
      deep: item.deep,
      parentId: item.parentId
    });
  });
  data.forEach((item) => {
    const parentId = String(item.parentId);
    if (parentId === '0') {
      tree.push(map.get(item.id));
    } else {
      const parent = map.get(parentId);
      if (parent) {
        parent.children.push(map.get(item.id));
      }
    }
  });
  return tree;
};


//分词检索

// 1. 全局索引(初始化时构建)
let categorySearchIndex = null;
let nodes_=[];

function buildSearchIndex(data) {
  const index = {
    // 嵌套Map结构:关键词 → 节点ID**
    keywordMap: new Map(),
   
    // 基础映射
    nodeMap: new Map(data.map(node => [node.id, node])),
    childrenMap: new Map(),
  };

  // 构建子节点映射和关键词索引
  data.forEach(node => {
    // 子节点映射
    if (!index.childrenMap.has(node.parentId)) {
      index.childrenMap.set(node.parentId, []);
    }
    index.childrenMap.get(node.parentId).push(node);

    // 关键词分词索引(核心优化)
    const words = splitKeywords(node.categoryName);
    words.forEach(word => {
      if (!index.keywordMap.has(word)) {
        index.keywordMap.set(word, new Set());
      }
      index.keywordMap.get(word).add(node.id);
    });
  });

  return index;
}

// 中文分词简易版(可根据需求优化)
function splitKeywords(text) {
  const words = [];
  const cleaned = text.toLowerCase()
    .replace(/\s+/g, '')   // 移除空格
    .replace(/\/+/g, '/'); // 统一斜杠格式

  // 生成所有可能的分词组合(包含完整路径)
  const segments = cleaned.split('/');
 
  // 添加完整路径(如"婴幼儿睡袋/睡袋套装")
  words.push(cleaned);
 
  // 添加各段及其组合
  segments.forEach(segment => {
    // 单段分词(如"婴幼儿睡袋")
    for (let i = 0; i < segment.length; i++) {
      for (let len = 1; len <= 4 && i + len <= segment.length; len++) {
        words.push(segment.substr(i, len));
      }
    }
   
    // 跨段组合(如"睡袋套装")
    if (segments.length > 1) {
      words.push(...segments.filter(s => s !== segment));
    }
  });

  return [...new Set(words)]; // 去重
}

// 2. 搜索方法(带防抖)
const changeTreeCons = debounce(async (e) => {
  const searchValue = e.target.value.trim().toLowerCase();
  if (!searchValue) {
    categoryOptions.value = convertToTree(categoryOptionsList);
    return;
  }

  // 从索引中查找匹配节点(核心优化点)
  const matchedIds = new Set();
  for (const [word, idSet] of categorySearchIndex.keywordMap) {
    if (word.includes(searchValue)) {
      idSet.forEach(id => matchedIds.add(id));
    }
  }

  // 收集需要保留的节点
  const keepNodes = new Set();
  const processNode = (nodeId) => {
    if (keepNodes.has(nodeId)) return;
   
    keepNodes.add(nodeId);
   
    // 向上追溯父节点
    let currentId = nodeId;
    while (true) {
      const parentId = categorySearchIndex.nodeMap.get(currentId)?.parentId;
      if (!parentId || parentId === 0 || keepNodes.has(parentId)) break;
      keepNodes.add(parentId);
      currentId = parentId;
    }
   
    // 向下收集子节点
    const stack = [nodeId];
    while (stack.length) {
      const current = stack.pop();
      const children = categorySearchIndex.childrenMap.get(current) || [];
      for (const child of children) {
        if (!keepNodes.has(child.id)) {
          keepNodes.add(child.id);
          stack.push(child.id);
        }
      }
    }
  };

  // 处理匹配节点
  matchedIds.forEach(processNode);

  // 生成结果
  const result = categoryOptionsList.filter(node =>
    keepNodes.has(node.id)
  );

  categoryOptions.value = convertToTree(result);
}, 300);

// 自定义过滤方法 - 保留父节点路径
const filterNodeMethod = (value, data) => {
  if (!value) return true;
  return checkNodeOrChildren(data, value);
};
// 递归检查节点及其子节点是否包含搜索词
const checkNodeOrChildren = (node, value) => {
  if (node.label.includes(value)|| nodes_.includes(node.parentId)) {
    nodes_.push(node.value);
    return true
  };
  return false;
};



//接口请求到数据后
convertToTree(response.data.rows);
categorySearchIndex = buildSearchIndex(categoryOptionsList);


    发表

    还没有评论哦,来抢个沙发吧!