feat(nacos): show conflict config diff in copy preview

This commit is contained in:
二丫讲梵 authored and GitHub committed 2026-09-08 16:47:39 +08:00
1 parent 538aeed99c
commit cbd769d36b
5 files changed
+134 -11

No files matched your search

@@ -1,11 +1,12 @@
<script setup lang="ts">
import { computed, ref, watch } from "vue";
import { AlertTriangle, Archive, CheckCircle2, FileUp, Loader2 } from "@lucide/vue";
import { AlertTriangle, Archive, CheckCircle2, FileUp, GitCompareArrows, Loader2 } from "@lucide/vue";
import { useI18n } from "vue-i18n";
import NacosConfigDiffDialog from "@/components/nacos/NacosConfigDiffDialog.vue";
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, NacosConfigDataIdMapping, NacosConfigKey, NacosConfigSelectionScope, NacosConflictPolicy, NacosNamespaceInfo } from "@/types/nacos";
import type { NacosBatchPreview, NacosBatchPreviewDiff, NacosBatchReport, NacosConfigDataIdMapping, NacosConfigKey, NacosConfigSelectionScope, NacosConflictPolicy, NacosNamespaceInfo } from "@/types/nacos";
import { nacosNamespaceIdentity } from "@/lib/nacos/nacosNamespaceVisibility";
export type NacosBatchDialogMode = "export" | "import" | "copy";
@@ -58,6 +59,8 @@ const policy = ref<NacosConflictPolicy>("ABORT");
const targetNamespace = ref("");
const targetGroup = ref("");
const targetDataIdDrafts = ref<NacosConfigDataIdMapping[]>([]);
const previewDiffOpen = ref(false);
const selectedPreviewDiff = ref<NacosBatchPreviewDiff | null>(null);
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`);
@@ -117,6 +120,11 @@ function batchStatusClass(status: string) {
return "text-muted-foreground";
}
function showPreviewDiff(diff: NacosBatchPreviewDiff) {
selectedPreviewDiff.value = diff;
previewDiffOpen.value = true;
}
function resetTargetNamespace() {
if (targetNamespaces.value.some((item) => JSON.stringify(item.namespace) === targetNamespace.value)) return;
targetNamespace.value = targetNamespaces.value[0] ? JSON.stringify(targetNamespaces.value[0].namespace) : "";
@@ -171,6 +179,14 @@ watch(
watch(targetNamespaces, () => {
if (props.open) resetTargetNamespace();
});
watch(
() => props.preview,
() => {
previewDiffOpen.value = false;
selectedPreviewDiff.value = null;
},
);
</script>
<template>
@@ -302,7 +318,12 @@ watch(targetNamespaces, () => {
<div class="truncate text-muted-foreground">{{ item.group }} · {{ item.namespace || "public" }}</div>
<div v-if="item.message" class="break-all text-destructive">{{ item.message }}</div>
</div>
<Badge variant="outline" :class="batchStatusClass(item.status)">{{ batchStatusLabel(item.status) }}</Badge>
<div class="flex shrink-0 items-center gap-1">
<Button v-if="item.diff" size="icon" variant="ghost" class="h-7 w-7" :title="t('nacos.configDiffTitle')" :aria-label="t('nacos.configDiffTitle')" @click="showPreviewDiff(item.diff)">
<GitCompareArrows class="h-4 w-4" />
</Button>
<Badge variant="outline" :class="batchStatusClass(item.status)">{{ batchStatusLabel(item.status) }}</Badge>
</div>
</div>
</div>
</div>
@@ -358,4 +379,6 @@ watch(targetNamespaces, () => {
</DialogFooter>
</DialogContent>
</Dialog>
<NacosConfigDiffDialog v-model:open="previewDiffOpen" :before="selectedPreviewDiff?.beforeContent ?? ''" :after="selectedPreviewDiff?.afterContent ?? ''" :format="selectedPreviewDiff?.format" :show-confirm="false" />
</template>
@@ -4,7 +4,20 @@ import { createApp, defineComponent, h, nextTick, type App } from "vue";
import { afterEach, describe, expect, it, vi } from "vitest";
import i18n from "@/i18n";
import NacosConfigBatchDialog from "@/components/nacos/NacosConfigBatchDialog.vue";
import type { NacosBatchReport, NacosConfigKey } from "@/types/nacos";
import type { NacosBatchPreview, NacosBatchReport, NacosConfigKey } from "@/types/nacos";
vi.mock("@/components/nacos/NacosConfigDiffDialog.vue", () => ({
default: defineComponent({
props: {
open: Boolean,
before: String,
after: String,
format: String,
showConfirm: Boolean,
},
template: '<div v-if="open" data-testid="nacos-preview-diff" :data-before="before" :data-after="after" :data-format="format" :data-show-confirm="showConfirm" />',
}),
}));
const mountedApps: App[] = [];
@@ -16,6 +29,7 @@ async function mountDialog(
],
report: NacosBatchReport | null = null,
selectedKeys: NacosConfigKey[] = [{ namespace: "shared", group: "DEFAULT_GROUP", dataId: "app.yaml" }],
preview: NacosBatchPreview | null = null,
) {
const onTargetConnectionChange = vi.fn();
const onPreview = vi.fn();
@@ -39,7 +53,7 @@ async function mountDialog(
sourceConnectionId: "source",
currentNamespace: "shared",
namespaces,
preview: null,
preview,
report,
onTargetConnectionChange,
onPreview,
@@ -232,4 +246,32 @@ describe("NacosConfigBatchDialog cross-connection sync", () => {
expect(document.body.textContent).toContain("Failed");
expect(document.body.textContent).toContain("target unavailable");
});
it("opens a read-only diff for a conflicting copy preview", async () => {
await mountDialog("remote", undefined, null, undefined, {
planHash: "preview-hash",
total: 1,
created: 0,
conflicts: 1,
invalid: 0,
items: [
{
namespace: "shared",
group: "DEFAULT_GROUP",
dataId: "app.yaml",
status: "conflict",
diff: { beforeContent: "port: 8080", afterContent: "port: 9090", format: "yaml" },
},
],
});
(document.body.querySelector('[aria-label="Config Content Compare"]') as HTMLButtonElement).click();
await nextTick();
const diff = document.body.querySelector("[data-testid=nacos-preview-diff]") as HTMLElement;
expect(diff.dataset.before).toBe("port: 8080");
expect(diff.dataset.after).toBe("port: 9090");
expect(diff.dataset.format).toBe("yaml");
expect(diff.dataset.showConfirm).toBe("false");
});
});
+7
View File
@@ -358,12 +358,19 @@ export interface NacosConfigSelector {
export type NacosConflictPolicy = "ABORT" | "SKIP" | "OVERWRITE";
export interface NacosBatchPreviewDiff {
beforeContent: string;
afterContent: string;
format?: string;
}
export interface NacosBatchPreviewItem {
namespace: string;
group: string;
dataId: string;
status: string;
message?: string;
diff?: NacosBatchPreviewDiff;
}
export interface NacosBatchPreview {