mirror of
				https://github.com/YunaiV/ruoyi-vue-pro.git
				synced 2025-10-31 18:49:06 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			189 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			189 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
| <template>
 | |
|   <div :class="{'show':show}" class="header-search">
 | |
|     <svg-icon class-name="search-icon" icon-class="search" @click.stop="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>
 | |
| // fuse is a lightweight fuzzy-search module
 | |
| // make search results more in line with expectations
 | |
| import Fuse from 'fuse.js'
 | |
| import path from 'path'
 | |
| 
 | |
| export default {
 | |
|   name: 'HeaderSearch',
 | |
|   data() {
 | |
|     return {
 | |
|       search: '',
 | |
|       options: [],
 | |
|       searchPool: [],
 | |
|       show: false,
 | |
|       fuse: undefined
 | |
|     }
 | |
|   },
 | |
|   computed: {
 | |
|     routes() {
 | |
|       return this.$store.getters.permission_routes
 | |
|     }
 | |
|   },
 | |
|   watch: {
 | |
|     routes() {
 | |
|       this.searchPool = this.generateRoutes(this.routes)
 | |
|     },
 | |
|     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.generateRoutes(this.routes)
 | |
|   },
 | |
|   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) {
 | |
|       if(this.ishttp(val.path)) {
 | |
|         // http(s):// 路径新窗口打开
 | |
|         window.open(val.path, "_blank");
 | |
|       } else {
 | |
|         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
 | |
|     generateRoutes(routes, basePath = '/', prefixTitle = []) {
 | |
|       let res = []
 | |
| 
 | |
|       for (const router of routes) {
 | |
|         // skip hidden router
 | |
|         if (router.hidden) { continue }
 | |
| 
 | |
|         const data = {
 | |
|           path: !this.ishttp(router.path) ? path.resolve(basePath, router.path) : router.path,
 | |
|           title: [...prefixTitle]
 | |
|         }
 | |
| 
 | |
|         if (router.meta && router.meta.title) {
 | |
|           data.title = [...data.title, router.meta.title]
 | |
| 
 | |
|           if (router.redirect !== 'noRedirect') {
 | |
|             // only push the routes with title
 | |
|             // special case: need to exclude parent router without redirect
 | |
|             res.push(data)
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         // recursive child routes
 | |
|         if (router.children) {
 | |
|           const tempRoutes = this.generateRoutes(router.children, data.path, data.title)
 | |
|           if (tempRoutes.length >= 1) {
 | |
|             res = [...res, ...tempRoutes]
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|       return res
 | |
|     },
 | |
|     querySearch(query) {
 | |
|       if (query !== '') {
 | |
|         this.options = this.fuse.search(query)
 | |
|       } else {
 | |
|         this.options = []
 | |
|       }
 | |
|     },
 | |
|     ishttp(url) {
 | |
|       return url.indexOf('http://') !== -1 || url.indexOf('https://') !== -1
 | |
|     }
 | |
|   }
 | |
| }
 | |
| </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;
 | |
| 
 | |
|     ::v-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>
 | 
