先上原始数据 树结构的菜单
首先先把树转成一维数组 便于搜索遍历 然后加一个level属性 体现层级关系 代码如下:
// 树结构数据 转成一维数组
function treeToList(datas) {
var arr = [];
console.log(datas);
formateData(datas, 0);
function formateData(datas, level) {
var level = level || 0;
level++;
for (var i in datas) {
// console.log(datas[i]);
const obj = {
id: datas[i]['id'],
name: datas[i]['name'],
url: datas[i]['url'],
level: level,
pid: datas[i]['pid'],
};
arr.push(obj);
if (datas[i].children) {
formateData(datas[i].children, level);
}
}
}
return arr;
}
// 取menu数据 进行遍历
const treeMenuList = treeToList(store.state.menus);
这里打印出来的 treeMenuList 已经是一维数组 并且里面有level层级 如下图:
但是我们的需求 是需要把菜单的层级 展示出来 就是要取到他的上级的name和他拼接 一直到最顶级 需求如下图 但是现在数据里还没有达到要求
(主页/合同管理/合同准备/合同准备审核) 要有一个这样的字符串 展示层级关系
// 获取当前已经匹配到的数组的父级层级关系
function getMenuStr(item) {
let astr = '';
// 这里的item是 传入的一维数组里的每一项
loopAcquisition(item);
// 函数查找父级
function loopAcquisition(data) {
if (data.pid == 0) {
astr = data.name;
} else {
let obj = {};
treeMenuList.map((e) => {
// 找到匹配的父级 给父级字符串拼接
if (e.id == data.pid) {
obj = e;
astr += '/' + e.name;
}
});
// 如果当前的父级不是一级 也就是pid不是0 说明还有父级 继续查找
obj.pid != 0 && loopAcquisition(obj);
}
}
return astr;
}
// 上面的方法这样调用 在el-input的输入事件里面找到一维数组的匹配项 并且拼接父级字符串:
// 搜索框change事件 开始搜索
let timer = null; //计时器
const searchMenuIng = (val: any) => {
clearTimeout(timer);
timer = setTimeout(() => {
// 在这里执行输入事件的处理逻辑
if (val == '') {
// 如果没有输入东西 不展示搜索联想
return (startSearch.value = false);
}
startSearch.value = true;
// 查找一维数组里 包含输入框值的数组项
const arr = treeMenuList.filter((item: any) => {
// 查找末级 能点进去的菜单
if (item.pid != '0' && item.url != undefined) {
return item.name.includes(val);
}
});
// 找到以后 数据处理 给这些项 拼接父级层级的字符串
arr.map((e) => {
if (e.pid != '0') {
e.strname = strFormatter(e.name + getMenuStr(e));
}
});
// 最后得到的结果
searchResult.value = arr;
}, 500); // 设置延迟时间(毫秒)
};
// 格式化字符串
const strFormatter = (str: string) => {
if (str === '') {
return '';
} else {
return str.split('/').reverse().join('/');
}
};
最后我们实现的效果 :
下面附上全部代码 仅供参考~
<template>
<el-popover placement="bottom" :width="520" trigger="click" ref="popoverRef">
<template #reference>
<vxe-button type="text" icon="vxe-icon-search"></vxe-button>
</template>
<el-input size="middle" v-model.trim="searchMenuText" @input="searchMenuIng" @focus="searchMenuIng(searchMenuText)" clearable placeholder="请输入菜单名">
<template #prefix>
<el-icon class="el-input__icon">
<search />
</el-icon>
</template>
</el-input>
<div class="search-results" v-if="startSearch">
<div class="result-box">查询结果</div>
<div class="search-box">
<div class="search-item" v-for="(item, ind) in searchResult" @click="openMenuPage(item)">
<span class="nemu-name" v-html="highlightedText(item.name, searchMenuText)"></span>
<span class="url-str">{{ item.strname }}</span>
</div>
</div>
</div>
</el-popover>
</template>
<script lang="ts">
import { ref, toRefs, computed, defineComponent, onMounted, reactive } from 'vue';
import { registerDynamicToRouterAndNext } from '@/router';
import { useDebounce } from '@/utils/utils';
import { useStore } from 'vuex';
export default defineComponent({
name: 'FmisSearchMenus',
props: {},
setup(props, context) {
const state = reactive({});
const searchMenuText = ref('');
const startSearch = ref(false);
const searchResult = ref([]);
const popoverRef = ref();
// 树结构数据 转成一维数组
function treeToList(datas) {
var arr = [];
formateData(datas, 0);
function formateData(datas, level) {
var level = level || 0;
level++;
for (var i in datas) {
const obj = {
id: datas[i]['id'],
name: datas[i]['name'],
url: datas[i]['url'],
level: level,
pid: datas[i]['pid'],
};
arr.push(obj);
if (datas[i].children) {
formateData(datas[i].children, level);
}
}
}
return arr;
}
const store = useStore();
// 取menu数据 进行遍历
const treeMenuList = treeToList(store.state.menus);
// 获取当前已经匹配到的数组的父级层级关系
function getMenuStr(item) {
let astr = '';
// 这里的item是 传入的一维数组里的每一项
loopAcquisition(item);
// 函数查找父级
function loopAcquisition(data) {
if (data.pid == 0) {
astr = data.name;
} else {
let obj = {};
treeMenuList.map((e) => {
// 找到匹配的父级 给父级字符串拼接
if (e.id == data.pid) {
obj = e;
astr += '/' + e.name;
}
});
// 如果当前的父级不是一级 也就是pid不是0 说明还有父级 继续查找
obj.pid != 0 && loopAcquisition(obj);
}
}
return astr;
}
// 搜索框change事件 开始搜索
let timer = null; //计时器
const searchMenuIng = (val: any) => {
clearTimeout(timer);
timer = setTimeout(() => {
// 在这里执行输入事件的处理逻辑
if (val == '') {
return (startSearch.value = false);
}
startSearch.value = true;
const arr = treeMenuList.filter((item: any) => {
if (item.pid != '0' && item.url != undefined) {
return item.name.includes(val);
}
});
// 找到以后 数据处理 给这些项 拼接父级层级的字符串
arr.map((e) => {
if (e.pid != '0') {
e.strname = strFormatter(e.name + getMenuStr(e));
}
});
searchResult.value = arr;
}, 500); // 设置延迟时间(毫秒)
};
// 格式化字符串
const strFormatter = (str: string) => {
if (str === '') {
return '';
} else {
return str.split('/').reverse().join('/');
}
};
// 打开当前菜单
const openMenuPage = (key: any) => {
const routeParams = {
path: key.url.startsWith('/') ? key.url : '/' + key.url,
};
registerDynamicToRouterAndNext(routeParams);
popoverRef.value.hide();
};
// 搜索提示高亮 方法
const highlightedText = (val, keyword) => {
const Reg = new RegExp(keyword, 'i');
if (val) {
const res = val.replace(Reg, `<span style="color: rgb(62, 142, 247)">${keyword}</span>`);
return res;
}
};
return {
...toRefs(state),
searchMenuText,
treeMenuList,
searchMenuIng,
startSearch,
searchResult,
openMenuPage,
highlightedText,
useDebounce,
popoverRef,
};
},
});
</script>
<style scoped lang="less">
.search-results {
height: 180px;
overflow: hidden;
width: 100%;
z-index: 10000;
padding: 10px;
.result-box {
margin-bottom: 10px;
}
.search-box {
height: 140px;
overflow-y: scroll;
.search-item {
line-height: 28px;
cursor: pointer;
height: 28px;
font-size: 13px;
span:first-child {
margin-right: 40px;
font-weight: 700;
font-size: 14px;
}
}
}
}
</style>