feat(grid): show column comments in condition suggestions

This commit is contained in:
zipg authored and GitHub committed 2026-07-17 00:05:50 +08:00
1 parent ecec25f0f4
commit dfd47bc54c
7 files changed
+174 -21

No files matched your search

@@ -41,6 +41,64 @@ describe("useDataGridConditionEditor", () => {
expect(value.value).toBe("status = customer_name");
});
it("reuses column comments for field suggestions without adding them to history", async () => {
const scope = { connectionId: "connection", database: "db", tableName: "users" };
rememberDataGridConditionHistory("where", scope, "customer_id = 1");
const value = ref("");
const editor = useDataGridConditionEditor({
kind: "where",
value,
columns: [
{ name: "customer_id", comment: "客户编号" },
{ name: "customer_name", comment: null },
],
historyScope: scope,
});
value.value = "cust";
await nextTick();
await vi.waitFor(() =>
expect(editor.suggestions.value).toEqual([
{ value: "customer_id", kind: "column", comment: "客户编号" },
{ value: "customer_name", kind: "column" },
]),
);
value.value = "customer";
editor.dismiss();
editor.openHistory();
expect(editor.suggestions.value).toEqual([{ value: "customer_id = 1", kind: "history" }]);
});
it.each(["where", "orderBy"] as const)("normalizes %s comments from different metadata providers", async (kind) => {
const value = ref("");
const editor = useDataGridConditionEditor({
kind,
value,
columns: [
"customer_plain",
{ name: "customer_native", comment: " 原生注释 " },
{ name: "customer_jdbc", comment: "JDBC remarks" },
{ name: "customer_null", comment: null },
{ name: "customer_blank", comment: " \n\t " },
{ name: "customer_invalid", comment: 42 } as unknown as { name: string; comment: string },
],
historyScope: {},
});
value.value = "cust";
await nextTick();
await vi.waitFor(() => expect(editor.suggestions.value).toHaveLength(6));
expect(editor.suggestions.value).toEqual([
{ value: "customer_plain", kind: "column" },
{ value: "customer_native", kind: "column", comment: "原生注释" },
{ value: "customer_jdbc", kind: "column", comment: "JDBC remarks" },
{ value: "customer_null", kind: "column" },
{ value: "customer_blank", kind: "column" },
{ value: "customer_invalid", kind: "column" },
]);
});
it("ignores stale asynchronous suggestion responses", async () => {
vi.useFakeTimers();
const value = ref("");
@@ -3,9 +3,17 @@ import { forgetDataGridConditionHistory, loadDataGridConditionHistory, rememberD
export type DataGridConditionSuggestionKind = "column" | "history";
export interface DataGridConditionColumnSuggestion {
name: string;
comment?: string | null;
}
export type DataGridConditionColumnOption = string | DataGridConditionColumnSuggestion;
export interface DataGridConditionSuggestion {
value: string;
kind: DataGridConditionSuggestionKind;
comment?: string;
}
export interface DataGridConditionSuggestionContext {
@@ -20,7 +28,7 @@ export type DataGridConditionSuggestionProvider = (context: DataGridConditionSug
export interface UseDataGridConditionEditorOptions {
kind: DataGridConditionHistoryKind;
value: Ref<string>;
columns?: MaybeRefOrGetter<readonly string[] | undefined>;
columns?: MaybeRefOrGetter<readonly DataGridConditionColumnOption[] | undefined>;
historyScope: MaybeRefOrGetter<DataGridConditionHistoryScope>;
suggestionProvider?: DataGridConditionSuggestionProvider;
suggestionDebounceMs?: number;
@@ -30,6 +38,11 @@ export interface UseDataGridConditionEditorOptions {
const WHERE_TOKEN_PATTERN = /([^\s,()><=!&|]+)$/;
const ORDER_BY_TOKEN_PATTERN = /([^\s,()]+)$/;
function normalizedColumnComment(column: DataGridConditionColumnOption): string | undefined {
if (typeof column === "string" || typeof column.comment !== "string") return undefined;
return column.comment.trim() || undefined;
}
function activeToken(kind: DataGridConditionHistoryKind, value: string): string {
return (
value
@@ -73,10 +86,20 @@ export function useDataGridConditionEditor(options: UseDataGridConditionEditorOp
historyOpen.value = false;
}
function defaultSuggestions(token: string): readonly string[] {
function defaultSuggestions(token: string): DataGridConditionSuggestion[] {
const normalizedToken = token.toLowerCase();
if (!normalizedToken) return [];
return (toValue(options.columns) ?? []).filter((column) => column.toLowerCase().startsWith(normalizedToken) && column.toLowerCase() !== normalizedToken);
const seen = new Set<string>();
const suggestions: DataGridConditionSuggestion[] = [];
for (const column of toValue(options.columns) ?? []) {
const value = typeof column === "string" ? column : column.name;
const normalizedValue = value.toLowerCase();
if (!normalizedValue.startsWith(normalizedToken) || normalizedValue === normalizedToken || seen.has(value)) continue;
seen.add(value);
const comment = normalizedColumnComment(column);
suggestions.push({ value, kind: "column", ...(comment ? { comment } : {}) });
}
return suggestions;
}
async function loadSuggestions(value: string, requestId: number, controller: AbortController) {
@@ -84,11 +107,11 @@ export function useDataGridConditionEditor(options: UseDataGridConditionEditorOp
if (!token) return;
suggestionsLoading.value = true;
try {
const values = options.suggestionProvider ? await options.suggestionProvider({ kind: options.kind, value, token, signal: controller.signal }) : defaultSuggestions(token);
const values = options.suggestionProvider ? await options.suggestionProvider({ kind: options.kind, value, token, signal: controller.signal }) : undefined;
// A slower request must never replace suggestions for a newer editor value.
if (controller.signal.aborted || requestId !== suggestionRequestId || options.value.value !== value || historyOpen.value) return;
const limit = options.suggestionLimit ?? 8;
suggestions.value = [...new Set(values)].slice(0, limit).map((suggestion) => ({ value: suggestion, kind: "column" }));
suggestions.value = values ? [...new Set(values)].slice(0, limit).map((suggestion) => ({ value: suggestion, kind: "column" })) : defaultSuggestions(token).slice(0, limit);
highlightedIndex.value = suggestions.value.length > 0 ? 0 : -1;
} catch (error) {
if (!controller.signal.aborted && requestId === suggestionRequestId) {