diff --git a/web/src/components/OperationSetting.js b/web/src/components/OperationSetting.js index 7566faa..ecf3d77 100644 --- a/web/src/components/OperationSetting.js +++ b/web/src/components/OperationSetting.js @@ -1,5 +1,7 @@ import React, { useEffect, useState } from 'react'; import { Divider, Form, Grid, Header } from 'semantic-ui-react'; +import { Card } from '@douyinfe/semi-ui'; +import GeneralSettings from '../pages/Setting/Operation/GeneralSettings.js'; import { API, showError, @@ -30,8 +32,8 @@ const OperationSetting = () => { AutomaticEnableChannelEnabled: '', ChannelDisableThreshold: 0, LogConsumeEnabled: '', - DisplayInCurrencyEnabled: '', - DisplayTokenStatEnabled: '', + DisplayInCurrencyEnabled: false, + DisplayTokenStatEnabled: false, CheckSensitiveEnabled: '', CheckSensitiveOnPromptEnabled: '', CheckSensitiveOnCompletionEnabled: '', @@ -45,7 +47,7 @@ const OperationSetting = () => { DataExportEnabled: '', DataExportDefaultTime: 'hour', DataExportInterval: 5, - DefaultCollapseSidebar: '', // 默认折叠侧边栏 + DefaultCollapseSidebar: false, // 默认折叠侧边栏 RetryTimes: 0, }); const [originInputs, setOriginInputs] = useState({}); @@ -72,8 +74,16 @@ const OperationSetting = () => { ) { item.value = JSON.stringify(JSON.parse(item.value), null, 2); } - newInputs[item.key] = item.value; + if ( + item.key.endsWith('Enabled') || + ['DefaultCollapseSidebar'].includes(item.key) + ) { + newInputs[item.key] = item.value === 'true' ? true : false; + } else { + newInputs[item.key] = item.value; + } }); + setInputs(newInputs); setOriginInputs(newInputs); } else { @@ -224,396 +234,334 @@ const OperationSetting = () => { showError('日志清理失败:' + message); }; return ( - - -
-
- 通用设置 -
- - - - - - - - - - - - - { - submitConfig('general').then(); - }} - > - 保存通用设置 - - -
- 绘图设置 -
- - - - - - - - -
- 屏蔽词过滤设置 -
- - - - - - {/**/} - - {/**/} - {/* */} - {/**/} - {/**/} - {/* */} - {/**/} - - - - { - submitConfig('words').then(); - }} - > - 保存屏蔽词设置 - - -
- 日志设置 -
- - - - - { - setHistoryTimestamp(value); + <> + + {/* 通用设置 */} + + + + + + +
+ 绘图设置 +
+ + + + + + + + +
+ 屏蔽词过滤设置 +
+ + + + + + {/**/} + + {/**/} + {/* */} + {/**/} + {/**/} + {/* */} + {/**/} + + + + { + submitConfig('words').then(); }} - /> -
- { - deleteHistoryLogs().then(); - }} - > - 清理历史日志 - - -
- 数据看板 -
- - - - - - -
- 监控设置 -
- - - - - + > + 保存屏蔽词设置 + + +
+ 日志设置 +
+ + + + + { + setHistoryTimestamp(value); + }} + /> + + { + deleteHistoryLogs().then(); + }} + > + 清理历史日志 + + +
+ 数据看板 +
- -
- { - submitConfig('monitor').then(); - }} - > - 保存监控设置 - - -
- 额度设置 -
- - - - - - - { - submitConfig('quota').then(); - }} - > - 保存额度设置 - - -
- 倍率设置 -
- - - - - - - - - - { - submitConfig('ratio').then(); - }} - > - 保存倍率设置 - - -
-
+ + + + + +
+ 监控设置 +
+ + + + + + + + + { + submitConfig('monitor').then(); + }} + > + 保存监控设置 + + +
+ 额度设置 +
+ + + + + + + { + submitConfig('quota').then(); + }} + > + 保存额度设置 + + +
+ 倍率设置 +
+ + + + + + + + + + { + submitConfig('ratio').then(); + }} + > + 保存倍率设置 + + + + + ); }; diff --git a/web/src/helpers/utils.js b/web/src/helpers/utils.js index 78fa3d6..0ca3476 100644 --- a/web/src/helpers/utils.js +++ b/web/src/helpers/utils.js @@ -220,3 +220,28 @@ export function shouldShowPrompt(id) { export function setPromptShown(id) { localStorage.setItem(`prompt-${id}`, 'true'); } + +/** + * 比较两个对象的属性,找出有变化的属性,并返回包含变化属性信息的数组 + * @param {Object} oldObject - 旧对象 + * @param {Object} newObject - 新对象 + * @return {Array} 包含变化属性信息的数组,每个元素是一个对象,包含 key, oldValue 和 newValue + */ +export function compareObjects(oldObject, newObject) { + const changedProperties = []; + + // 比较两个对象的属性 + for (const key in oldObject) { + if (oldObject.hasOwnProperty(key) && newObject.hasOwnProperty(key)) { + if (oldObject[key] !== newObject[key]) { + changedProperties.push({ + key: key, + oldValue: oldObject[key], + newValue: newObject[key], + }); + } + } + } + + return changedProperties; +} diff --git a/web/src/pages/Setting/Operation/GeneralSettings.js b/web/src/pages/Setting/Operation/GeneralSettings.js new file mode 100644 index 0000000..6805a3b --- /dev/null +++ b/web/src/pages/Setting/Operation/GeneralSettings.js @@ -0,0 +1,198 @@ +import React, { useEffect, useState, useRef } from 'react'; +import { Button, Col, Form, Row, Spin } from '@douyinfe/semi-ui'; +import { + compareObjects, + API, + showError, + showSuccess, + showWarning, +} from '../../../helpers'; + +export default function GeneralSettings(props) { + const [loading, setLoading] = useState(false); + const [inputs, setInputs] = useState({ + TopUpLink: '', + ChatLink: '', + ChatLink2: '', + QuotaPerUnit: '', + RetryTimes: '', + DisplayInCurrencyEnabled: false, + DisplayTokenStatEnabled: false, + DefaultCollapseSidebar: false, + }); + const refForm = useRef(); + const [inputsRow, setInputsRow] = useState(inputs); + function onChange(value, e) { + const name = e.target.id; + setInputs((inputs) => ({ ...inputs, [name]: value })); + } + function onSubmit() { + 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)); + }); + } + + 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 ( + <> + +
(refForm.current = formAPI)} + style={{ marginBottom: 15 }} + > + + + + + + + + + + + + + + + + + + + + + { + setInputs({ + ...inputs, + DisplayInCurrencyEnabled: value, + }); + }} + /> + + + + setInputs({ + ...inputs, + DisplayTokenStatEnabled: value, + }) + } + /> + + + + setInputs({ + ...inputs, + DefaultCollapseSidebar: value, + }) + } + /> + + + + + + +
+
+ + ); +}