long
2021-11-12 794f2a2560a00eb7e008eb94219c82654488831f
提交 | 用户 | age
36b711 1 <template>
L 2   <div :class="{'show':show}" class="header-search">
3     <svg-icon class-name="search-icon" icon-class="search" @click.stop="click" />
4     <el-select
5       ref="headerSearchSelect"
6       v-model="search"
7       :remote-method="querySearch"
8       filterable
9       default-first-option
10       remote
11       placeholder="Search"
12       class="header-search-select"
13       @change="change"
14     >
15       <el-option v-for="item in options" :key="item.path" :value="item" :label="item.title.join(' > ')" />
16     </el-select>
17   </div>
18 </template>
19
20 <script>
21 // fuse is a lightweight fuzzy-search module
22 // make search results more in line with expectations
23 import Fuse from 'fuse.js'
24 import path from 'path'
25
26 export default {
27   name: 'HeaderSearch',
28   data() {
29     return {
30       search: '',
31       options: [],
32       searchPool: [],
33       show: false,
34       fuse: undefined
35     }
36   },
37   computed: {
38     routes() {
39       return this.$store.getters.permission_routes
40     }
41   },
42   watch: {
43     routes() {
44       this.searchPool = this.generateRoutes(this.routes)
45     },
46     searchPool(list) {
47       this.initFuse(list)
48     },
49     show(value) {
50       if (value) {
51         document.body.addEventListener('click', this.close)
52       } else {
53         document.body.removeEventListener('click', this.close)
54       }
55     }
56   },
57   mounted() {
58     this.searchPool = this.generateRoutes(this.routes)
59   },
60   methods: {
61     click() {
62       this.show = !this.show
63       if (this.show) {
64         this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.focus()
65       }
66     },
67     close() {
68       this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.blur()
69       this.options = []
70       this.show = false
71     },
72     change(val) {
73       this.$router.push(val.path)
74       this.search = ''
75       this.options = []
76       this.$nextTick(() => {
77         this.show = false
78       })
79     },
80     initFuse(list) {
81       this.fuse = new Fuse(list, {
82         shouldSort: true,
83         threshold: 0.4,
84         location: 0,
85         distance: 100,
86         maxPatternLength: 32,
87         minMatchCharLength: 1,
88         keys: [{
89           name: 'title',
90           weight: 0.7
91         }, {
92           name: 'path',
93           weight: 0.3
94         }]
95       })
96     },
97     // Filter out the routes that can be displayed in the sidebar
98     // And generate the internationalized title
99     generateRoutes(routes, basePath = '/', prefixTitle = []) {
100       let res = []
101
102       for (const router of routes) {
103         // skip hidden router
104         if (router.hidden) { continue }
105
106         const data = {
107           path: path.resolve(basePath, router.path),
108           title: [...prefixTitle]
109         }
110
111         if (router.meta && router.meta.title) {
112           data.title = [...data.title, router.meta.title]
113
114           if (router.redirect !== 'noRedirect') {
115             // only push the routes with title
116             // special case: need to exclude parent router without redirect
117             res.push(data)
118           }
119         }
120
121         // recursive child routes
122         if (router.children) {
123           const tempRoutes = this.generateRoutes(router.children, data.path, data.title)
124           if (tempRoutes.length >= 1) {
125             res = [...res, ...tempRoutes]
126           }
127         }
128       }
129       return res
130     },
131     querySearch(query) {
132       if (query !== '') {
133         this.options = this.fuse.search(query)
134       } else {
135         this.options = []
136       }
137     }
138   }
139 }
140 </script>
141
142 <style lang="scss" scoped>
143 .header-search {
144   font-size: 0 !important;
145
146   .search-icon {
147     cursor: pointer;
148     font-size: 18px;
149     vertical-align: middle;
150   }
151
152   .header-search-select {
153     font-size: 18px;
154     transition: width 0.2s;
155     width: 0;
156     overflow: hidden;
157     background: transparent;
158     border-radius: 0;
159     display: inline-block;
160     vertical-align: middle;
161
162     ::v-deep .el-input__inner {
163       border-radius: 0;
164       border: 0;
165       padding-left: 0;
166       padding-right: 0;
167       box-shadow: none !important;
168       border-bottom: 1px solid #d9d9d9;
169       vertical-align: middle;
170     }
171   }
172
173   &.show {
174     .header-search-select {
175       width: 210px;
176       margin-left: 10px;
177     }
178   }
179 }
180 </style>