feat: Enhance Operation Settings with Group and Model Ratio Management
- Added new components for GroupRatioSettings and ModelRatioSettings to manage group and model ratios. - Integrated tabs in OperationSetting to switch between model and visual ratio settings. - Updated translations for new settings and improved existing ones in the English locale file. - Refactored ModelSettingsVisualEditor to support dynamic pricing and ratio configurations. This update improves the user interface for managing operational settings, enhancing usability and localization support.
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { Card, Spin } from '@douyinfe/semi-ui';
|
import { Card, Spin, Tabs } from '@douyinfe/semi-ui';
|
||||||
import SettingsGeneral from '../pages/Setting/Operation/SettingsGeneral.js';
|
import SettingsGeneral from '../pages/Setting/Operation/SettingsGeneral.js';
|
||||||
import SettingsDrawing from '../pages/Setting/Operation/SettingsDrawing.js';
|
import SettingsDrawing from '../pages/Setting/Operation/SettingsDrawing.js';
|
||||||
import SettingsSensitiveWords from '../pages/Setting/Operation/SettingsSensitiveWords.js';
|
import SettingsSensitiveWords from '../pages/Setting/Operation/SettingsSensitiveWords.js';
|
||||||
@@ -9,11 +9,16 @@ import SettingsMonitoring from '../pages/Setting/Operation/SettingsMonitoring.js
|
|||||||
import SettingsCreditLimit from '../pages/Setting/Operation/SettingsCreditLimit.js';
|
import SettingsCreditLimit from '../pages/Setting/Operation/SettingsCreditLimit.js';
|
||||||
import SettingsMagnification from '../pages/Setting/Operation/SettingsMagnification.js';
|
import SettingsMagnification from '../pages/Setting/Operation/SettingsMagnification.js';
|
||||||
import ModelSettingsVisualEditor from '../pages/Setting/Operation/ModelSettingsVisualEditor.js';
|
import ModelSettingsVisualEditor from '../pages/Setting/Operation/ModelSettingsVisualEditor.js';
|
||||||
|
import GroupRatioSettings from '../pages/Setting/Operation/GroupRatioSettings.js';
|
||||||
|
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';
|
||||||
|
|
||||||
const OperationSetting = () => {
|
const OperationSetting = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
let [inputs, setInputs] = useState({
|
let [inputs, setInputs] = useState({
|
||||||
QuotaForNewUser: 0,
|
QuotaForNewUser: 0,
|
||||||
QuotaForInviter: 0,
|
QuotaForInviter: 0,
|
||||||
@@ -138,13 +143,20 @@ const OperationSetting = () => {
|
|||||||
<Card style={{ marginTop: '10px' }}>
|
<Card style={{ marginTop: '10px' }}>
|
||||||
<SettingsChats options={inputs} refresh={onRefresh} />
|
<SettingsChats options={inputs} refresh={onRefresh} />
|
||||||
</Card>
|
</Card>
|
||||||
{/* 倍率设置 */}
|
{/* 分组倍率设置 */}
|
||||||
<Card style={{ marginTop: '10px' }}>
|
<Card style={{ marginTop: '10px' }}>
|
||||||
<SettingsMagnification options={inputs} refresh={onRefresh} />
|
<GroupRatioSettings options={inputs} refresh={onRefresh} />
|
||||||
</Card>
|
</Card>
|
||||||
{/*可视化倍率设置*/}
|
{/* 合并模型倍率设置和可视化倍率设置 */}
|
||||||
<Card style={{ marginTop: '10px' }}>
|
<Card style={{ marginTop: '10px' }}>
|
||||||
<ModelSettingsVisualEditor options={inputs} refresh={onRefresh} />
|
<Tabs type="line">
|
||||||
|
<Tabs.TabPane tab={t('模型倍率设置')} itemKey="model">
|
||||||
|
<ModelRatioSettings options={inputs} refresh={onRefresh} />
|
||||||
|
</Tabs.TabPane>
|
||||||
|
<Tabs.TabPane tab={t('可视化倍率设置')} itemKey="visual">
|
||||||
|
<ModelSettingsVisualEditor options={inputs} refresh={onRefresh} />
|
||||||
|
</Tabs.TabPane>
|
||||||
|
</Tabs>
|
||||||
</Card>
|
</Card>
|
||||||
</Spin>
|
</Spin>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -1161,7 +1161,7 @@
|
|||||||
"默认折叠侧边栏": "Default collapse sidebar",
|
"默认折叠侧边栏": "Default collapse sidebar",
|
||||||
"聊天链接功能已经弃用,请使用下方聊天设置功能": "Chat link function has been deprecated, please use the chat settings below",
|
"聊天链接功能已经弃用,请使用下方聊天设置功能": "Chat link function has been deprecated, please use the chat settings below",
|
||||||
"你似乎并没有修改什么": "You seem to have not modified anything",
|
"你似乎并没有修改什么": "You seem to have not modified anything",
|
||||||
"令牌聊天设置": "Token chat settings",
|
"令牌聊天设置": "Chat settings",
|
||||||
"必须将上方聊天链接全部设置为空,才能使用下方聊天设置功能": "Must set all chat links above to empty to use the chat settings below",
|
"必须将上方聊天链接全部设置为空,才能使用下方聊天设置功能": "Must set all chat links above to empty to use the chat settings below",
|
||||||
"链接中的{key}将自动替换为sk-xxxx,{address}将自动替换为系统设置的服务器地址,末尾不带/和/v1": "The {key} in the link will be automatically replaced with sk-xxxx, the {address} will be automatically replaced with the server address in system settings, and the end will not have / and /v1",
|
"链接中的{key}将自动替换为sk-xxxx,{address}将自动替换为系统设置的服务器地址,末尾不带/和/v1": "The {key} in the link will be automatically replaced with sk-xxxx, the {address} will be automatically replaced with the server address in system settings, and the end will not have / and /v1",
|
||||||
"聊天配置": "Chat configuration",
|
"聊天配置": "Chat configuration",
|
||||||
@@ -1217,5 +1217,19 @@
|
|||||||
"确定要修改所有子渠道权重为 ": "Confirm to modify all sub-channel weights to ",
|
"确定要修改所有子渠道权重为 ": "Confirm to modify all sub-channel weights to ",
|
||||||
" 吗?": "?",
|
" 吗?": "?",
|
||||||
"修改子渠道优先级": "Modify sub-channel priority",
|
"修改子渠道优先级": "Modify sub-channel priority",
|
||||||
"确定要修改所有子渠道优先级为 ": "Confirm to modify all sub-channel priorities to "
|
"确定要修改所有子渠道优先级为 ": "Confirm to modify all sub-channel priorities to ",
|
||||||
|
"分组设置": "Group settings",
|
||||||
|
"用户可选分组": "User selectable groups",
|
||||||
|
"保存分组倍率设置": "Save group ratio settings",
|
||||||
|
"模型倍率设置": "Model ratio settings",
|
||||||
|
"可视化倍率设置": "Visual model ratio settings",
|
||||||
|
"确定重置模型倍率吗?": "Confirm to reset model ratio?",
|
||||||
|
"模型固定价格": "Model price per call",
|
||||||
|
"模型补全倍率(仅对自定义模型有效)": "Model completion ratio (only effective for custom models)",
|
||||||
|
"保存模型倍率设置": "Save model ratio settings",
|
||||||
|
"重置模型倍率": "Reset model ratio",
|
||||||
|
"一次调用消耗多少刀,优先级大于模型倍率": "How much USD one call costs, priority over model ratio",
|
||||||
|
"仅对自定义模型有效": "Only effective for custom models",
|
||||||
|
"添加模型": "Add model",
|
||||||
|
"应用更改": "Apply changes"
|
||||||
}
|
}
|
||||||
131
web/src/pages/Setting/Operation/GroupRatioSettings.js
Normal file
131
web/src/pages/Setting/Operation/GroupRatioSettings.js
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
import React, { useEffect, useState, useRef } from 'react';
|
||||||
|
import { Button, Col, Form, Row, Spin } from '@douyinfe/semi-ui';
|
||||||
|
import {
|
||||||
|
compareObjects,
|
||||||
|
API,
|
||||||
|
showError,
|
||||||
|
showSuccess,
|
||||||
|
showWarning,
|
||||||
|
verifyJSON,
|
||||||
|
} from '../../../helpers';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
export default function GroupRatioSettings(props) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [inputs, setInputs] = useState({
|
||||||
|
GroupRatio: '',
|
||||||
|
UserUsableGroups: ''
|
||||||
|
});
|
||||||
|
const refForm = useRef();
|
||||||
|
const [inputsRow, setInputsRow] = useState(inputs);
|
||||||
|
|
||||||
|
async function onSubmit() {
|
||||||
|
try {
|
||||||
|
await refForm.current.validate().then(() => {
|
||||||
|
const updateArray = compareObjects(inputs, inputsRow);
|
||||||
|
if (!updateArray.length) return showWarning(t('你似乎并没有修改什么'));
|
||||||
|
|
||||||
|
const requestQueue = updateArray.map((item) => {
|
||||||
|
const value = typeof inputs[item.key] === 'boolean'
|
||||||
|
? String(inputs[item.key])
|
||||||
|
: inputs[item.key];
|
||||||
|
return API.put('/api/option/', { key: item.key, value });
|
||||||
|
});
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
Promise.all(requestQueue)
|
||||||
|
.then((res) => {
|
||||||
|
if (res.includes(undefined)) {
|
||||||
|
return showError(requestQueue.length > 1 ? t('部分保存失败,请重试') : t('保存失败'));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < res.length; i++) {
|
||||||
|
if (!res[i].data.success) {
|
||||||
|
return showError(res[i].data.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showSuccess(t('保存成功'));
|
||||||
|
props.refresh();
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Unexpected error:', error);
|
||||||
|
showError(t('保存失败,请重试'));
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setLoading(false);
|
||||||
|
});
|
||||||
|
}).catch(() => {
|
||||||
|
showError(t('请检查输入'));
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
showError(t('请检查输入'));
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const currentInputs = {};
|
||||||
|
for (let key in props.options) {
|
||||||
|
if (Object.keys(inputs).includes(key)) {
|
||||||
|
currentInputs[key] = props.options[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setInputs(currentInputs);
|
||||||
|
setInputsRow(structuredClone(currentInputs));
|
||||||
|
refForm.current.setValues(currentInputs);
|
||||||
|
}, [props.options]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Spin spinning={loading}>
|
||||||
|
<Form
|
||||||
|
values={inputs}
|
||||||
|
getFormApi={(formAPI) => (refForm.current = formAPI)}
|
||||||
|
style={{ marginBottom: 15 }}
|
||||||
|
>
|
||||||
|
<Form.Section text={t('分组设置')}>
|
||||||
|
<Row gutter={16}>
|
||||||
|
<Col span={16}>
|
||||||
|
<Form.TextArea
|
||||||
|
label={t('分组倍率')}
|
||||||
|
placeholder={t('为一个 JSON 文本,键为分组名称,值为倍率')}
|
||||||
|
field={'GroupRatio'}
|
||||||
|
autosize={{ minRows: 6, maxRows: 12 }}
|
||||||
|
trigger='blur'
|
||||||
|
stopValidateWithError
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
validator: (rule, value) => verifyJSON(value),
|
||||||
|
message: t('不是合法的 JSON 字符串')
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
onChange={(value) => setInputs({ ...inputs, GroupRatio: value })}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row gutter={16}>
|
||||||
|
<Col span={16}>
|
||||||
|
<Form.TextArea
|
||||||
|
label={t('用户可选分组')}
|
||||||
|
placeholder={t('为一个 JSON 文本,键为分组名称,值为分组描述')}
|
||||||
|
field={'UserUsableGroups'}
|
||||||
|
autosize={{ minRows: 6, maxRows: 12 }}
|
||||||
|
trigger='blur'
|
||||||
|
stopValidateWithError
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
validator: (rule, value) => verifyJSON(value),
|
||||||
|
message: t('不是合法的 JSON 字符串')
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
onChange={(value) => setInputs({ ...inputs, UserUsableGroups: value })}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Form.Section>
|
||||||
|
</Form>
|
||||||
|
<Button onClick={onSubmit}>{t('保存分组倍率设置')}</Button>
|
||||||
|
</Spin>
|
||||||
|
);
|
||||||
|
}
|
||||||
178
web/src/pages/Setting/Operation/ModelRatioSettings.js
Normal file
178
web/src/pages/Setting/Operation/ModelRatioSettings.js
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
import React, { useEffect, useState, useRef } from 'react';
|
||||||
|
import { Button, Col, Form, Popconfirm, Row, Space, Spin } from '@douyinfe/semi-ui';
|
||||||
|
import {
|
||||||
|
compareObjects,
|
||||||
|
API,
|
||||||
|
showError,
|
||||||
|
showSuccess,
|
||||||
|
showWarning,
|
||||||
|
verifyJSON,
|
||||||
|
} from '../../../helpers';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
export default function ModelRatioSettings(props) {
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [inputs, setInputs] = useState({
|
||||||
|
ModelPrice: '',
|
||||||
|
ModelRatio: '',
|
||||||
|
CompletionRatio: '',
|
||||||
|
});
|
||||||
|
const refForm = useRef();
|
||||||
|
const [inputsRow, setInputsRow] = useState(inputs);
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
async function onSubmit() {
|
||||||
|
try {
|
||||||
|
await refForm.current.validate().then(() => {
|
||||||
|
const updateArray = compareObjects(inputs, inputsRow);
|
||||||
|
if (!updateArray.length) return showWarning(t('你似乎并没有修改什么'));
|
||||||
|
|
||||||
|
const requestQueue = updateArray.map((item) => {
|
||||||
|
const value = typeof inputs[item.key] === 'boolean'
|
||||||
|
? String(inputs[item.key])
|
||||||
|
: inputs[item.key];
|
||||||
|
return API.put('/api/option/', { key: item.key, value });
|
||||||
|
});
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
Promise.all(requestQueue)
|
||||||
|
.then((res) => {
|
||||||
|
if (res.includes(undefined)) {
|
||||||
|
return showError(requestQueue.length > 1 ? t('部分保存失败,请重试') : t('保存失败'));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < res.length; i++) {
|
||||||
|
if (!res[i].data.success) {
|
||||||
|
return showError(res[i].data.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showSuccess(t('保存成功'));
|
||||||
|
props.refresh();
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Unexpected error:', error);
|
||||||
|
showError(t('保存失败,请重试'));
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setLoading(false);
|
||||||
|
});
|
||||||
|
}).catch(() => {
|
||||||
|
showError(t('请检查输入'));
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
showError(t('请检查输入'));
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function resetModelRatio() {
|
||||||
|
try {
|
||||||
|
let res = await API.post(`/api/option/rest_model_ratio`);
|
||||||
|
if (res.data.success) {
|
||||||
|
showSuccess(res.data.message);
|
||||||
|
props.refresh();
|
||||||
|
} else {
|
||||||
|
showError(res.data.message);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
showError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const currentInputs = {};
|
||||||
|
for (let key in props.options) {
|
||||||
|
if (Object.keys(inputs).includes(key)) {
|
||||||
|
currentInputs[key] = props.options[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setInputs(currentInputs);
|
||||||
|
setInputsRow(structuredClone(currentInputs));
|
||||||
|
refForm.current.setValues(currentInputs);
|
||||||
|
}, [props.options]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Spin spinning={loading}>
|
||||||
|
<Form
|
||||||
|
values={inputs}
|
||||||
|
getFormApi={(formAPI) => (refForm.current = formAPI)}
|
||||||
|
style={{ marginBottom: 15 }}
|
||||||
|
>
|
||||||
|
<Form.Section>
|
||||||
|
<Row gutter={16}>
|
||||||
|
<Col span={16}>
|
||||||
|
<Form.TextArea
|
||||||
|
label={t('模型固定价格')}
|
||||||
|
extraText={t('一次调用消耗多少刀,优先级大于模型倍率')}
|
||||||
|
placeholder={t('为一个 JSON 文本,键为模型名称,值为一次调用消耗多少刀,比如 "gpt-4-gizmo-*": 0.1,一次消耗0.1刀')}
|
||||||
|
field={'ModelPrice'}
|
||||||
|
autosize={{ minRows: 6, maxRows: 12 }}
|
||||||
|
trigger='blur'
|
||||||
|
stopValidateWithError
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
validator: (rule, value) => verifyJSON(value),
|
||||||
|
message: '不是合法的 JSON 字符串'
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
onChange={(value) => setInputs({ ...inputs, ModelPrice: value })}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row gutter={16}>
|
||||||
|
<Col span={16}>
|
||||||
|
<Form.TextArea
|
||||||
|
label={t('模型倍率')}
|
||||||
|
placeholder={t('为一个 JSON 文本,键为模型名称,值为倍率')}
|
||||||
|
field={'ModelRatio'}
|
||||||
|
autosize={{ minRows: 6, maxRows: 12 }}
|
||||||
|
trigger='blur'
|
||||||
|
stopValidateWithError
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
validator: (rule, value) => verifyJSON(value),
|
||||||
|
message: '不是合法的 JSON 字符串'
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
onChange={(value) => setInputs({ ...inputs, ModelRatio: value })}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row gutter={16}>
|
||||||
|
<Col span={16}>
|
||||||
|
<Form.TextArea
|
||||||
|
label={t('模型补全倍率(仅对自定义模型有效)')}
|
||||||
|
extraText={t('仅对自定义模型有效')}
|
||||||
|
placeholder={t('为一个 JSON 文本,键为模型名称,值为倍率')}
|
||||||
|
field={'CompletionRatio'}
|
||||||
|
autosize={{ minRows: 6, maxRows: 12 }}
|
||||||
|
trigger='blur'
|
||||||
|
stopValidateWithError
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
validator: (rule, value) => verifyJSON(value),
|
||||||
|
message: '不是合法的 JSON 字符串'
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
onChange={(value) => setInputs({ ...inputs, CompletionRatio: value })}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Form.Section>
|
||||||
|
</Form>
|
||||||
|
<Space>
|
||||||
|
<Button onClick={onSubmit}>{t('保存模型倍率设置')}</Button>
|
||||||
|
<Popconfirm
|
||||||
|
title={t('确定重置模型倍率吗?')}
|
||||||
|
content={t('此修改将不可逆')}
|
||||||
|
okType={'danger'}
|
||||||
|
position={'top'}
|
||||||
|
onConfirm={resetModelRatio}
|
||||||
|
>
|
||||||
|
<Button type={'danger'}>{t('重置模型倍率')}</Button>
|
||||||
|
</Popconfirm>
|
||||||
|
</Space>
|
||||||
|
</Spin>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -4,7 +4,10 @@ import { Table, Button, Input, Modal, Form, Space } from '@douyinfe/semi-ui';
|
|||||||
import { IconDelete, IconPlus, IconSearch, IconSave } from '@douyinfe/semi-icons';
|
import { IconDelete, IconPlus, IconSearch, IconSave } from '@douyinfe/semi-icons';
|
||||||
import { showError, showSuccess } from '../../../helpers';
|
import { showError, showSuccess } from '../../../helpers';
|
||||||
import { API } from '../../../helpers';
|
import { API } from '../../../helpers';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
export default function ModelSettingsVisualEditor(props) {
|
export default function ModelSettingsVisualEditor(props) {
|
||||||
|
const { t } = useTranslation();
|
||||||
const [models, setModels] = useState([]);
|
const [models, setModels] = useState([]);
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
const [currentModel, setCurrentModel] = useState(null);
|
const [currentModel, setCurrentModel] = useState(null);
|
||||||
@@ -122,51 +125,50 @@ export default function ModelSettingsVisualEditor(props) {
|
|||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
title: '模型名称',
|
title: t('模型名称'),
|
||||||
dataIndex: 'name',
|
dataIndex: 'name',
|
||||||
key: 'name',
|
key: 'name',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '固定价格',
|
title: t('模型固定价格'),
|
||||||
dataIndex: 'price',
|
dataIndex: 'price',
|
||||||
key: 'price',
|
key: 'price',
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<Input
|
<Input
|
||||||
value={text}
|
value={text}
|
||||||
placeholder="按量计价"
|
placeholder={t('按量计费')}
|
||||||
onChange={value => updateModel(record.name, 'price', value)}
|
onChange={value => updateModel(record.name, 'price', value)}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '模型倍率',
|
title: t('模型倍率'),
|
||||||
dataIndex: 'ratio',
|
dataIndex: 'ratio',
|
||||||
key: 'ratio',
|
key: 'ratio',
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<Input
|
<Input
|
||||||
value={text}
|
value={text}
|
||||||
|
placeholder={record.price !== '' ? t('模型倍率') : t('默认补全倍率')}
|
||||||
placeholder={record.price !== '' ? '固定价格' : '默认补全倍率'}
|
|
||||||
disabled={record.price !== ''}
|
disabled={record.price !== ''}
|
||||||
onChange={value => updateModel(record.name, 'ratio', value)}
|
onChange={value => updateModel(record.name, 'ratio', value)}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '补全倍率',
|
title: t('补全倍率'),
|
||||||
dataIndex: 'completionRatio',
|
dataIndex: 'completionRatio',
|
||||||
key: 'completionRatio',
|
key: 'completionRatio',
|
||||||
render: (text, record) => (
|
render: (text, record) => (
|
||||||
<Input
|
<Input
|
||||||
value={text}
|
value={text}
|
||||||
placeholder={record.price !== '' ? '固定价格' : '默认补全倍率'}
|
placeholder={record.price !== '' ? t('补全倍率') : t('默认补全倍率')}
|
||||||
disabled={record.price !== ''}
|
disabled={record.price !== ''}
|
||||||
onChange={value => updateModel(record.name, 'completionRatio', value)}
|
onChange={value => updateModel(record.name, 'completionRatio', value)}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '操作',
|
title: t('操作'),
|
||||||
key: 'action',
|
key: 'action',
|
||||||
render: (_, record) => (
|
render: (_, record) => (
|
||||||
<Button
|
<Button
|
||||||
@@ -219,22 +221,20 @@ export default function ModelSettingsVisualEditor(props) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h3>模型价格</h3>
|
|
||||||
<Space vertical align="start" style={{ width: '100%' }}>
|
<Space vertical align="start" style={{ width: '100%' }}>
|
||||||
<Space>
|
<Space>
|
||||||
<Button icon={<IconPlus />} onClick={() => setVisible(true)}>
|
<Button icon={<IconPlus />} onClick={() => setVisible(true)}>
|
||||||
添加模型
|
{t('添加模型')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button type="primary" icon={<IconSave />} onClick={SubmitData}>
|
<Button type="primary" icon={<IconSave />} onClick={SubmitData}>
|
||||||
应用更改
|
{t('应用更改')}
|
||||||
</Button>
|
</Button>
|
||||||
<Input
|
<Input
|
||||||
prefix={<IconSearch />}
|
prefix={<IconSearch />}
|
||||||
placeholder="搜索模型名称"
|
placeholder={t('搜索模型名称')}
|
||||||
value={searchText}
|
value={searchText}
|
||||||
onChange={value => {
|
onChange={value => {
|
||||||
setSearchText(value)
|
setSearchText(value)
|
||||||
// 搜索时重置页码
|
|
||||||
setCurrentPage(1);
|
setCurrentPage(1);
|
||||||
}}
|
}}
|
||||||
style={{ width: 200 }}
|
style={{ width: 200 }}
|
||||||
@@ -242,12 +242,18 @@ export default function ModelSettingsVisualEditor(props) {
|
|||||||
</Space>
|
</Space>
|
||||||
<Table
|
<Table
|
||||||
columns={columns}
|
columns={columns}
|
||||||
dataSource={pagedData} // 使用分页后的数据
|
dataSource={pagedData}
|
||||||
pagination={{
|
pagination={{
|
||||||
currentPage: currentPage,
|
currentPage: currentPage,
|
||||||
pageSize: pageSize,
|
pageSize: pageSize,
|
||||||
total: filteredModels.length,
|
total: filteredModels.length,
|
||||||
onPageChange: page => setCurrentPage(page),
|
onPageChange: page => setCurrentPage(page),
|
||||||
|
formatPageText: (page) =>
|
||||||
|
t('第 {{start}} - {{end}} 条,共 {{total}} 条', {
|
||||||
|
start: page.currentStart,
|
||||||
|
end: page.currentEnd,
|
||||||
|
total: filteredModels.length
|
||||||
|
}),
|
||||||
showTotal: true,
|
showTotal: true,
|
||||||
showSizeChanger: false
|
showSizeChanger: false
|
||||||
}}
|
}}
|
||||||
@@ -255,7 +261,7 @@ export default function ModelSettingsVisualEditor(props) {
|
|||||||
</Space>
|
</Space>
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
title="添加模型"
|
title={t('添加模型')}
|
||||||
visible={visible}
|
visible={visible}
|
||||||
onCancel={() => setVisible(false)}
|
onCancel={() => setVisible(false)}
|
||||||
onOk={() => {
|
onOk={() => {
|
||||||
@@ -263,32 +269,49 @@ export default function ModelSettingsVisualEditor(props) {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Form>
|
<Form>
|
||||||
<p>请输入固定价格或者模型倍率+补全倍率</p>
|
|
||||||
<Form.Input
|
<Form.Input
|
||||||
field="name"
|
field="name"
|
||||||
label="模型名称"
|
label={t('模型名称')}
|
||||||
placeholder="strawberry"
|
placeholder="strawberry"
|
||||||
required
|
required
|
||||||
onChange={value => setCurrentModel(prev => ({ ...prev, name: value }))}
|
onChange={value => setCurrentModel(prev => ({ ...prev, name: value }))}
|
||||||
/>
|
/>
|
||||||
<Form.Input
|
<Form.Switch
|
||||||
field="price"
|
field="priceMode"
|
||||||
label="固定价格(每次)"
|
label={<>{t('定价模式')}:{currentModel?.priceMode ? t("固定价格") : t("倍率模式")}</>}
|
||||||
placeholder="输入每次价格"
|
onChange={checked => {
|
||||||
onChange={value => setCurrentModel(prev => ({ ...prev, price: value }))}
|
setCurrentModel(prev => ({
|
||||||
/>
|
...prev,
|
||||||
<Form.Input
|
price: '',
|
||||||
field="ratio"
|
ratio: '',
|
||||||
label="模型倍率"
|
completionRatio: '',
|
||||||
placeholder="输入模型倍率"
|
priceMode: checked
|
||||||
onChange={value => setCurrentModel(prev => ({ ...prev, ratio: value }))}
|
}));
|
||||||
/>
|
}}
|
||||||
<Form.Input
|
|
||||||
field="completionRatio"
|
|
||||||
label="补全倍率"
|
|
||||||
placeholder="输入补全价格"
|
|
||||||
onChange={value => setCurrentModel(prev => ({ ...prev, completionRatio: value }))}
|
|
||||||
/>
|
/>
|
||||||
|
{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>
|
</Form>
|
||||||
</Modal>
|
</Modal>
|
||||||
</>
|
</>
|
||||||
|
|||||||
Reference in New Issue
Block a user