fix: change agent model to single

main
yannyang 2026-08-03 18:35:28 +08:00
parent b0de11aff6
commit cabea3f83a
3 changed files with 77 additions and 34 deletions

View File

@ -1,19 +1,21 @@
import { useState } from 'react'; import { useState } from 'react';
import { Button, Checkbox, Dropdown } from 'antd'; import { Button, Radio, Dropdown } from 'antd';
import { DownOutlined } from '@ant-design/icons'; import { DownOutlined } from '@ant-design/icons';
import { AiModel } from '../../../api'; import { AiModel } from '../../../api';
import { DEFAULT_RH_40X40_GRAY } from '../../../constants'; import { DEFAULT_RH_40X40_GRAY } from '../../../constants';
interface ModelCheckboxDropdownProps { interface ModelSelectDropdownProps {
value?: string[]; value?: string;
onChange?: (value: string[]) => void; onChange?: (value: string) => void;
models: AiModel[]; models: AiModel[];
isMobile?: boolean; isMobile?: boolean;
} }
export default function ModelCheckboxDropdown({ value = [], onChange, models, isMobile = false }: ModelCheckboxDropdownProps) { export default function ModelSelectDropdown({ value, onChange, models, isMobile = false }: ModelSelectDropdownProps) {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const summary = value.length ? `${value.length} 个已选` : '选择模型';
const selectedModel = models.find(m => m.id === value);
const summary = selectedModel ? selectedModel.model_name : '选择模型';
return ( return (
<Dropdown <Dropdown
@ -22,43 +24,44 @@ export default function ModelCheckboxDropdown({ value = [], onChange, models, is
onOpenChange={setOpen} onOpenChange={setOpen}
popupRender={() => ( popupRender={() => (
<div className={`agent-model-dropdown-panel${isMobile ? ' is-h5-panel' : ''}`} onClick={(e) => e.stopPropagation()}> <div className={`agent-model-dropdown-panel${isMobile ? ' is-h5-panel' : ''}`} onClick={(e) => e.stopPropagation()}>
<Checkbox.Group <Radio.Group
value={value} value={value}
onChange={(checked) => onChange?.(checked.map((item) => String(item)))} onChange={(e) => {
className="agent-model-checkbox-group" onChange?.(e.target.value);
setOpen(false); // 单选选中后自动关闭
}}
className="agent-model-radio-group"
> >
{models.map((m) => { {models.map((m) => {
const inputPrice = 2 * m.model_ratio; const inputPrice = 2 * m.model_ratio;
const outputPrice = inputPrice * m.completion_ratio; const outputPrice = inputPrice * m.completion_ratio;
return ( return (
<Checkbox key={m.id} value={m.id} className="agent-model-checkbox-item"> <Radio key={m.id} value={m.id} className="agent-model-radio-item">
<div className="agent-model-checkbox-content"> <div className="agent-model-radio-content">
<div className="agent-model-checkbox-meta"> <div className="agent-model-radio-meta">
<img <img
src={m.icon || DEFAULT_RH_40X40_GRAY} src={m.icon || DEFAULT_RH_40X40_GRAY}
alt={m.model_name} alt={m.model_name}
className="agent-model-checkbox-icon" className="agent-model-radio-icon"
/> />
<span className="agent-model-checkbox-name">{m.model_name}</span> <span className="agent-model-radio-name">{m.model_name}</span>
</div> </div>
<div className="agent-model-checkbox-price"> <div className="agent-model-radio-price">
<span>: ${inputPrice.toFixed(2)}/M</span> <span>: ${inputPrice.toFixed(2)}/M</span>
<span>: ${outputPrice.toFixed(2)}/M</span> <span>: ${outputPrice.toFixed(2)}/M</span>
</div> </div>
</div> </div>
</Checkbox> </Radio>
); );
})} })}
</Checkbox.Group> </Radio.Group>
</div> </div>
)} )}
> >
<Button type="text" block className="agent-model-dropdown-trigger"> <Button type="text" block className="agent-model-dropdown-trigger">
<span className="agent-model-dropdown-summary">{summary}</span> <span className="agent-model-dropdown-summary"></span>
<span className="agent-model-dropdown-values"> <span className="agent-model-dropdown-values">
{value.length {summary}
? value.map(id => models.find(m => m.id === id)?.model_name || id).join(', ')
: '未选择'}
</span> </span>
<DownOutlined className="agent-model-dropdown-arrow" /> <DownOutlined className="agent-model-dropdown-arrow" />
</Button> </Button>

View File

@ -1,7 +1,7 @@
import { Form, InputNumber } from 'antd'; import { Form, InputNumber } from 'antd';
import { SettingOutlined } from '@ant-design/icons'; import { SettingOutlined } from '@ant-design/icons';
import { parseModelSelections } from '../../constants'; import { parseModelSelections } from '../../constants';
import ModelCheckboxDropdown from '../ModelCheckboxDropdown'; import ModelSelectDropdown from '../ModelSelectDropdown';
interface ModelSettingsCardProps { interface ModelSettingsCardProps {
models: any[]; models: any[];
@ -22,17 +22,18 @@ export default function ModelSettingsCard({ models, isMobile = false }: ModelSet
required required
rules={[ rules={[
{ {
validator: async (_rule, value) => { required: true,
const selected = parseModelSelections(value); message: '请选择一个模型',
if (selected.length > 0) return;
throw new Error('请选择至少一个模型');
},
}, },
]} ]}
className="mb-0" className="mb-0"
getValueProps={(value) => ({ value: parseModelSelections(value) })} getValueProps={(value) => {
// 如果后端返回的是数组或 JSON 字符串,取第一个作为单选值
const selected = parseModelSelections(value);
return { value: selected[0] || '' };
}}
> >
<ModelCheckboxDropdown models={models} isMobile={isMobile} /> <ModelSelectDropdown models={models} isMobile={isMobile} />
</Form.Item> </Form.Item>
<Form.Item name="temperature" label="Temperature" className="mb-0" hidden> <Form.Item name="temperature" label="Temperature" className="mb-0" hidden>
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">

View File

@ -946,14 +946,16 @@ body {
} }
} }
.agent-model-checkbox-group { .agent-model-checkbox-group,
.agent-model-radio-group {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 10px; gap: 10px;
width: 100%; width: 100%;
} }
.agent-model-checkbox-item { .agent-model-checkbox-item,
.agent-model-radio-item {
display: flex; display: flex;
align-items: flex-start; align-items: flex-start;
margin-inline-start: 0; margin-inline-start: 0;
@ -963,15 +965,52 @@ body {
background: var(--color-surface); background: var(--color-surface);
} }
.agent-model-checkbox-item .ant-checkbox { .agent-model-checkbox-item .ant-checkbox,
.agent-model-radio-item .ant-radio {
margin-top: 3px; margin-top: 3px;
} }
.agent-model-checkbox-item .ant-checkbox + span { .agent-model-checkbox-item .ant-checkbox + span,
.agent-model-radio-item .ant-radio + span {
width: 100%; width: 100%;
padding-inline-start: 10px; padding-inline-start: 10px;
} }
.agent-model-radio-content {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
width: 100%;
}
.agent-model-radio-meta {
display: flex;
align-items: center;
gap: 8px;
min-width: 0;
flex: 1;
}
.agent-model-radio-name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-weight: 500;
color: var(--color-text);
}
.agent-model-radio-price {
display: flex;
flex-direction: column;
align-items: flex-end;
justify-content: center;
flex-shrink: 0;
margin-left: 8px;
font-size: 10px;
color: var(--color-text-tertiary);
}
.agent-model-checkbox-content { .agent-model-checkbox-content {
display: flex; display: flex;
align-items: center; align-items: center;
@ -988,7 +1027,7 @@ body {
flex: 1; flex: 1;
} }
.agent-model-checkbox-icon { .agent-model-radio-icon {
width: 20px; width: 20px;
height: 20px; height: 20px;
object-fit: contain; object-fit: contain;