fix(mysql): open editor when creating a new event
This commit is contained in:
1 parent
c24e4352e8
commit
508369ee39
9 files changed
+368
-18
No files matched your search
@@ -155,6 +155,7 @@ import { loadObjectMetadataFacet } from "@/lib/metadata/objectMetadataCache";
|
||||
import { invalidateObjectMetadataCache } from "@/lib/metadata/objectMetadataCache";
|
||||
import { invalidateObjectDdl } from "@/lib/metadata/objectDdlCache";
|
||||
import { invalidateObjectBrowserRowsCache } from "@/lib/table/objectBrowserRowsCache";
|
||||
import { resolveInitialEventEditorRequest } from "@/lib/table/eventEditorRequest";
|
||||
|
||||
type ObjectFilter = ObjectBrowserFilter;
|
||||
type ObjectBrowserColumnKey = "select" | "name" | "type" | "estimatedRows" | "totalBytes" | "created_at" | "updated_at" | "comment";
|
||||
@@ -167,6 +168,8 @@ const props = defineProps<{
|
||||
initialEventName?: string;
|
||||
initialEventReadOnly?: boolean;
|
||||
initialEventOpenRequestId?: number;
|
||||
/** 显式"新建事件"请求号:每次菜单点击递增,用于打开/重新进入 CREATE 编辑器 */
|
||||
initialEventCreateRequestId?: number;
|
||||
initialObjectFilter?: "tables" | "events";
|
||||
viewport?: ObjectBrowserViewport;
|
||||
}>();
|
||||
@@ -2711,18 +2714,33 @@ function applyObjectBrowserRows(nextRows: ObjectBrowserRow[]) {
|
||||
}
|
||||
|
||||
function openInitialEventIfNeeded() {
|
||||
const name = props.initialEventName?.trim();
|
||||
const requestKey = `${props.initialEventOpenRequestId ?? 0}:${name}`;
|
||||
if (!name || openedInitialEvent.value === requestKey || loadingObjects.value) return;
|
||||
const name = props.initialEventName?.trim() ?? "";
|
||||
const decision = resolveInitialEventEditorRequest({
|
||||
eventCreateRequestId: props.initialEventCreateRequestId,
|
||||
eventName: props.initialEventName,
|
||||
eventOpenRequestId: props.initialEventOpenRequestId,
|
||||
openedRequestKey: openedInitialEvent.value,
|
||||
hasEventRow: rows.value.some((candidate) => candidate.type === "EVENT" && candidate.name === name),
|
||||
loadingObjects: loadingObjects.value,
|
||||
});
|
||||
if (decision.type === "ignore") return;
|
||||
openedInitialEvent.value = decision.requestKey;
|
||||
if (decision.type === "create") {
|
||||
// 新建事件:不依赖对象列表中的 EVENT row,直接进入 CREATE 编辑器。
|
||||
// MySqlEventEditor 收到空 name 时会以 CREATE 模式渲染。
|
||||
sidePanelGuard.start();
|
||||
sidePanelRow.value = null;
|
||||
sourceRow.value = null;
|
||||
sidePanelMode.value = "event-editor";
|
||||
return;
|
||||
}
|
||||
const row = rows.value.find((candidate) => candidate.type === "EVENT" && candidate.name === name);
|
||||
if (!row) return;
|
||||
openedInitialEvent.value = requestKey;
|
||||
openEventEditor(row);
|
||||
if (row) openEventEditor(row);
|
||||
}
|
||||
|
||||
function finishObjectBrowserRowsLoad() {
|
||||
loadingObjects.value = false;
|
||||
const preferredFilter = props.initialObjectFilter ?? (props.initialEventName ? "events" : "tables");
|
||||
const preferredFilter = props.initialObjectFilter ?? (props.initialEventName || props.initialEventCreateRequestId !== undefined ? "events" : "tables");
|
||||
if (!userHasSelectedFilter.value && objectCounts.value[preferredFilter] > 0) {
|
||||
// The default table filter is a presentation choice, not a user query
|
||||
// change, so preserve the tab's saved scroll offset across remounts.
|
||||
@@ -2733,8 +2751,8 @@ function finishObjectBrowserRowsLoad() {
|
||||
restoreObjectBrowserViewport();
|
||||
}
|
||||
|
||||
watch([() => props.initialEventName, () => props.initialEventOpenRequestId], ([name, requestId], [previousName, previousRequestId]) => {
|
||||
if (name !== previousName || requestId !== previousRequestId) openedInitialEvent.value = "";
|
||||
watch([() => props.initialEventName, () => props.initialEventOpenRequestId, () => props.initialEventCreateRequestId], ([name, requestId, createRequestId], [previousName, previousRequestId, previousCreateRequestId]) => {
|
||||
if (name !== previousName || requestId !== previousRequestId || createRequestId !== previousCreateRequestId) openedInitialEvent.value = "";
|
||||
openInitialEventIfNeeded();
|
||||
});
|
||||
|
||||
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
const objectBrowserSource = readFileSync(new URL("../ObjectBrowser.vue", import.meta.url), "utf8");
|
||||
const contentAreaSource = readFileSync(new URL("../../layout/ContentArea.vue", import.meta.url), "utf8");
|
||||
|
||||
function functionBody(name: string, source: string): string {
|
||||
const signature = new RegExp(`(?:async\\s+)?function\\s+${name}\\s*\\([^)]*\\)\\s*(?::\\s*[^\\{]+)?\\{`, "m").exec(source);
|
||||
if (!signature) throw new Error(`Missing function ${name}`);
|
||||
const bodyStart = signature.index + signature[0].length;
|
||||
let depth = 1;
|
||||
for (let index = bodyStart; index < source.length; index += 1) {
|
||||
if (source[index] === "{") depth += 1;
|
||||
else if (source[index] === "}") depth -= 1;
|
||||
if (depth === 0) return source.slice(bodyStart, index);
|
||||
}
|
||||
throw new Error(`Unclosed function ${name}`);
|
||||
}
|
||||
|
||||
describe("ObjectBrowser MySQL Event CREATE navigation", () => {
|
||||
it("accepts an explicit create request prop next to the edit props", () => {
|
||||
expect(objectBrowserSource).toContain("initialEventCreateRequestId?: number;");
|
||||
expect(objectBrowserSource).toMatch(/initialEventName\?: string;[\s\S]*?initialEventCreateRequestId\?: number;[\s\S]*?initialObjectFilter\?: "tables" \| "events";/);
|
||||
});
|
||||
|
||||
it("routes the initial event request through the shared resolver", () => {
|
||||
const openInitialEventIfNeeded = functionBody("openInitialEventIfNeeded", objectBrowserSource);
|
||||
expect(openInitialEventIfNeeded).toContain("resolveInitialEventEditorRequest({");
|
||||
expect(openInitialEventIfNeeded).toContain("eventCreateRequestId: props.initialEventCreateRequestId");
|
||||
expect(openInitialEventIfNeeded).toContain("openedRequestKey: openedInitialEvent.value");
|
||||
expect(openInitialEventIfNeeded).toContain("hasEventRow: rows.value.some");
|
||||
expect(openInitialEventIfNeeded).toContain("loadingObjects: loadingObjects.value");
|
||||
});
|
||||
|
||||
it("opens the CREATE editor without any existing EVENT row", () => {
|
||||
const openInitialEventIfNeeded = functionBody("openInitialEventIfNeeded", objectBrowserSource);
|
||||
expect(openInitialEventIfNeeded).toContain('if (decision.type === "create") {');
|
||||
expect(openInitialEventIfNeeded).toContain("sidePanelRow.value = null;");
|
||||
expect(openInitialEventIfNeeded).toContain('sidePanelMode.value = "event-editor";');
|
||||
expect(openInitialEventIfNeeded).toContain("openedInitialEvent.value = decision.requestKey;");
|
||||
// The editor must see an empty name so MySqlEventEditor renders CREATE mode
|
||||
expect(openInitialEventIfNeeded).not.toContain("sidePanelRow.value = row;");
|
||||
});
|
||||
|
||||
it("renders the editor with sidePanelRow name (empty in create mode -> CREATE SQL)", () => {
|
||||
expect(objectBrowserSource).toContain('<MySqlEventEditor :connection="props.connection" :database="props.database" :schema="sidePanelRow?.schema || selectedSchema || props.database" :name="sidePanelRow?.name"');
|
||||
});
|
||||
|
||||
it("re-triggers a create request when the request id changes on a reused tab", () => {
|
||||
expect(objectBrowserSource).toContain("() => props.initialEventCreateRequestId");
|
||||
expect(objectBrowserSource).toMatch(/watch\(\[\(\) => props\.initialEventName, \(\) => props\.initialEventOpenRequestId, \(\) => props\.initialEventCreateRequestId\][\s\S]*?openedInitialEvent\.value = "";[\s\S]*?openInitialEventIfNeeded\(\);/);
|
||||
});
|
||||
|
||||
it("lists the events filter as the preferred view for a create request", () => {
|
||||
expect(objectBrowserSource).toMatch(/const preferredFilter = props\.initialObjectFilter \?\? \(props\.initialEventName \|\| props\.initialEventCreateRequestId !== undefined \? "events" : "tables"\);/);
|
||||
});
|
||||
|
||||
it("ContentArea passes the create request id into ObjectBrowser", () => {
|
||||
expect(contentAreaSource).toContain(':initial-event-name="activeTab.objectBrowser?.eventName"');
|
||||
expect(contentAreaSource).toContain(':initial-event-create-request-id="activeTab.objectBrowser?.eventCreateRequestId"');
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user