feature[Navbar]: add header-search component(#1591)
This commit is contained in:
		@@ -45,6 +45,7 @@
 | 
				
			|||||||
    "echarts": "4.1.0",
 | 
					    "echarts": "4.1.0",
 | 
				
			||||||
    "element-ui": "2.4.10",
 | 
					    "element-ui": "2.4.10",
 | 
				
			||||||
    "file-saver": "1.3.8",
 | 
					    "file-saver": "1.3.8",
 | 
				
			||||||
 | 
					    "fuse.js": "3.4.2",
 | 
				
			||||||
    "js-cookie": "2.2.0",
 | 
					    "js-cookie": "2.2.0",
 | 
				
			||||||
    "jsonlint": "1.6.3",
 | 
					    "jsonlint": "1.6.3",
 | 
				
			||||||
    "jszip": "3.1.5",
 | 
					    "jszip": "3.1.5",
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										187
									
								
								src/components/HeaderSearch/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								src/components/HeaderSearch/index.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,187 @@
 | 
				
			|||||||
 | 
					<template>
 | 
				
			||||||
 | 
					  <div :class="{'show':show}" class="header-search">
 | 
				
			||||||
 | 
					    <svg-icon class-name="search-icon" icon-class="search" @click="click" />
 | 
				
			||||||
 | 
					    <el-select
 | 
				
			||||||
 | 
					      ref="headerSearchSelect"
 | 
				
			||||||
 | 
					      v-model="search"
 | 
				
			||||||
 | 
					      :remote-method="querySearch"
 | 
				
			||||||
 | 
					      filterable
 | 
				
			||||||
 | 
					      default-first-option
 | 
				
			||||||
 | 
					      remote
 | 
				
			||||||
 | 
					      placeholder="Search"
 | 
				
			||||||
 | 
					      class="header-search-select"
 | 
				
			||||||
 | 
					      @change="change">
 | 
				
			||||||
 | 
					      <el-option v-for="item in options" :key="item.path" :value="item" :label="item.title.join(' > ')"/>
 | 
				
			||||||
 | 
					    </el-select>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script>
 | 
				
			||||||
 | 
					import Fuse from 'fuse.js'
 | 
				
			||||||
 | 
					import path from 'path'
 | 
				
			||||||
 | 
					import i18n from '@/lang'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default {
 | 
				
			||||||
 | 
					  name: 'HeaderSearch',
 | 
				
			||||||
 | 
					  data() {
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      search: '',
 | 
				
			||||||
 | 
					      options: [],
 | 
				
			||||||
 | 
					      searchPool: [],
 | 
				
			||||||
 | 
					      show: false,
 | 
				
			||||||
 | 
					      fuse: undefined
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  computed: {
 | 
				
			||||||
 | 
					    routers() {
 | 
				
			||||||
 | 
					      return this.$store.getters.permission_routers
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    lang() {
 | 
				
			||||||
 | 
					      return this.$store.getters.language
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  watch: {
 | 
				
			||||||
 | 
					    lang() {
 | 
				
			||||||
 | 
					      this.searchPool = this.generateRouters(this.routers)
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    routers() {
 | 
				
			||||||
 | 
					      this.searchPool = this.generateRouters(this.routers)
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    searchPool(list) {
 | 
				
			||||||
 | 
					      this.initFuse(list)
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    show(value) {
 | 
				
			||||||
 | 
					      if (value) {
 | 
				
			||||||
 | 
					        document.body.addEventListener('click', this.close)
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        document.body.removeEventListener('click', this.close)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  mounted() {
 | 
				
			||||||
 | 
					    this.searchPool = this.generateRouters(this.routers)
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  methods: {
 | 
				
			||||||
 | 
					    click() {
 | 
				
			||||||
 | 
					      this.show = !this.show
 | 
				
			||||||
 | 
					      if (this.show) {
 | 
				
			||||||
 | 
					        this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.focus()
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    close() {
 | 
				
			||||||
 | 
					      this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.blur()
 | 
				
			||||||
 | 
					      this.options = []
 | 
				
			||||||
 | 
					      this.show = false
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    change(val) {
 | 
				
			||||||
 | 
					      this.$router.push(val.path)
 | 
				
			||||||
 | 
					      this.search = ''
 | 
				
			||||||
 | 
					      this.options = []
 | 
				
			||||||
 | 
					      this.$nextTick(() => {
 | 
				
			||||||
 | 
					        this.show = false
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    initFuse(list) {
 | 
				
			||||||
 | 
					      this.fuse = new Fuse(list, {
 | 
				
			||||||
 | 
					        shouldSort: true,
 | 
				
			||||||
 | 
					        threshold: 0.4,
 | 
				
			||||||
 | 
					        location: 0,
 | 
				
			||||||
 | 
					        distance: 100,
 | 
				
			||||||
 | 
					        maxPatternLength: 32,
 | 
				
			||||||
 | 
					        minMatchCharLength: 1,
 | 
				
			||||||
 | 
					        keys: [{
 | 
				
			||||||
 | 
					          name: 'title',
 | 
				
			||||||
 | 
					          weight: 0.7
 | 
				
			||||||
 | 
					        }, {
 | 
				
			||||||
 | 
					          name: 'path',
 | 
				
			||||||
 | 
					          weight: 0.3
 | 
				
			||||||
 | 
					        }]
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    // Filter out the routes that can be displayed in the sidebar
 | 
				
			||||||
 | 
					    // And generate the internationalized title
 | 
				
			||||||
 | 
					    generateRouters(routers, basePath = '/', prefixTitle = []) {
 | 
				
			||||||
 | 
					      let res = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      for (const router of routers) {
 | 
				
			||||||
 | 
					        // skip hidden router
 | 
				
			||||||
 | 
					        if (router.hidden) { continue }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const data = {
 | 
				
			||||||
 | 
					          path: path.resolve(basePath, router.path),
 | 
				
			||||||
 | 
					          title: [...prefixTitle]
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (router.meta && router.meta.title) {
 | 
				
			||||||
 | 
					          // generate internationalized title
 | 
				
			||||||
 | 
					          const i18ntitle = i18n.t(`route.${router.meta.title}`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          data.title = [...data.title, i18ntitle]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          if (router.redirect !== 'noredirect') {
 | 
				
			||||||
 | 
					            // only push the routes with title
 | 
				
			||||||
 | 
					            // special case: need to exclude parent router without redirect
 | 
				
			||||||
 | 
					            res.push(data)
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // recursive child routers
 | 
				
			||||||
 | 
					        if (router.children) {
 | 
				
			||||||
 | 
					          const tempRouters = this.generateRouters(router.children, data.path, data.title)
 | 
				
			||||||
 | 
					          if (tempRouters.length >= 1) {
 | 
				
			||||||
 | 
					            res = [...res, ...tempRouters]
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return res
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    querySearch(query) {
 | 
				
			||||||
 | 
					      if (query !== '') {
 | 
				
			||||||
 | 
					        this.options = this.fuse.search(query)
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        this.options = []
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style lang="scss" scoped>
 | 
				
			||||||
 | 
					.header-search {
 | 
				
			||||||
 | 
					  font-size: 0 !important;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .search-icon {
 | 
				
			||||||
 | 
					    cursor: pointer;
 | 
				
			||||||
 | 
					    font-size: 18px;
 | 
				
			||||||
 | 
					    vertical-align: middle;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  .header-search-select {
 | 
				
			||||||
 | 
					    font-size: 18px;
 | 
				
			||||||
 | 
					    transition: width 0.2s;
 | 
				
			||||||
 | 
					    width: 0;
 | 
				
			||||||
 | 
					    overflow: hidden;
 | 
				
			||||||
 | 
					    background: transparent;
 | 
				
			||||||
 | 
					    border-radius: 0;
 | 
				
			||||||
 | 
					    display: inline-block;
 | 
				
			||||||
 | 
					    vertical-align: middle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /deep/ .el-input__inner {
 | 
				
			||||||
 | 
					      border-radius: 0;
 | 
				
			||||||
 | 
					      border: 0;
 | 
				
			||||||
 | 
					      padding-left: 0;
 | 
				
			||||||
 | 
					      padding-right: 0;
 | 
				
			||||||
 | 
					      box-shadow: none !important;
 | 
				
			||||||
 | 
					      border-bottom: 1px solid #d9d9d9;
 | 
				
			||||||
 | 
					      vertical-align: middle;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  &.show {
 | 
				
			||||||
 | 
					    .header-search-select {
 | 
				
			||||||
 | 
					      width: 210px;
 | 
				
			||||||
 | 
					      margin-left: 10px;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
							
								
								
									
										1
									
								
								src/icons/svg/search.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/icons/svg/search.svg
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M124.884 109.812L94.256 79.166c-.357-.357-.757-.629-1.129-.914a50.366 50.366 0 0 0 8.186-27.59C101.327 22.689 78.656 0 50.67 0 22.685 0 0 22.688 0 50.663c0 27.989 22.685 50.663 50.656 50.663 10.186 0 19.643-3.03 27.6-8.201.286.385.557.771.9 1.114l30.628 30.632a10.633 10.633 0 0 0 7.543 3.129c2.728 0 5.457-1.043 7.543-3.115 4.171-4.157 4.171-10.915.014-15.073M50.671 85.338C31.557 85.338 16 69.78 16 50.663c0-19.102 15.557-34.661 34.67-34.661 19.115 0 34.657 15.559 34.657 34.675 0 19.102-15.557 34.661-34.656 34.661"/></svg>
 | 
				
			||||||
| 
		 After Width: | Height: | Size: 600 B  | 
@@ -6,24 +6,26 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    <div class="right-menu">
 | 
					    <div class="right-menu">
 | 
				
			||||||
      <template v-if="device!=='mobile'">
 | 
					      <template v-if="device!=='mobile'">
 | 
				
			||||||
        <error-log class="errLog-container right-menu-item"/>
 | 
					        <search class="right-menu-item" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <error-log class="errLog-container right-menu-item hover-effect"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <el-tooltip :content="$t('navbar.screenfull')" effect="dark" placement="bottom">
 | 
					        <el-tooltip :content="$t('navbar.screenfull')" effect="dark" placement="bottom">
 | 
				
			||||||
          <screenfull class="right-menu-item"/>
 | 
					          <screenfull class="right-menu-item hover-effect"/>
 | 
				
			||||||
        </el-tooltip>
 | 
					        </el-tooltip>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <el-tooltip :content="$t('navbar.size')" effect="dark" placement="bottom">
 | 
					        <el-tooltip :content="$t('navbar.size')" effect="dark" placement="bottom">
 | 
				
			||||||
          <size-select class="right-menu-item"/>
 | 
					          <size-select class="right-menu-item hover-effect"/>
 | 
				
			||||||
        </el-tooltip>
 | 
					        </el-tooltip>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <lang-select class="right-menu-item"/>
 | 
					        <lang-select class="right-menu-item hover-effect"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <el-tooltip :content="$t('navbar.theme')" effect="dark" placement="bottom">
 | 
					        <el-tooltip :content="$t('navbar.theme')" effect="dark" placement="bottom">
 | 
				
			||||||
          <theme-picker class="theme-picker right-menu-item"/>
 | 
					          <theme-picker class="right-menu-item hover-effect"/>
 | 
				
			||||||
        </el-tooltip>
 | 
					        </el-tooltip>
 | 
				
			||||||
      </template>
 | 
					      </template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <el-dropdown class="avatar-container right-menu-item" trigger="click">
 | 
					      <el-dropdown class="avatar-container right-menu-item hover-effect" trigger="click">
 | 
				
			||||||
        <div class="avatar-wrapper">
 | 
					        <div class="avatar-wrapper">
 | 
				
			||||||
          <img :src="avatar+'?imageView2/1/w/80/h/80'" class="user-avatar">
 | 
					          <img :src="avatar+'?imageView2/1/w/80/h/80'" class="user-avatar">
 | 
				
			||||||
          <i class="el-icon-caret-bottom"/>
 | 
					          <i class="el-icon-caret-bottom"/>
 | 
				
			||||||
@@ -57,6 +59,7 @@ import Screenfull from '@/components/Screenfull'
 | 
				
			|||||||
import SizeSelect from '@/components/SizeSelect'
 | 
					import SizeSelect from '@/components/SizeSelect'
 | 
				
			||||||
import LangSelect from '@/components/LangSelect'
 | 
					import LangSelect from '@/components/LangSelect'
 | 
				
			||||||
import ThemePicker from '@/components/ThemePicker'
 | 
					import ThemePicker from '@/components/ThemePicker'
 | 
				
			||||||
 | 
					import Search from '@/components/HeaderSearch'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
  components: {
 | 
					  components: {
 | 
				
			||||||
@@ -66,7 +69,8 @@ export default {
 | 
				
			|||||||
    Screenfull,
 | 
					    Screenfull,
 | 
				
			||||||
    SizeSelect,
 | 
					    SizeSelect,
 | 
				
			||||||
    LangSelect,
 | 
					    LangSelect,
 | 
				
			||||||
    ThemePicker
 | 
					    ThemePicker,
 | 
				
			||||||
 | 
					    Search
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  computed: {
 | 
					  computed: {
 | 
				
			||||||
    ...mapGetters([
 | 
					    ...mapGetters([
 | 
				
			||||||
@@ -100,6 +104,7 @@ export default {
 | 
				
			|||||||
    float: left;
 | 
					    float: left;
 | 
				
			||||||
    cursor: pointer;
 | 
					    cursor: pointer;
 | 
				
			||||||
    transition: background .3s;
 | 
					    transition: background .3s;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    &:hover {
 | 
					    &:hover {
 | 
				
			||||||
      background: rgba(0, 0, 0, .025)
 | 
					      background: rgba(0, 0, 0, .025)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -124,26 +129,35 @@ export default {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    .right-menu-item {
 | 
					    .right-menu-item {
 | 
				
			||||||
      cursor: pointer;
 | 
					 | 
				
			||||||
      display: inline-block;
 | 
					      display: inline-block;
 | 
				
			||||||
      padding: 0 8px;
 | 
					      padding: 0 8px;
 | 
				
			||||||
      height: 100%;
 | 
					      height: 100%;
 | 
				
			||||||
      font-size: 20px;
 | 
					      font-size: 18px;
 | 
				
			||||||
      color: #5a5e66;
 | 
					      color: #5a5e66;
 | 
				
			||||||
      vertical-align: text-bottom;
 | 
					      vertical-align: text-bottom;
 | 
				
			||||||
      transition: background .3s;
 | 
					
 | 
				
			||||||
      &:hover {
 | 
					      &.hover-effect {
 | 
				
			||||||
        background: rgba(0, 0, 0, .025)
 | 
					        cursor: pointer;
 | 
				
			||||||
 | 
					        transition: background .3s;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        &:hover {
 | 
				
			||||||
 | 
					          background: rgba(0, 0, 0, .025)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    .avatar-container {
 | 
					    .avatar-container {
 | 
				
			||||||
      margin-right: 30px;
 | 
					      margin-right: 30px;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      .avatar-wrapper {
 | 
					      .avatar-wrapper {
 | 
				
			||||||
        margin-top: 5px;
 | 
					        margin-top: 5px;
 | 
				
			||||||
        position: relative;
 | 
					        position: relative;
 | 
				
			||||||
 | 
					<<<<<<< HEAD
 | 
				
			||||||
        line-height: initial;
 | 
					        line-height: initial;
 | 
				
			||||||
        cursor: pointer;
 | 
					        cursor: pointer;
 | 
				
			||||||
 | 
					=======
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					>>>>>>> c71f311... feature[Navbar]: add header-search component (#1591)
 | 
				
			||||||
        .user-avatar {
 | 
					        .user-avatar {
 | 
				
			||||||
          width: 40px;
 | 
					          width: 40px;
 | 
				
			||||||
          height: 40px;
 | 
					          height: 40px;
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user