2024-12-13 14:10:38 +08:00
|
|
|
|
// ModelSettingsVisualEditor.js
|
|
|
|
|
|
import React, { useEffect, useState } from 'react';
|
|
|
|
|
|
import { Table, Button, Input, Modal, Form, Space } from '@douyinfe/semi-ui';
|
2024-12-13 14:42:02 +08:00
|
|
|
|
import { IconDelete, IconPlus, IconSearch, IconSave } from '@douyinfe/semi-icons';
|
2024-12-13 14:10:38 +08:00
|
|
|
|
import { showError, showSuccess } from '../../../helpers';
|
|
|
|
|
|
import { API } from '../../../helpers';
|
2024-12-14 22:13:31 +08:00
|
|
|
|
import { useTranslation } from 'react-i18next';
|
|
|
|
|
|
|
2024-12-13 14:10:38 +08:00
|
|
|
|
export default function ModelSettingsVisualEditor(props) {
|
2024-12-14 22:13:31 +08:00
|
|
|
|
const { t } = useTranslation();
|
2024-12-13 14:10:38 +08:00
|
|
|
|
const [models, setModels] = useState([]);
|
|
|
|
|
|
const [visible, setVisible] = useState(false);
|
|
|
|
|
|
const [currentModel, setCurrentModel] = useState(null);
|
|
|
|
|
|
const [searchText, setSearchText] = useState('');
|
|
|
|
|
|
const [currentPage, setCurrentPage] = useState(1);
|
2024-12-13 14:29:43 +08:00
|
|
|
|
const [loading, setLoading] = useState(false);
|
2024-12-13 14:10:38 +08:00
|
|
|
|
const pageSize = 10;
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const modelPrice = JSON.parse(props.options.ModelPrice || '{}');
|
|
|
|
|
|
const modelRatio = JSON.parse(props.options.ModelRatio || '{}');
|
|
|
|
|
|
const completionRatio = JSON.parse(props.options.CompletionRatio || '{}');
|
|
|
|
|
|
|
|
|
|
|
|
// 合并所有模型名称
|
|
|
|
|
|
const modelNames = new Set([
|
|
|
|
|
|
...Object.keys(modelPrice),
|
|
|
|
|
|
...Object.keys(modelRatio),
|
|
|
|
|
|
...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]
|
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
|
|
setModels(modelData);
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('JSON解析错误:', error);
|
|
|
|
|
|
}
|
|
|
|
|
|
}, [props.options]);
|
|
|
|
|
|
|
|
|
|
|
|
// 首先声明分页相关的工具函数
|
|
|
|
|
|
const getPagedData = (data, currentPage, pageSize) => {
|
|
|
|
|
|
const start = (currentPage - 1) * pageSize;
|
|
|
|
|
|
const end = start + pageSize;
|
|
|
|
|
|
return data.slice(start, end);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 在 return 语句之前,先处理过滤和分页逻辑
|
|
|
|
|
|
const filteredModels = models.filter(model =>
|
|
|
|
|
|
searchText ? model.name.toLowerCase().includes(searchText.toLowerCase()) : true
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
// 然后基于过滤后的数据计算分页数据
|
|
|
|
|
|
const pagedData = getPagedData(filteredModels, currentPage, pageSize);
|
|
|
|
|
|
|
2024-12-13 14:29:43 +08:00
|
|
|
|
const SubmitData = async () => {
|
2024-12-13 14:42:02 +08:00
|
|
|
|
setLoading(true);
|
2024-12-13 14:10:38 +08:00
|
|
|
|
const output = {
|
|
|
|
|
|
ModelPrice: {},
|
|
|
|
|
|
ModelRatio: {},
|
|
|
|
|
|
CompletionRatio: {}
|
|
|
|
|
|
};
|
|
|
|
|
|
let currentConvertModelName = '';
|
2024-12-13 14:42:02 +08:00
|
|
|
|
|
2024-12-13 14:10:38 +08:00
|
|
|
|
try {
|
2024-12-13 14:29:43 +08:00
|
|
|
|
// 数据转换
|
2024-12-13 14:10:38 +08:00
|
|
|
|
models.forEach(model => {
|
|
|
|
|
|
currentConvertModelName = model.name;
|
2024-12-13 14:42:02 +08:00
|
|
|
|
if (model.price !== '') {
|
|
|
|
|
|
// 如果价格不为空,则转换为浮点数,忽略倍率参数
|
|
|
|
|
|
output.ModelPrice[model.name] = parseFloat(model.price)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if (model.ratio !== '') output.ModelRatio[model.name] = parseFloat(model.ratio);
|
2024-12-19 19:08:04 +08:00
|
|
|
|
if (model.completionRatio !== '') output.CompletionRatio[model.name] = parseFloat(model.completionRatio);
|
2024-12-13 14:42:02 +08:00
|
|
|
|
}
|
2024-12-13 14:10:38 +08:00
|
|
|
|
});
|
2024-12-13 14:42:02 +08:00
|
|
|
|
|
2024-12-13 14:29:43 +08:00
|
|
|
|
// 准备API请求数组
|
|
|
|
|
|
const finalOutput = {
|
|
|
|
|
|
ModelPrice: JSON.stringify(output.ModelPrice, null, 2),
|
2024-12-13 14:42:02 +08:00
|
|
|
|
ModelRatio: JSON.stringify(output.ModelRatio, null, 2),
|
2024-12-13 14:29:43 +08:00
|
|
|
|
CompletionRatio: JSON.stringify(output.CompletionRatio, null, 2)
|
|
|
|
|
|
};
|
2024-12-13 14:42:02 +08:00
|
|
|
|
|
2024-12-13 14:29:43 +08:00
|
|
|
|
const requestQueue = Object.entries(finalOutput).map(([key, value]) => {
|
|
|
|
|
|
return API.put('/api/option/', {
|
|
|
|
|
|
key,
|
|
|
|
|
|
value
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
2024-12-13 14:42:02 +08:00
|
|
|
|
|
2024-12-13 14:29:43 +08:00
|
|
|
|
// 批量处理请求
|
|
|
|
|
|
const results = await Promise.all(requestQueue);
|
2024-12-13 14:42:02 +08:00
|
|
|
|
|
2024-12-13 14:29:43 +08:00
|
|
|
|
// 验证结果
|
|
|
|
|
|
if (requestQueue.length === 1) {
|
|
|
|
|
|
if (results.includes(undefined)) return;
|
|
|
|
|
|
} else if (requestQueue.length > 1) {
|
|
|
|
|
|
if (results.includes(undefined)) {
|
|
|
|
|
|
return showError('部分保存失败,请重试');
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-12-13 14:42:02 +08:00
|
|
|
|
|
2024-12-13 14:29:43 +08:00
|
|
|
|
// 检查每个请求的结果
|
|
|
|
|
|
for (const res of results) {
|
|
|
|
|
|
if (!res.data.success) {
|
|
|
|
|
|
return showError(res.data.message);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-12-13 14:42:02 +08:00
|
|
|
|
|
2024-12-13 14:29:43 +08:00
|
|
|
|
showSuccess('保存成功');
|
|
|
|
|
|
props.refresh();
|
2024-12-13 14:42:02 +08:00
|
|
|
|
|
2024-12-13 14:10:38 +08:00
|
|
|
|
} catch (error) {
|
2024-12-13 14:29:43 +08:00
|
|
|
|
console.error('保存失败:', error);
|
|
|
|
|
|
showError('保存失败,请重试');
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
setLoading(false);
|
2024-12-13 14:10:38 +08:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const columns = [
|
|
|
|
|
|
{
|
2024-12-14 22:13:31 +08:00
|
|
|
|
title: t('模型名称'),
|
2024-12-13 14:10:38 +08:00
|
|
|
|
dataIndex: 'name',
|
|
|
|
|
|
key: 'name',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
2024-12-14 22:13:31 +08:00
|
|
|
|
title: t('模型固定价格'),
|
2024-12-13 14:10:38 +08:00
|
|
|
|
dataIndex: 'price',
|
|
|
|
|
|
key: 'price',
|
|
|
|
|
|
render: (text, record) => (
|
|
|
|
|
|
<Input
|
|
|
|
|
|
value={text}
|
2024-12-14 22:13:31 +08:00
|
|
|
|
placeholder={t('按量计费')}
|
2024-12-13 14:10:38 +08:00
|
|
|
|
onChange={value => updateModel(record.name, 'price', value)}
|
|
|
|
|
|
/>
|
|
|
|
|
|
)
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
2024-12-14 22:13:31 +08:00
|
|
|
|
title: t('模型倍率'),
|
2024-12-13 14:10:38 +08:00
|
|
|
|
dataIndex: 'ratio',
|
|
|
|
|
|
key: 'ratio',
|
|
|
|
|
|
render: (text, record) => (
|
|
|
|
|
|
<Input
|
|
|
|
|
|
value={text}
|
2024-12-14 22:13:31 +08:00
|
|
|
|
placeholder={record.price !== '' ? t('模型倍率') : t('默认补全倍率')}
|
2024-12-13 14:42:02 +08:00
|
|
|
|
disabled={record.price !== ''}
|
2024-12-13 14:10:38 +08:00
|
|
|
|
onChange={value => updateModel(record.name, 'ratio', value)}
|
|
|
|
|
|
/>
|
|
|
|
|
|
)
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
2024-12-14 22:13:31 +08:00
|
|
|
|
title: t('补全倍率'),
|
2024-12-13 14:10:38 +08:00
|
|
|
|
dataIndex: 'completionRatio',
|
|
|
|
|
|
key: 'completionRatio',
|
|
|
|
|
|
render: (text, record) => (
|
|
|
|
|
|
<Input
|
|
|
|
|
|
value={text}
|
2024-12-14 22:13:31 +08:00
|
|
|
|
placeholder={record.price !== '' ? t('补全倍率') : t('默认补全倍率')}
|
2024-12-13 14:42:02 +08:00
|
|
|
|
disabled={record.price !== ''}
|
2024-12-13 14:10:38 +08:00
|
|
|
|
onChange={value => updateModel(record.name, 'completionRatio', value)}
|
|
|
|
|
|
/>
|
|
|
|
|
|
)
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
2024-12-14 22:13:31 +08:00
|
|
|
|
title: t('操作'),
|
2024-12-13 14:10:38 +08:00
|
|
|
|
key: 'action',
|
|
|
|
|
|
render: (_, record) => (
|
|
|
|
|
|
<Button
|
|
|
|
|
|
icon={<IconDelete />}
|
|
|
|
|
|
type="danger"
|
|
|
|
|
|
onClick={() => deleteModel(record.name)}
|
|
|
|
|
|
/>
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
const updateModel = (name, field, value) => {
|
2024-12-13 14:42:02 +08:00
|
|
|
|
if (isNaN(value)) {
|
|
|
|
|
|
showError('请输入数字');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2024-12-13 14:10:38 +08:00
|
|
|
|
setModels(prev =>
|
|
|
|
|
|
prev.map(model =>
|
|
|
|
|
|
model.name === name
|
|
|
|
|
|
? { ...model, [field]: value }
|
|
|
|
|
|
: model
|
|
|
|
|
|
)
|
|
|
|
|
|
);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const deleteModel = (name) => {
|
|
|
|
|
|
setModels(prev => prev.filter(model => model.name !== name));
|
|
|
|
|
|
};
|
|
|
|
|
|
const addModel = (values) => {
|
2024-12-13 14:29:43 +08:00
|
|
|
|
// 检查模型名称是否存在, 如果存在则拒绝添加
|
|
|
|
|
|
if (models.some(model => model.name === values.name)) {
|
|
|
|
|
|
showError('模型名称已存在');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
setModels(prev => [{
|
2024-12-13 14:10:38 +08:00
|
|
|
|
name: values.name,
|
|
|
|
|
|
price: values.price || '',
|
|
|
|
|
|
ratio: values.ratio || '',
|
|
|
|
|
|
completionRatio: values.completionRatio || ''
|
2024-12-13 14:29:43 +08:00
|
|
|
|
}, ...prev]);
|
2024-12-13 14:10:38 +08:00
|
|
|
|
setVisible(false);
|
2024-12-13 14:29:43 +08:00
|
|
|
|
showSuccess('添加成功');
|
2024-12-13 14:10:38 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<>
|
|
|
|
|
|
<Space vertical align="start" style={{ width: '100%' }}>
|
|
|
|
|
|
<Space>
|
|
|
|
|
|
<Button icon={<IconPlus />} onClick={() => setVisible(true)}>
|
2024-12-14 22:13:31 +08:00
|
|
|
|
{t('添加模型')}
|
2024-12-13 14:10:38 +08:00
|
|
|
|
</Button>
|
2024-12-13 14:29:43 +08:00
|
|
|
|
<Button type="primary" icon={<IconSave />} onClick={SubmitData}>
|
2024-12-14 22:13:31 +08:00
|
|
|
|
{t('应用更改')}
|
2024-12-13 14:10:38 +08:00
|
|
|
|
</Button>
|
|
|
|
|
|
<Input
|
|
|
|
|
|
prefix={<IconSearch />}
|
2024-12-14 22:13:31 +08:00
|
|
|
|
placeholder={t('搜索模型名称')}
|
2024-12-13 14:10:38 +08:00
|
|
|
|
value={searchText}
|
|
|
|
|
|
onChange={value => {
|
|
|
|
|
|
setSearchText(value)
|
|
|
|
|
|
setCurrentPage(1);
|
|
|
|
|
|
}}
|
|
|
|
|
|
style={{ width: 200 }}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</Space>
|
|
|
|
|
|
<Table
|
|
|
|
|
|
columns={columns}
|
2024-12-14 22:13:31 +08:00
|
|
|
|
dataSource={pagedData}
|
2024-12-13 14:10:38 +08:00
|
|
|
|
pagination={{
|
|
|
|
|
|
currentPage: currentPage,
|
|
|
|
|
|
pageSize: pageSize,
|
|
|
|
|
|
total: filteredModels.length,
|
|
|
|
|
|
onPageChange: page => setCurrentPage(page),
|
2024-12-14 22:13:31 +08:00
|
|
|
|
formatPageText: (page) =>
|
|
|
|
|
|
t('第 {{start}} - {{end}} 条,共 {{total}} 条', {
|
|
|
|
|
|
start: page.currentStart,
|
|
|
|
|
|
end: page.currentEnd,
|
|
|
|
|
|
total: filteredModels.length
|
|
|
|
|
|
}),
|
2024-12-13 14:10:38 +08:00
|
|
|
|
showTotal: true,
|
|
|
|
|
|
showSizeChanger: false
|
|
|
|
|
|
}}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</Space>
|
|
|
|
|
|
|
|
|
|
|
|
<Modal
|
2024-12-14 22:13:31 +08:00
|
|
|
|
title={t('添加模型')}
|
2024-12-13 14:10:38 +08:00
|
|
|
|
visible={visible}
|
|
|
|
|
|
onCancel={() => setVisible(false)}
|
|
|
|
|
|
onOk={() => {
|
|
|
|
|
|
currentModel && addModel(currentModel);
|
|
|
|
|
|
}}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Form>
|
|
|
|
|
|
<Form.Input
|
|
|
|
|
|
field="name"
|
2024-12-14 22:13:31 +08:00
|
|
|
|
label={t('模型名称')}
|
2024-12-13 14:45:49 +08:00
|
|
|
|
placeholder="strawberry"
|
2024-12-13 14:10:38 +08:00
|
|
|
|
required
|
|
|
|
|
|
onChange={value => setCurrentModel(prev => ({ ...prev, name: value }))}
|
|
|
|
|
|
/>
|
2024-12-14 22:13:31 +08:00
|
|
|
|
<Form.Switch
|
|
|
|
|
|
field="priceMode"
|
|
|
|
|
|
label={<>{t('定价模式')}:{currentModel?.priceMode ? t("固定价格") : t("倍率模式")}</>}
|
|
|
|
|
|
onChange={checked => {
|
|
|
|
|
|
setCurrentModel(prev => ({
|
|
|
|
|
|
...prev,
|
|
|
|
|
|
price: '',
|
|
|
|
|
|
ratio: '',
|
|
|
|
|
|
completionRatio: '',
|
|
|
|
|
|
priceMode: checked
|
|
|
|
|
|
}));
|
|
|
|
|
|
}}
|
2024-12-13 14:10:38 +08:00
|
|
|
|
/>
|
2024-12-14 22:13:31 +08:00
|
|
|
|
{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 }))}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</>
|
|
|
|
|
|
)}
|
2024-12-13 14:10:38 +08:00
|
|
|
|
</Form>
|
|
|
|
|
|
</Modal>
|
|
|
|
|
|
</>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|