feat(models): Revamp EditModelModal UI and UX

This commit significantly refactors the `EditModelModal` component to streamline the user interface and enhance usability, aligning it with the interaction patterns found elsewhere in the application.

- **Consolidated Layout:** Merged the "Vendor Information" and "Feature Configuration" sections into a single "Basic Information" card. This simplifies the form, reduces clutter, and makes all settings accessible in one view.

- **Improved Prefill Groups:** Replaced the separate `Select` dropdowns for tag and endpoint groups with a more intuitive button-based system within the `extraText` of the `TagInput` components.

- **Additive Button Logic:** The prefill group buttons now operate in an additive mode. Users can click multiple group buttons to incrementally add tags or endpoints, with duplicates being automatically handled.

- **Clear Functionality:** Added "Clear" buttons for both tags and endpoints, allowing users to easily reset the fields.

- **Code Cleanup:** Removed the unused `endpointOptions` constant and unnecessary icon imports (`Building`, `Settings`) to keep the codebase clean.
This commit is contained in:
t0ng7u
2025-08-06 03:29:45 +08:00
parent 7c814a5fd9
commit 94506bee99
8 changed files with 171 additions and 116 deletions

View File

@@ -35,13 +35,13 @@ import {
Save,
X,
FileText,
Building,
Settings,
} from 'lucide-react';
import { API, showError, showSuccess } from '../../../../helpers';
import { useTranslation } from 'react-i18next';
import { useIsMobile } from '../../../../hooks/common/useIsMobile';
const { Text, Title } = Typography;
const nameRuleOptions = [
{ label: '精确名称匹配', value: 0 },
{ label: '前缀名称匹配', value: 1 },
@@ -49,16 +49,6 @@ const nameRuleOptions = [
{ label: '后缀名称匹配', value: 3 },
];
const endpointOptions = [
{ label: 'OpenAI', value: 'openai' },
{ label: 'Anthropic', value: 'anthropic' },
{ label: 'Gemini', value: 'gemini' },
{ label: 'Image Generation', value: 'image-generation' },
{ label: 'Jina Rerank', value: 'jina-rerank' },
];
const { Text, Title } = Typography;
const EditModelModal = (props) => {
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
@@ -332,23 +322,6 @@ const EditModelModal = (props) => {
showClear
/>
</Col>
<Col span={24}>
<Form.Select
field='tag_group'
label={t('标签组')}
placeholder={t('选择标签组后将自动填充标签')}
optionList={tagGroups.map(g => ({ label: g.name, value: g.id }))}
showClear
onChange={(value) => {
const g = tagGroups.find(item => item.id === value);
if (g && formApiRef.current) {
formApiRef.current.setValue('tags', g.items || []);
}
}}
style={{ width: '100%' }}
/>
</Col>
<Col span={24}>
<Form.TagInput
field='tags'
@@ -366,23 +339,40 @@ const EditModelModal = (props) => {
formApiRef.current.setValue('tags', normalized);
}}
style={{ width: '100%' }}
extraText={(
<Space wrap>
{tagGroups.map(group => (
<Button
key={group.id}
size='small'
type='primary'
onClick={() => {
if (formApiRef.current) {
const currentTags = formApiRef.current.getValue('tags') || [];
const newTags = [...currentTags, ...(group.items || [])];
const uniqueTags = [...new Set(newTags)];
formApiRef.current.setValue('tags', uniqueTags);
}
}}
>
{group.name}
</Button>
))}
<Button
size='small'
type='warning'
onClick={() => {
if (formApiRef.current) {
formApiRef.current.setValue('tags', []);
}
}}
>
{t('清除标签')}
</Button>
</Space>
)}
/>
</Col>
</Row>
</Card>
{/* 供应商信息 */}
<Card className='!rounded-2xl shadow-sm border-0'>
<div className='flex items-center mb-2'>
<Avatar size='small' color='blue' className='mr-2 shadow-md'>
<Building size={16} />
</Avatar>
<div>
<Text className='text-lg font-medium'>{t('供应商信息')}</Text>
<div className='text-xs text-gray-600'>{t('设置模型的供应商相关信息')}</div>
</div>
</div>
<Row gutter={12}>
<Col span={24}>
<Form.Select
field='vendor_id'
@@ -400,47 +390,46 @@ const EditModelModal = (props) => {
style={{ width: '100%' }}
/>
</Col>
</Row>
</Card>
{/* 功能配置 */}
<Card className='!rounded-2xl shadow-sm border-0'>
<div className='flex items-center mb-2'>
<Avatar size='small' color='purple' className='mr-2 shadow-md'>
<Settings size={16} />
</Avatar>
<div>
<Text className='text-lg font-medium'>{t('功能配置')}</Text>
<div className='text-xs text-gray-600'>{t('设置模型的功能和状态')}</div>
</div>
</div>
<Row gutter={12}>
<Col span={24}>
<Form.Select
field='endpoint_group'
label={t('端点组')}
placeholder={t('选择端点组后将自动填充端点')}
optionList={endpointGroups.map(g => ({ label: g.name, value: g.id }))}
showClear
style={{ width: '100%' }}
onChange={(value) => {
const g = endpointGroups.find(item => item.id === value);
if (g && formApiRef.current) {
formApiRef.current.setValue('endpoints', g.items || []);
}
}}
/>
</Col>
<Col span={24}>
<Form.Select
<Form.TagInput
field='endpoints'
label={t('支持端点')}
placeholder={t('选择模型支持的端点类型')}
optionList={endpointOptions}
multiple
placeholder={t('输入端点名称,按回车添加')}
addOnBlur
showClear
style={{ width: '100%' }}
extraText={(
<Space wrap>
{endpointGroups.map(group => (
<Button
key={group.id}
size='small'
type='primary'
onClick={() => {
if (formApiRef.current) {
const currentEndpoints = formApiRef.current.getValue('endpoints') || [];
const newEndpoints = [...currentEndpoints, ...(group.items || [])];
const uniqueEndpoints = [...new Set(newEndpoints)];
formApiRef.current.setValue('endpoints', uniqueEndpoints);
}
}}
>
{group.name}
</Button>
))}
<Button
size='small'
type='warning'
onClick={() => {
if (formApiRef.current) {
formApiRef.current.setValue('endpoints', []);
}
}}
>
{t('清除端点')}
</Button>
</Space>
)}
/>
</Col>
<Col span={24}>

View File

@@ -111,7 +111,7 @@ const MissingModelsModal = ({
<Typography.Text strong className="!text-[var(--semi-color-text-0)] !text-base">
{t('未配置的模型列表')}
</Typography.Text>
<Typography.Text type="tertiary" className="!text-xs flex items-center">
<Typography.Text type="tertiary" size="small">
{t('共')} {missingModels.length} {t('个未配置模型')}
</Typography.Text>
</div>