diff --git a/frontend/packages/common/src/components/aoplatform/LanguageSetting.tsx b/frontend/packages/common/src/components/aoplatform/LanguageSetting.tsx index c54bc0a4..90a20efa 100644 --- a/frontend/packages/common/src/components/aoplatform/LanguageSetting.tsx +++ b/frontend/packages/common/src/components/aoplatform/LanguageSetting.tsx @@ -1,8 +1,8 @@ -import { Dropdown, Button } from 'antd' -import i18n from '@common/locales' -import { memo, useEffect, useMemo } from 'react' import { useGlobalContext } from '@common/contexts/GlobalStateContext' +import i18n from '@common/locales' import { Icon } from '@iconify/react/dist/iconify.js' +import { Button, Dropdown } from 'antd' +import { memo, useEffect, useMemo } from 'react' const LanguageSetting = ({ mode = 'light' }: { mode?: 'dark' | 'light' }) => { const { dispatch, state } = useGlobalContext() @@ -48,12 +48,17 @@ const LanguageSetting = ({ mode = 'light' }: { mode?: 'dark' | 'light' }) => { const langLabel = useMemo(() => items.find((item) => item?.key === state.language)?.title, [state.language]) useEffect(() => { - const savedLang = sessionStorage.getItem('i18nextLng') - const browserLang = navigator.language || navigator.userLanguage - if (savedLang) return - - dispatch({ type: 'UPDATE_LANGUAGE', language: browserLang }) + const savedLang = i18n.language || sessionStorage.getItem('i18nextLng') + if (savedLang && state.language !== savedLang) { + dispatch({ type: 'UPDATE_LANGUAGE', language: savedLang }) + } else if (!savedLang) { + const browserLang = navigator.language + const supportedLang = items.find((item) => item.key === browserLang) ? browserLang : 'zh-CN' + dispatch({ type: 'UPDATE_LANGUAGE', language: supportedLang }) + i18n.changeLanguage(supportedLang) + } }, []) + return ( { const { key } = e dispatch({ type: 'UPDATE_LANGUAGE', language: key }) i18n.changeLanguage(key) + sessionStorage.setItem('i18nextLng', key) } }} > diff --git a/frontend/packages/core/src/pages/aiApis/aiApisLayout.tsx b/frontend/packages/core/src/pages/aiApis/aiApisLayout.tsx index 3a67332c..b956887a 100644 --- a/frontend/packages/core/src/pages/aiApis/aiApisLayout.tsx +++ b/frontend/packages/core/src/pages/aiApis/aiApisLayout.tsx @@ -1,14 +1,15 @@ -import { useEffect } from "react"; -import { Outlet, useLocation, useNavigate } from "react-router-dom"; +import { useEffect } from 'react' +import { Outlet, useLocation, useNavigate } from 'react-router-dom' -export default function GlobalPolicyLayout(){ - const location = useLocation() - const pathName = location.pathname - const navigator = useNavigate() - useEffect(()=>{ - if(pathName === '/aiApis'){ - navigator('/aiApis/list') - } - },[pathName]) - return () -} \ No newline at end of file +export default function GlobalPolicyLayout() { + const location = useLocation() + const pathName = location.pathname + const navigator = useNavigate() + useEffect(() => { + if (pathName === '/aiApis') { + const queryParams = new URLSearchParams(location.search).toString() + navigator(`/aiApis/list${queryParams ? `?${queryParams}` : ''}`) + } + }, [pathName]) + return +} diff --git a/frontend/packages/core/src/pages/aiSetting/AIFlowChart.tsx b/frontend/packages/core/src/pages/aiSetting/AIFlowChart.tsx index b61d82da..dfd90d5c 100644 --- a/frontend/packages/core/src/pages/aiSetting/AIFlowChart.tsx +++ b/frontend/packages/core/src/pages/aiSetting/AIFlowChart.tsx @@ -94,6 +94,9 @@ const AIFlowChart = () => { if (!modelData.length) return const positions = calculateNodePositions(modelData) + const firstSuccessModel = modelData.find((model) => model.status === 'enabled') + console.log(firstSuccessModel) + // subtract 5 to make sure the service node is aligned with the top model node const serviceY = positions[modelData[0].id].y - 5 const newNodes = [ @@ -116,7 +119,8 @@ const AIFlowChart = () => { status: model.status, defaultLlm: model.defaultLlm, logo: model.logo, - id: model.id + id: model.id, + alternativeModel: firstSuccessModel } })), ...modelData.map((model) => ({ @@ -140,8 +144,12 @@ const AIFlowChart = () => { source: 'apiService', target: model.id, label: `${model.api_count} apis`, - data: { id: model.id }, - animated: true + data: { + id: model.id, + status: model.status + }, + animated: true, + style: { stroke: model.status === 'enabled' ? '#52c41a' : '#ff4d4f' } })), ...modelData.map((model) => ({ id: `${model.id}-keys-edge`, diff --git a/frontend/packages/core/src/pages/aiSetting/components/CustomEdge.tsx b/frontend/packages/core/src/pages/aiSetting/components/CustomEdge.tsx index 7e350a2c..35d585f5 100644 --- a/frontend/packages/core/src/pages/aiSetting/components/CustomEdge.tsx +++ b/frontend/packages/core/src/pages/aiSetting/components/CustomEdge.tsx @@ -1,4 +1,4 @@ -import { BaseEdge, EdgeLabelRenderer, EdgeProps, getSmoothStepPath } from '@xyflow/react' +import { BaseEdge, EdgeLabelRenderer, EdgeProps, getSmoothStepPath, useStore } from '@xyflow/react' export default function CustomEdge({ id, @@ -11,14 +11,26 @@ export default function CustomEdge({ style = {}, markerEnd, label, - data + data, + source, + target }: EdgeProps) { - const [edgePath, labelX, labelY] = getSmoothStepPath({ + // Get all edges to check for duplicates + const edges = useStore((state) => state.edges) + + // Find duplicate edges between the same source and target + const duplicateEdges = edges.filter((edge) => edge.source === source && edge.target === target) + const edgeIndex = duplicateEdges.findIndex((edge) => edge.id === id) + + // Adjust the path if this is a duplicate edge + const offset = edgeIndex * 20 // 20px offset for each duplicate edge + + const [edgePath] = getSmoothStepPath({ sourceX, - sourceY, + sourceY: sourceY, sourcePosition, targetX, - targetY, + targetY: targetY + offset, targetPosition, borderRadius: 16 }) @@ -27,7 +39,14 @@ export default function CustomEdge({ return ( <> - + {label && ( {label} diff --git a/frontend/packages/core/src/pages/aiSetting/components/ModelCardNode.tsx b/frontend/packages/core/src/pages/aiSetting/components/ModelCardNode.tsx index 115177e1..c08bac0b 100644 --- a/frontend/packages/core/src/pages/aiSetting/components/ModelCardNode.tsx +++ b/frontend/packages/core/src/pages/aiSetting/components/ModelCardNode.tsx @@ -8,10 +8,11 @@ import { AiSettingListItem, ModelDetailData, ModelStatus } from '../types' type ModelCardNodeData = ModelDetailData & { id: string position: { x: number; y: number } + alternativeModel?: ModelDetailData } export const ModelCardNode: React.FC<{ data: ModelCardNodeData }> = ({ data }) => { - const { name, status, defaultLlm, logo } = data + const { name, status, defaultLlm, logo, alternativeModel } = data const { openConfigModal } = useAiSetting() const getStatusIcon = (status: ModelStatus) => { @@ -62,6 +63,11 @@ export const ModelCardNode: React.FC<{ data: ModelCardNodeData }> = ({ data }) = {$t('默认:')} {defaultLlm} + {status !== 'enabled' && alternativeModel && ( +
+ {$t('关联 API 已转用')} {alternativeModel.name}/{alternativeModel.defaultLlm} +
+ )} )