diff --git a/src/features/providers/ProvidersWorkbenchPage.tsx b/src/features/providers/ProvidersWorkbenchPage.tsx index f8d704a..28feb8a 100644 --- a/src/features/providers/ProvidersWorkbenchPage.tsx +++ b/src/features/providers/ProvidersWorkbenchPage.tsx @@ -1,4 +1,4 @@ -import { useCallback, useMemo, useState } from 'react'; +import { useCallback, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { usePageTransitionLayer } from '@/components/common/PageTransitionLayer'; import { useHeaderRefresh } from '@/hooks/useHeaderRefresh'; @@ -15,7 +15,7 @@ import type { OpenAISortBy, SortDir, } from './components/OpenAIBrandToolbar'; -import { ProviderSheet } from './sheets/ProviderSheet'; +import { ProviderSheet, type ProviderSheetHandle } from './sheets/ProviderSheet'; import { useProviderWorkbench } from './useProviderWorkbench'; import type { ProviderBrand, ProviderResource } from './types'; import styles from './ProvidersWorkbenchPage.module.scss'; @@ -81,6 +81,7 @@ export function ProvidersWorkbenchPage() { mode: 'detail', resource: null, }); + const sheetRef = useRef(null); const connected = connectionStatus === 'connected'; const { usageByProvider, refreshRecentRequests } = useProviderRecentRequests({ @@ -372,13 +373,19 @@ export function ProvidersWorkbenchPage() { groups={groups} activeBrand={activeGroup.id} onSelect={(brand) => { - setActiveBrand(brand); - setFilter(''); - setOpenaiSelectedModels(new Set()); - // 关闭 Sheet 以避免数据错位 - if (sheetState.open && sheetState.brand !== brand) { - closeSheet(); - } + const isSwitching = sheetState.open && sheetState.brand !== brand; + const proceed = isSwitching && sheetRef.current + ? sheetRef.current.confirmDiscardIfDirty() + : Promise.resolve(true); + void proceed.then((ok) => { + if (!ok) return; + setActiveBrand(brand); + setFilter(''); + setOpenaiSelectedModels(new Set()); + if (isSwitching) { + closeSheet(); + } + }); }} /> { diff --git a/src/features/providers/sheets/ProviderSheet.tsx b/src/features/providers/sheets/ProviderSheet.tsx index 4813c9d..50c6427 100644 --- a/src/features/providers/sheets/ProviderSheet.tsx +++ b/src/features/providers/sheets/ProviderSheet.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useId, useState } from 'react'; +import { useCallback, useEffect, useId, useImperativeHandle, useState, type Ref } from 'react'; import { useTranslation } from 'react-i18next'; import { Sheet } from '@/components/ui/Sheet'; import { IconLoader2, IconPencil } from '@/components/ui/icons'; @@ -24,6 +24,10 @@ export interface ProviderSheetState { resource: ProviderResource | null; } +export interface ProviderSheetHandle { + confirmDiscardIfDirty: () => Promise; +} + interface ProviderSheetProps { state: ProviderSheetState; onClose: () => void; @@ -31,6 +35,7 @@ interface ProviderSheetProps { workbench: UseProviderWorkbenchResult; onCreated: () => void; onUpdated: () => void; + ref?: Ref; } export function ProviderSheet({ @@ -40,6 +45,7 @@ export function ProviderSheet({ workbench, onCreated, onUpdated, + ref, }: ProviderSheetProps) { const { t } = useTranslation(); const { showConfirmation } = useNotificationStore(); @@ -79,6 +85,8 @@ export function ProviderSheet({ }); }, [isDirty, isEditingForm, showConfirmation, submitting, t]); + useImperativeHandle(ref, () => ({ confirmDiscardIfDirty }), [confirmDiscardIfDirty]); + const handleCancelClick = useCallback(() => { void confirmDiscardIfDirty().then((ok) => { if (ok) onClose();