Skip to content

Commit

Permalink
perf(menu->search): highlight match chars when search menu (#3880)
Browse files Browse the repository at this point in the history
* fix: state mutations in computed getters should be avoided

* fix: type about getDataSourceRef

* perf(menu search): highlight match chars when search menu
  • Loading branch information
xachary authored May 31, 2024
1 parent a6086f4 commit d5fed8a
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 4 deletions.
16 changes: 15 additions & 1 deletion src/components/Application/src/search/AppSearchModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,14 @@
<Icon :icon="item.icon || 'mdi:form-select'" :size="20" />
</div>
<div :class="`${prefixCls}-list__item-text`">
{{ item.name }}
<!-- 搜索结果包含的字符着色 -->
<span
v-for="(each, i) in item.chars"
:key="i"
:class="{ highlight: each.highlight }"
>
{{ each.char }}
</span>
</div>
<div :class="`${prefixCls}-list__item-enter`">
<Icon icon="ant-design:enter-outlined" :size="20" />
Expand Down Expand Up @@ -254,6 +261,13 @@
&-text {
flex: 1;
// 搜索结果包含的字符着色
& > span {
&.highlight {
color: lighten(@primary-color, 20%);
}
}
}
&-enter {
Expand Down
111 changes: 108 additions & 3 deletions src/components/Application/src/search/useMenuSearch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export interface SearchResult {
name: string;
path: string;
icon?: string;
// 搜索结果包含的字符着色
chars: { char: string; highlight: boolean }[];
}

// Translate special characters
Expand Down Expand Up @@ -68,11 +70,85 @@ export function useMenuSearch(refs: Ref<HTMLElement[]>, scrollWrap: Ref, emit: A
const { name, path, icon, children, hideMenu, meta } = item;
if (
!hideMenu &&
reg.test(name?.toLowerCase()) &&
reg.test(name?.toLowerCase() ?? '') &&
(!children?.length || meta?.hideChildrenInMenu)
) {
const chars: { char: string; highlight: boolean }[] = [];

// 显示字符串
const label = (parent?.name ? `${parent.name} > ${name}` : name) ?? '';
const labelChars = label.split('');
let labelPointer = 0;

const keywordChars = keyword.value.split('');
const keywordLength = keywordChars.length;
let keywordPointer = 0;

// 用于查找完整关键词的匹配
let includePointer = 0;

// 优先查找完整关键词的匹配
if (label.toLowerCase().includes(keyword.value.toLowerCase())) {
while (includePointer < labelChars.length) {
if (
label.toLowerCase().slice(includePointer, includePointer + keywordLength) ===
keyword.value.toLowerCase()
) {
chars.push(
...label
.substring(labelPointer, includePointer)
.split('')
.map((v) => ({
char: v,
highlight: false,
})),
);
chars.push(
...label
.slice(includePointer, includePointer + keywordLength)
.split('')
.map((v) => ({
char: v,
highlight: true,
})),
);
includePointer += keywordLength;
labelPointer = includePointer;
} else {
includePointer++;
}
}
}

// 查找满足关键词顺序的匹配
while (labelPointer < labelChars.length) {
keywordPointer = 0;
while (keywordPointer < keywordChars.length) {
if (keywordChars[keywordPointer] !== void 0 && labelChars[labelPointer] !== void 0) {
if (
keywordChars[keywordPointer].toLowerCase() ===
labelChars[labelPointer].toLowerCase()
) {
chars.push({
char: labelChars[labelPointer],
highlight: true,
});
keywordPointer++;
} else {
chars.push({
char: labelChars[labelPointer],
highlight: false,
});
}
} else {
keywordPointer++;
}
labelPointer++;
}
}
ret.push({
name: parent?.name ? `${parent.name} > ${name}` : name,
name: label,
chars,
path,
icon,
});
Expand All @@ -81,7 +157,36 @@ export function useMenuSearch(refs: Ref<HTMLElement[]>, scrollWrap: Ref, emit: A
ret.push(...handlerSearchResult(children, reg, item));
}
});
return ret;

// 排序
return ret.sort((a, b) => {
if (
a.name.toLowerCase().includes(keyword.value.toLowerCase()) &&
b.name.toLowerCase().includes(keyword.value.toLowerCase())
) {
// 两者都存在完整关键词的匹配

// 匹配数量
const ca =
a.name.toLowerCase().match(new RegExp(keyword.value.toLowerCase(), 'g'))?.length ?? 0;
const cb =
b.name.toLowerCase().match(new RegExp(keyword.value.toLowerCase(), 'g'))?.length ?? 0;

// 匹配数量越多的优先显示,数量相同的按字符串排序
return ca === cb ? a.name.toLowerCase().localeCompare(b.name.toLowerCase()) : cb - ca;
} else {
if (a.name.toLowerCase().includes(keyword.value.toLowerCase())) {
// 完整关键词的匹配优先
return -1;
} else if (b.name.toLowerCase().includes(keyword.value.toLowerCase())) {
// 完整关键词的匹配优先
return 1;
} else {
// 按字符串排序
return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
}
}
});
}

// Activate when the mouse moves to a certain line
Expand Down

0 comments on commit d5fed8a

Please sign in to comment.