feat: Add new model management features
- Implement `/api/channel/models_enabled` endpoint to retrieve enabled models - Add `EnabledListModels` handler in controller - Create new `ModelRatioNotSetEditor` component for managing unset model ratios - Update router to include new models_enabled route - Add internationalization support for new model management UI - Include GPT-4.5 preview model in OpenAI model list
This commit is contained in:
@@ -216,6 +216,13 @@ func DashboardListModels(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func EnabledListModels(c *gin.Context) {
|
||||||
|
c.JSON(200, gin.H{
|
||||||
|
"success": true,
|
||||||
|
"data": model.GetEnabledModels(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func RetrieveModel(c *gin.Context) {
|
func RetrieveModel(c *gin.Context) {
|
||||||
modelId := c.Param("model")
|
modelId := c.Param("model")
|
||||||
if aiModel, ok := openAIModelsMap[modelId]; ok {
|
if aiModel, ok := openAIModelsMap[modelId]; ok {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ var ModelList = []string{
|
|||||||
"chatgpt-4o-latest",
|
"chatgpt-4o-latest",
|
||||||
"gpt-4o", "gpt-4o-2024-05-13", "gpt-4o-2024-08-06", "gpt-4o-2024-11-20",
|
"gpt-4o", "gpt-4o-2024-05-13", "gpt-4o-2024-08-06", "gpt-4o-2024-11-20",
|
||||||
"gpt-4o-mini", "gpt-4o-mini-2024-07-18",
|
"gpt-4o-mini", "gpt-4o-mini-2024-07-18",
|
||||||
|
"gpt-4.5-preview", "gpt-4.5-preview-2025-02-27",
|
||||||
"o1-preview", "o1-preview-2024-09-12",
|
"o1-preview", "o1-preview-2024-09-12",
|
||||||
"o1-mini", "o1-mini-2024-09-12",
|
"o1-mini", "o1-mini-2024-09-12",
|
||||||
"o3-mini", "o3-mini-2025-01-31",
|
"o3-mini", "o3-mini-2025-01-31",
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ func SetApiRouter(router *gin.Engine) {
|
|||||||
channelRoute.GET("/", controller.GetAllChannels)
|
channelRoute.GET("/", controller.GetAllChannels)
|
||||||
channelRoute.GET("/search", controller.SearchChannels)
|
channelRoute.GET("/search", controller.SearchChannels)
|
||||||
channelRoute.GET("/models", controller.ChannelListModels)
|
channelRoute.GET("/models", controller.ChannelListModels)
|
||||||
|
channelRoute.GET("/models_enabled", controller.EnabledListModels)
|
||||||
channelRoute.GET("/:id", controller.GetChannel)
|
channelRoute.GET("/:id", controller.GetChannel)
|
||||||
channelRoute.GET("/test", controller.TestAllChannels)
|
channelRoute.GET("/test", controller.TestAllChannels)
|
||||||
channelRoute.GET("/test/:id", controller.TestChannel)
|
channelRoute.GET("/test/:id", controller.TestChannel)
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import ModelRatioSettings from '../pages/Setting/Operation/ModelRatioSettings.js
|
|||||||
import { API, showError, showSuccess } from '../helpers';
|
import { API, showError, showSuccess } from '../helpers';
|
||||||
import SettingsChats from '../pages/Setting/Operation/SettingsChats.js';
|
import SettingsChats from '../pages/Setting/Operation/SettingsChats.js';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import ModelRatioNotSetEditor from '../pages/Setting/Operation/ModelRationNotSetEditor.js';
|
||||||
|
|
||||||
const OperationSetting = () => {
|
const OperationSetting = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -158,6 +159,9 @@ const OperationSetting = () => {
|
|||||||
<Tabs.TabPane tab={t('可视化倍率设置')} itemKey="visual">
|
<Tabs.TabPane tab={t('可视化倍率设置')} itemKey="visual">
|
||||||
<ModelSettingsVisualEditor options={inputs} refresh={onRefresh} />
|
<ModelSettingsVisualEditor options={inputs} refresh={onRefresh} />
|
||||||
</Tabs.TabPane>
|
</Tabs.TabPane>
|
||||||
|
<Tabs.TabPane tab={t('未设置倍率模型')} itemKey="unset_models">
|
||||||
|
<ModelRatioNotSetEditor options={inputs} refresh={onRefresh} />
|
||||||
|
</Tabs.TabPane>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</Card>
|
</Card>
|
||||||
</Spin>
|
</Spin>
|
||||||
|
|||||||
@@ -1280,5 +1280,42 @@
|
|||||||
"频率限制的周期(分钟)": "Rate limit period (minutes)",
|
"频率限制的周期(分钟)": "Rate limit period (minutes)",
|
||||||
"只包括请求成功的次数": "Only include successful request times",
|
"只包括请求成功的次数": "Only include successful request times",
|
||||||
"保存模型速率限制": "Save model rate limit settings",
|
"保存模型速率限制": "Save model rate limit settings",
|
||||||
"速率限制设置": "Rate limit settings"
|
"速率限制设置": "Rate limit settings",
|
||||||
|
"获取启用模型失败:": "Failed to get enabled models:",
|
||||||
|
"获取启用模型失败": "Failed to get enabled models",
|
||||||
|
"JSON解析错误:": "JSON parsing error:",
|
||||||
|
"保存失败:": "Save failed:",
|
||||||
|
"输入模型倍率": "Enter model ratio",
|
||||||
|
"输入补全倍率": "Enter completion ratio",
|
||||||
|
"请输入数字": "Please enter a number",
|
||||||
|
"模型名称已存在": "Model name already exists",
|
||||||
|
"添加成功": "Added successfully",
|
||||||
|
"请先选择需要批量设置的模型": "Please select models for batch setting first",
|
||||||
|
"请输入模型倍率和补全倍率": "Please enter model ratio and completion ratio",
|
||||||
|
"请输入有效的数字": "Please enter a valid number",
|
||||||
|
"请输入填充值": "Please enter a value",
|
||||||
|
"批量设置成功": "Batch setting successful",
|
||||||
|
"已为 {{count}} 个模型设置{{type}}": "Set {{type}} for {{count}} models",
|
||||||
|
"固定价格": "Fixed Price",
|
||||||
|
"模型倍率和补全倍率": "Model Ratio and Completion Ratio",
|
||||||
|
"批量设置": "Batch Setting",
|
||||||
|
"搜索模型名称": "Search model name",
|
||||||
|
"此页面仅显示未设置价格或倍率的模型,设置后将自动从列表中移除": "This page only shows models without price or ratio settings. After setting, they will be automatically removed from the list",
|
||||||
|
"没有未设置的模型": "No unconfigured models",
|
||||||
|
"定价模式": "Pricing Mode",
|
||||||
|
"固定价格(每次)": "Fixed Price (per use)",
|
||||||
|
"输入每次价格": "Enter per-use price",
|
||||||
|
"批量设置模型参数": "Batch Set Model Parameters",
|
||||||
|
"设置类型": "Setting Type",
|
||||||
|
"模型倍率值": "Model Ratio Value",
|
||||||
|
"补全倍率值": "Completion Ratio Value",
|
||||||
|
"请输入模型倍率": "Enter model ratio",
|
||||||
|
"请输入补全倍率": "Enter completion ratio",
|
||||||
|
"请输入数值": "Enter a value",
|
||||||
|
"将为选中的 ": "Will set for selected ",
|
||||||
|
" 个模型设置相同的值": " models with the same value",
|
||||||
|
"当前设置类型: ": "Current setting type: ",
|
||||||
|
"固定价格值": "Fixed Price Value",
|
||||||
|
"未设置倍率模型": "Models without ratio settings",
|
||||||
|
"模型倍率和补全倍率同时设置": "Both model ratio and completion ratio are set"
|
||||||
}
|
}
|
||||||
|
|||||||
550
web/src/pages/Setting/Operation/ModelRationNotSetEditor.js
Normal file
550
web/src/pages/Setting/Operation/ModelRationNotSetEditor.js
Normal file
@@ -0,0 +1,550 @@
|
|||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { Table, Button, Input, Modal, Form, Space, Typography, Radio, Notification } from '@douyinfe/semi-ui';
|
||||||
|
import { IconDelete, IconPlus, IconSearch, IconSave, IconBolt } from '@douyinfe/semi-icons';
|
||||||
|
import { showError, showSuccess } from '../../../helpers';
|
||||||
|
import { API } from '../../../helpers';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
export default function ModelRatioNotSetEditor(props) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [models, setModels] = useState([]);
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
const [batchVisible, setBatchVisible] = useState(false);
|
||||||
|
const [currentModel, setCurrentModel] = useState(null);
|
||||||
|
const [searchText, setSearchText] = useState('');
|
||||||
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
|
const [pageSize, setPageSize] = useState(10);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [enabledModels, setEnabledModels] = useState([]);
|
||||||
|
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
|
||||||
|
const [batchFillType, setBatchFillType] = useState('ratio');
|
||||||
|
const [batchFillValue, setBatchFillValue] = useState('');
|
||||||
|
const [batchRatioValue, setBatchRatioValue] = useState('');
|
||||||
|
const [batchCompletionRatioValue, setBatchCompletionRatioValue] = useState('');
|
||||||
|
const { Text } = Typography;
|
||||||
|
// 定义可选的每页显示条数
|
||||||
|
const pageSizeOptions = [10, 20, 50, 100];
|
||||||
|
|
||||||
|
const getAllEnabledModels = async () => {
|
||||||
|
try {
|
||||||
|
const res = await API.get('/api/channel/models_enabled');
|
||||||
|
const { success, message, data } = res.data;
|
||||||
|
if (success) {
|
||||||
|
setEnabledModels(data);
|
||||||
|
} else {
|
||||||
|
showError(message);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(t('获取启用模型失败:'), error);
|
||||||
|
showError(t('获取启用模型失败'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// 获取所有启用的模型
|
||||||
|
getAllEnabledModels();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
try {
|
||||||
|
const modelPrice = JSON.parse(props.options.ModelPrice || '{}');
|
||||||
|
const modelRatio = JSON.parse(props.options.ModelRatio || '{}');
|
||||||
|
const completionRatio = JSON.parse(props.options.CompletionRatio || '{}');
|
||||||
|
|
||||||
|
// 找出所有未设置价格和倍率的模型
|
||||||
|
const unsetModels = enabledModels.filter(modelName => {
|
||||||
|
const hasPrice = modelPrice[modelName] !== undefined;
|
||||||
|
const hasRatio = modelRatio[modelName] !== undefined;
|
||||||
|
const hasCompletionRatio = completionRatio[modelName] !== undefined;
|
||||||
|
|
||||||
|
// 如果模型既没有价格也没有倍率设置,则显示
|
||||||
|
return !(hasPrice || (hasRatio && hasCompletionRatio));
|
||||||
|
});
|
||||||
|
|
||||||
|
// 创建模型数据
|
||||||
|
const modelData = unsetModels.map(name => ({
|
||||||
|
name,
|
||||||
|
price: '',
|
||||||
|
ratio: '',
|
||||||
|
completionRatio: ''
|
||||||
|
}));
|
||||||
|
|
||||||
|
setModels(modelData);
|
||||||
|
// 清空选择
|
||||||
|
setSelectedRowKeys([]);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(t('JSON解析错误:'), error);
|
||||||
|
}
|
||||||
|
}, [props.options, enabledModels]);
|
||||||
|
|
||||||
|
// 首先声明分页相关的工具函数
|
||||||
|
const getPagedData = (data, currentPage, pageSize) => {
|
||||||
|
const start = (currentPage - 1) * pageSize;
|
||||||
|
const end = start + pageSize;
|
||||||
|
return data.slice(start, end);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理页面大小变化
|
||||||
|
const handlePageSizeChange = (size) => {
|
||||||
|
setPageSize(size);
|
||||||
|
// 重新计算当前页,避免数据丢失
|
||||||
|
const totalPages = Math.ceil(filteredModels.length / size);
|
||||||
|
if (currentPage > totalPages) {
|
||||||
|
setCurrentPage(totalPages || 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 在 return 语句之前,先处理过滤和分页逻辑
|
||||||
|
const filteredModels = models.filter(model =>
|
||||||
|
searchText ? model.name.toLowerCase().includes(searchText.toLowerCase()) : true
|
||||||
|
);
|
||||||
|
|
||||||
|
// 然后基于过滤后的数据计算分页数据
|
||||||
|
const pagedData = getPagedData(filteredModels, currentPage, pageSize);
|
||||||
|
|
||||||
|
const SubmitData = async () => {
|
||||||
|
setLoading(true);
|
||||||
|
const output = {
|
||||||
|
ModelPrice: JSON.parse(props.options.ModelPrice || '{}'),
|
||||||
|
ModelRatio: JSON.parse(props.options.ModelRatio || '{}'),
|
||||||
|
CompletionRatio: JSON.parse(props.options.CompletionRatio || '{}')
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 数据转换 - 只处理已修改的模型
|
||||||
|
models.forEach(model => {
|
||||||
|
// 只有当用户设置了值时才更新
|
||||||
|
if (model.price !== '') {
|
||||||
|
// 如果价格不为空,则转换为浮点数,忽略倍率参数
|
||||||
|
output.ModelPrice[model.name] = parseFloat(model.price);
|
||||||
|
} else {
|
||||||
|
if (model.ratio !== '') output.ModelRatio[model.name] = parseFloat(model.ratio);
|
||||||
|
if (model.completionRatio !== '') output.CompletionRatio[model.name] = parseFloat(model.completionRatio);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 准备API请求数组
|
||||||
|
const finalOutput = {
|
||||||
|
ModelPrice: JSON.stringify(output.ModelPrice, null, 2),
|
||||||
|
ModelRatio: JSON.stringify(output.ModelRatio, null, 2),
|
||||||
|
CompletionRatio: JSON.stringify(output.CompletionRatio, null, 2)
|
||||||
|
};
|
||||||
|
|
||||||
|
const requestQueue = Object.entries(finalOutput).map(([key, value]) => {
|
||||||
|
return API.put('/api/option/', {
|
||||||
|
key,
|
||||||
|
value
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 批量处理请求
|
||||||
|
const results = await Promise.all(requestQueue);
|
||||||
|
|
||||||
|
// 验证结果
|
||||||
|
if (requestQueue.length === 1) {
|
||||||
|
if (results.includes(undefined)) return;
|
||||||
|
} else if (requestQueue.length > 1) {
|
||||||
|
if (results.includes(undefined)) {
|
||||||
|
return showError(t('部分保存失败,请重试'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查每个请求的结果
|
||||||
|
for (const res of results) {
|
||||||
|
if (!res.data.success) {
|
||||||
|
return showError(res.data.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showSuccess(t('保存成功'));
|
||||||
|
props.refresh();
|
||||||
|
// 重新获取未设置的模型
|
||||||
|
getAllEnabledModels();
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error(t('保存失败:'), error);
|
||||||
|
showError(t('保存失败,请重试'));
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: t('模型名称'),
|
||||||
|
dataIndex: 'name',
|
||||||
|
key: 'name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('模型固定价格'),
|
||||||
|
dataIndex: 'price',
|
||||||
|
key: 'price',
|
||||||
|
render: (text, record) => (
|
||||||
|
<Input
|
||||||
|
value={text}
|
||||||
|
placeholder={t('按量计费')}
|
||||||
|
onChange={value => updateModel(record.name, 'price', value)}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('模型倍率'),
|
||||||
|
dataIndex: 'ratio',
|
||||||
|
key: 'ratio',
|
||||||
|
render: (text, record) => (
|
||||||
|
<Input
|
||||||
|
value={text}
|
||||||
|
placeholder={record.price !== '' ? t('模型倍率') : t('输入模型倍率')}
|
||||||
|
disabled={record.price !== ''}
|
||||||
|
onChange={value => updateModel(record.name, 'ratio', value)}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('补全倍率'),
|
||||||
|
dataIndex: 'completionRatio',
|
||||||
|
key: 'completionRatio',
|
||||||
|
render: (text, record) => (
|
||||||
|
<Input
|
||||||
|
value={text}
|
||||||
|
placeholder={record.price !== '' ? t('补全倍率') : t('输入补全倍率')}
|
||||||
|
disabled={record.price !== ''}
|
||||||
|
onChange={value => updateModel(record.name, 'completionRatio', value)}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const updateModel = (name, field, value) => {
|
||||||
|
if (value !== '' && isNaN(value)) {
|
||||||
|
showError(t('请输入数字'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setModels(prev =>
|
||||||
|
prev.map(model =>
|
||||||
|
model.name === name
|
||||||
|
? { ...model, [field]: value }
|
||||||
|
: model
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const addModel = (values) => {
|
||||||
|
// 检查模型名称是否存在, 如果存在则拒绝添加
|
||||||
|
if (models.some(model => model.name === values.name)) {
|
||||||
|
showError(t('模型名称已存在'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setModels(prev => [{
|
||||||
|
name: values.name,
|
||||||
|
price: values.price || '',
|
||||||
|
ratio: values.ratio || '',
|
||||||
|
completionRatio: values.completionRatio || ''
|
||||||
|
}, ...prev]);
|
||||||
|
setVisible(false);
|
||||||
|
showSuccess(t('添加成功'));
|
||||||
|
};
|
||||||
|
|
||||||
|
// 批量填充功能
|
||||||
|
const handleBatchFill = () => {
|
||||||
|
if (selectedRowKeys.length === 0) {
|
||||||
|
showError(t('请先选择需要批量设置的模型'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (batchFillType === 'bothRatio') {
|
||||||
|
if (batchRatioValue === '' || batchCompletionRatioValue === '') {
|
||||||
|
showError(t('请输入模型倍率和补全倍率'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isNaN(batchRatioValue) || isNaN(batchCompletionRatioValue)) {
|
||||||
|
showError(t('请输入有效的数字'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (batchFillValue === '') {
|
||||||
|
showError(t('请输入填充值'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isNaN(batchFillValue)) {
|
||||||
|
showError(t('请输入有效的数字'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据选择的类型批量更新模型
|
||||||
|
setModels(prev =>
|
||||||
|
prev.map(model => {
|
||||||
|
if (selectedRowKeys.includes(model.name)) {
|
||||||
|
if (batchFillType === 'price') {
|
||||||
|
return {
|
||||||
|
...model,
|
||||||
|
price: batchFillValue,
|
||||||
|
ratio: '',
|
||||||
|
completionRatio: ''
|
||||||
|
};
|
||||||
|
} else if (batchFillType === 'ratio') {
|
||||||
|
return {
|
||||||
|
...model,
|
||||||
|
price: '',
|
||||||
|
ratio: batchFillValue
|
||||||
|
};
|
||||||
|
} else if (batchFillType === 'completionRatio') {
|
||||||
|
return {
|
||||||
|
...model,
|
||||||
|
price: '',
|
||||||
|
completionRatio: batchFillValue
|
||||||
|
};
|
||||||
|
} else if (batchFillType === 'bothRatio') {
|
||||||
|
return {
|
||||||
|
...model,
|
||||||
|
price: '',
|
||||||
|
ratio: batchRatioValue,
|
||||||
|
completionRatio: batchCompletionRatioValue
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return model;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
setBatchVisible(false);
|
||||||
|
Notification.success({
|
||||||
|
title: t('批量设置成功'),
|
||||||
|
content: t('已为 {{count}} 个模型设置{{type}}', {
|
||||||
|
count: selectedRowKeys.length,
|
||||||
|
type: batchFillType === 'price' ? t('固定价格') :
|
||||||
|
batchFillType === 'ratio' ? t('模型倍率') :
|
||||||
|
batchFillType === 'completionRatio' ? t('补全倍率') : t('模型倍率和补全倍率')
|
||||||
|
}),
|
||||||
|
duration: 3,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBatchTypeChange = (value) => {
|
||||||
|
console.log(t('Changing batch type to:'), value);
|
||||||
|
setBatchFillType(value);
|
||||||
|
|
||||||
|
// 切换类型时清空对应的值
|
||||||
|
if (value !== 'bothRatio') {
|
||||||
|
setBatchFillValue('');
|
||||||
|
} else {
|
||||||
|
setBatchRatioValue('');
|
||||||
|
setBatchCompletionRatioValue('');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const rowSelection = {
|
||||||
|
selectedRowKeys,
|
||||||
|
onChange: (selectedKeys) => {
|
||||||
|
setSelectedRowKeys(selectedKeys);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Space vertical align="start" style={{ width: '100%' }}>
|
||||||
|
<Space>
|
||||||
|
<Button icon={<IconPlus />} onClick={() => setVisible(true)}>
|
||||||
|
{t('添加模型')}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
icon={<IconBolt />}
|
||||||
|
type="secondary"
|
||||||
|
onClick={() => setBatchVisible(true)}
|
||||||
|
disabled={selectedRowKeys.length === 0}
|
||||||
|
>
|
||||||
|
{t('批量设置')} ({selectedRowKeys.length})
|
||||||
|
</Button>
|
||||||
|
<Button type="primary" icon={<IconSave />} onClick={SubmitData} loading={loading}>
|
||||||
|
{t('应用更改')}
|
||||||
|
</Button>
|
||||||
|
<Input
|
||||||
|
prefix={<IconSearch />}
|
||||||
|
placeholder={t('搜索模型名称')}
|
||||||
|
value={searchText}
|
||||||
|
onChange={value => {
|
||||||
|
setSearchText(value)
|
||||||
|
setCurrentPage(1);
|
||||||
|
}}
|
||||||
|
style={{ width: 200 }}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
|
||||||
|
<Text>{t('此页面仅显示未设置价格或倍率的模型,设置后将自动从列表中移除')}</Text>
|
||||||
|
|
||||||
|
<Table
|
||||||
|
columns={columns}
|
||||||
|
dataSource={pagedData}
|
||||||
|
rowSelection={rowSelection}
|
||||||
|
rowKey="name"
|
||||||
|
pagination={{
|
||||||
|
currentPage: currentPage,
|
||||||
|
pageSize: pageSize,
|
||||||
|
total: filteredModels.length,
|
||||||
|
onPageChange: page => setCurrentPage(page),
|
||||||
|
onPageSizeChange: handlePageSizeChange,
|
||||||
|
pageSizeOptions: pageSizeOptions,
|
||||||
|
formatPageText: (page) =>
|
||||||
|
t('第 {{start}} - {{end}} 条,共 {{total}} 条', {
|
||||||
|
start: page.currentStart,
|
||||||
|
end: page.currentEnd,
|
||||||
|
total: filteredModels.length
|
||||||
|
}),
|
||||||
|
showTotal: true,
|
||||||
|
showSizeChanger: true
|
||||||
|
}}
|
||||||
|
empty={
|
||||||
|
<div style={{ textAlign: 'center', padding: '20px' }}>
|
||||||
|
{t('没有未设置的模型')}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
|
||||||
|
{/* 添加模型弹窗 */}
|
||||||
|
<Modal
|
||||||
|
title={t('添加模型')}
|
||||||
|
visible={visible}
|
||||||
|
onCancel={() => setVisible(false)}
|
||||||
|
onOk={() => {
|
||||||
|
currentModel && addModel(currentModel);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Form>
|
||||||
|
<Form.Input
|
||||||
|
field="name"
|
||||||
|
label={t('模型名称')}
|
||||||
|
placeholder="strawberry"
|
||||||
|
required
|
||||||
|
onChange={value => setCurrentModel(prev => ({ ...prev, name: value }))}
|
||||||
|
/>
|
||||||
|
<Form.Switch
|
||||||
|
field="priceMode"
|
||||||
|
label={<>{t('定价模式')}:{currentModel?.priceMode ? t("固定价格") : t("倍率模式")}</>}
|
||||||
|
onChange={checked => {
|
||||||
|
setCurrentModel(prev => ({
|
||||||
|
...prev,
|
||||||
|
price: '',
|
||||||
|
ratio: '',
|
||||||
|
completionRatio: '',
|
||||||
|
priceMode: checked
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{currentModel?.priceMode ? (
|
||||||
|
<Form.Input
|
||||||
|
field="price"
|
||||||
|
label={t('固定价格(每次)')}
|
||||||
|
placeholder={t('输入每次价格')}
|
||||||
|
onChange={value => setCurrentModel(prev => ({ ...prev, price: value }))}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Form.Input
|
||||||
|
field="ratio"
|
||||||
|
label={t('模型倍率')}
|
||||||
|
placeholder={t('输入模型倍率')}
|
||||||
|
onChange={value => setCurrentModel(prev => ({ ...prev, ratio: value }))}
|
||||||
|
/>
|
||||||
|
<Form.Input
|
||||||
|
field="completionRatio"
|
||||||
|
label={t('补全倍率')}
|
||||||
|
placeholder={t('输入补全价格')}
|
||||||
|
onChange={value => setCurrentModel(prev => ({ ...prev, completionRatio: value }))}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Form>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
{/* 批量设置弹窗 */}
|
||||||
|
<Modal
|
||||||
|
title={t('批量设置模型参数')}
|
||||||
|
visible={batchVisible}
|
||||||
|
onCancel={() => setBatchVisible(false)}
|
||||||
|
onOk={handleBatchFill}
|
||||||
|
width={500}
|
||||||
|
>
|
||||||
|
<Form>
|
||||||
|
<Form.Section text={t('设置类型')}>
|
||||||
|
<div style={{ marginBottom: '16px' }}>
|
||||||
|
<Space>
|
||||||
|
<Radio
|
||||||
|
checked={batchFillType === 'price'}
|
||||||
|
onChange={() => handleBatchTypeChange('price')}
|
||||||
|
>
|
||||||
|
{t('固定价格')}
|
||||||
|
</Radio>
|
||||||
|
<Radio
|
||||||
|
checked={batchFillType === 'ratio'}
|
||||||
|
onChange={() => handleBatchTypeChange('ratio')}
|
||||||
|
>
|
||||||
|
{t('模型倍率')}
|
||||||
|
</Radio>
|
||||||
|
<Radio
|
||||||
|
checked={batchFillType === 'completionRatio'}
|
||||||
|
onChange={() => handleBatchTypeChange('completionRatio')}
|
||||||
|
>
|
||||||
|
{t('补全倍率')}
|
||||||
|
</Radio>
|
||||||
|
<Radio
|
||||||
|
checked={batchFillType === 'bothRatio'}
|
||||||
|
onChange={() => handleBatchTypeChange('bothRatio')}
|
||||||
|
>
|
||||||
|
{t('模型倍率和补全倍率同时设置')}
|
||||||
|
</Radio>
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
</Form.Section>
|
||||||
|
|
||||||
|
{batchFillType === 'bothRatio' ? (
|
||||||
|
<>
|
||||||
|
<Form.Input
|
||||||
|
field="batchRatioValue"
|
||||||
|
label={t('模型倍率值')}
|
||||||
|
placeholder={t('请输入模型倍率')}
|
||||||
|
value={batchRatioValue}
|
||||||
|
onChange={value => setBatchRatioValue(value)}
|
||||||
|
/>
|
||||||
|
<Form.Input
|
||||||
|
field="batchCompletionRatioValue"
|
||||||
|
label={t('补全倍率值')}
|
||||||
|
placeholder={t('请输入补全倍率')}
|
||||||
|
value={batchCompletionRatioValue}
|
||||||
|
onChange={value => setBatchCompletionRatioValue(value)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<Form.Input
|
||||||
|
field="batchFillValue"
|
||||||
|
label={
|
||||||
|
batchFillType === 'price'
|
||||||
|
? t('固定价格值')
|
||||||
|
: batchFillType === 'ratio'
|
||||||
|
? t('模型倍率值')
|
||||||
|
: t('补全倍率值')
|
||||||
|
}
|
||||||
|
placeholder={t('请输入数值')}
|
||||||
|
value={batchFillValue}
|
||||||
|
onChange={value => setBatchFillValue(value)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Text type="tertiary">
|
||||||
|
{t('将为选中的 ')} <Text strong>{selectedRowKeys.length}</Text> {t(' 个模型设置相同的值')}
|
||||||
|
</Text>
|
||||||
|
<div style={{ marginTop: '8px' }}>
|
||||||
|
<Text type="tertiary">
|
||||||
|
{t('当前设置类型: ')} <Text strong>{
|
||||||
|
batchFillType === 'price' ? t('固定价格') :
|
||||||
|
batchFillType === 'ratio' ? t('模型倍率') :
|
||||||
|
batchFillType === 'completionRatio' ? t('补全倍率') : t('模型倍率和补全倍率')
|
||||||
|
}</Text>
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user