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

目录

前言

总体界面如下所示:

正文

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>
相关推荐
Jonathan Star8 小时前
沉浸式雨天海岸:用A-Frame打造WebXR互动场景
前端·javascript
工业甲酰苯胺8 小时前
实现 json path 来评估函数式解析器的损耗
java·前端·json
老前端的功夫8 小时前
Web应用的永生之术:PWA落地与实践深度指南
java·开发语言·前端·javascript·css·node.js
LilySesy9 小时前
ABAP+WHERE字段长度不一致报错解决
java·前端·javascript·bug·sap·abap·alv
Wang's Blog10 小时前
前端FAQ: Vue 3 与 Vue 2 相⽐有哪些重要的改进?
前端·javascript·vue.js
再希10 小时前
React+Tailwind CSS+Shadcn UI
前端·react.js·ui
用户479492835691510 小时前
JavaScript 的 NaN !== NaN 之谜:从 CPU 指令到 IEEE 754 标准的完整解密
前端·javascript
群联云防护小杜10 小时前
国产化环境下 Web 应用如何满足等保 2.0?从 Nginx 配置到 AI 防护实战
运维·前端·nginx
醉方休11 小时前
Web3.js 全面解析
前端·javascript·electron
前端开发爱好者11 小时前
前端新玩具:Vike 发布!
前端·javascript