✨ refactor: unify model-select searching & UX across the dashboard
This patch standardises how all “model” (and related) `<Select>` components handle searching.
Highlights
• Added a shared helper `modelSelectFilter` to `helpers/utils.js` – performs case-insensitive value-based matching, independent of ReactNode labels.
• Removed the temporary `helpers/selectFilter.js`; all components now import from the core helpers barrel.
• Updated Selects to use the new filter *and* set `autoClearSearchValue={false}` so the query text is preserved after a choice is made.
Affected components
• Channel editor (EditChannelModal) – channel type & model lists
• Tag editor (EditTagModal) – model list
• Token editor (EditTokenModal) – model-limit list
• Playground SettingsPanel – model selector
Benefits
✓ Consistent search behaviour across the app
✓ Better user experience when selecting multiple items
✓ Cleaner codebase with one canonical filter implementation
This commit is contained in:
@@ -33,7 +33,7 @@ import {
|
||||
Settings,
|
||||
} from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { renderGroupOption } from '../../helpers';
|
||||
import { renderGroupOption, modelSelectFilter } from '../../helpers';
|
||||
import ParameterControl from './ParameterControl';
|
||||
import ImageUrlInput from './ImageUrlInput';
|
||||
import ConfigManager from './ConfigManager';
|
||||
@@ -173,8 +173,8 @@ const SettingsPanel = ({
|
||||
name='model'
|
||||
required
|
||||
selection
|
||||
searchPosition='dropdown'
|
||||
filter
|
||||
filter={modelSelectFilter}
|
||||
autoClearSearchValue={false}
|
||||
onChange={(value) => onInputChange('model', value)}
|
||||
value={inputs.model}
|
||||
autoComplete='new-password'
|
||||
|
||||
@@ -46,7 +46,7 @@ import {
|
||||
Col,
|
||||
Highlight,
|
||||
} from '@douyinfe/semi-ui';
|
||||
import { getChannelModels, copy, getChannelIcon, getModelCategories } from '../../../../helpers';
|
||||
import { getChannelModels, copy, getChannelIcon, getModelCategories, modelSelectFilter } from '../../../../helpers';
|
||||
import {
|
||||
IconSave,
|
||||
IconClose,
|
||||
@@ -853,7 +853,8 @@ const EditChannelModal = (props) => {
|
||||
rules={[{ required: true, message: t('请选择渠道类型') }]}
|
||||
optionList={channelOptionList}
|
||||
style={{ width: '100%' }}
|
||||
filter
|
||||
filter={modelSelectFilter}
|
||||
autoClearSearchValue={false}
|
||||
searchPosition='dropdown'
|
||||
onSearch={(value) => setChannelSearchValue(value)}
|
||||
renderOptionItem={renderChannelOption}
|
||||
@@ -1251,7 +1252,8 @@ const EditChannelModal = (props) => {
|
||||
placeholder={t('请选择该渠道所支持的模型')}
|
||||
rules={[{ required: true, message: t('请选择模型') }]}
|
||||
multiple
|
||||
filter
|
||||
filter={modelSelectFilter}
|
||||
autoClearSearchValue={false}
|
||||
searchPosition='dropdown'
|
||||
optionList={modelOptions}
|
||||
style={{ width: '100%' }}
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
showSuccess,
|
||||
showWarning,
|
||||
verifyJSON,
|
||||
modelSelectFilter,
|
||||
} from '../../../../helpers';
|
||||
import {
|
||||
SideSheet,
|
||||
@@ -394,7 +395,8 @@ const EditTagModal = (props) => {
|
||||
label={t('模型')}
|
||||
placeholder={t('请选择该渠道所支持的模型,留空则不更改')}
|
||||
multiple
|
||||
filter
|
||||
filter={modelSelectFilter}
|
||||
autoClearSearchValue={false}
|
||||
searchPosition='dropdown'
|
||||
optionList={modelOptions}
|
||||
style={{ width: '100%' }}
|
||||
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
renderGroupOption,
|
||||
renderQuotaWithPrompt,
|
||||
getModelCategories,
|
||||
modelSelectFilter,
|
||||
} from '../../../../helpers';
|
||||
import { useIsMobile } from '../../../../hooks/common/useIsMobile.js';
|
||||
import {
|
||||
@@ -513,7 +514,8 @@ const EditTokenModal = (props) => {
|
||||
multiple
|
||||
optionList={models}
|
||||
extraText={t('非必要,不建议启用模型限制')}
|
||||
filter
|
||||
filter={modelSelectFilter}
|
||||
autoClearSearchValue={false}
|
||||
searchPosition='dropdown'
|
||||
showClear
|
||||
style={{ width: '100%' }}
|
||||
|
||||
@@ -557,3 +557,13 @@ export function setTableCompactMode(compact, tableKey = 'global') {
|
||||
modes[tableKey] = compact;
|
||||
writeTableCompactModes(modes);
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// Select 组件统一过滤逻辑
|
||||
// 解决 label 为 ReactNode(带图标等)时无法用内置 filter 搜索的问题。
|
||||
// 使用方式: <Select filter={modelSelectFilter} ... />
|
||||
export const modelSelectFilter = (input, option) => {
|
||||
if (!input) return true;
|
||||
const val = (option?.value || '').toString().toLowerCase();
|
||||
return val.includes(input.trim().toLowerCase());
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user