diff --git a/apps/desktop/src/components/nacos/NacosAdminConsole.vue b/apps/desktop/src/components/nacos/NacosAdminConsole.vue index 9e6c55095a..2d6d0347d8 100644 --- a/apps/desktop/src/components/nacos/NacosAdminConsole.vue +++ b/apps/desktop/src/components/nacos/NacosAdminConsole.vue @@ -16,7 +16,7 @@ import DangerConfirmDialog from "@/components/editor/DangerConfirmDialog.vue"; import EditorSearchPanel from "@/components/editor/EditorSearchPanel.vue"; import NacosConfigDiffDialog from "@/components/nacos/NacosConfigDiffDialog.vue"; import NacosConfigHistoryDialog from "@/components/nacos/NacosConfigHistoryDialog.vue"; -import NacosConfigBatchDialog, { type NacosBatchDialogMode, type NacosConfigTransferTarget } from "@/components/nacos/NacosConfigBatchDialog.vue"; +import NacosConfigBatchDialog, { type NacosBatchDialogMode, type NacosConfigTransferDialogPayload, type NacosConfigTransferTarget } from "@/components/nacos/NacosConfigBatchDialog.vue"; import NacosContentSearchDialog from "@/components/nacos/NacosContentSearchDialog.vue"; import { useToast } from "@/composables/useToast"; import { useNacosConfigListColumnResize, type ToggleableNacosConfigListColumnKey } from "@/composables/useNacosConfigListColumnResize"; @@ -67,7 +67,6 @@ import type { NacosConnectionInfo, NacosContentMatch, NacosContentSearchResult, - NacosConflictPolicy, NacosInstanceInfo, NacosInstancePatch, NacosInstanceRef, @@ -435,6 +434,8 @@ function selectedKeys(): NacosConfigKey[] { }); } +const selectedConfigTransferKeys = computed(() => selectedKeys()); + function isBatchDeleteSnapshotInScope(snapshot: NacosBatchDeleteSnapshot) { return snapshot.connectionId === props.connectionId && snapshot.namespace === namespace.value; } @@ -1245,7 +1246,7 @@ async function exportConfigArchive(scope: NacosConfigSelectionScope) { const batchTransferRequest = shallowRef(null); -async function previewBatch(payload: { scope: NacosConfigSelectionScope; targetConnectionId: string; targetNamespace: string; targetGroup: string; policy: NacosConflictPolicy }) { +async function previewBatch(payload: NacosConfigTransferDialogPayload) { batchLoading.value = true; batchError.value = ""; batchPreview.value = null; @@ -1262,6 +1263,7 @@ async function previewBatch(payload: { scope: NacosConfigSelectionScope; targetC source: buildConfigSelector(payload.scope), targetNamespace: payload.targetNamespace, targetGroup: payload.targetGroup || undefined, + dataIdMappings: payload.dataIdMappings, conflictPolicy: payload.policy, }; batchTransferRequest.value = req; @@ -1274,7 +1276,7 @@ async function previewBatch(payload: { scope: NacosConfigSelectionScope; targetC } } -async function applyBatch(payload: { scope: NacosConfigSelectionScope; targetConnectionId: string; targetNamespace: string; targetGroup: string; policy: NacosConflictPolicy }) { +async function applyBatch(payload: NacosConfigTransferDialogPayload) { if (batchLoading.value || batchReport.value || !batchPreview.value) return; if (payload.policy === "OVERWRITE" && !window.confirm(t("nacos.overwriteConfirm"))) return; const targetConnectionId = batchMode.value === "import" ? props.connectionId : payload.targetConnectionId; @@ -3030,6 +3032,7 @@ onBeforeUnmount(() => { :mode="batchMode" :loading="batchLoading" :selected-count="selectedConfigCount" + :selected-keys="selectedConfigTransferKeys" :filtered-count="configTotal" :target-connections="batchTargetConnections" :target-connection-id="batchTargetConnectionId" diff --git a/apps/desktop/src/components/nacos/NacosConfigBatchDialog.vue b/apps/desktop/src/components/nacos/NacosConfigBatchDialog.vue index 71e78ce0aa..0ae62ce109 100644 --- a/apps/desktop/src/components/nacos/NacosConfigBatchDialog.vue +++ b/apps/desktop/src/components/nacos/NacosConfigBatchDialog.vue @@ -5,7 +5,7 @@ import { useI18n } from "vue-i18n"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"; -import type { NacosBatchPreview, NacosBatchReport, NacosConfigSelectionScope, NacosConflictPolicy, NacosNamespaceInfo } from "@/types/nacos"; +import type { NacosBatchPreview, NacosBatchReport, NacosConfigDataIdMapping, NacosConfigKey, NacosConfigSelectionScope, NacosConflictPolicy, NacosNamespaceInfo } from "@/types/nacos"; import { nacosNamespaceIdentity } from "@/lib/nacos/nacosNamespaceVisibility"; export type NacosBatchDialogMode = "export" | "import" | "copy"; @@ -15,11 +15,21 @@ export interface NacosConfigTransferTarget { label: string; } +export interface NacosConfigTransferDialogPayload { + scope: NacosConfigSelectionScope; + targetConnectionId: string; + targetNamespace: string; + targetGroup: string; + dataIdMappings?: NacosConfigDataIdMapping[]; + policy: NacosConflictPolicy; +} + const props = defineProps<{ open: boolean; mode: NacosBatchDialogMode; loading: boolean; selectedCount: number; + selectedKeys: NacosConfigKey[]; filteredCount: number; targetConnections: NacosConfigTransferTarget[]; targetConnectionId: string; @@ -37,8 +47,8 @@ const emit = defineEmits<{ chooseFile: []; reset: []; targetConnectionChange: [connectionId: string]; - preview: [payload: { scope: NacosConfigSelectionScope; targetConnectionId: string; targetNamespace: string; targetGroup: string; policy: NacosConflictPolicy }]; - apply: [payload: { scope: NacosConfigSelectionScope; targetConnectionId: string; targetNamespace: string; targetGroup: string; policy: NacosConflictPolicy }]; + preview: [payload: NacosConfigTransferDialogPayload]; + apply: [payload: NacosConfigTransferDialogPayload]; export: [scope: NacosConfigSelectionScope]; }>(); @@ -47,6 +57,7 @@ const scope = ref("selected"); const policy = ref("ABORT"); const targetNamespace = ref(""); const targetGroup = ref(""); +const targetDataIdDrafts = ref([]); const titleKey = computed(() => `nacos.batch${props.mode[0].toUpperCase()}${props.mode.slice(1)}Title`); const descriptionKey = computed(() => `nacos.batch${props.mode[0].toUpperCase()}${props.mode.slice(1)}Description`); @@ -111,6 +122,32 @@ function resetTargetNamespace() { targetNamespace.value = targetNamespaces.value[0] ? JSON.stringify(targetNamespaces.value[0].namespace) : ""; } +function resetTargetDataIdDrafts() { + targetDataIdDrafts.value = props.selectedKeys.map((key) => ({ + sourceGroup: key.group, + sourceDataId: key.dataId, + targetDataId: "", + })); +} + +function transferPayload(): NacosConfigTransferDialogPayload { + const dataIdMappings = + scope.value === "selected" + ? targetDataIdDrafts.value.flatMap((mapping) => { + const targetDataId = mapping.targetDataId.trim(); + return targetDataId && targetDataId !== mapping.sourceDataId ? [{ ...mapping, targetDataId }] : []; + }) + : []; + return { + scope: scope.value, + targetConnectionId: props.targetConnectionId, + targetNamespace: selectedTargetNamespace.value, + targetGroup: targetGroup.value.trim(), + ...(dataIdMappings.length ? { dataIdMappings } : {}), + policy: policy.value, + }; +} + watch( () => props.open, (open) => { @@ -118,6 +155,7 @@ watch( scope.value = props.selectedCount ? "selected" : "filtered"; policy.value = "ABORT"; targetGroup.value = ""; + resetTargetDataIdDrafts(); resetTargetNamespace(); }, { immediate: true }, @@ -206,7 +244,29 @@ watch(targetNamespaces, () => {
{{ t("nacos.targetGroup") }}
- + +
+
+
{{ t("nacos.targetDataIds") }}
+

{{ t("nacos.targetDataIdsHint") }}

+
+ +

{{ t("nacos.copyKeepsSource") }}

@@ -285,11 +345,11 @@ watch(targetNamespaces, () => { {{ t("nacos.exportZip") }}