index.vue 4.3 KB

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