From f8bf7a26b3da3d52733d13bf7cdd309cdcff0692 Mon Sep 17 00:00:00 2001 From: RuoYi Date: Sun, 22 Mar 2026 19:10:21 +0800 Subject: [PATCH] =?UTF-8?q?=E8=8F=9C=E5=8D=95=E6=90=9C=E7=B4=A2=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E6=96=87=E6=9C=AC=E9=AB=98=E4=BA=AE&=E6=95=B0?= =?UTF-8?q?=E9=87=8F=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/HeaderSearch/index.vue | 213 ++++++++++++++++++++++---- 1 file changed, 179 insertions(+), 34 deletions(-) diff --git a/src/components/HeaderSearch/index.vue b/src/components/HeaderSearch/index.vue index d325951..6626d26 100644 --- a/src/components/HeaderSearch/index.vue +++ b/src/components/HeaderSearch/index.vue @@ -5,6 +5,7 @@ v-model="show" width="600" @close="close" + @opened="onDialogOpened" :show-close="false" append-to-body > @@ -22,24 +23,55 @@ > +
+ 找到 {{ options.length }} 个结果 +
+
-
-
- -
-
- + +
@@ -65,36 +97,40 @@ const routes = computed(() => usePermissionStore().defaultRoutes) function click() { show.value = !show.value if (show.value) { - headerSearchSelectRef.value && headerSearchSelectRef.value.focus() options.value = searchPool.value } } +function onDialogOpened() { + nextTick(() => { + headerSearchSelectRef.value && headerSearchSelectRef.value.focus() + }) +} + function close() { headerSearchSelectRef.value && headerSearchSelectRef.value.blur() search.value = '' - options.value = [] + options.value = searchPool.value show.value = false activeIndex.value = -1 } function change(val) { - const path = val.path + const p = val.path const query = val.query - if (isHttp(path)) { + if (isHttp(p)) { // http(s):// 路径新窗口打开 - const pindex = path.indexOf("http") - window.open(path.substr(pindex, path.length), "_blank") + const pindex = p.indexOf("http") + window.open(p.substr(pindex, p.length), "_blank") } else { if (query) { - router.push({ path: path, query: JSON.parse(query) }) + router.push({ path: p, query: JSON.parse(query) }) } else { - router.push(path) + router.push(p) } } - search.value = '' - options.value = [] + options.value = searchPool.value nextTick(() => { show.value = false }) @@ -103,8 +139,7 @@ function change(val) { function initFuse(list) { fuse.value = new Fuse(list, { shouldSort: true, - threshold: 0.4, - location: 0, + threshold: 0.2, distance: 100, minMatchCharLength: 1, keys: [{ @@ -117,13 +152,9 @@ function initFuse(list) { }) } -// Filter out the routes that can be displayed in the sidebar -// And generate the internationalized title function generateRoutes(routes, basePath = '', prefixTitle = []) { let res = [] - for (const r of routes) { - // skip hidden router if (r.hidden) { continue } const p = r.path.length > 0 && r.path[0] === '/' ? r.path : '/' + r.path const data = { @@ -131,21 +162,16 @@ function generateRoutes(routes, basePath = '', prefixTitle = []) { title: [...prefixTitle], icon: '' } - if (r.meta && r.meta.title) { data.title = [...data.title, r.meta.title] data.icon = r.meta.icon if (r.redirect !== "noRedirect") { - // only push the routes with title - // special case: need to exclude parent router without redirect res.push(data) } } if (r.query) { data.query = r.query } - - // recursive child routes if (r.children) { const tempRoutes = generateRoutes(r.children, data.path, data.title) if (tempRoutes.length >= 1) { @@ -159,7 +185,18 @@ function generateRoutes(routes, basePath = '', prefixTitle = []) { function querySearch(query) { activeIndex.value = -1 if (query !== '') { - options.value = fuse.value.search(query).map((item) => item.item) ?? searchPool.value + const q = query.toLowerCase() + const pathMatches = searchPool.value.filter(item => + item.path.toLowerCase().includes(q) + ) + const fuseMatches = fuse.value.search(query).map(item => item.item) + const merged = [...pathMatches] + fuseMatches.forEach(item => { + if (!merged.find(m => m.path === item.path)) { + merged.push(item) + } + }) + options.value = merged } else { options.value = searchPool.value } @@ -187,6 +224,18 @@ function selectActiveResult() { } } +function highlightText(text) { + if (!text) return '' + if (!search.value) return text + const keyword = escapeRegExp(search.value) + const reg = new RegExp(`(${keyword})`, 'gi') + return text.replace(reg, '$1') +} + +function escapeRegExp(str) { + return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') +} + onMounted(() => { searchPool.value = generateRoutes(routes.value) }) @@ -197,6 +246,20 @@ watch(searchPool, (list) => {