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 (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ setInputs({
+ ...inputs,
+ DisplayInCurrencyEnabled: value,
+ });
+ }}
+ />
+
+
+
+ setInputs({
+ ...inputs,
+ DisplayTokenStatEnabled: value,
+ })
+ }
+ />
+
+
+
+ setInputs({
+ ...inputs,
+ DefaultCollapseSidebar: value,
+ })
+ }
+ />
+
+
+
+
+
+
+
+
+ >
+ );
+}