el-tree-select搜索优化分词检索
组件自带的搜索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);
您还未登录, 登录 后可进行评论
发表
还没有评论哦,来抢个沙发吧!