🎛️ feat(web): add “Conflict Rates” filter & highlight in Model Settings Visual Editor (#1286)
Introduce the ability to quickly locate models with conflicting billing configurations. Key points • Added `hasConflict` flag to detect models that define both a fixed price (`ModelPrice`) and any ratio (`ModelRatio` or `CompletionRatio`). • Added “Show Only Conflict Rates” `Checkbox` to toolbar; filtering logic now supports keyword + conflict filtering. • Display a red `Tag` beside the model name when a conflict is detected for immediate visual feedback. • Kept `hasConflict` state in sync during add, update and delete operations. • Imported `Checkbox` and `Tag` from **@douyinfe/semi-ui**. • Minor UI tweaks (circle tag style, margin) for consistency. This enhancement helps administrators swiftly identify and resolve incompatible pricing rules, addressing the need discussed in issue #1286.
This commit is contained in:
@@ -1726,5 +1726,7 @@
|
||||
"放大编辑": "Expand editor",
|
||||
"编辑公告内容": "Edit announcement content",
|
||||
"自适应列表": "Adaptive list",
|
||||
"紧凑列表": "Compact list"
|
||||
"紧凑列表": "Compact list",
|
||||
"仅显示矛盾倍率": "Only show conflicting ratios",
|
||||
"矛盾": "Conflict"
|
||||
}
|
||||
@@ -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) => (
|
||||
<span>
|
||||
{text}
|
||||
{record.hasConflict && (
|
||||
<Tag color='red' shape='circle' className='ml-2'>
|
||||
{t('矛盾')}
|
||||
</Tag>
|
||||
)}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
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 }}
|
||||
/>
|
||||
<Checkbox
|
||||
checked={conflictOnly}
|
||||
onChange={(e) => {
|
||||
setConflictOnly(e.target.checked);
|
||||
setCurrentPage(1);
|
||||
}}
|
||||
>
|
||||
{t('仅显示矛盾倍率')}
|
||||
</Checkbox>
|
||||
</Space>
|
||||
<Table
|
||||
columns={columns}
|
||||
|
||||
Reference in New Issue
Block a user