refactor: 运营设置-倍率设置

This commit is contained in:
QuentinHsu 2024-05-13 17:55:15 +08:00
parent 698af0786d
commit b283365ebc
No known key found for this signature in database
GPG Key ID: 20D465A435D740D0
2 changed files with 242 additions and 260 deletions

View File

@ -1,6 +1,6 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { Divider, Form, Grid, Header } from 'semantic-ui-react'; import { Divider, Form, Grid, Header } from 'semantic-ui-react';
import { Card } from '@douyinfe/semi-ui'; import { Card, Spin } 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';
@ -8,6 +8,7 @@ import SettingsLog from '../pages/Setting/Operation/SettingsLog.js';
import SettingsDataDashboard from '../pages/Setting/Operation/SettingsDataDashboard.js'; import SettingsDataDashboard from '../pages/Setting/Operation/SettingsDataDashboard.js';
import SettingsMonitoring from '../pages/Setting/Operation/SettingsMonitoring.js'; 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 { import {
API, API,
@ -17,10 +18,7 @@ import {
verifyJSON, verifyJSON,
} from '../helpers'; } from '../helpers';
import { useTheme } from '../context/Theme';
const OperationSetting = () => { const OperationSetting = () => {
let now = new Date();
let [inputs, setInputs] = useState({ let [inputs, setInputs] = useState({
QuotaForNewUser: 0, QuotaForNewUser: 0,
QuotaForInviter: 0, QuotaForInviter: 0,
@ -58,7 +56,7 @@ const OperationSetting = () => {
DefaultCollapseSidebar: false, // 默认折叠侧边栏 DefaultCollapseSidebar: false, // 默认折叠侧边栏
RetryTimes: 0, RetryTimes: 0,
}); });
const [originInputs, setOriginInputs] = useState({});
let [loading, setLoading] = useState(false); let [loading, setLoading] = useState(false);
const getOptions = async () => { const getOptions = async () => {
@ -86,271 +84,62 @@ const OperationSetting = () => {
}); });
setInputs(newInputs); setInputs(newInputs);
setOriginInputs(newInputs);
} else { } else {
showError(message); showError(message);
} }
}; };
async function onRefresh() {
const theme = useTheme(); try {
const isDark = theme === 'dark'; setLoading(true);
await getOptions();
showSuccess('刷新成功');
} catch (error) {
showError('刷新失败');
} finally {
setLoading(false);
}
}
useEffect(() => { useEffect(() => {
getOptions().then(); getOptions();
}, []); }, []);
const updateOption = async (key, value) => {
setLoading(true);
if (key.endsWith('Enabled')) {
value = inputs[key] === 'true' ? 'false' : 'true';
}
if (key === 'DefaultCollapseSidebar') {
value = inputs[key] === 'true' ? 'false' : 'true';
}
console.log(key, value);
const res = await API.put('/api/option/', {
key,
value,
});
const { success, message } = res.data;
if (success) {
setInputs((inputs) => ({ ...inputs, [key]: value }));
} else {
showError(message);
}
setLoading(false);
};
const handleInputChange = async (e, { name, value }) => {
if (
name.endsWith('Enabled') ||
name === 'DataExportInterval' ||
name === 'DataExportDefaultTime' ||
name === 'DefaultCollapseSidebar'
) {
if (name === 'DataExportDefaultTime') {
localStorage.setItem('data_export_default_time', value);
} else if (name === 'MjNotifyEnabled') {
localStorage.setItem('mj_notify_enabled', value);
}
await updateOption(name, value);
} else {
setInputs((inputs) => ({ ...inputs, [name]: value }));
}
};
const submitConfig = async (group) => {
switch (group) {
case 'monitor':
if (
originInputs['ChannelDisableThreshold'] !==
inputs.ChannelDisableThreshold
) {
await updateOption(
'ChannelDisableThreshold',
inputs.ChannelDisableThreshold,
);
}
if (
originInputs['QuotaRemindThreshold'] !== inputs.QuotaRemindThreshold
) {
await updateOption(
'QuotaRemindThreshold',
inputs.QuotaRemindThreshold,
);
}
break;
case 'ratio':
if (originInputs['ModelRatio'] !== inputs.ModelRatio) {
if (!verifyJSON(inputs.ModelRatio)) {
showError('模型倍率不是合法的 JSON 字符串');
return;
}
await updateOption('ModelRatio', inputs.ModelRatio);
}
if (originInputs['CompletionRatio'] !== inputs.CompletionRatio) {
if (!verifyJSON(inputs.CompletionRatio)) {
showError('模型补全倍率不是合法的 JSON 字符串');
return;
}
await updateOption('CompletionRatio', inputs.CompletionRatio);
}
if (originInputs['GroupRatio'] !== inputs.GroupRatio) {
if (!verifyJSON(inputs.GroupRatio)) {
showError('分组倍率不是合法的 JSON 字符串');
return;
}
await updateOption('GroupRatio', inputs.GroupRatio);
}
if (originInputs['ModelPrice'] !== inputs.ModelPrice) {
if (!verifyJSON(inputs.ModelPrice)) {
showError('模型固定价格不是合法的 JSON 字符串');
return;
}
await updateOption('ModelPrice', inputs.ModelPrice);
}
break;
case 'words':
if (originInputs['SensitiveWords'] !== inputs.SensitiveWords) {
await updateOption('SensitiveWords', inputs.SensitiveWords);
}
break;
case 'quota':
if (originInputs['QuotaForNewUser'] !== inputs.QuotaForNewUser) {
await updateOption('QuotaForNewUser', inputs.QuotaForNewUser);
}
if (originInputs['QuotaForInvitee'] !== inputs.QuotaForInvitee) {
await updateOption('QuotaForInvitee', inputs.QuotaForInvitee);
}
if (originInputs['QuotaForInviter'] !== inputs.QuotaForInviter) {
await updateOption('QuotaForInviter', inputs.QuotaForInviter);
}
if (originInputs['PreConsumedQuota'] !== inputs.PreConsumedQuota) {
await updateOption('PreConsumedQuota', inputs.PreConsumedQuota);
}
break;
case 'general':
if (originInputs['TopUpLink'] !== inputs.TopUpLink) {
await updateOption('TopUpLink', inputs.TopUpLink);
}
if (originInputs['ChatLink'] !== inputs.ChatLink) {
await updateOption('ChatLink', inputs.ChatLink);
}
if (originInputs['ChatLink2'] !== inputs.ChatLink2) {
await updateOption('ChatLink2', inputs.ChatLink2);
}
if (originInputs['QuotaPerUnit'] !== inputs.QuotaPerUnit) {
await updateOption('QuotaPerUnit', inputs.QuotaPerUnit);
}
if (originInputs['RetryTimes'] !== inputs.RetryTimes) {
await updateOption('RetryTimes', inputs.RetryTimes);
}
break;
}
};
return ( return (
<> <>
{/* 通用设置 */} <Spin spinning={loading} size='large'>
<Card style={{ marginTop: '10px' }}> {/* 通用设置 */}
<SettingsGeneral options={inputs} /> <Card style={{ marginTop: '10px' }}>
</Card> <SettingsGeneral options={inputs} />
{/* 绘图设置 */} </Card>
<Card style={{ marginTop: '10px' }}> {/* 绘图设置 */}
<SettingsDrawing options={inputs} /> <Card style={{ marginTop: '10px' }}>
</Card> <SettingsDrawing options={inputs} />
{/* 屏蔽词过滤设置 */} </Card>
<Card style={{ marginTop: '10px' }}> {/* 屏蔽词过滤设置 */}
<SettingsSensitiveWords options={inputs} /> <Card style={{ marginTop: '10px' }}>
</Card> <SettingsSensitiveWords options={inputs} />
{/* 日志设置 */} </Card>
<Card style={{ marginTop: '10px' }}> {/* 日志设置 */}
<SettingsLog options={inputs} /> <Card style={{ marginTop: '10px' }}>
</Card> <SettingsLog options={inputs} />
{/* 数据看板 */} </Card>
<Card style={{ marginTop: '10px' }}> {/* 数据看板 */}
<SettingsDataDashboard options={inputs} /> <Card style={{ marginTop: '10px' }}>
</Card> <SettingsDataDashboard options={inputs} />
{/* 监控设置 */} </Card>
<Card style={{ marginTop: '10px' }}> {/* 监控设置 */}
<SettingsMonitoring options={inputs} /> <Card style={{ marginTop: '10px' }}>
</Card> <SettingsMonitoring options={inputs} />
{/* 额度设置 */} </Card>
<Card style={{ marginTop: '10px' }}> {/* 额度设置 */}
<SettingsCreditLimit options={inputs} /> <Card style={{ marginTop: '10px' }}>
</Card> <SettingsCreditLimit options={inputs} />
<Grid columns={1}> </Card>
<Grid.Column> {/* 倍率设置 */}
<Form loading={loading} inverted={isDark}> <Card style={{ marginTop: '10px' }}>
{/*<Form.Group inline>*/} <SettingsMagnification options={inputs} />
{/* <Form.Checkbox*/} </Card>
{/* checked={inputs.StopOnSensitiveEnabled === 'true'}*/} </Spin>
{/* label='在检测到屏蔽词时,立刻停止生成,否则替换屏蔽词'*/}
{/* name='StopOnSensitiveEnabled'*/}
{/* onChange={handleInputChange}*/}
{/* />*/}
{/*</Form.Group>*/}
{/*<Form.Group>*/}
{/* <Form.Input*/}
{/* label="流模式下缓存队列,默认不缓存,设置越大检测越准确,但是回复会有卡顿感"*/}
{/* name="StreamCacheTextLength"*/}
{/* onChange={handleInputChange}*/}
{/* value={inputs.StreamCacheQueueLength}*/}
{/* type="number"*/}
{/* min="0"*/}
{/* placeholder="例如10"*/}
{/* />*/}
{/*</Form.Group>*/}
<Divider />
<Header as='h3' inverted={isDark}>
倍率设置
</Header>
<Form.Group widths='equal'>
<Form.TextArea
label='模型固定价格(一次调用消耗多少刀,优先级大于模型倍率)'
name='ModelPrice'
onChange={handleInputChange}
style={{
minHeight: 250,
fontFamily: 'JetBrains Mono, Consolas',
}}
autoComplete='new-password'
value={inputs.ModelPrice}
placeholder='为一个 JSON 文本,键为模型名称,值为一次调用消耗多少刀,比如 "gpt-4-gizmo-*": 0.1一次消耗0.1刀'
/>
</Form.Group>
<Form.Group widths='equal'>
<Form.TextArea
label='模型倍率'
name='ModelRatio'
onChange={handleInputChange}
style={{
minHeight: 250,
fontFamily: 'JetBrains Mono, Consolas',
}}
autoComplete='new-password'
value={inputs.ModelRatio}
placeholder='为一个 JSON 文本,键为模型名称,值为倍率'
/>
</Form.Group>
<Form.Group widths='equal'>
<Form.TextArea
label='模型补全倍率(仅对自定义模型有效)'
name='CompletionRatio'
onChange={handleInputChange}
style={{
minHeight: 250,
fontFamily: 'JetBrains Mono, Consolas',
}}
autoComplete='new-password'
value={inputs.CompletionRatio}
placeholder='为一个 JSON 文本,键为分组名称,值为倍率'
/>
</Form.Group>
<Form.Group widths='equal'>
<Form.TextArea
label='分组倍率'
name='GroupRatio'
onChange={handleInputChange}
style={{
minHeight: 250,
fontFamily: 'JetBrains Mono, Consolas',
}}
autoComplete='new-password'
value={inputs.GroupRatio}
placeholder='为一个 JSON 文本,键为分组名称,值为倍率'
/>
</Form.Group>
<Form.Button
onClick={() => {
submitConfig('ratio').then();
}}
>
保存倍率设置
</Form.Button>
</Form>
</Grid.Column>
</Grid>
</> </>
); );
}; };

