fix(grid): avoid repeated search text allocations
This commit is contained in:
1 parent
9020a4dd51
commit
bd7065f244
6 files changed
+134
-12
No files matched your search
@@ -5,19 +5,34 @@ import { useDataGridSearch } from "@/composables/useDataGridSearch";
|
||||
describe("useDataGridSearch", () => {
|
||||
it("debounces matching across columns and cells", async () => {
|
||||
vi.useFakeTimers();
|
||||
const search = useDataGridSearch({ columns: ["id", "name"], rows: [[1, "Alice"]], getCellText: (row, column) => String(row[column] ?? "") });
|
||||
// getCellSearchText 契约:返回小写文本(调用方负责缓存小写副本)
|
||||
const search = useDataGridSearch({ columns: ["id", "name"], rows: [[1, "Alice"]], getCellSearchText: (row, column) => String(row[column] ?? "").toLowerCase() });
|
||||
search.searchText.value = "ali";
|
||||
await nextTick();
|
||||
expect(search.matches.value).toEqual([]);
|
||||
vi.advanceTimersByTime(150);
|
||||
await nextTick();
|
||||
expect(search.matches.value).toEqual([{ kind: "cell", displayRow: 0, col: 1 }]);
|
||||
// matchSet 用数值 key:(displayRow+1)*65536+col
|
||||
expect(search.matchSet.value.has((0 + 1) * 65536 + 1)).toBe(true);
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it("keys column-name matches with displayRow -1", async () => {
|
||||
vi.useFakeTimers();
|
||||
const search = useDataGridSearch({ columns: ["id", "name"], rows: [], getCellSearchText: () => "" });
|
||||
search.searchText.value = "nam";
|
||||
await nextTick();
|
||||
vi.advanceTimersByTime(150);
|
||||
await nextTick();
|
||||
expect(search.matches.value).toEqual([{ kind: "column", displayRow: -1, col: 1 }]);
|
||||
expect(search.matchSet.value.has((-1 + 1) * 65536 + 1)).toBe(true);
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it("suggests columns and replaces only the active token", async () => {
|
||||
const columns = ref(["customer_id", "created_at"]);
|
||||
const search = useDataGridSearch({ columns, rows: [], getCellText: () => "" });
|
||||
const search = useDataGridSearch({ columns, rows: [], getCellSearchText: () => "" });
|
||||
search.searchText.value = "status = cus";
|
||||
await nextTick();
|
||||
expect(search.suggestions.value).toEqual(["customer_id"]);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { computed, getCurrentScope, nextTick, onScopeDispose, ref, toValue, watch, type MaybeRefOrGetter } from "vue";
|
||||
import { dataGridSearchMatchKey } from "@/lib/dataGrid/canvasDataGridRenderer";
|
||||
|
||||
export type DataGridSearchMatch = {
|
||||
kind: "cell" | "column";
|
||||
@@ -10,7 +11,9 @@ export type UseDataGridSearchOptions<Row> = {
|
||||
columns: MaybeRefOrGetter<readonly string[]>;
|
||||
suggestionColumns?: MaybeRefOrGetter<readonly string[]>;
|
||||
rows: MaybeRefOrGetter<readonly Row[]>;
|
||||
getCellText: (row: Row, columnIndex: number) => string;
|
||||
/** 必须返回小写文本(查询词已小写)。调用方可据此缓存小写副本,
|
||||
* 避免每次按键对全部单元格重新分配 toLowerCase 字符串。 */
|
||||
getCellSearchText: (row: Row, columnIndex: number) => string;
|
||||
debounceMs?: number;
|
||||
onNavigate?: (match: DataGridSearchMatch) => void;
|
||||
};
|
||||
@@ -38,12 +41,13 @@ export function useDataGridSearch<Row>(options: UseDataGridSearchOptions<Row>) {
|
||||
});
|
||||
toValue(options.rows).forEach((row, displayRow) => {
|
||||
columns.forEach((_, col) => {
|
||||
if (options.getCellText(row, col).toLowerCase().includes(query)) result.push({ kind: "cell", displayRow, col });
|
||||
if (options.getCellSearchText(row, col).includes(query)) result.push({ kind: "cell", displayRow, col });
|
||||
});
|
||||
});
|
||||
return result;
|
||||
});
|
||||
const matchSet = computed(() => new Set(matches.value.map((match) => `${match.kind}:${match.displayRow}:${match.col}`)));
|
||||
// 数值 key(列头匹配 displayRow=-1),避免每匹配一次字符串拼接
|
||||
const matchSet = computed(() => new Set(matches.value.map((match) => dataGridSearchMatchKey(match.displayRow, match.col))));
|
||||
const currentMatch = computed(() => matches.value[currentMatchIndex.value] ?? null);
|
||||
|
||||
function clearTimer() {
|
||||
|
||||
Reference in new issue
Block a user