提交 | 用户 | 时间
|
0ca254
|
1 |
<template> |
A |
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="option in options" :key="option.item.path" :value="option.item" :label="option.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/dist/fuse.min.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 |
const path = val.path; |
|
74 |
const query = val.query; |
|
75 |
if(this.ishttp(val.path)) { |
|
76 |
// http(s):// 路径新窗口打开 |
|
77 |
const pindex = path.indexOf("http"); |
|
78 |
window.open(path.substr(pindex, path.length), "_blank"); |
|
79 |
} else { |
|
80 |
if (query) { |
|
81 |
this.$router.push({ path: path, query: JSON.parse(query) }); |
|
82 |
} else { |
|
83 |
this.$router.push(path) |
|
84 |
} |
|
85 |
} |
|
86 |
this.search = '' |
|
87 |
this.options = [] |
|
88 |
this.$nextTick(() => { |
|
89 |
this.show = false |
|
90 |
}) |
|
91 |
}, |
|
92 |
initFuse(list) { |
|
93 |
this.fuse = new Fuse(list, { |
|
94 |
shouldSort: true, |
|
95 |
threshold: 0.4, |
|
96 |
location: 0, |
|
97 |
distance: 100, |
|
98 |
minMatchCharLength: 1, |
|
99 |
keys: [{ |
|
100 |
name: 'title', |
|
101 |
weight: 0.7 |
|
102 |
}, { |
|
103 |
name: 'path', |
|
104 |
weight: 0.3 |
|
105 |
}] |
|
106 |
}) |
|
107 |
}, |
|
108 |
// Filter out the routes that can be displayed in the sidebar |
|
109 |
// And generate the internationalized title |
|
110 |
generateRoutes(routes, basePath = '/', prefixTitle = []) { |
|
111 |
let res = [] |
|
112 |
|
|
113 |
for (const router of routes) { |
|
114 |
// skip hidden router |
|
115 |
if (router.hidden) { continue } |
|
116 |
|
|
117 |
const data = { |
|
118 |
path: !this.ishttp(router.path) ? path.resolve(basePath, router.path) : router.path, |
|
119 |
title: [...prefixTitle] |
|
120 |
} |
|
121 |
|
|
122 |
if (router.meta && router.meta.title) { |
|
123 |
data.title = [...data.title, router.meta.title] |
|
124 |
|
|
125 |
if (router.redirect !== 'noRedirect') { |
|
126 |
// only push the routes with title |
|
127 |
// special case: need to exclude parent router without redirect |
|
128 |
res.push(data) |
|
129 |
} |
|
130 |
} |
|
131 |
|
|
132 |
if (router.query) { |
|
133 |
data.query = router.query |
|
134 |
} |
|
135 |
|
|
136 |
// recursive child routes |
|
137 |
if (router.children) { |
|
138 |
const tempRoutes = this.generateRoutes(router.children, data.path, data.title) |
|
139 |
if (tempRoutes.length >= 1) { |
|
140 |
res = [...res, ...tempRoutes] |
|
141 |
} |
|
142 |
} |
|
143 |
} |
|
144 |
return res |
|
145 |
}, |
|
146 |
querySearch(query) { |
|
147 |
if (query !== '') { |
|
148 |
this.options = this.fuse.search(query) |
|
149 |
} else { |
|
150 |
this.options = [] |
|
151 |
} |
|
152 |
}, |
|
153 |
ishttp(url) { |
|
154 |
return url.indexOf('http://') !== -1 || url.indexOf('https://') !== -1 |
|
155 |
} |
|
156 |
} |
|
157 |
} |
|
158 |
</script> |
|
159 |
|
|
160 |
<style lang="scss" scoped> |
|
161 |
.header-search { |
|
162 |
font-size: 0 !important; |
|
163 |
|
|
164 |
.search-icon { |
|
165 |
cursor: pointer; |
|
166 |
font-size: 18px; |
|
167 |
vertical-align: middle; |
|
168 |
} |
|
169 |
|
|
170 |
.header-search-select { |
|
171 |
font-size: 18px; |
|
172 |
transition: width 0.2s; |
|
173 |
width: 0; |
|
174 |
overflow: hidden; |
|
175 |
background: transparent; |
|
176 |
border-radius: 0; |
|
177 |
display: inline-block; |
|
178 |
vertical-align: middle; |
|
179 |
|
|
180 |
::v-deep .el-input__inner { |
|
181 |
border-radius: 0; |
|
182 |
border: 0; |
|
183 |
padding-left: 0; |
|
184 |
padding-right: 0; |
|
185 |
box-shadow: none !important; |
|
186 |
border-bottom: 1px solid #d9d9d9; |
|
187 |
vertical-align: middle; |
|
188 |
} |
|
189 |
} |
|
190 |
|
|
191 |
&.show { |
|
192 |
.header-search-select { |
|
193 |
width: 210px; |
|
194 |
margin-left: 10px; |
|
195 |
} |
|
196 |
} |
|
197 |
} |
|
198 |
</style> |