SettingsGeneral.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. import React, { useEffect, useState, useRef } from 'react';
  2. import {
  3. Banner,
  4. Button,
  5. Col,
  6. Form,
  7. Row,
  8. Spin,
  9. Modal,
  10. } from '@douyinfe/semi-ui';
  11. import {
  12. compareObjects,
  13. API,
  14. showError,
  15. showSuccess,
  16. showWarning,
  17. } from '../../../helpers';
  18. import { useTranslation } from 'react-i18next';
  19. export default function GeneralSettings(props) {
  20. const { t } = useTranslation();
  21. const [loading, setLoading] = useState(false);
  22. const [showQuotaWarning, setShowQuotaWarning] = useState(false);
  23. const [inputs, setInputs] = useState({
  24. TopUpLink: '',
  25. 'general_setting.docs_link': '',
  26. QuotaPerUnit: '',
  27. RetryTimes: '',
  28. USDExchangeRate: '',
  29. DisplayInCurrencyEnabled: false,
  30. DisplayTokenStatEnabled: false,
  31. DefaultCollapseSidebar: false,
  32. DemoSiteEnabled: false,
  33. SelfUseModeEnabled: false,
  34. });
  35. const refForm = useRef();
  36. const [inputsRow, setInputsRow] = useState(inputs);
  37. function handleFieldChange(fieldName) {
  38. return (value) => {
  39. setInputs((inputs) => ({ ...inputs, [fieldName]: value }));
  40. };
  41. }
  42. function onSubmit() {
  43. const updateArray = compareObjects(inputs, inputsRow);
  44. if (!updateArray.length) return showWarning(t('你似乎并没有修改什么'));
  45. const requestQueue = updateArray.map((item) => {
  46. let value = '';
  47. if (typeof inputs[item.key] === 'boolean') {
  48. value = String(inputs[item.key]);
  49. } else {
  50. value = inputs[item.key];
  51. }
  52. return API.put('/api/option/', {
  53. key: item.key,
  54. value,
  55. });
  56. });
  57. setLoading(true);
  58. Promise.all(requestQueue)
  59. .then((res) => {
  60. if (requestQueue.length === 1) {
  61. if (res.includes(undefined)) return;
  62. } else if (requestQueue.length > 1) {
  63. if (res.includes(undefined))
  64. return showError(t('部分保存失败,请重试'));
  65. }
  66. showSuccess(t('保存成功'));
  67. props.refresh();
  68. })
  69. .catch(() => {
  70. showError(t('保存失败,请重试'));
  71. })
  72. .finally(() => {
  73. setLoading(false);
  74. });
  75. }
  76. useEffect(() => {
  77. const currentInputs = {};
  78. for (let key in props.options) {
  79. if (Object.keys(inputs).includes(key)) {
  80. currentInputs[key] = props.options[key];
  81. }
  82. }
  83. setInputs(currentInputs);
  84. setInputsRow(structuredClone(currentInputs));
  85. refForm.current.setValues(currentInputs);
  86. }, [props.options]);
  87. return (
  88. <>
  89. <Spin spinning={loading}>
  90. <Form
  91. values={inputs}
  92. getFormApi={(formAPI) => (refForm.current = formAPI)}
  93. style={{ marginBottom: 15 }}
  94. >
  95. <Form.Section text={t('通用设置')}>
  96. <Row gutter={16}>
  97. <Col xs={24} sm={12} md={8} lg={8} xl={8}>
  98. <Form.Input
  99. field={'TopUpLink'}
  100. label={t('充值链接')}
  101. initValue={''}
  102. placeholder={t('例如发卡网站的购买链接')}
  103. onChange={handleFieldChange('TopUpLink')}
  104. showClear
  105. />
  106. </Col>
  107. <Col xs={24} sm={12} md={8} lg={8} xl={8}>
  108. <Form.Input
  109. field={'general_setting.docs_link'}
  110. label={t('文档地址')}
  111. initValue={''}
  112. placeholder={t('例如 https://docs.newapi.pro')}
  113. onChange={handleFieldChange('general_setting.docs_link')}
  114. showClear
  115. />
  116. </Col>
  117. <Col xs={24} sm={12} md={8} lg={8} xl={8}>
  118. <Form.Input
  119. field={'QuotaPerUnit'}
  120. label={t('单位美元额度')}
  121. initValue={''}
  122. placeholder={t('一单位货币能兑换的额度')}
  123. onChange={handleFieldChange('QuotaPerUnit')}
  124. showClear
  125. onClick={() => setShowQuotaWarning(true)}
  126. />
  127. </Col>
  128. <Col xs={24} sm={12} md={8} lg={8} xl={8}>
  129. <Form.Input
  130. field={'USDExchangeRate'}
  131. label={t('美元汇率(非充值汇率,仅用于定价页面换算)')}
  132. initValue={''}
  133. placeholder={t('美元汇率')}
  134. onChange={handleFieldChange('USDExchangeRate')}
  135. showClear
  136. />
  137. </Col>
  138. <Col xs={24} sm={12} md={8} lg={8} xl={8}>
  139. <Form.Input
  140. field={'RetryTimes'}
  141. label={t('失败重试次数')}
  142. initValue={''}
  143. placeholder={t('失败重试次数')}
  144. onChange={handleFieldChange('RetryTimes')}
  145. showClear
  146. />
  147. </Col>
  148. </Row>
  149. <Row gutter={16}>
  150. <Col xs={24} sm={12} md={8} lg={8} xl={8}>
  151. <Form.Switch
  152. field={'DisplayInCurrencyEnabled'}
  153. label={t('以货币形式显示额度')}
  154. size='default'
  155. checkedText='|'
  156. uncheckedText='〇'
  157. onChange={handleFieldChange('DisplayInCurrencyEnabled')}
  158. />
  159. </Col>
  160. <Col xs={24} sm={12} md={8} lg={8} xl={8}>
  161. <Form.Switch
  162. field={'DisplayTokenStatEnabled'}
  163. label={t('额度查询接口返回令牌额度而非用户额度')}
  164. size='default'
  165. checkedText='|'
  166. uncheckedText='〇'
  167. onChange={handleFieldChange('DisplayTokenStatEnabled')}
  168. />
  169. </Col>
  170. <Col xs={24} sm={12} md={8} lg={8} xl={8}>
  171. <Form.Switch
  172. field={'DefaultCollapseSidebar'}
  173. label={t('默认折叠侧边栏')}
  174. size='default'
  175. checkedText='|'
  176. uncheckedText='〇'
  177. onChange={handleFieldChange('DefaultCollapseSidebar')}
  178. />
  179. </Col>
  180. </Row>
  181. <Row>
  182. <Col xs={24} sm={12} md={8} lg={8} xl={8}>
  183. <Form.Switch
  184. field={'DemoSiteEnabled'}
  185. label={t('演示站点模式')}
  186. size='default'
  187. checkedText='|'
  188. uncheckedText='〇'
  189. onChange={handleFieldChange('DemoSiteEnabled')}
  190. />
  191. </Col>
  192. <Col xs={24} sm={12} md={8} lg={8} xl={8}>
  193. <Form.Switch
  194. field={'SelfUseModeEnabled'}
  195. label={t('自用模式')}
  196. extraText={t('开启后不限制:必须设置模型倍率')}
  197. size='default'
  198. checkedText='|'
  199. uncheckedText='〇'
  200. onChange={handleFieldChange('SelfUseModeEnabled')}
  201. />
  202. </Col>
  203. </Row>
  204. <Row>
  205. <Button size='default' onClick={onSubmit}>
  206. {t('保存通用设置')}
  207. </Button>
  208. </Row>
  209. </Form.Section>
  210. </Form>
  211. </Spin>
  212. <Modal
  213. title={t('警告')}
  214. visible={showQuotaWarning}
  215. onOk={() => setShowQuotaWarning(false)}
  216. onCancel={() => setShowQuotaWarning(false)}
  217. closeOnEsc={true}
  218. width={500}
  219. >
  220. <Banner
  221. type='warning'
  222. description={t(
  223. '此设置用于系统内部计算,默认值500000是为了精确到6位小数点设计,不推荐修改。',
  224. )}
  225. bordered
  226. fullMode={false}
  227. closeIcon={null}
  228. />
  229. </Modal>
  230. </>
  231. );
  232. }