Newer
Older
DH_Apicture / src / components / HeaderSearch / index.vue
@zhangqy zhangqy on 29 Nov 4 KB first commit
  1. <template>
  2. <div :class="{ show: show }" class="header-search">
  3. <svg-icon
  4. class-name="search-icon"
  5. icon-class="search"
  6. @click.stop="click"
  7. />
  8. <el-select
  9. ref="headerSearchSelectRef"
  10. v-model="search"
  11. :remote-method="querySearch"
  12. filterable
  13. default-first-option
  14. remote
  15. placeholder="Search"
  16. class="header-search-select"
  17. @change="change"
  18. >
  19. <el-option
  20. v-for="option in options"
  21. :key="option.item.path"
  22. :value="option.item"
  23. :label="option.item.title.join(' > ')"
  24. />
  25. </el-select>
  26. </div>
  27. </template>
  28.  
  29. <script setup>
  30. import Fuse from "fuse.js";
  31. import { getNormalPath } from "@/utils/ruoyi";
  32. import { isHttp } from "@/utils/validate";
  33. import usePermissionStore from "@/store/modules/permission";
  34.  
  35. const search = ref("");
  36. const options = ref([]);
  37. const searchPool = ref([]);
  38. const show = ref(false);
  39. const fuse = ref(undefined);
  40. const headerSearchSelectRef = ref(null);
  41. const router = useRouter();
  42. const routes = computed(() => usePermissionStore().routes);
  43.  
  44. function click() {
  45. show.value = !show.value;
  46. if (show.value) {
  47. headerSearchSelectRef.value && headerSearchSelectRef.value.focus();
  48. }
  49. }
  50. function close() {
  51. headerSearchSelectRef.value && headerSearchSelectRef.value.blur();
  52. options.value = [];
  53. show.value = false;
  54. }
  55. function change(val) {
  56. const path = val.path;
  57. if (isHttp(path)) {
  58. // http(s):// 路径新窗口打开
  59. const pindex = path.indexOf("http");
  60. window.open(path.substr(pindex, path.length), "_blank");
  61. } else {
  62. router.push(path);
  63. }
  64.  
  65. search.value = "";
  66. options.value = [];
  67. nextTick(() => {
  68. show.value = false;
  69. });
  70. }
  71. function initFuse(list) {
  72. fuse.value = new Fuse(list, {
  73. shouldSort: true,
  74. threshold: 0.4,
  75. location: 0,
  76. distance: 100,
  77. minMatchCharLength: 1,
  78. keys: [
  79. {
  80. name: "title",
  81. weight: 0.7,
  82. },
  83. {
  84. name: "path",
  85. weight: 0.3,
  86. },
  87. ],
  88. });
  89. }
  90. // Filter out the routes that can be displayed in the sidebar
  91. // And generate the internationalized title
  92. function generateRoutes(routes, basePath = "", prefixTitle = []) {
  93. let res = [];
  94.  
  95. for (const r of routes) {
  96. // skip hidden router
  97. if (r.hidden) {
  98. continue;
  99. }
  100. const p = r.path.length > 0 && r.path[0] === "/" ? r.path : "/" + r.path;
  101. const data = {
  102. path: !isHttp(r.path) ? getNormalPath(basePath + p) : r.path,
  103. title: [...prefixTitle],
  104. };
  105.  
  106. if (r.meta && r.meta.title) {
  107. data.title = [...data.title, r.meta.title];
  108.  
  109. if (r.redirect !== "noRedirect") {
  110. // only push the routes with title
  111. // special case: need to exclude parent router without redirect
  112. res.push(data);
  113. }
  114. }
  115.  
  116. // recursive child routes
  117. if (r.children) {
  118. const tempRoutes = generateRoutes(r.children, data.path, data.title);
  119. if (tempRoutes.length >= 1) {
  120. res = [...res, ...tempRoutes];
  121. }
  122. }
  123. }
  124. return res;
  125. }
  126. function querySearch(query) {
  127. if (query !== "") {
  128. options.value = fuse.value.search(query);
  129. } else {
  130. options.value = [];
  131. }
  132. }
  133.  
  134. onMounted(() => {
  135. searchPool.value = generateRoutes(routes.value);
  136. });
  137.  
  138. watchEffect(() => {
  139. searchPool.value = generateRoutes(routes.value);
  140. });
  141.  
  142. watch(show, (value) => {
  143. if (value) {
  144. document.body.addEventListener("click", close);
  145. } else {
  146. document.body.removeEventListener("click", close);
  147. }
  148. });
  149.  
  150. watch(searchPool, (list) => {
  151. initFuse(list);
  152. });
  153. </script>
  154.  
  155. <style lang="scss" scoped>
  156. .header-search {
  157. font-size: 0 !important;
  158.  
  159. .search-icon {
  160. cursor: pointer;
  161. font-size: 18px;
  162. vertical-align: middle;
  163. }
  164.  
  165. .header-search-select {
  166. font-size: 18px;
  167. transition: width 0.2s;
  168. width: 0;
  169. overflow: hidden;
  170. background: transparent;
  171. border-radius: 0;
  172. display: inline-block;
  173. vertical-align: middle;
  174.  
  175. :deep(.el-input__inner) {
  176. border-radius: 0;
  177. border: 0;
  178. padding-left: 0;
  179. padding-right: 0;
  180. box-shadow: none !important;
  181. border-bottom: 1px solid #d9d9d9;
  182. vertical-align: middle;
  183. }
  184. }
  185.  
  186. &.show {
  187. .header-search-select {
  188. width: 210px;
  189. margin-left: 10px;
  190. }
  191. }
  192. }
  193. </style>