OperationSetting.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. import React, { useEffect, useState } from 'react';
  2. import { Divider, Form, Grid, Header } from 'semantic-ui-react';
  3. import { Card } from '@douyinfe/semi-ui';
  4. import SettingsGeneral from '../pages/Setting/Operation/SettingsGeneral.js';
  5. import SettingsDrawing from '../pages/Setting/Operation/SettingsDrawing.js';
  6. import SettingsSensitiveWords from '../pages/Setting/Operation/SettingsSensitiveWords.js';
  7. import SettingsLog from '../pages/Setting/Operation/SettingsLog.js';
  8. import SettingsDataDashboard from '../pages/Setting/Operation/SettingsDataDashboard.js';
  9. import SettingsMonitoring from '../pages/Setting/Operation/SettingsMonitoring.js';
  10. import SettingsCreditLimit from '../pages/Setting/Operation/SettingsCreditLimit.js';
  11. import {
  12. API,
  13. showError,
  14. showSuccess,
  15. timestamp2string,
  16. verifyJSON,
  17. } from '../helpers';
  18. import { useTheme } from '../context/Theme';
  19. const OperationSetting = () => {
  20. let now = new Date();
  21. let [inputs, setInputs] = useState({
  22. QuotaForNewUser: 0,
  23. QuotaForInviter: 0,
  24. QuotaForInvitee: 0,
  25. QuotaRemindThreshold: 0,
  26. PreConsumedQuota: 0,
  27. StreamCacheQueueLength: 0,
  28. ModelRatio: '',
  29. ModelPrice: '',
  30. GroupRatio: '',
  31. TopUpLink: '',
  32. ChatLink: '',
  33. ChatLink2: '', // 添加的新状态变量
  34. QuotaPerUnit: 0,
  35. AutomaticDisableChannelEnabled: false,
  36. AutomaticEnableChannelEnabled: false,
  37. ChannelDisableThreshold: 0,
  38. LogConsumeEnabled: false,
  39. DisplayInCurrencyEnabled: false,
  40. DisplayTokenStatEnabled: false,
  41. CheckSensitiveEnabled: false,
  42. CheckSensitiveOnPromptEnabled: false,
  43. CheckSensitiveOnCompletionEnabled: '',
  44. StopOnSensitiveEnabled: '',
  45. SensitiveWords: '',
  46. MjNotifyEnabled: false,
  47. MjAccountFilterEnabled: false,
  48. MjModeClearEnabled: false,
  49. MjForwardUrlEnabled: false,
  50. DrawingEnabled: false,
  51. DataExportEnabled: false,
  52. DataExportDefaultTime: 'hour',
  53. DataExportInterval: 5,
  54. DefaultCollapseSidebar: false, // 默认折叠侧边栏
  55. RetryTimes: 0,
  56. });
  57. const [originInputs, setOriginInputs] = useState({});
  58. let [loading, setLoading] = useState(false);
  59. const getOptions = async () => {
  60. const res = await API.get('/api/option/');
  61. const { success, message, data } = res.data;
  62. if (success) {
  63. let newInputs = {};
  64. data.forEach((item) => {
  65. if (
  66. item.key === 'ModelRatio' ||
  67. item.key === 'GroupRatio' ||
  68. item.key === 'ModelPrice'
  69. ) {
  70. item.value = JSON.stringify(JSON.parse(item.value), null, 2);
  71. }
  72. if (
  73. item.key.endsWith('Enabled') ||
  74. ['DefaultCollapseSidebar'].includes(item.key)
  75. ) {
  76. newInputs[item.key] = item.value === 'true' ? true : false;
  77. } else {
  78. newInputs[item.key] = item.value;
  79. }
  80. });
  81. setInputs(newInputs);
  82. setOriginInputs(newInputs);
  83. } else {
  84. showError(message);
  85. }
  86. };
  87. const theme = useTheme();
  88. const isDark = theme === 'dark';
  89. useEffect(() => {
  90. getOptions().then();
  91. }, []);
  92. const updateOption = async (key, value) => {
  93. setLoading(true);
  94. if (key.endsWith('Enabled')) {
  95. value = inputs[key] === 'true' ? 'false' : 'true';
  96. }
  97. if (key === 'DefaultCollapseSidebar') {
  98. value = inputs[key] === 'true' ? 'false' : 'true';
  99. }
  100. console.log(key, value);
  101. const res = await API.put('/api/option/', {
  102. key,
  103. value,
  104. });
  105. const { success, message } = res.data;
  106. if (success) {
  107. setInputs((inputs) => ({ ...inputs, [key]: value }));
  108. } else {
  109. showError(message);
  110. }
  111. setLoading(false);
  112. };
  113. const handleInputChange = async (e, { name, value }) => {
  114. if (
  115. name.endsWith('Enabled') ||
  116. name === 'DataExportInterval' ||
  117. name === 'DataExportDefaultTime' ||
  118. name === 'DefaultCollapseSidebar'
  119. ) {
  120. if (name === 'DataExportDefaultTime') {
  121. localStorage.setItem('data_export_default_time', value);
  122. } else if (name === 'MjNotifyEnabled') {
  123. localStorage.setItem('mj_notify_enabled', value);
  124. }
  125. await updateOption(name, value);
  126. } else {
  127. setInputs((inputs) => ({ ...inputs, [name]: value }));
  128. }
  129. };
  130. const submitConfig = async (group) => {
  131. switch (group) {
  132. case 'monitor':
  133. if (
  134. originInputs['ChannelDisableThreshold'] !==
  135. inputs.ChannelDisableThreshold
  136. ) {
  137. await updateOption(
  138. 'ChannelDisableThreshold',
  139. inputs.ChannelDisableThreshold,
  140. );
  141. }
  142. if (
  143. originInputs['QuotaRemindThreshold'] !== inputs.QuotaRemindThreshold
  144. ) {
  145. await updateOption(
  146. 'QuotaRemindThreshold',
  147. inputs.QuotaRemindThreshold,
  148. );
  149. }
  150. break;
  151. case 'ratio':
  152. if (originInputs['ModelRatio'] !== inputs.ModelRatio) {
  153. if (!verifyJSON(inputs.ModelRatio)) {
  154. showError('模型倍率不是合法的 JSON 字符串');
  155. return;
  156. }
  157. await updateOption('ModelRatio', inputs.ModelRatio);
  158. }
  159. if (originInputs['GroupRatio'] !== inputs.GroupRatio) {
  160. if (!verifyJSON(inputs.GroupRatio)) {
  161. showError('分组倍率不是合法的 JSON 字符串');
  162. return;
  163. }
  164. await updateOption('GroupRatio', inputs.GroupRatio);
  165. }
  166. if (originInputs['ModelPrice'] !== inputs.ModelPrice) {
  167. if (!verifyJSON(inputs.ModelPrice)) {
  168. showError('模型固定价格不是合法的 JSON 字符串');
  169. return;
  170. }
  171. await updateOption('ModelPrice', inputs.ModelPrice);
  172. }
  173. break;
  174. case 'words':
  175. if (originInputs['SensitiveWords'] !== inputs.SensitiveWords) {
  176. await updateOption('SensitiveWords', inputs.SensitiveWords);
  177. }
  178. break;
  179. case 'quota':
  180. if (originInputs['QuotaForNewUser'] !== inputs.QuotaForNewUser) {
  181. await updateOption('QuotaForNewUser', inputs.QuotaForNewUser);
  182. }
  183. if (originInputs['QuotaForInvitee'] !== inputs.QuotaForInvitee) {
  184. await updateOption('QuotaForInvitee', inputs.QuotaForInvitee);
  185. }
  186. if (originInputs['QuotaForInviter'] !== inputs.QuotaForInviter) {
  187. await updateOption('QuotaForInviter', inputs.QuotaForInviter);
  188. }
  189. if (originInputs['PreConsumedQuota'] !== inputs.PreConsumedQuota) {
  190. await updateOption('PreConsumedQuota', inputs.PreConsumedQuota);
  191. }
  192. break;
  193. case 'general':
  194. if (originInputs['TopUpLink'] !== inputs.TopUpLink) {
  195. await updateOption('TopUpLink', inputs.TopUpLink);
  196. }
  197. if (originInputs['ChatLink'] !== inputs.ChatLink) {
  198. await updateOption('ChatLink', inputs.ChatLink);
  199. }
  200. if (originInputs['ChatLink2'] !== inputs.ChatLink2) {
  201. await updateOption('ChatLink2', inputs.ChatLink2);
  202. }
  203. if (originInputs['QuotaPerUnit'] !== inputs.QuotaPerUnit) {
  204. await updateOption('QuotaPerUnit', inputs.QuotaPerUnit);
  205. }
  206. if (originInputs['RetryTimes'] !== inputs.RetryTimes) {
  207. await updateOption('RetryTimes', inputs.RetryTimes);
  208. }
  209. break;
  210. }
  211. };
  212. return (
  213. <>
  214. {/* 通用设置 */}
  215. <Card style={{ marginTop: '10px' }}>
  216. <SettingsGeneral options={inputs} />
  217. </Card>
  218. {/* 绘图设置 */}
  219. <Card style={{ marginTop: '10px' }}>
  220. <SettingsDrawing options={inputs} />
  221. </Card>
  222. {/* 屏蔽词过滤设置 */}
  223. <Card style={{ marginTop: '10px' }}>
  224. <SettingsSensitiveWords options={inputs} />
  225. </Card>
  226. {/* 日志设置 */}
  227. <Card style={{ marginTop: '10px' }}>
  228. <SettingsLog options={inputs} />
  229. </Card>
  230. {/* 数据看板 */}
  231. <Card style={{ marginTop: '10px' }}>
  232. <SettingsDataDashboard options={inputs} />
  233. </Card>
  234. {/* 监控设置 */}
  235. <Card style={{ marginTop: '10px' }}>
  236. <SettingsMonitoring options={inputs} />
  237. </Card>
  238. {/* 额度设置 */}
  239. <Card style={{ marginTop: '10px' }}>
  240. <SettingsCreditLimit options={inputs} />
  241. </Card>
  242. <Grid columns={1}>
  243. <Grid.Column>
  244. <Form loading={loading} inverted={isDark}>
  245. {/*<Form.Group inline>*/}
  246. {/* <Form.Checkbox*/}
  247. {/* checked={inputs.StopOnSensitiveEnabled === 'true'}*/}
  248. {/* label='在检测到屏蔽词时,立刻停止生成,否则替换屏蔽词'*/}
  249. {/* name='StopOnSensitiveEnabled'*/}
  250. {/* onChange={handleInputChange}*/}
  251. {/* />*/}
  252. {/*</Form.Group>*/}
  253. {/*<Form.Group>*/}
  254. {/* <Form.Input*/}
  255. {/* label="流模式下缓存队列,默认不缓存,设置越大检测越准确,但是回复会有卡顿感"*/}
  256. {/* name="StreamCacheTextLength"*/}
  257. {/* onChange={handleInputChange}*/}
  258. {/* value={inputs.StreamCacheQueueLength}*/}
  259. {/* type="number"*/}
  260. {/* min="0"*/}
  261. {/* placeholder="例如:10"*/}
  262. {/* />*/}
  263. {/*</Form.Group>*/}
  264. <Divider />
  265. <Header as='h3' inverted={isDark}>
  266. 倍率设置
  267. </Header>
  268. <Form.Group widths='equal'>
  269. <Form.TextArea
  270. label='模型固定价格(一次调用消耗多少刀,优先级大于模型倍率)'
  271. name='ModelPrice'
  272. onChange={handleInputChange}
  273. style={{
  274. minHeight: 250,
  275. fontFamily: 'JetBrains Mono, Consolas',
  276. }}
  277. autoComplete='new-password'
  278. value={inputs.ModelPrice}
  279. placeholder='为一个 JSON 文本,键为模型名称,值为一次调用消耗多少刀,比如 "gpt-4-gizmo-*": 0.1,一次消耗0.1刀'
  280. />
  281. </Form.Group>
  282. <Form.Group widths='equal'>
  283. <Form.TextArea
  284. label='模型倍率'
  285. name='ModelRatio'
  286. onChange={handleInputChange}
  287. style={{
  288. minHeight: 250,
  289. fontFamily: 'JetBrains Mono, Consolas',
  290. }}
  291. autoComplete='new-password'
  292. value={inputs.ModelRatio}
  293. placeholder='为一个 JSON 文本,键为模型名称,值为倍率'
  294. />
  295. </Form.Group>
  296. <Form.Group widths='equal'>
  297. <Form.TextArea
  298. label='分组倍率'
  299. name='GroupRatio'
  300. onChange={handleInputChange}
  301. style={{
  302. minHeight: 250,
  303. fontFamily: 'JetBrains Mono, Consolas',
  304. }}
  305. autoComplete='new-password'
  306. value={inputs.GroupRatio}
  307. placeholder='为一个 JSON 文本,键为分组名称,值为倍率'
  308. />
  309. </Form.Group>
  310. <Form.Button
  311. onClick={() => {
  312. submitConfig('ratio').then();
  313. }}
  314. >
  315. 保存倍率设置
  316. </Form.Button>
  317. </Form>
  318. </Grid.Column>
  319. </Grid>
  320. </>
  321. );
  322. };
  323. export default OperationSetting;