feat(mongo): add dropIndex and dropIndexes support across full stack
This commit is contained in:
1 parent
bb72a58e54
commit
93eeeaa9c6
29 files changed
+1142
-24
No files matched your search
@@ -21,6 +21,7 @@ const props = withDefaults(
|
||||
title?: string;
|
||||
message?: string;
|
||||
details?: string;
|
||||
detailsText?: string;
|
||||
confirmLabel?: string;
|
||||
showSuppressToggle?: boolean;
|
||||
suppressToggleLabel?: string;
|
||||
@@ -32,6 +33,7 @@ const props = withDefaults(
|
||||
title: "",
|
||||
message: "",
|
||||
details: "",
|
||||
detailsText: "",
|
||||
confirmLabel: "",
|
||||
showSuppressToggle: false,
|
||||
suppressToggleLabel: "",
|
||||
@@ -73,6 +75,7 @@ function onConfirm() {
|
||||
|
||||
<div class="py-4 min-w-0">
|
||||
<p class="text-sm text-muted-foreground mb-3">{{ message || t("dangerDialog.message") }}</p>
|
||||
<p v-if="detailsText" class="text-xs text-muted-foreground mb-3 whitespace-pre-line">{{ detailsText }}</p>
|
||||
<div v-if="code" class="relative">
|
||||
<Button variant="ghost" size="icon-xs" class="absolute top-1 right-1 z-10 h-6 w-6" :class="wrap ? 'text-foreground bg-accent' : 'text-muted-foreground'" :title="t('dangerDialog.wrapLines')" @click="wrap = !wrap">
|
||||
<TextWrap class="h-3.5 w-3.5" />
|
||||
|
||||
@@ -7194,6 +7194,11 @@ const indexes = ref<IndexInfo[]>([]);
|
||||
const indexesLoaded = ref(false);
|
||||
const indexesLoading = ref(false);
|
||||
const indexesError = ref("");
|
||||
const showDropMongoIndexConfirm = ref(false);
|
||||
const dropMongoIndexLoading = ref(false);
|
||||
const pendingDropMongoIndex = ref<IndexInfo | null>(null);
|
||||
const showDropAllMongoIndexesConfirm = ref(false);
|
||||
const dropAllMongoIndexesLoading = ref(false);
|
||||
const foreignKeys = ref<ForeignKeyInfo[]>([]);
|
||||
const foreignKeysLoaded = ref(false);
|
||||
const foreignKeysLoading = ref(false);
|
||||
@@ -7272,6 +7277,8 @@ function toggleCellDetailPanelLayout() {
|
||||
|
||||
const tableMetadataCapabilities = computed(() => getTableMetadataCapabilities(props.databaseType));
|
||||
const canOpenTableStructureEditor = computed(() => !!props.connectionId && !!props.database && !!props.tableMeta?.tableName && supportsTableStructureEditing(resolvedDatabaseType.value));
|
||||
const mongoConnectionConfig = computed(() => connectionStore.getConfig(props.connectionId ?? ""));
|
||||
const canManageMongoIndexes = computed(() => resolvedDatabaseType.value === "mongodb" && !!props.connectionId && !!props.database && !!props.tableMeta?.tableName && mongoConnectionConfig.value?.db_type === "mongodb" && mongoConnectionConfig.value?.driver_profile !== "mongodb-legacy");
|
||||
const tableInfoTabs = computed(() => {
|
||||
const tabs: TableInfoTabItem[] = [];
|
||||
if (tableMetadataCapabilities.value.columns) {
|
||||
@@ -7359,6 +7366,11 @@ async function fetchIndexes() {
|
||||
}
|
||||
}
|
||||
|
||||
async function reloadIndexes() {
|
||||
indexesLoaded.value = false;
|
||||
await fetchIndexes();
|
||||
}
|
||||
|
||||
async function fetchForeignKeys() {
|
||||
if (!props.connectionId || !props.tableMeta || foreignKeysLoaded.value || foreignKeysLoading.value) return;
|
||||
foreignKeysLoading.value = true;
|
||||
@@ -7604,6 +7616,62 @@ const filteredIndexes = computed(() => {
|
||||
return indexes.value.filter((i) => i.name.toLowerCase().includes(q) || i.columns.some((c) => c.toLowerCase().includes(q)));
|
||||
});
|
||||
|
||||
const droppableMongoIndexes = computed(() => indexes.value.filter((index) => !index.is_primary));
|
||||
const dropMongoIndexConfirmMessage = computed(() =>
|
||||
pendingDropMongoIndex.value
|
||||
? t("contextMenu.confirmDropMongoIndexMessage", {
|
||||
name: pendingDropMongoIndex.value.name,
|
||||
collection: props.tableMeta?.tableName || "",
|
||||
})
|
||||
: "",
|
||||
);
|
||||
const dropAllMongoIndexesConfirmMessage = computed(() => t("contextMenu.confirmDropMongoAllIndexesMessage", { name: props.tableMeta?.tableName || "" }));
|
||||
const dropAllMongoIndexesConfirmDetails = computed(() => t("contextMenu.confirmDropMongoAllIndexesDetails"));
|
||||
const dropMongoIndexPreview = computed(() => (pendingDropMongoIndex.value ? `db.getCollection(${JSON.stringify(props.tableMeta?.tableName || "")}).dropIndex(${JSON.stringify(pendingDropMongoIndex.value.name)})` : ""));
|
||||
const dropAllMongoIndexesPreview = computed(() => `db.getCollection(${JSON.stringify(props.tableMeta?.tableName || "")}).dropIndexes()`);
|
||||
|
||||
function requestDropMongoIndex(index: IndexInfo) {
|
||||
pendingDropMongoIndex.value = index;
|
||||
showDropMongoIndexConfirm.value = true;
|
||||
}
|
||||
|
||||
function requestDropAllMongoIndexes() {
|
||||
showDropAllMongoIndexesConfirm.value = true;
|
||||
}
|
||||
|
||||
async function confirmDropMongoIndex() {
|
||||
if (!props.connectionId || !props.database || !props.tableMeta?.tableName || !pendingDropMongoIndex.value || dropMongoIndexLoading.value) return;
|
||||
dropMongoIndexLoading.value = true;
|
||||
try {
|
||||
await connectionStore.ensureConnected(props.connectionId);
|
||||
await api.mongoDropIndexes(props.connectionId, props.database, props.tableMeta.tableName, JSON.stringify(pendingDropMongoIndex.value.name), true);
|
||||
toast(t("contextMenu.dropTableChildObjectSuccess", { name: pendingDropMongoIndex.value.name }), 3000);
|
||||
showDropMongoIndexConfirm.value = false;
|
||||
pendingDropMongoIndex.value = null;
|
||||
await reloadIndexes();
|
||||
} catch (e: any) {
|
||||
toast(t("contextMenu.tableOperationFailed", { message: e?.message || String(e) }), 5000);
|
||||
} finally {
|
||||
dropMongoIndexLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function confirmDropAllMongoIndexes() {
|
||||
if (!props.connectionId || !props.database || !props.tableMeta?.tableName || dropAllMongoIndexesLoading.value) return;
|
||||
dropAllMongoIndexesLoading.value = true;
|
||||
try {
|
||||
await connectionStore.ensureConnected(props.connectionId);
|
||||
const result = await api.mongoDropIndexes(props.connectionId, props.database, props.tableMeta.tableName, undefined, false);
|
||||
toast(t("contextMenu.dropAllIndexesSuccess", { count: result.dropped_names.length, name: props.tableMeta.tableName }), 3000);
|
||||
showDropAllMongoIndexesConfirm.value = false;
|
||||
await reloadIndexes();
|
||||
} catch (e: any) {
|
||||
toast(t("contextMenu.tableOperationFailed", { message: e?.message || String(e) }), 5000);
|
||||
} finally {
|
||||
dropAllMongoIndexesLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
const filteredForeignKeys = computed(() => {
|
||||
if (!searchQuery.value) return foreignKeys.value;
|
||||
const q = searchQuery.value.toLowerCase();
|
||||
@@ -9326,6 +9394,12 @@ const gridContextMenuItems = computed<ContextMenuItem[]>(() => {
|
||||
<WrapText class="w-3 h-3" />
|
||||
</Button>
|
||||
</div>
|
||||
<div v-else-if="activeTableInfoTab === 'indexes' && canManageMongoIndexes" class="table-info-actions flex min-w-0 shrink-0 items-center gap-1">
|
||||
<Button variant="ghost" size="sm" class="table-info-action-button h-6 px-2 text-xs text-destructive hover:text-destructive" :disabled="indexesLoading || dropAllMongoIndexesLoading || droppableMongoIndexes.length === 0" @click="requestDropAllMongoIndexes">
|
||||
<Trash2 class="w-3 h-3" />
|
||||
<span class="table-info-action-label">{{ t("contextMenu.dropAllIndexes") }}</span>
|
||||
</Button>
|
||||
</div>
|
||||
<Button v-if="canOpenTableStructureEditor" variant="ghost" size="sm" class="table-info-action-button h-6 px-2 text-xs" :title="t('contextMenu.editStructure')" :aria-label="t('contextMenu.editStructure')" @click="openTableStructureEditor">
|
||||
<PencilRuler class="w-3 h-3" />
|
||||
<span class="table-info-action-label">{{ t("contextMenu.editStructure") }}</span>
|
||||
@@ -9413,14 +9487,22 @@ const gridContextMenuItems = computed<ContextMenuItem[]>(() => {
|
||||
</div>
|
||||
<div v-else class="divide-y">
|
||||
<div v-for="index in filteredIndexes" :key="index.name" class="p-3 text-xs">
|
||||
<div class="font-medium truncate">{{ index.name }}</div>
|
||||
<div class="mt-1 flex flex-wrap gap-1">
|
||||
<span v-if="index.is_primary" class="rounded bg-amber-500/10 px-1.5 py-0.5 text-amber-600">PK</span>
|
||||
<span v-if="index.is_unique" class="rounded bg-emerald-500/10 px-1.5 py-0.5 text-emerald-600">UNIQUE</span>
|
||||
<span v-if="index.index_type" class="rounded bg-muted px-1.5 py-0.5 text-muted-foreground">{{ index.index_type }}</span>
|
||||
</div>
|
||||
<div class="mt-2 font-mono text-[11px] text-muted-foreground break-all">
|
||||
{{ index.columns.join(", ") }}
|
||||
<div class="flex items-start gap-2">
|
||||
<div class="min-w-0 flex-1">
|
||||
<div class="font-medium truncate">{{ index.name }}</div>
|
||||
<div class="mt-1 flex flex-wrap gap-1">
|
||||
<span v-if="index.is_primary" class="rounded bg-amber-500/10 px-1.5 py-0.5 text-amber-600">PK</span>
|
||||
<span v-if="index.is_unique" class="rounded bg-emerald-500/10 px-1.5 py-0.5 text-emerald-600">UNIQUE</span>
|
||||
<span v-if="index.index_type" class="rounded bg-muted px-1.5 py-0.5 text-muted-foreground">{{ index.index_type }}</span>
|
||||
</div>
|
||||
<div class="mt-2 font-mono text-[11px] text-muted-foreground break-all">
|
||||
{{ index.columns.join(", ") }}
|
||||
</div>
|
||||
</div>
|
||||
<Button v-if="canManageMongoIndexes && !index.is_primary" variant="ghost" size="sm" class="h-7 shrink-0 px-2 text-[11px] text-destructive hover:text-destructive" @click="requestDropMongoIndex(index)">
|
||||
<Trash2 class="mr-1 h-3 w-3" />
|
||||
{{ t("contextMenu.dropIndex") }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -10143,6 +10225,27 @@ const gridContextMenuItems = computed<ContextMenuItem[]>(() => {
|
||||
:confirm-label="pendingDeleteRowIds.length > 1 ? t('grid.deleteRows', { count: pendingDeleteRowIds.length }) : t('grid.deleteRow')"
|
||||
@confirm="confirmDeleteRow"
|
||||
/>
|
||||
<DangerConfirmDialog
|
||||
v-model:open="showDropMongoIndexConfirm"
|
||||
:title="t('contextMenu.confirmDropIndexTitle')"
|
||||
:message="dropMongoIndexConfirmMessage"
|
||||
:details="dropMongoIndexPreview"
|
||||
:confirm-label="t('contextMenu.dropIndex')"
|
||||
:loading="dropMongoIndexLoading"
|
||||
:close-on-confirm="false"
|
||||
@confirm="confirmDropMongoIndex"
|
||||
/>
|
||||
<DangerConfirmDialog
|
||||
v-model:open="showDropAllMongoIndexesConfirm"
|
||||
:title="t('contextMenu.dropAllIndexes')"
|
||||
:message="dropAllMongoIndexesConfirmMessage"
|
||||
:details-text="dropAllMongoIndexesConfirmDetails"
|
||||
:sql="dropAllMongoIndexesPreview"
|
||||
:confirm-label="t('contextMenu.dropAllIndexes')"
|
||||
:loading="dropAllMongoIndexesLoading"
|
||||
:close-on-confirm="false"
|
||||
@confirm="confirmDropAllMongoIndexes"
|
||||
/>
|
||||
<ImagePreviewDialog v-model:open="imagePreviewOpen" :src="imagePreviewSrc" :title="imagePreviewTitle" />
|
||||
<component v-if="previewDialogOpen && previewDialogConfig" :is="previewDialogConfig.component" v-model:open="previewDialogOpen" v-bind="previewDialogConfig.props" />
|
||||
<ExportProgressDialog v-model:open="exportProgressDialog" v-bind="exportProgressState" :disable-cancel="!exportCancelHandler" @cancel="cancelActiveExport" />
|
||||
|
||||
@@ -1528,6 +1528,10 @@ const showDropDatabaseConfirm = ref(false);
|
||||
const dropDatabaseLoading = ref(false);
|
||||
const showDropMongoCollectionConfirm = ref(false);
|
||||
const dropMongoCollectionLoading = ref(false);
|
||||
const showDropMongoIndexConfirm = ref(false);
|
||||
const dropMongoIndexLoading = ref(false);
|
||||
const showDropAllMongoIndexesConfirm = ref(false);
|
||||
const dropAllMongoIndexesLoading = ref(false);
|
||||
const showFlushRedisDbConfirm = ref(false);
|
||||
const showCreateSchemaDialog = ref(false);
|
||||
const createSchemaName = ref("");
|
||||
@@ -1756,6 +1760,7 @@ function canDropTreeNode(node: TreeNode): boolean {
|
||||
if (node.type === "view" || node.type === "materialized_view" || node.type === "procedure" || node.type === "function") {
|
||||
return !!node.connectionId && !!node.database && !!dropObjectSqlOptionsForNode(node);
|
||||
}
|
||||
if (canDropMongoIndexNode(node)) return true;
|
||||
return canDropTableChildObjectNode(node);
|
||||
}
|
||||
|
||||
@@ -1770,16 +1775,41 @@ function selectedBatchDropTargets(): TreeNode[] {
|
||||
return selected;
|
||||
}
|
||||
|
||||
function selectedBatchMongoIndexTargets(): TreeNode[] {
|
||||
const targets = selectedBatchDropTargets();
|
||||
return targets.length > 1 && targets.every((node) => canDropMongoIndexNode(node)) ? targets : [];
|
||||
}
|
||||
|
||||
function selectedBatchIndexTableName(targets: TreeNode[]): string | null {
|
||||
const first = targets[0];
|
||||
if (!first) return null;
|
||||
const table = first.tableName || first.label;
|
||||
return table && targets.every((node) => (node.tableName || node.label) === table) ? table : null;
|
||||
}
|
||||
|
||||
function batchDropMenuLabel(): string {
|
||||
return t("contextMenu.batchDrop", { count: selectedBatchDropTargets().length });
|
||||
const targets = selectedBatchDropTargets();
|
||||
if (targets.length > 1 && targets.every((node) => node.type === "index")) {
|
||||
return t("contextMenu.batchDropIndexes", { count: targets.length });
|
||||
}
|
||||
return t("contextMenu.batchDrop", { count: targets.length });
|
||||
}
|
||||
|
||||
function batchDropConfirmTitle(): string {
|
||||
return t("contextMenu.confirmBatchDropTitle", { count: selectedBatchDropTargets().length });
|
||||
const targets = selectedBatchDropTargets();
|
||||
if (targets.length > 1 && targets.every((node) => node.type === "index")) {
|
||||
return t("contextMenu.confirmDropIndexTitle");
|
||||
}
|
||||
return t("contextMenu.confirmBatchDropTitle", { count: targets.length });
|
||||
}
|
||||
|
||||
function batchDropConfirmMessage(): string {
|
||||
return t("contextMenu.confirmBatchDropMessage", { count: selectedBatchDropTargets().length });
|
||||
const targets = selectedBatchDropTargets();
|
||||
const table = selectedBatchIndexTableName(targets);
|
||||
if (targets.length > 1 && targets.every((node) => node.type === "index") && table) {
|
||||
return t("contextMenu.confirmDropBatchIndexesMessage", { count: targets.length, table });
|
||||
}
|
||||
return t("contextMenu.confirmBatchDropMessage", { count: targets.length });
|
||||
}
|
||||
|
||||
async function dropSqlForTreeNode(node: TreeNode): Promise<string | null> {
|
||||
@@ -1792,6 +1822,9 @@ async function dropSqlForTreeNode(node: TreeNode): Promise<string | null> {
|
||||
}
|
||||
const objectOptions = dropObjectSqlOptionsForNode(node);
|
||||
if (objectOptions) return buildDropObjectSql(objectOptions);
|
||||
if (canDropMongoIndexNode(node)) {
|
||||
return `db.getCollection("${(node.tableName || "").replace(/\\/g, "\\\\").replace(/"/g, '\\"')}").dropIndex(${JSON.stringify(mongoIndexNameForNode(node))})`;
|
||||
}
|
||||
const childOptions = dropTableChildObjectSqlOptionsForNode(node);
|
||||
if (childOptions && canDropTableChildObjectNode(node)) return buildDropTableChildObjectSql(childOptions);
|
||||
return null;
|
||||
@@ -1799,6 +1832,11 @@ async function dropSqlForTreeNode(node: TreeNode): Promise<string | null> {
|
||||
|
||||
async function refreshBatchDropPreviewSql() {
|
||||
const targets = selectedBatchDropTargets();
|
||||
const mongoIndexTargets = selectedBatchMongoIndexTargets();
|
||||
if (mongoIndexTargets.length) {
|
||||
batchDropPreviewSql.value = mongoIndexTargets.map((target) => mongoIndexDropPreview(target, mongoIndexNameForNode(target))).join("\n");
|
||||
return;
|
||||
}
|
||||
const statements: string[] = [];
|
||||
for (const target of targets) {
|
||||
const sql = await dropSqlForTreeNode(target);
|
||||
@@ -1832,6 +1870,10 @@ function requestDropSelectedNode(): boolean {
|
||||
requestDropObject();
|
||||
return true;
|
||||
}
|
||||
if (canDropMongoIndex.value) {
|
||||
dropMongoIndex();
|
||||
return true;
|
||||
}
|
||||
if (canDropTableChildObject.value) {
|
||||
requestDropTableChildObject();
|
||||
return true;
|
||||
@@ -1974,6 +2016,32 @@ async function confirmBatchDrop() {
|
||||
const targets = selectedBatchDropTargets();
|
||||
if (!targets.length) return;
|
||||
try {
|
||||
const mongoIndexTargets = selectedBatchMongoIndexTargets();
|
||||
if (mongoIndexTargets.length) {
|
||||
const grouped = new Map<string, TreeNode[]>();
|
||||
for (const target of mongoIndexTargets) {
|
||||
const key = `${target.connectionId}:${target.database}:${target.tableName || ""}`;
|
||||
const list = grouped.get(key) ?? [];
|
||||
list.push(target);
|
||||
grouped.set(key, list);
|
||||
}
|
||||
let droppedCount = 0;
|
||||
for (const groupTargets of grouped.values()) {
|
||||
const first = groupTargets[0];
|
||||
if (!first?.connectionId || !first.database || !first.tableName) continue;
|
||||
await connectionStore.ensureConnected(first.connectionId);
|
||||
const names = groupTargets.map((target) => mongoIndexNameForNode(target));
|
||||
const result = await api.mongoDropIndexes(first.connectionId, first.database, first.tableName, JSON.stringify(names.length === 1 ? names[0] : names), false);
|
||||
const dropped = new Set(result.dropped_names);
|
||||
droppedCount += result.dropped_names.length;
|
||||
for (const target of groupTargets) {
|
||||
if (dropped.has(mongoIndexNameForNode(target))) connectionStore.removeTreeNode(target.id);
|
||||
}
|
||||
}
|
||||
toast(t("contextMenu.batchDropSuccess", { count: droppedCount }), 3000);
|
||||
showBatchDropConfirm.value = false;
|
||||
return;
|
||||
}
|
||||
for (const target of targets) {
|
||||
if (!target.connectionId || !target.database) continue;
|
||||
await connectionStore.ensureConnected(target.connectionId);
|
||||
@@ -2041,6 +2109,32 @@ const canDropMongoCollection = computed(() => {
|
||||
return props.node.type === "mongo-collection" && !!props.node.database && config?.driver_profile !== "mongodb-legacy";
|
||||
});
|
||||
|
||||
function mongoIndexNameForNode(node: TreeNode): string {
|
||||
if (node.type !== "index") return "";
|
||||
return node.meta && "name" in node.meta ? node.meta.name : node.label.replace(/\s+\(.+\)$/, "");
|
||||
}
|
||||
|
||||
function canDropMongoIndexNode(node: TreeNode): boolean {
|
||||
if (node.type !== "index" || !node.connectionId || !node.database || !node.tableName) return false;
|
||||
const config = connectionStore.getConfig(node.connectionId);
|
||||
return config?.db_type === "mongodb" && config.driver_profile !== "mongodb-legacy" && mongoIndexNameForNode(node) !== "_id_";
|
||||
}
|
||||
|
||||
const canDropMongoIndex = computed(() => canDropMongoIndexNode(props.node));
|
||||
|
||||
function mongoIndexDropPreview(node: Pick<TreeNode, "tableName">, indexName: string): string {
|
||||
return `db.getCollection(${JSON.stringify(node.tableName || "")}).dropIndex(${JSON.stringify(indexName)})`;
|
||||
}
|
||||
|
||||
const canDropAllMongoIndexes = computed(() => {
|
||||
const config = props.node.connectionId ? connectionStore.getConfig(props.node.connectionId) : undefined;
|
||||
return props.node.type === "mongo-collection" && !!props.node.database && config?.db_type === "mongodb" && config.driver_profile !== "mongodb-legacy";
|
||||
});
|
||||
|
||||
function mongoDropAllIndexesPreview(node: Pick<TreeNode, "label">): string {
|
||||
return `db.getCollection(${JSON.stringify(node.label)}).dropIndexes()`;
|
||||
}
|
||||
|
||||
const canCreateSchema = computed(() => {
|
||||
const config = props.node.connectionId ? connectionStore.getConfig(props.node.connectionId) : undefined;
|
||||
return props.node.type === "database" && usesTreeSchemaMode(effectiveDatabaseTypeForConnection(config)) && !connectionUsesDatabaseObjectTreeMode(config);
|
||||
@@ -2417,6 +2511,16 @@ function dropMongoCollection() {
|
||||
showDropMongoCollectionConfirm.value = true;
|
||||
}
|
||||
|
||||
function dropMongoIndex() {
|
||||
dropMongoIndexLoading.value = false;
|
||||
showDropMongoIndexConfirm.value = true;
|
||||
}
|
||||
|
||||
function dropAllMongoIndexes() {
|
||||
dropAllMongoIndexesLoading.value = false;
|
||||
showDropAllMongoIndexesConfirm.value = true;
|
||||
}
|
||||
|
||||
function flushRedisDb() {
|
||||
showFlushRedisDbConfirm.value = true;
|
||||
}
|
||||
@@ -2486,6 +2590,53 @@ async function confirmDropMongoCollection() {
|
||||
}
|
||||
}
|
||||
|
||||
function mongoIndexesGroupNodeId(node: Pick<TreeNode, "connectionId" | "database" | "schema" | "tableName" | "label">): string | null {
|
||||
if (!node.connectionId || !node.database) return null;
|
||||
const tableName = node.tableName || node.label;
|
||||
return node.schema ? `${node.connectionId}:${node.database}:${node.schema}:${tableName}:__indexes` : `${node.connectionId}:${node.database}:${tableName}:__indexes`;
|
||||
}
|
||||
|
||||
async function refreshMongoIndexTree(node: Pick<TreeNode, "connectionId" | "database" | "schema" | "tableName" | "label">) {
|
||||
const nodeId = mongoIndexesGroupNodeId(node);
|
||||
if (!node.connectionId || !node.database || !nodeId) return;
|
||||
await connectionStore.loadIndexes(node.connectionId, node.database, node.tableName || node.label, node.schema, nodeId);
|
||||
}
|
||||
|
||||
async function confirmDropMongoIndex() {
|
||||
const node = props.node;
|
||||
if (!canDropMongoIndexNode(node) || !node.connectionId || !node.database || !node.tableName || dropMongoIndexLoading.value) return;
|
||||
dropMongoIndexLoading.value = true;
|
||||
try {
|
||||
await connectionStore.ensureConnected(node.connectionId);
|
||||
const indexName = mongoIndexNameForNode(node);
|
||||
await api.mongoDropIndexes(node.connectionId, node.database, node.tableName, JSON.stringify(indexName), true);
|
||||
toast(t("contextMenu.dropTableChildObjectSuccess", { name: indexName }), 3000);
|
||||
showDropMongoIndexConfirm.value = false;
|
||||
await refreshMongoIndexTree(node);
|
||||
} catch (e: any) {
|
||||
toast(t("contextMenu.tableOperationFailed", { message: e?.message || String(e) }), 5000);
|
||||
} finally {
|
||||
dropMongoIndexLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function confirmDropAllMongoIndexes() {
|
||||
const node = props.node;
|
||||
if (node.type !== "mongo-collection" || !node.connectionId || !node.database || dropAllMongoIndexesLoading.value) return;
|
||||
dropAllMongoIndexesLoading.value = true;
|
||||
try {
|
||||
await connectionStore.ensureConnected(node.connectionId);
|
||||
const result = await api.mongoDropIndexes(node.connectionId, node.database, node.label, undefined, false);
|
||||
toast(t("contextMenu.dropAllIndexesSuccess", { count: result.dropped_names.length, name: node.label }), 3000);
|
||||
showDropAllMongoIndexesConfirm.value = false;
|
||||
await refreshMongoIndexTree(node);
|
||||
} catch (e: any) {
|
||||
toast(t("contextMenu.tableOperationFailed", { message: e?.message || String(e) }), 5000);
|
||||
} finally {
|
||||
dropAllMongoIndexesLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function openCreateSchemaDialog() {
|
||||
createSchemaName.value = "";
|
||||
showCreateSchemaDialog.value = true;
|
||||
@@ -3958,9 +4109,14 @@ function treeItemMenuItems(): ContextMenuItem[] {
|
||||
items.push({ label: "", separator: true });
|
||||
items.push({ label: t("contextMenu.viewData"), action: toggle, icon: TableProperties });
|
||||
items.push({ label: t("contextMenu.newQuery"), action: newQuery, icon: TerminalSquare });
|
||||
if (canDropMongoCollection.value) {
|
||||
if (canDropAllMongoIndexes.value || canDropMongoCollection.value) {
|
||||
items.push({ label: "", separator: true });
|
||||
items.push({ label: t("contextMenu.dropCollection"), action: dropMongoCollection, icon: Trash2, shortcut: shortcutDelete, variant: "destructive" as const });
|
||||
if (canDropAllMongoIndexes.value) {
|
||||
items.push({ label: t("contextMenu.dropAllIndexes"), action: dropAllMongoIndexes, icon: Trash2, variant: "destructive" as const });
|
||||
}
|
||||
if (canDropMongoCollection.value) {
|
||||
items.push({ label: t("contextMenu.dropCollection"), action: dropMongoCollection, icon: Trash2, shortcut: shortcutDelete, variant: "destructive" as const });
|
||||
}
|
||||
}
|
||||
return items;
|
||||
}
|
||||
@@ -4108,7 +4264,16 @@ function treeItemMenuItems(): ContextMenuItem[] {
|
||||
|
||||
if (node.type === "index" || node.type === "fkey" || node.type === "trigger") {
|
||||
items.push({ label: t("contextMenu.copyName"), action: copyName, icon: Copy, shortcut: shortcutCopyName.value });
|
||||
if (canDropTableChildObject.value) {
|
||||
if (node.type === "index" && canDropMongoIndex.value) {
|
||||
items.push({ label: "", separator: true });
|
||||
items.push({
|
||||
label: deleteMenuLabel(t("contextMenu.dropIndex")),
|
||||
action: deleteMenuAction(dropMongoIndex),
|
||||
icon: Trash2,
|
||||
shortcut: shortcutDelete,
|
||||
variant: "destructive" as const,
|
||||
});
|
||||
} else if (canDropTableChildObject.value) {
|
||||
items.push({ label: "", separator: true });
|
||||
items.push({
|
||||
label: deleteMenuLabel(dropTableChildObjectMenuLabel()),
|
||||
@@ -4625,6 +4790,29 @@ function treeItemMenuItems(): ContextMenuItem[] {
|
||||
@confirm="confirmDropMongoCollection"
|
||||
/>
|
||||
|
||||
<DangerConfirmDialog
|
||||
v-model:open="showDropMongoIndexConfirm"
|
||||
:title="t('contextMenu.confirmDropIndexTitle')"
|
||||
:message="t('contextMenu.confirmDropMongoIndexMessage', { name: mongoIndexNameForNode(node), collection: node.tableName || '' })"
|
||||
:details="mongoIndexDropPreview(node, mongoIndexNameForNode(node))"
|
||||
:confirm-label="t('contextMenu.dropIndex')"
|
||||
:loading="dropMongoIndexLoading"
|
||||
:close-on-confirm="false"
|
||||
@confirm="confirmDropMongoIndex"
|
||||
/>
|
||||
|
||||
<DangerConfirmDialog
|
||||
v-model:open="showDropAllMongoIndexesConfirm"
|
||||
:title="t('contextMenu.dropAllIndexes')"
|
||||
:message="t('contextMenu.confirmDropMongoAllIndexesMessage', { name: node.label })"
|
||||
:details-text="t('contextMenu.confirmDropMongoAllIndexesDetails')"
|
||||
:sql="mongoDropAllIndexesPreview(node)"
|
||||
:confirm-label="t('contextMenu.dropAllIndexes')"
|
||||
:loading="dropAllMongoIndexesLoading"
|
||||
:close-on-confirm="false"
|
||||
@confirm="confirmDropAllMongoIndexes"
|
||||
/>
|
||||
|
||||
<DangerConfirmDialog v-model:open="showFlushRedisDbConfirm" :title="t('redis.flushDb')" :message="t('redis.flushDbMessage')" :details="t('redis.flushDbDetails', { db: node.database })" :confirm-label="t('redis.flushDbConfirm')" @confirm="confirmFlushRedisDb" />
|
||||
|
||||
<Dialog v-model:open="showCreateSchemaDialog">
|
||||
|
||||
Reference in new issue
Block a user