feat: add search function on provider list & optimize ui behaviors when dragging provider item (#2706)

* 添加“腾讯云TI”供应商及其支持的deepseek模型

* add search feature for model providers & adjust ui element behaviors when dragging

* dev merge fix

* merge fix
This commit is contained in:
fullex 2025-03-04 10:27:18 +08:00 committed by GitHub
parent 0e4f06e86a
commit 640d3783a0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 61 additions and 16 deletions

View File

@ -562,7 +562,7 @@ export const PROVIDER_CONFIG = {
models: 'https://cloud.baidu.com/doc/WENXINWORKSHOP/s/Fm2vrveyu'
}
},
'tencent-cloud-ti': {
'tentent-cloud-ti': {
api: {
url: 'https://api.lkeap.cloud.tencent.com'
},

View File

@ -782,7 +782,8 @@
"remove_duplicate_keys": "Remove Duplicate Keys",
"remove_invalid_keys": "Remove Invalid Keys",
"search_placeholder": "Search model id or name",
"title": "Model Provider"
"title": "Model Provider",
"search": "Search Providers..."
},
"proxy": {
"mode": {

View File

@ -782,7 +782,8 @@
"remove_duplicate_keys": "重複キーを削除",
"remove_invalid_keys": "無効なキーを削除",
"search_placeholder": "モデルIDまたは名前を検索",
"title": "モデルプロバイダー"
"title": "モデルプロバイダー",
"search": "プロバイダーを検索..."
},
"proxy": {
"mode": {

View File

@ -782,7 +782,8 @@
"remove_duplicate_keys": "Удалить дубликаты ключей",
"remove_invalid_keys": "Удалить недействительные ключи",
"search_placeholder": "Поиск по ID или имени модели",
"title": "Провайдеры моделей"
"title": "Провайдеры моделей",
"search": "Поиск поставщиков..."
},
"proxy": {
"mode": {

View File

@ -782,7 +782,8 @@
"remove_duplicate_keys": "移除重复密钥",
"remove_invalid_keys": "删除无效密钥",
"search_placeholder": "搜索模型 ID 或名称",
"title": "模型服务"
"title": "模型服务",
"search": "搜索模型平台..."
},
"proxy": {
"mode": {

View File

@ -781,7 +781,8 @@
"remove_duplicate_keys": "移除重複密鑰",
"remove_invalid_keys": "刪除無效密鑰",
"search_placeholder": "搜尋模型 ID 或名稱",
"title": "模型提供者"
"title": "模型提供者",
"search": "搜尋模型平台..."
},
"proxy": {
"mode": {

View File

@ -5,7 +5,7 @@ import { getProviderLogo } from '@renderer/config/providers'
import { useAllProviders, useProviders } from '@renderer/hooks/useProvider'
import { Provider } from '@renderer/types'
import { droppableReorder, generateColorFromChar, getFirstCharacter, uuid } from '@renderer/utils'
import { Avatar, Button, Dropdown, MenuProps, Tag } from 'antd'
import { Avatar, Button, Dropdown, Input, MenuProps, Tag } from 'antd'
import { FC, useState } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
@ -18,6 +18,7 @@ const ProvidersList: FC = () => {
const { updateProviders, addProvider, removeProvider, updateProvider } = useProviders()
const [selectedProvider, setSelectedProvider] = useState<Provider>(providers[0])
const { t } = useTranslation()
const [searchText, setSearchText] = useState<string>('')
const [dragging, setDragging] = useState(false)
const onDragEnd = (result: DropResult) => {
@ -95,17 +96,58 @@ const ProvidersList: FC = () => {
return menus
}
//will match the providers and the models that provider provides
const filteredProviders = providers.filter((provider) => {
// 获取 provider 的名称
const providerName = provider.isSystem ? t(`provider.${provider.id}`) : provider.name
// 检查 provider 的 id 和 name 是否匹配搜索条件
const isProviderMatch =
provider.id.toLowerCase().includes(searchText.toLowerCase()) ||
providerName.toLowerCase().includes(searchText.toLowerCase())
// 检查 provider.models 中是否有 model 的 id 或 name 匹配搜索条件
const isModelMatch = provider.models.some((model) => {
return (
model.id.toLowerCase().includes(searchText.toLowerCase()) ||
model.name.toLowerCase().includes(searchText.toLowerCase())
)
})
// 如果 provider 或 model 匹配,则保留该 provider
return isProviderMatch || isModelMatch
})
return (
<Container>
<ProviderListContainer>
<AddButtonWrapper>
<Input
type="text"
placeholder={t('settings.provider.search')}
value={searchText}
onChange={(e) => setSearchText(e.target.value)}
onKeyDown={(e) => {
if (e.key === 'Escape') {
setSearchText('')
}
}}
allowClear
disabled={dragging}
/>
</AddButtonWrapper>
<Scrollbar>
<ProviderList>
<DragDropContext onDragStart={() => setDragging(true)} onDragEnd={onDragEnd}>
<Droppable droppableId="droppable">
{(provided) => (
<div {...provided.droppableProps} ref={provided.innerRef}>
{providers.map((provider, index) => (
<Draggable key={`draggable_${provider.id}_${index}`} draggableId={provider.id} index={index}>
{filteredProviders.map((provider, index) => (
<Draggable
key={`draggable_${provider.id}_${index}`}
draggableId={provider.id}
index={index}
isDragDisabled={searchText.length > 0}>
{(provided) => (
<div
ref={provided.innerRef}
@ -148,13 +190,11 @@ const ProvidersList: FC = () => {
</DragDropContext>
</ProviderList>
</Scrollbar>
{!dragging && (
<AddButtonWrapper>
<Button style={{ width: '100%' }} icon={<PlusOutlined />} onClick={onAddProvider}>
{t('button.add')}
</Button>
</AddButtonWrapper>
)}
<AddButtonWrapper>
<Button style={{ width: '100%' }} icon={<PlusOutlined />} onClick={onAddProvider} disabled={dragging}>
{t('button.add')}
</Button>
</AddButtonWrapper>
</ProviderListContainer>
<ProviderSetting provider={selectedProvider} key={JSON.stringify(selectedProvider)} />
</Container>