From 379eeedde216cc0ddcd481faf25824719b19bcae Mon Sep 17 00:00:00 2001 From: jialin Date: Sun, 8 Feb 2026 17:16:58 +0800 Subject: [PATCH] fix: deduplicate in select provider models and target models --- src/pages/maas-provider/forms/model-item.tsx | 29 +++++++--- .../maas-provider/forms/supported-models.tsx | 7 ++- .../model-routes/components/route-targets.tsx | 4 +- src/pages/model-routes/forms/targets.tsx | 10 ++-- .../hooks/use-target-source-models.tsx | 55 +++++++++++-------- src/pages/model-routes/index.tsx | 18 ++++-- 6 files changed, 80 insertions(+), 43 deletions(-) diff --git a/src/pages/maas-provider/forms/model-item.tsx b/src/pages/maas-provider/forms/model-item.tsx index ac422896..58a79690 100644 --- a/src/pages/maas-provider/forms/model-item.tsx +++ b/src/pages/maas-provider/forms/model-item.tsx @@ -9,7 +9,7 @@ import { } from '@ant-design/icons'; import { useIntl } from '@umijs/max'; import { Button, Form, Tooltip } from 'antd'; -import React from 'react'; +import React, { useMemo } from 'react'; import styled from 'styled-components'; import { useFormContext } from '../config/form-context'; import { FormData, ProviderModel } from '../config/types'; @@ -128,25 +128,36 @@ const ModelItem: React.FC = ({ }; // filter out already selected models, but keep the current one - const selectedModelMap = new Map( - selectedModelList?.map((model) => [model.name, true]) - ); - const filteredOptions = () => { + const selectedModelSet = useMemo(() => { + return new Set(selectedModelList?.map((model) => model.name)); + }, [selectedModelList]); + + const filteredOptions = useMemo(() => { return providerModelList.filter((model) => { - return model.value === item.name || !selectedModelMap.has(model.value); + return model.value === item.name || !selectedModelSet.has(model.value); }); - }; + }, [providerModelList, item.name, selectedModelSet]); return ( { + return ( + option!.value.toLowerCase().includes(inputValue.toLowerCase()) || + option.label + ?.toString() + .toLowerCase() + .includes(inputValue.toLowerCase()) + ); + } + }} onOpenChange={onOpenChange} suffixIcon={renderSuffixIcon()} value={item.name} onChange={handleOnChange} - options={filteredOptions()} + options={filteredOptions} placeholder={intl.formatMessage({ id: 'providers.table.models' })} /> { const handleOpenChange = async (open: boolean) => { try { - await form.validateFields(['api_key']); + await form.validateFields(['api_key', ['config', 'type']]); const currentAPIKey = form.getFieldValue('api_key') || ''; + console.log('handleOpenChange', { open, currentAPIKey }); // Avoid repeated requests with the same API key if ( @@ -54,7 +55,9 @@ const SupportedModels = () => { } }); } - } catch (error) {} + } catch (error) { + prevAPIKeyRef.current = ''; + } }; const updateModelList = (models: ProviderModel[]) => { diff --git a/src/pages/model-routes/components/route-targets.tsx b/src/pages/model-routes/components/route-targets.tsx index a8076528..85eeec9d 100644 --- a/src/pages/model-routes/components/route-targets.tsx +++ b/src/pages/model-routes/components/route-targets.tsx @@ -88,7 +88,7 @@ const RouteItem: React.FC = ({ {renderProviderSource()} - + {data.weight > 0 && ( @@ -112,7 +112,7 @@ const RouteItem: React.FC = ({ )} - + { const selectedDataList = [...dataList, { value: fallbackValues.value }]; - const selectedKeys = selectedDataList - .filter((item) => item.value) - .map((item) => buildKey(item.value)); + const selectedKeys = new Set( + selectedDataList + .filter((item) => item.value) + .map((item) => buildKey(item.value)) + ); return sourceModels .map((model) => { const children = model.children?.filter((child) => { const key = buildKey([child.data?.parentId, child.value]); - return !selectedKeys.includes(key) || key === currKey; + return !selectedKeys.has(key) || key === currKey; }); return { diff --git a/src/pages/model-routes/hooks/use-target-source-models.tsx b/src/pages/model-routes/hooks/use-target-source-models.tsx index 21020bbb..f1926a94 100644 --- a/src/pages/model-routes/hooks/use-target-source-models.tsx +++ b/src/pages/model-routes/hooks/use-target-source-models.tsx @@ -5,6 +5,7 @@ import { queryMaasProviders } from '@/pages/maas-provider/apis'; import ProviderLogo from '@/pages/maas-provider/components/provider-logo'; import { MaasProviderItem } from '@/pages/maas-provider/config/types'; import { useIntl } from '@umijs/max'; +import _ from 'lodash'; import React, { useState } from 'react'; import styled from 'styled-components'; @@ -52,30 +53,27 @@ const useTargetSourceModels = () => { value: 'deployments', providerType: 'deployments', parent: true, - children: models.items?.map?.((model: ModelListItem) => ({ - label: model.name, - value: model.id, - data: { - model_id: model.id, - parentId: 'deployments' - }, - source: 'deployment' - })) + children: _.uniqBy( + models.items?.map?.((model: ModelListItem) => [ + model.id, + { + label: model.name, + value: model.id, + data: { + model_id: model.id, + parentId: 'deployments' + }, + source: 'deployment' + } + ]), + 'value' + ) } ].filter((group) => group.children && group.children.length > 0); const providerOptions: CascaderOption[] = providers.items - ?.map?.((provider: MaasProviderItem) => ({ - label: ( - - - {provider.name} - - ), - value: provider.id, - parent: true, - providerType: provider.config?.type, - children: provider.models?.map?.((model) => ({ + ?.map?.((provider: MaasProviderItem) => { + const children = provider.models?.map?.((model) => ({ label: model.name, value: model.name, data: { @@ -84,8 +82,21 @@ const useTargetSourceModels = () => { parentId: provider.id }, source: 'providerModel' - })) - })) + })); + + return { + label: ( + + + {provider.name} + + ), + value: provider.id, + parent: true, + providerType: provider.config?.type, + children: _.uniqBy(children, 'value') + }; + }) .filter((group) => group.children && group.children.length > 0); setSourceModels([...modelsList, ...providerOptions]); diff --git a/src/pages/model-routes/index.tsx b/src/pages/model-routes/index.tsx index 670c165d..324ddf36 100644 --- a/src/pages/model-routes/index.tsx +++ b/src/pages/model-routes/index.tsx @@ -65,8 +65,13 @@ const ModelRoutes: React.FC = () => { const { watchDataList: allRouteTargets, deleteItemFromCache } = useWatchList(MODEL_ROUTE_TARGETS); const [expandAtom] = useAtom(expandKeysAtom); - const { handleExpandChange, handleExpandAll, expandedRowKeys } = - useExpandedRowKeys(expandAtom); + const { + handleExpandChange, + handleExpandAll, + updateExpandedRowKeys, + removeExpandedRowKey, + expandedRowKeys + } = useExpandedRowKeys(expandAtom); const intl = useIntl(); const { openRouteModalStatus, openRouteModal, closeRouteModal } = useCreateRoute(); @@ -109,17 +114,22 @@ const ModelRoutes: React.FC = () => { ...data }; try { + let data: ListItem = {} as any; if (openRouteModalStatus.action === PageAction.EDIT) { - await updateModelRoute({ + data = await updateModelRoute({ data: params, id: openRouteModalStatus.currentData!.id }); } if (openRouteModalStatus.action === PageAction.CREATE) { - await createModelRoute({ + data = await createModelRoute({ data: params }); } + + if (data.targets > 0) { + updateExpandedRowKeys([data.id, ...expandedRowKeys]); + } fetchData(); closeRouteModal(); message.success(intl.formatMessage({ id: 'common.message.success' }));