diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json index 295c464d..8c808d01 100644 --- a/web/src/i18n/locales/en.json +++ b/web/src/i18n/locales/en.json @@ -1726,5 +1726,7 @@ "放大编辑": "Expand editor", "编辑公告内容": "Edit announcement content", "自适应列表": "Adaptive list", - "紧凑列表": "Compact list" + "紧凑列表": "Compact list", + "仅显示矛盾倍率": "Only show conflicting ratios", + "矛盾": "Conflict" } \ No newline at end of file diff --git a/web/src/pages/Setting/Ratio/ModelSettingsVisualEditor.js b/web/src/pages/Setting/Ratio/ModelSettingsVisualEditor.js index 983f3afe..ca3e396d 100644 --- a/web/src/pages/Setting/Ratio/ModelSettingsVisualEditor.js +++ b/web/src/pages/Setting/Ratio/ModelSettingsVisualEditor.js @@ -8,7 +8,9 @@ import { Form, Space, RadioGroup, - Radio + Radio, + Checkbox, + Tag } from '@douyinfe/semi-ui'; import { IconDelete, @@ -30,6 +32,7 @@ export default function ModelSettingsVisualEditor(props) { const [loading, setLoading] = useState(false); const [pricingMode, setPricingMode] = useState('per-token'); // 'per-token' or 'per-request' const [pricingSubMode, setPricingSubMode] = useState('ratio'); // 'ratio' or 'token-price' + const [conflictOnly, setConflictOnly] = useState(false); const formRef = useRef(null); const pageSize = 10; const quotaPerUnit = getQuotaPerUnit(); @@ -47,13 +50,19 @@ export default function ModelSettingsVisualEditor(props) { ...Object.keys(completionRatio), ]); - const modelData = Array.from(modelNames).map((name) => ({ - name, - price: modelPrice[name] === undefined ? '' : modelPrice[name], - ratio: modelRatio[name] === undefined ? '' : modelRatio[name], - completionRatio: - completionRatio[name] === undefined ? '' : completionRatio[name], - })); + const modelData = Array.from(modelNames).map((name) => { + const price = modelPrice[name] === undefined ? '' : modelPrice[name]; + const ratio = modelRatio[name] === undefined ? '' : modelRatio[name]; + const comp = completionRatio[name] === undefined ? '' : completionRatio[name]; + + return { + name, + price, + ratio, + completionRatio: comp, + hasConflict: price !== '' && (ratio !== '' || comp !== ''), + }; + }); setModels(modelData); } catch (error) { @@ -69,11 +78,13 @@ export default function ModelSettingsVisualEditor(props) { }; // 在 return 语句之前,先处理过滤和分页逻辑 - const filteredModels = models.filter((model) => - searchText + const filteredModels = models.filter((model) => { + const keywordMatch = searchText ? model.name.toLowerCase().includes(searchText.toLowerCase()) - : true, - ); + : true; + const conflictMatch = conflictOnly ? model.hasConflict : true; + return keywordMatch && conflictMatch; + }); // 然后基于过滤后的数据计算分页数据 const pagedData = getPagedData(filteredModels, currentPage, pageSize); @@ -152,6 +163,16 @@ export default function ModelSettingsVisualEditor(props) { title: t('模型名称'), dataIndex: 'name', key: 'name', + render: (text, record) => ( + + {text} + {record.hasConflict && ( + + {t('矛盾')} + + )} + + ), }, { title: t('模型固定价格'), @@ -219,9 +240,13 @@ export default function ModelSettingsVisualEditor(props) { return; } setModels((prev) => - prev.map((model) => - model.name === name ? { ...model, [field]: value } : model, - ), + prev.map((model) => { + if (model.name !== name) return model; + const updated = { ...model, [field]: value }; + updated.hasConflict = + updated.price !== '' && (updated.ratio !== '' || updated.completionRatio !== ''); + return updated; + }), ); }; @@ -296,16 +321,18 @@ export default function ModelSettingsVisualEditor(props) { if (existingModelIndex >= 0) { // Update existing model setModels((prev) => - prev.map((model, index) => - index === existingModelIndex - ? { - name: values.name, - price: values.price || '', - ratio: values.ratio || '', - completionRatio: values.completionRatio || '', - } - : model, - ), + prev.map((model, index) => { + if (index !== existingModelIndex) return model; + const updated = { + name: values.name, + price: values.price || '', + ratio: values.ratio || '', + completionRatio: values.completionRatio || '', + }; + updated.hasConflict = + updated.price !== '' && (updated.ratio !== '' || updated.completionRatio !== ''); + return updated; + }), ); setVisible(false); showSuccess(t('更新成功')); @@ -317,15 +344,17 @@ export default function ModelSettingsVisualEditor(props) { return; } - setModels((prev) => [ - { + setModels((prev) => { + const newModel = { name: values.name, price: values.price || '', ratio: values.ratio || '', completionRatio: values.completionRatio || '', - }, - ...prev, - ]); + }; + newModel.hasConflict = + newModel.price !== '' && (newModel.ratio !== '' || newModel.completionRatio !== ''); + return [newModel, ...prev]; + }); setVisible(false); showSuccess(t('添加成功')); } @@ -427,6 +456,15 @@ export default function ModelSettingsVisualEditor(props) { }} style={{ width: 200 }} /> + { + setConflictOnly(e.target.checked); + setCurrentPage(1); + }} + > + {t('仅显示矛盾倍率')} +