菜单树列表转到前台进行搜索和层级关系展示

先上原始数据  树结构的菜单

 首先先把树转成一维数组 便于搜索遍历 然后加一个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>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值