View File

@ -0,0 +1,193 @@
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';
export default function SettingsMagnification(props) {
const [loading, setLoading] = useState(false);
const [inputs, setInputs] = useState({
ModelPrice: '',
ModelRatio: '',
CompletionRatio: '',
GroupRatio: '',
});
const refForm = useRef();
const [inputsRow, setInputsRow] = useState(inputs);
async function onSubmit() {
try {
await refForm.current.validate();
const updateArray = compareObjects(inputs, inputsRow);
if (!updateArray.length) return showWarning('你似乎并没有修改什么');
const requestQueue = updateArray.map((item) => {
let value = '';
if (typeof inputs[item.key] === 'boolean') {
value = String(inputs[item.key]);
} else {
value = inputs[item.key];
}
return API.put('/api/option/', {
key: item.key,
value,
});
});
setLoading(true);
Promise.all(requestQueue)
.then((res) => {
if (requestQueue.length === 1) {
if (res.includes(undefined)) return;
} else if (requestQueue.length > 1) {
if (res.includes(undefined)) return showError('部分更新失败');
}
showSuccess('更新成功');
})
.catch(() => {
showError('更新失败');
})
.finally(() => {
setLoading(false);
setInputsRow(structuredClone(inputs));
});
} catch (error) {
showError('请检查输入');
console.error(error);
} finally {
}
}
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={'倍率设置'}>
<Row gutter={16}>
<Col span={16}>
<Form.TextArea
label={'模型固定价格'}
extraText={'一次调用消耗多少刀,优先级大于模型倍率'}
placeholder={
'为一个 JSON 文本,键为模型名称,值为一次调用消耗多少刀,比如 "gpt-4-gizmo-*": 0.1一次消耗0.1刀'
}
field={'ModelPrice'}
autosize={{ minRows: 6, maxRows: 12 }}
trigger='blur'
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={'模型倍率'}
extraText={''}
placeholder={'为一个 JSON 文本,键为模型名称,值为倍率'}
field={'ModelRatio'}
autosize={{ minRows: 6, maxRows: 12 }}
trigger='blur'
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={'模型补全倍率'}
extraText={'仅对自定义模型有效'}
placeholder={'为一个 JSON 文本,键为模型名称,值为倍率'}
field={'CompletionRatio'}
autosize={{ minRows: 6, maxRows: 12 }}
trigger='blur'
rules={[
{
validator: (rule, value) => verifyJSON(value),
message: '不是合法的 JSON 字符串',
},
]}
onChange={(value) =>
setInputs({
...inputs,
CompletionRatio: value,
})
}
/>
</Col>
</Row>
<Row gutter={16}>
<Col span={16}>
<Form.TextArea
label={'分组倍率'}
extraText={''}
placeholder={'为一个 JSON 文本,键为分组名称,值为倍率'}
field={'GroupRatio'}
autosize={{ minRows: 6, maxRows: 12 }}
trigger='blur'
rules={[
{
validator: (rule, value) => verifyJSON(value),
message: '不是合法的 JSON 字符串',
},
]}
onChange={(value) =>
setInputs({
...inputs,
GroupRatio: value,
})
}
/>
</Col>
</Row>
<Row>
<Button size='large' onClick={onSubmit}>
保存倍率设置
</Button>
</Row>
</Form.Section>
</Form>
</Spin>
</>
);
}