فهرست منبع

refactor: 运营设置-倍率设置

QuentinHsu 1 سال پیش
والد
کامیت
b283365ebc
2فایلهای تغییر یافته به همراه242 افزوده شده و 260 حذف شده
  1. 49 260
      web/src/components/OperationSetting.js
  2. 193 0
      web/src/pages/Setting/Operation/SettingsMagnification.js

+ 49 - 260
web/src/components/OperationSetting.js

@@ -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);
     }
     }
   };
   };
-
-  const theme = useTheme();
-  const isDark = theme === 'dark';
+  async function onRefresh() {
+    try {
+      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 (
     <>
     <>
-      {/* 通用设置 */}
-      <Card style={{ marginTop: '10px' }}>
-        <SettingsGeneral options={inputs} />
-      </Card>
-      {/* 绘图设置 */}
-      <Card style={{ marginTop: '10px' }}>
-        <SettingsDrawing options={inputs} />
-      </Card>
-      {/* 屏蔽词过滤设置 */}
-      <Card style={{ marginTop: '10px' }}>
-        <SettingsSensitiveWords options={inputs} />
-      </Card>
-      {/* 日志设置 */}
-      <Card style={{ marginTop: '10px' }}>
-        <SettingsLog options={inputs} />
-      </Card>
-      {/* 数据看板 */}
-      <Card style={{ marginTop: '10px' }}>
-        <SettingsDataDashboard options={inputs} />
-      </Card>
-      {/* 监控设置 */}
-      <Card style={{ marginTop: '10px' }}>
-        <SettingsMonitoring options={inputs} />
-      </Card>
-      {/* 额度设置 */}
-      <Card style={{ marginTop: '10px' }}>
-        <SettingsCreditLimit options={inputs} />
-      </Card>
-      <Grid columns={1}>
-        <Grid.Column>
-          <Form loading={loading} inverted={isDark}>
-            {/*<Form.Group inline>*/}
-            {/*  <Form.Checkbox*/}
-            {/*    checked={inputs.StopOnSensitiveEnabled === 'true'}*/}
-            {/*    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>
+      <Spin spinning={loading} size='large'>
+        {/* 通用设置 */}
+        <Card style={{ marginTop: '10px' }}>
+          <SettingsGeneral options={inputs} />
+        </Card>
+        {/* 绘图设置 */}
+        <Card style={{ marginTop: '10px' }}>
+          <SettingsDrawing options={inputs} />
+        </Card>
+        {/* 屏蔽词过滤设置 */}
+        <Card style={{ marginTop: '10px' }}>
+          <SettingsSensitiveWords options={inputs} />
+        </Card>
+        {/* 日志设置 */}
+        <Card style={{ marginTop: '10px' }}>
+          <SettingsLog options={inputs} />
+        </Card>
+        {/* 数据看板 */}
+        <Card style={{ marginTop: '10px' }}>
+          <SettingsDataDashboard options={inputs} />
+        </Card>
+        {/* 监控设置 */}
+        <Card style={{ marginTop: '10px' }}>
+          <SettingsMonitoring options={inputs} />
+        </Card>
+        {/* 额度设置 */}
+        <Card style={{ marginTop: '10px' }}>
+          <SettingsCreditLimit options={inputs} />
+        </Card>
+        {/* 倍率设置 */}
+        <Card style={{ marginTop: '10px' }}>
+          <SettingsMagnification options={inputs} />
+        </Card>
+      </Spin>
     </>
     </>
   );
   );
 };
 };

+ 193 - 0
web/src/pages/Setting/Operation/SettingsMagnification.js

@@ -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>
+    </>
+  );
+}