feat(mysql): manage and edit MySQL events

This commit is contained in:
miracle authored and GitHub committed 2026-08-23 12:23:46 +08:00
1 parent 701f188e45
commit d8bc5153d2
35 files changed
+981 -27

No files matched your search

@@ -28,6 +28,7 @@ import {
Pin,
ArrowRightLeft,
Download,
Eye,
Upload,
FileCode,
Network,
@@ -966,6 +967,10 @@ function runRowClickAction(clickDetail: number, requestId: number) {
openMongoTreeData(node);
return;
}
if (node.type === "event") {
void openObjectBrowser();
return;
}
const action = treeNodeRowAction(node.type, canExpand.value, settingsStore.editorSettings.sidebarActivation, currentDatabaseType(), settingsStore.editorSettings.sidebarOpenDatabaseOnSingleClick, canOpenObjectBrowser.value);
// WebKit can keep incrementing click.detail while the pointer moves quickly
// between adjacent rows. Nacos entries are idempotent navigation targets, so
@@ -1323,6 +1328,14 @@ function requestDeleteSelectedNode(): boolean {
function onDoubleClick(event: MouseEvent) {
if (dataTabOpenModeFromTreeClick(activeNode.value.type, event, settingsStore.editorSettings.shortcuts.openDataInNewTab) === "new-tab") return;
if (activeNode.value.type === "event") {
void openObjectBrowser();
return;
}
if (activeNode.value.type === "group-events") {
void openObjectBrowser();
return;
}
const action = treeNodeRowDoubleClickAction(activeNode.value.type, canOpenObjectBrowser.value, settingsStore.editorSettings.sidebarActivation, canExpand.value, currentDatabaseType(), canOpenConnectionDatabaseBrowser.value, settingsStore.editorSettings.sidebarOpenDatabaseOnSingleClick);
if (action === "open-database-browser") {
void openDatabaseBrowser();
@@ -1471,7 +1484,7 @@ async function confirmDeleteSavedSqlFile() {
releaseActiveNodeReference([node.id]);
}
async function openObjectBrowser() {
async function openObjectBrowser(eventReadOnly = false, openEventEditor = false) {
const node = activeNode.value;
if (!node.connectionId) return;
try {
@@ -1479,7 +1492,7 @@ async function openObjectBrowser() {
connectionStore.activeConnectionId = node.connectionId;
if (hasTreeNodeDatabaseContext(node)) {
queryStore.openObjectBrowser(node.connectionId, node.database, node.schema, node.catalog);
queryStore.openObjectBrowser(node.connectionId, node.database, node.schema, node.catalog, openEventEditor && node.type === "event" ? node.objectName || node.label : undefined, eventReadOnly, node.type === "event" || node.type === "group-events" ? "events" : undefined);
return;
}
@@ -1488,7 +1501,7 @@ async function openObjectBrowser() {
const options = await getDatabaseOptions(node.connectionId);
const database = resolveDefaultDatabase(connection, options);
if (database) {
queryStore.openObjectBrowser(node.connectionId, database);
queryStore.openObjectBrowser(node.connectionId, database, undefined, undefined, openEventEditor && node.type === "event" ? node.objectName || node.label : undefined, eventReadOnly, node.type === "event" || node.type === "group-events" ? "events" : undefined);
} else {
await toggle();
}
@@ -2018,10 +2031,10 @@ function dropObjectSqlOptions(): DropObjectSqlOptions | null {
}
function dropObjectSqlOptionsForNode(node: TreeNode): DropObjectSqlOptions | null {
if (node.type !== "view" && node.type !== "materialized_view" && node.type !== "procedure" && node.type !== "function") return null;
if (node.type !== "view" && node.type !== "materialized_view" && node.type !== "procedure" && node.type !== "function" && node.type !== "event") return null;
return {
databaseType: tableStructureDatabaseTypeForNode(node),
objectType: node.type === "view" ? "VIEW" : node.type === "materialized_view" ? "MATERIALIZED_VIEW" : node.type === "procedure" ? "PROCEDURE" : "FUNCTION",
objectType: node.type === "view" ? "VIEW" : node.type === "materialized_view" ? "MATERIALIZED_VIEW" : node.type === "procedure" ? "PROCEDURE" : node.type === "function" ? "FUNCTION" : "EVENT",
schema: node.schema,
name: node.objectName || node.label,
signature: node.signature,
@@ -2082,6 +2095,7 @@ function dropObjectMenuLabel(): string {
if (activeNode.value.type === "materialized_view") return t("contextMenu.dropView");
if (activeNode.value.type === "procedure") return t("contextMenu.dropProcedure");
if (activeNode.value.type === "function") return t("contextMenu.dropFunction");
if (activeNode.value.type === "event") return t("contextMenu.dropObject");
return t("contextMenu.dropObject");
}
@@ -2243,7 +2257,7 @@ function requestDropTableChildObject() {
function canDropTreeNode(node: TreeNode): boolean {
if (isSqlServerLinkedNode(node)) return false;
if (node.type === "table") return !!node.connectionId && !!node.database;
if (node.type === "view" || node.type === "materialized_view" || node.type === "procedure" || node.type === "function") {
if (node.type === "view" || node.type === "materialized_view" || node.type === "procedure" || node.type === "function" || node.type === "event") {
return !!node.connectionId && !!node.database && !!dropObjectSqlOptionsForNode(node);
}
if (canDropMongoIndexNode(node)) return true;
@@ -2756,7 +2770,8 @@ async function confirmDropObject() {
await connectionStore.ensureConnected(node.connectionId);
const sql = dropObjectPreviewSql.value || (await buildDropObjectSql(options));
await executeTreeNodeSqlWithProductionGuard(node, sql, { database: node.database, schema: node.schema });
const msgKey = node.type === "view" ? "contextMenu.dropViewSuccess" : node.type === "materialized_view" ? "contextMenu.dropViewSuccess" : node.type === "procedure" ? "contextMenu.dropProcedureSuccess" : "contextMenu.dropFunctionSuccess";
const msgKey =
node.type === "view" ? "contextMenu.dropViewSuccess" : node.type === "materialized_view" ? "contextMenu.dropViewSuccess" : node.type === "procedure" ? "contextMenu.dropProcedureSuccess" : node.type === "function" ? "contextMenu.dropFunctionSuccess" : "contextMenu.dropEventSuccess";
toast(t(msgKey, { name: node.label }), 3000);
closeDroppedTableObjectTabsForNode(node);
// Refresh the parent object list so group badges and children stay in sync.
@@ -5549,7 +5564,18 @@ function buildObjectSidebarMenu(context: SidebarMenuFactoryContext): boolean {
return true;
}
if (node.type === "sequence" || node.type === "event") {
if (node.type === "event") {
// Event definitions use the dedicated editor hosted by ObjectBrowser.
// Open that surface from the tree instead of routing through the generic source dialog.
items.push({ label: t("contextMenu.editObject"), action: () => openObjectBrowser(false, true), icon: Pencil });
items.push({ label: t("contextMenu.viewObject"), action: () => openObjectBrowser(true, true), icon: Eye });
items.push({ label: t("contextMenu.copyName"), action: copyName, icon: Copy, shortcut: shortcutCopyName.value });
items.push({ label: t("contextMenu.dropObject"), action: deleteMenuAction(requestDropObject), icon: Trash2, shortcut: shortcutDelete, variant: "destructive" as const });
items.push({ label: t("contextMenu.refreshChildren"), action: refresh, icon: RefreshCw, shortcut: shortcutRefresh });
return true;
}
if (node.type === "sequence") {
items.push({ label: t("contextMenu.viewSource"), action: () => openObjectSourceDialog(false), icon: Code2 });
items.push({ label: t("contextMenu.copyName"), action: copyName, icon: Copy, shortcut: shortcutCopyName.value });
items.push({ label: t("contextMenu.changeOpenMode"), action: () => emit("open-settings", "navigation"), icon: Settings2 });
@@ -5632,6 +5658,9 @@ function buildObjectGroupSidebarMenu(context: SidebarMenuFactoryContext): boolea
if (node.type === "group-views" && node.connectionId && node.database) {
items.push({ label: t("contextMenu.createView"), action: createView, icon: Plus });
}
if (node.type === "group-events" && node.connectionId && node.database) {
items.push({ label: t("contextMenu.createEvent"), action: openObjectBrowser, icon: Plus });
}
if (mysqlObjectTemplate) {
items.push({ label: t(mysqlObjectTemplate.titleKey), action: createMysqlObjectTemplate, icon: Plus });
}