前端实现菜单搜索搜索(功能模版)

目录

前言

总体界面如下所示:

正文

html 复制代码
<template>
  <div class="avue-searchs"
       @click.self="handleEsc">
    <div class="avue-searchs__title">菜单搜索</div>
    <div class="avue-searchs__content">
      <div class="avue-searchs__form">
        <el-input :placeholder="$t('search')"
                  v-model="value"
                  @keydown.esc.native="handleEsc">
          <el-button slot="append"
                     icon="el-icon-search"></el-button>
        </el-input>
        <p>
          <el-tag>你可以使用快捷键esc 关闭</el-tag>
        </p>
      </div>
      <div class="avue-searchs__list">
        <el-scrollbar class="avue-searchs__scrollbar">
          <div class="avue-searchs__item"
               v-for="(item,index) in menus"
               :key="index"
               @click="handleSelect(item)">
            <i :class="[item[iconKey],'avue-searchs__item-icon']"></i>
            <span class="avue-searchs__item-title">{{item[labelKey]}}</span>
            <div class="avue-searchs__item-path">
              {{item[pathKey]}}
            </div>
          </div>
        </el-scrollbar>
      </div>
    </div>
  </div>
</template>

<script>
  import config from './sidebar/config.js'
  import {mapGetters} from "vuex";

  export default {
    data() {
      return {
        config: config,
        value: "",
        menus: [],
        menuList: []
      }
    },
    created() {
      this.getMenuList();
    },
    watch: {
      value() {
        this.querySearch();
      },
      menu() {
        this.getMenuList();
      }
    },
    computed: {
      labelKey() {
        return this.website.menu.props.label || this.config.propsDefault.label;
      },
      pathKey() {
        return this.website.menu.props.path || this.config.propsDefault.path;
      },
      iconKey() {
        return this.website.menu.props.icon || this.config.propsDefault.icon;
      },
      childrenKey() {
        return (
          this.website.menu.props.children || this.config.propsDefault.children
        );
      },
      ...mapGetters(["menu", "website"])
    },
    methods: {
      handleEsc() {
        this.$parent.isSearch = false;
      },
      getMenuList() {
        const findMenu = list => {
          for (let i = 0; i < list.length; i++) {
            const ele = Object.assign({}, list[i]);
            if (this.validatenull(ele[this.childrenKey])) {
              this.menuList.push(ele);
            } else {
              findMenu(ele[this.childrenKey]);
            }
          }
        };
        this.menuList = [];
        findMenu(this.menu);
        this.menus = this.menuList;
      },
      querySearch() {
        var restaurants = this.menuList;
        var queryString = this.value
        this.menus = queryString
          ? this.menuList.filter(this.createFilter(queryString))
          : restaurants;
      },
      createFilter(queryString) {
        return restaurant => {
          return (
            restaurant.name.toLowerCase().indexOf(queryString.toLowerCase()) ===
            0
          );
        };
      },
      handleSelect(item) {
        this.handleEsc();
        this.value = "";
        this.$router.push({
          path: this.$router.$avueRouter.getPath({
            name: item[this.labelKey],
            src: item[this.pathKey]
          }, item.meta),
          query: item.query
        });
      }
    }
  }
</script>

<style lang="scss" scoped>
  .avue-searchs {
    padding-top: 50px;
    width: 100%;
    height: 100%;
    background-color: #fff;
    z-index: 1024;

    &__title {
      margin-bottom: 60px;
      text-align: center;
      font-size: 42px;
      font-weight: bold;
      letter-spacing: 2px;
      text-indent: 2px;
    }

    &__form {
      margin: 0 auto 50px auto;
      width: 50%;
      text-align: center;

      p {
        margin-top: 20px;
      }
    }

    &__scrollbar {
      height: 400px;
    }

    &__list {
      box-sizing: border-box;
      padding: 20px 30px;
      margin: 0 auto;
      width: 70%;
      border-radius: 4px;
      border: 1px solid #ebeef5;
      background-color: #fff;
      overflow: hidden;
      color: #303133;
      transition: 0.3s;
      box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
    }

    &__item {
      padding: 5px 0;
      border-bottom: 1px dashed #eee;

      &-icon {
        margin-right: 5px;
        font-size: 18px;
      }

      &-title {
        font-size: 20px;
        font-weight: 500;
        color: #333;
      }

      &-path {
        line-height: 30px;
        color: #666;
      }
    }
  }
</style>
相关推荐
失败又激情的man4 分钟前
python爬虫之数据存储
前端·数据库·python
互联网搬砖老肖5 分钟前
Web 架构之 API 安全防护:防刷、防爬、防泄漏
前端·安全·架构
小声读源码40 分钟前
【技巧】dify前端源代码修改第一弹-增加tab页
前端·pnpm·next.js·dify
假客套1 小时前
2025 后端自学UNIAPP【项目实战:旅游项目】7、景点详情页面【完结】
前端·uni-app·旅游
Captaincc1 小时前
Ilya 现身多大毕业演讲:AI 会完成我们能做的一切
前端·ai编程
teeeeeeemo1 小时前
Vue数据响应式原理解析
前端·javascript·vue.js·笔记·前端框架·vue
Sahas10191 小时前
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__ is not explicitly defined.
前端·javascript·vue.js
Jinxiansen02112 小时前
Vue 3 实战:【加强版】公司通知推送(WebSocket + token 校验 + 心跳机制)
前端·javascript·vue.js·websocket·typescript
MrSkye2 小时前
React入门:组件化思想?数据驱动?
前端·react.js·面试
BillKu2 小时前
Java解析前端传来的Unix时间戳
java·前端·unix