fix(plugin): close plugin tabs safely and hide load flash
This commit is contained in:
8 files changed
+66
-13
No files matched your search
@@ -61,6 +61,8 @@ import QueryLoadingState from "@/components/common/QueryLoadingState.vue";
|
||||
import QueryErrorActions from "@/components/common/QueryErrorActions.vue";
|
||||
import QueryMessagesView from "@/components/layout/QueryMessagesView.vue";
|
||||
import QueryResultToolbarActions from "@/components/layout/QueryResultToolbarActions.vue";
|
||||
import PluginFilesystemTab from "@/components/plugins/PluginFilesystemTab.vue";
|
||||
import PluginWorkbenchTab from "@/components/plugins/PluginWorkbenchTab.vue";
|
||||
import ResultSetNavigator from "@/components/layout/ResultSetNavigator.vue";
|
||||
import QueryResultViewSwitcher from "@/components/layout/QueryResultViewSwitcher.vue";
|
||||
import DataGridCopyFormatControl from "@/components/grid/DataGridCopyFormatControl.vue";
|
||||
@@ -123,8 +125,6 @@ const XuguServerDashboard = defineAsyncComponent(() => import("@/components/admi
|
||||
const DamengJobAdmin = defineAsyncComponent(() => import("@/components/admin/DamengJobAdmin.vue"));
|
||||
const DamengUserAdmin = defineAsyncComponent(() => import("@/components/admin/DamengUserAdmin.vue"));
|
||||
const DamengRoleAdmin = defineAsyncComponent(() => import("@/components/admin/DamengRoleAdmin.vue"));
|
||||
const PluginWorkbenchTab = defineAsyncComponent(() => import("@/components/plugins/PluginWorkbenchTab.vue"));
|
||||
const PluginFilesystemTab = defineAsyncComponent(() => import("@/components/plugins/PluginFilesystemTab.vue"));
|
||||
const ExplainPlanViewer = defineAsyncComponent(() => import("@/components/explain/ExplainPlanViewer.vue"));
|
||||
const QueryChart = defineAsyncComponent(() => import("@/components/chart/QueryChart.vue"));
|
||||
import { useQueryStore } from "@/stores/queryStore";
|
||||
@@ -2540,8 +2540,16 @@ defineExpose({
|
||||
</template>
|
||||
|
||||
<template v-else-if="activeTab.mode === 'plugin-workbench' && activeTab.pluginWorkbench">
|
||||
<div class="min-w-0 flex-1 min-h-0">
|
||||
<PluginWorkbenchTab ref="pluginWorkbenchRef" :key="activeTab.id" :plugin-id="activeTab.pluginWorkbench.pluginId" :contribution-id="activeTab.pluginWorkbench.contributionId" :connection-id="activeTab.connectionId || undefined" :context="activeTab.pluginWorkbench.context" />
|
||||
<div class="min-w-0 flex-1 min-h-0 bg-background">
|
||||
<PluginWorkbenchTab
|
||||
ref="pluginWorkbenchRef"
|
||||
:key="activeTab.id"
|
||||
:plugin-id="activeTab.pluginWorkbench.pluginId"
|
||||
:contribution-id="activeTab.pluginWorkbench.contributionId"
|
||||
:connection-id="activeTab.connectionId || undefined"
|
||||
:context="activeTab.pluginWorkbench.context"
|
||||
@close-tab="emit('closeTab', activeTab.id)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="activeTab.mode === 'plugin-filesystem' && activeTab.pluginFilesystem">
|
||||
|
||||
@@ -61,6 +61,7 @@ export interface ContentAreaSurfaceProps {
|
||||
* global active tab after an await (see redevelopment guide §6.1).
|
||||
*/
|
||||
export interface ContentAreaSurfaceEmits {
|
||||
closeTab: [tabId: string];
|
||||
"update:activeOutputView": [tabId: string, value: TabOutputView];
|
||||
fixWithAi: [tabId: string, errorMessage: string];
|
||||
sendSelectionToAi: [tabId: string, sql: string];
|
||||
|
||||
@@ -21,6 +21,7 @@ const emit = defineEmits<{
|
||||
error: [message: string];
|
||||
openWorkbench: [pluginId: string, contributionId: string, context?: PluginWorkbenchContext];
|
||||
openFilesystem: [pluginId: string, providerId: string, context?: PluginWorkbenchContext];
|
||||
closeTab: [];
|
||||
}>();
|
||||
|
||||
const { t, locale: appLocale } = useI18n();
|
||||
@@ -65,6 +66,7 @@ function createBridge() {
|
||||
readAsset: api.readPluginUiAsset,
|
||||
openWorkbench: async (pluginId, contributionId, context) => emit("openWorkbench", pluginId, contributionId, context),
|
||||
openFilesystem: async (pluginId, providerId, context) => emit("openFilesystem", pluginId, providerId, context),
|
||||
closeTab: () => emit("closeTab"),
|
||||
},
|
||||
appLocale.value,
|
||||
currentBridgeTheme(),
|
||||
@@ -130,8 +132,7 @@ async function loadWorkbench() {
|
||||
if (disposed || generation !== loadGeneration) return;
|
||||
error.value = cause instanceof Error ? cause.message : String(cause);
|
||||
emit("error", error.value);
|
||||
} finally {
|
||||
if (!disposed && generation === loadGeneration) loading.value = false;
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,6 +143,7 @@ function onMessage(event: MessageEvent) {
|
||||
function onFrameLoad() {
|
||||
bridge?.sendInit();
|
||||
emit("ready");
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
@@ -183,14 +185,14 @@ onBeforeUnmount(() => {
|
||||
|
||||
<template>
|
||||
<div class="relative flex size-full min-h-40 overflow-hidden bg-background">
|
||||
<div v-if="loading" class="absolute inset-0 z-10 flex items-center justify-center bg-background/80 text-sm text-muted-foreground backdrop-blur-sm">
|
||||
<Loader2 class="mr-2 size-4 animate-spin" />
|
||||
{{ t("pluginPlatform.loadingTitle", { title }) }}
|
||||
</div>
|
||||
<div v-else-if="error" class="m-auto flex max-w-lg items-start gap-3 rounded-lg border border-destructive/40 bg-destructive/5 p-4 text-sm text-destructive">
|
||||
<div v-if="error" class="m-auto flex max-w-lg items-start gap-3 rounded-lg border border-destructive/40 bg-destructive/5 p-4 text-sm text-destructive">
|
||||
<AlertTriangle class="mt-0.5 size-4 shrink-0" />
|
||||
<span>{{ error }}</span>
|
||||
</div>
|
||||
<iframe v-else ref="iframe" :title="title" :srcdoc="source" sandbox="allow-scripts" referrerpolicy="no-referrer" class="size-full border-0 bg-transparent" @load="onFrameLoad" />
|
||||
<iframe v-else-if="source" ref="iframe" :title="title" :srcdoc="source" sandbox="allow-scripts" referrerpolicy="no-referrer" class="size-full border-0 bg-transparent" @load="onFrameLoad" />
|
||||
<div v-if="loading && !error" class="absolute inset-0 z-10 flex items-center justify-center bg-background/80 text-sm text-muted-foreground backdrop-blur-sm">
|
||||
<Loader2 class="mr-2 size-4 animate-spin" />
|
||||
{{ t("pluginPlatform.loadingTitle", { title }) }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -19,6 +19,9 @@ const props = defineProps<{
|
||||
}>();
|
||||
|
||||
const { t, locale: appLocale } = useI18n();
|
||||
const emit = defineEmits<{
|
||||
closeTab: [];
|
||||
}>();
|
||||
const connectionStore = useConnectionStore();
|
||||
const queryStore = useQueryStore();
|
||||
const plugins = ref<InstalledPlugin[]>([]);
|
||||
@@ -102,6 +105,6 @@ defineExpose({ refresh });
|
||||
<AlertTriangle class="mt-0.5 size-4 shrink-0" />
|
||||
<span>{{ error || t("pluginPlatform.workbenchUnavailableFallback") }}</span>
|
||||
</div>
|
||||
<PluginWorkbenchHost v-else class="min-h-0 flex-1" :plugin="entry.plugin" :contribution="entry.contribution" :context="context" @open-workbench="openWorkbench" @open-filesystem="openFilesystem" />
|
||||
<PluginWorkbenchHost v-else class="min-h-0 flex-1" :plugin="entry.plugin" :contribution="entry.contribution" :context="context" @open-workbench="openWorkbench" @open-filesystem="openFilesystem" @close-tab="emit('closeTab')" />
|
||||
</div>
|
||||
</template>
|
||||
Reference in new issue
Block a user