mirror of
https://github.com/linux-do/new-api.git
synced 2025-11-09 15:43:41 +08:00
feat: 允许关闭绘图选项
This commit is contained in:
@@ -3,7 +3,8 @@ import {Divider, Form, Grid, Header} from 'semantic-ui-react';
|
|||||||
import {API, showError, showSuccess, timestamp2string, verifyJSON} from '../helpers';
|
import {API, showError, showSuccess, timestamp2string, verifyJSON} from '../helpers';
|
||||||
|
|
||||||
const OperationSetting = () => {
|
const OperationSetting = () => {
|
||||||
let now = new Date();let [inputs, setInputs] = useState({
|
let now = new Date();
|
||||||
|
let [inputs, setInputs] = useState({
|
||||||
QuotaForNewUser: 0,
|
QuotaForNewUser: 0,
|
||||||
QuotaForInviter: 0,
|
QuotaForInviter: 0,
|
||||||
QuotaForInvitee: 0,
|
QuotaForInvitee: 0,
|
||||||
@@ -20,28 +21,32 @@ const OperationSetting = () => {
|
|||||||
LogConsumeEnabled: '',
|
LogConsumeEnabled: '',
|
||||||
DisplayInCurrencyEnabled: '',
|
DisplayInCurrencyEnabled: '',
|
||||||
DisplayTokenStatEnabled: '',
|
DisplayTokenStatEnabled: '',
|
||||||
|
DrawingEnabled: '',
|
||||||
|
DataExportEnabled: '',
|
||||||
|
DataExportInterval: 5,
|
||||||
RetryTimes: 0
|
RetryTimes: 0
|
||||||
});
|
});
|
||||||
const [originInputs, setOriginInputs] = useState({});
|
const [originInputs, setOriginInputs] = useState({});
|
||||||
let [loading, setLoading] = useState(false);let [historyTimestamp, setHistoryTimestamp] = useState(timestamp2string(now.getTime() / 1000 - 30 * 24 * 3600)); // a month ago
|
let [loading, setLoading] = useState(false);
|
||||||
|
let [historyTimestamp, setHistoryTimestamp] = useState(timestamp2string(now.getTime() / 1000 - 30 * 24 * 3600)); // a month ago
|
||||||
|
|
||||||
const getOptions = async () => {
|
const getOptions = async () => {
|
||||||
const res = await API.get('/api/option/');
|
const res = await API.get('/api/option/');
|
||||||
const { success, message, data } = res.data;
|
const {success, message, data} = res.data;
|
||||||
if (success) {
|
if (success) {
|
||||||
let newInputs = {};
|
let newInputs = {};
|
||||||
data.forEach((item) => {
|
data.forEach((item) => {
|
||||||
if (item.key === 'ModelRatio' || item.key === 'GroupRatio'|| item.key === 'ModelPrice') {
|
if (item.key === 'ModelRatio' || item.key === 'GroupRatio' || item.key === 'ModelPrice') {
|
||||||
item.value = JSON.stringify(JSON.parse(item.value), null, 2);
|
item.value = JSON.stringify(JSON.parse(item.value), null, 2);
|
||||||
|
}
|
||||||
|
newInputs[item.key] = item.value;
|
||||||
|
});
|
||||||
|
setInputs(newInputs);
|
||||||
|
setOriginInputs(newInputs);
|
||||||
|
} else {
|
||||||
|
showError(message);
|
||||||
}
|
}
|
||||||
newInputs[item.key] = item.value;
|
};
|
||||||
});
|
|
||||||
setInputs(newInputs);
|
|
||||||
setOriginInputs(newInputs);
|
|
||||||
} else {
|
|
||||||
showError(message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getOptions().then();
|
getOptions().then();
|
||||||
@@ -66,87 +71,88 @@ const OperationSetting = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleInputChange = async (e, {name, value}) => {
|
const handleInputChange = async (e, {name, value}) => {
|
||||||
if (name.endsWith('Enabled')) {
|
if (name.endsWith('Enabled') || name === 'DataExportInterval') {
|
||||||
await updateOption(name, value);
|
await updateOption(name, value);
|
||||||
} else {
|
} else {
|
||||||
setInputs((inputs) => ({...inputs, [name]: value}));
|
setInputs((inputs) => ({...inputs, [name]: value}));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const submitConfig = async (group) => {
|
const submitConfig = async (group) => {
|
||||||
switch (group) {
|
switch (group) {
|
||||||
case 'monitor':
|
case 'monitor':
|
||||||
if (originInputs['ChannelDisableThreshold'] !== inputs.ChannelDisableThreshold) {
|
if (originInputs['ChannelDisableThreshold'] !== inputs.ChannelDisableThreshold) {
|
||||||
await updateOption('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['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 '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['QuotaPerUnit'] !== inputs.QuotaPerUnit) {
|
||||||
|
await updateOption('QuotaPerUnit', inputs.QuotaPerUnit);
|
||||||
|
}
|
||||||
|
if (originInputs['RetryTimes'] !== inputs.RetryTimes) {
|
||||||
|
await updateOption('RetryTimes', inputs.RetryTimes);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
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['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 '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['QuotaPerUnit'] !== inputs.QuotaPerUnit) {
|
|
||||||
await updateOption('QuotaPerUnit', inputs.QuotaPerUnit);
|
|
||||||
}
|
|
||||||
if (originInputs['RetryTimes'] !== inputs.RetryTimes) {
|
|
||||||
await updateOption('RetryTimes', inputs.RetryTimes);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const deleteHistoryLogs = async () => {
|
const deleteHistoryLogs = async () => {
|
||||||
console.log(inputs);
|
console.log(inputs);
|
||||||
const res = await API.delete(`/api/log/?target_timestamp=${Date.parse(historyTimestamp) / 1000}`);
|
const res = await API.delete(`/api/log/?target_timestamp=${Date.parse(historyTimestamp) / 1000}`);
|
||||||
const { success, message, data } = res.data;
|
const {success, message, data} = res.data;
|
||||||
if (success) {
|
if (success) {
|
||||||
showSuccess(`${data} 条日志已清理!`);
|
showSuccess(`${data} 条日志已清理!`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
showError('日志清理失败:' + message);
|
showError('日志清理失败:' + message);
|
||||||
};return (
|
};
|
||||||
|
return (
|
||||||
<Grid columns={1}>
|
<Grid columns={1}>
|
||||||
<Grid.Column>
|
<Grid.Column>
|
||||||
<Form loading={loading}>
|
<Form loading={loading}>
|
||||||
@@ -208,31 +214,58 @@ const OperationSetting = () => {
|
|||||||
name='DisplayTokenStatEnabled'
|
name='DisplayTokenStatEnabled'
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
/>
|
/>
|
||||||
|
<Form.Checkbox
|
||||||
|
checked={inputs.DrawingEnabled === 'true'}
|
||||||
|
label='启用绘图功能'
|
||||||
|
name='DrawingEnabled'
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
<Form.Button onClick={() => {
|
<Form.Button onClick={() => {
|
||||||
submitConfig('general').then();
|
submitConfig('general').then();
|
||||||
}}>保存通用设置</Form.Button><Divider />
|
}}>保存通用设置</Form.Button><Divider/>
|
||||||
<Header as='h3'>
|
<Header as='h3'>
|
||||||
日志设置
|
日志设置
|
||||||
</Header>
|
</Header>
|
||||||
<Form.Group inline>
|
<Form.Group inline>
|
||||||
<Form.Checkbox
|
<Form.Checkbox
|
||||||
checked={inputs.LogConsumeEnabled === 'true'}
|
checked={inputs.LogConsumeEnabled === 'true'}
|
||||||
label='启用额度消费日志记录'
|
label='启用额度消费日志记录'
|
||||||
name='LogConsumeEnabled'
|
name='LogConsumeEnabled'
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
/>
|
/>
|
||||||
</Form.Group>
|
|
||||||
<Form.Group widths={4}>
|
</Form.Group>
|
||||||
<Form.Input label='目标时间' value={historyTimestamp} type='datetime-local'
|
<Form.Group inline>
|
||||||
name='history_timestamp'
|
<Form.Checkbox
|
||||||
onChange={(e, { name, value }) => {
|
checked={inputs.DataExportEnabled === 'true'}
|
||||||
setHistoryTimestamp(value);
|
label='启用数据看板(实验性)'
|
||||||
}} />
|
name='DataExportEnabled'
|
||||||
</Form.Group>
|
onChange={handleInputChange}
|
||||||
<Form.Button onClick={() => {
|
/>
|
||||||
deleteHistoryLogs().then();
|
<Form.Input
|
||||||
}}>清理历史日志</Form.Button>
|
label='数据看板更新间隔(分钟,设置过短会影响数据库性能)'
|
||||||
|
name='DataExportInterval'
|
||||||
|
type={'number'}
|
||||||
|
step='1'
|
||||||
|
min='1'
|
||||||
|
onChange={handleInputChange}
|
||||||
|
autoComplete='new-password'
|
||||||
|
value={inputs.DataExportInterval}
|
||||||
|
placeholder='数据看板更新间隔(分钟,设置过短会影响数据库性能)'
|
||||||
|
/>
|
||||||
|
</Form.Group>
|
||||||
|
<Divider/>
|
||||||
|
<Form.Group widths={4}>
|
||||||
|
<Form.Input label='目标时间' value={historyTimestamp} type='datetime-local'
|
||||||
|
name='history_timestamp'
|
||||||
|
onChange={(e, {name, value}) => {
|
||||||
|
setHistoryTimestamp(value);
|
||||||
|
}}/>
|
||||||
|
</Form.Group>
|
||||||
|
<Form.Button onClick={() => {
|
||||||
|
deleteHistoryLogs().then();
|
||||||
|
}}>清理历史日志</Form.Button>
|
||||||
<Divider/>
|
<Divider/>
|
||||||
<Header as='h3'>
|
<Header as='h3'>
|
||||||
监控设置
|
监控设置
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {API, getLogo, getSystemName, isAdmin, isMobile, showSuccess} from '../he
|
|||||||
import '../index.css';
|
import '../index.css';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
IconAt,
|
IconCalendarClock,
|
||||||
IconHistogram,
|
IconHistogram,
|
||||||
IconGift,
|
IconGift,
|
||||||
IconKey,
|
IconKey,
|
||||||
@@ -74,11 +74,19 @@ let headerButtons = [
|
|||||||
to: '/log',
|
to: '/log',
|
||||||
icon: <IconHistogram/>
|
icon: <IconHistogram/>
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
text: '数据看版',
|
||||||
|
itemKey: 'detail',
|
||||||
|
to: '/detail',
|
||||||
|
icon: <IconCalendarClock />,
|
||||||
|
className: localStorage.getItem('enable_data_export') === 'true'?'semi-navigation-item-normal':'tableHiddle',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
text: '绘图',
|
text: '绘图',
|
||||||
itemKey: 'midjourney',
|
itemKey: 'midjourney',
|
||||||
to: '/midjourney',
|
to: '/midjourney',
|
||||||
icon: <IconImage/>
|
icon: <IconImage/>,
|
||||||
|
className: localStorage.getItem('enable_drawing') === 'true'?'semi-navigation-item-normal':'tableHiddle',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: '设置',
|
text: '设置',
|
||||||
@@ -133,6 +141,7 @@ const SiderBar = () => {
|
|||||||
setting: "/setting",
|
setting: "/setting",
|
||||||
about: "/about",
|
about: "/about",
|
||||||
chat: "/chat",
|
chat: "/chat",
|
||||||
|
detail: "/detail",
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
|
|||||||
Reference in New Issue
Block a user