OtherSetting.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. import React, { useEffect, useRef, useState } from 'react';
  2. import { Banner, Button, Col, Form, Row } from '@douyinfe/semi-ui';
  3. import { API, showError, showSuccess } from '../helpers';
  4. import { marked } from 'marked';
  5. const OtherSetting = () => {
  6. let [inputs, setInputs] = useState({
  7. Notice: '',
  8. SystemName: '',
  9. Logo: '',
  10. Footer: '',
  11. About: '',
  12. HomePageContent: '',
  13. });
  14. let [loading, setLoading] = useState(false);
  15. const [showUpdateModal, setShowUpdateModal] = useState(false);
  16. const [updateData, setUpdateData] = useState({
  17. tag_name: '',
  18. content: '',
  19. });
  20. const updateOption = async (key, value) => {
  21. setLoading(true);
  22. const res = await API.put('/api/option/', {
  23. key,
  24. value,
  25. });
  26. const { success, message } = res.data;
  27. if (success) {
  28. setInputs((inputs) => ({ ...inputs, [key]: value }));
  29. } else {
  30. showError(message);
  31. }
  32. setLoading(false);
  33. };
  34. const [loadingInput, setLoadingInput] = useState({
  35. Notice: false,
  36. SystemName: false,
  37. Logo: false,
  38. HomePageContent: false,
  39. About: false,
  40. Footer: false,
  41. });
  42. const handleInputChange = async (value, e) => {
  43. const name = e.target.id;
  44. setInputs((inputs) => ({ ...inputs, [name]: value }));
  45. };
  46. // 通用设置
  47. const formAPISettingGeneral = useRef();
  48. // 通用设置 - Notice
  49. const submitNotice = async () => {
  50. try {
  51. setLoadingInput((loadingInput) => ({ ...loadingInput, Notice: true }));
  52. await updateOption('Notice', inputs.Notice);
  53. showSuccess('公告已更新');
  54. } catch (error) {
  55. console.error('公告更新失败', error);
  56. showError('公告更新失败');
  57. } finally {
  58. setLoadingInput((loadingInput) => ({ ...loadingInput, Notice: false }));
  59. }
  60. };
  61. // 个性化设置
  62. const formAPIPersonalization = useRef();
  63. // 个性化设置 - SystemName
  64. const submitSystemName = async () => {
  65. try {
  66. setLoadingInput((loadingInput) => ({
  67. ...loadingInput,
  68. SystemName: true,
  69. }));
  70. await updateOption('SystemName', inputs.SystemName);
  71. showSuccess('系统名称已更新');
  72. } catch (error) {
  73. console.error('系统名称更新失败', error);
  74. showError('系统名称更新失败');
  75. } finally {
  76. setLoadingInput((loadingInput) => ({
  77. ...loadingInput,
  78. SystemName: false,
  79. }));
  80. }
  81. };
  82. // 个性化设置 - Logo
  83. const submitLogo = async () => {
  84. try {
  85. setLoadingInput((loadingInput) => ({ ...loadingInput, Logo: true }));
  86. await updateOption('Logo', inputs.Logo);
  87. showSuccess('Logo 已更新');
  88. } catch (error) {
  89. console.error('Logo 更新失败', error);
  90. showError('Logo 更新失败');
  91. } finally {
  92. setLoadingInput((loadingInput) => ({ ...loadingInput, Logo: false }));
  93. }
  94. };
  95. // 个性化设置 - 首页内容
  96. const submitOption = async (key) => {
  97. try {
  98. setLoadingInput((loadingInput) => ({
  99. ...loadingInput,
  100. HomePageContent: true,
  101. }));
  102. await updateOption(key, inputs[key]);
  103. showSuccess('首页内容已更新');
  104. } catch (error) {
  105. console.error('首页内容更新失败', error);
  106. showError('首页内容更新失败');
  107. } finally {
  108. setLoadingInput((loadingInput) => ({
  109. ...loadingInput,
  110. HomePageContent: false,
  111. }));
  112. }
  113. };
  114. // 个性化设置 - 关于
  115. const submitAbout = async () => {
  116. try {
  117. setLoadingInput((loadingInput) => ({ ...loadingInput, About: true }));
  118. await updateOption('About', inputs.About);
  119. showSuccess('关于内容已更新');
  120. } catch (error) {
  121. console.error('关于内容更新失败', error);
  122. showError('关于内容更新失败');
  123. } finally {
  124. setLoadingInput((loadingInput) => ({ ...loadingInput, About: false }));
  125. }
  126. };
  127. // 个性化设置 - 页脚
  128. const submitFooter = async () => {
  129. try {
  130. setLoadingInput((loadingInput) => ({ ...loadingInput, Footer: true }));
  131. await updateOption('Footer', inputs.Footer);
  132. showSuccess('页脚内容已更新');
  133. } catch (error) {
  134. console.error('页脚内容更新失败', error);
  135. showError('页脚内容更新失败');
  136. } finally {
  137. setLoadingInput((loadingInput) => ({ ...loadingInput, Footer: false }));
  138. }
  139. };
  140. const openGitHubRelease = () => {
  141. window.location = 'https://github.com/songquanpeng/one-api/releases/latest';
  142. };
  143. const checkUpdate = async () => {
  144. const res = await API.get(
  145. 'https://api.github.com/repos/songquanpeng/one-api/releases/latest',
  146. );
  147. const { tag_name, body } = res.data;
  148. if (tag_name === process.env.REACT_APP_VERSION) {
  149. showSuccess(`已是最新版本:${tag_name}`);
  150. } else {
  151. setUpdateData({
  152. tag_name: tag_name,
  153. content: marked.parse(body),
  154. });
  155. setShowUpdateModal(true);
  156. }
  157. };
  158. const getOptions = async () => {
  159. const res = await API.get('/api/option/');
  160. const { success, message, data } = res.data;
  161. if (success) {
  162. let newInputs = {};
  163. data.forEach((item) => {
  164. if (item.key in inputs) {
  165. newInputs[item.key] = item.value;
  166. }
  167. });
  168. setInputs(newInputs);
  169. formAPISettingGeneral.current.setValues(newInputs);
  170. formAPIPersonalization.current.setValues(newInputs);
  171. } else {
  172. showError(message);
  173. }
  174. };
  175. useEffect(() => {
  176. getOptions();
  177. }, []);
  178. return (
  179. <Row>
  180. <Col span={24}>
  181. {/* 通用设置 */}
  182. <Form
  183. values={inputs}
  184. getFormApi={(formAPI) => (formAPISettingGeneral.current = formAPI)}
  185. style={{ marginBottom: 15 }}
  186. >
  187. <Form.Section text={'通用设置'}>
  188. <Form.TextArea
  189. label={'公告'}
  190. placeholder={'在此输入新的公告内容,支持 Markdown & HTML 代码'}
  191. field={'Notice'}
  192. onChange={handleInputChange}
  193. style={{ fontFamily: 'JetBrains Mono, Consolas' }}
  194. autosize={{ minRows: 6, maxRows: 12 }}
  195. />
  196. <Button onClick={submitNotice} loading={loadingInput['Notice']}>
  197. 设置公告
  198. </Button>
  199. </Form.Section>
  200. </Form>
  201. {/* 个性化设置 */}
  202. <Form
  203. values={inputs}
  204. getFormApi={(formAPI) => (formAPIPersonalization.current = formAPI)}
  205. style={{ marginBottom: 15 }}
  206. >
  207. <Form.Section text={'个性化设置'}>
  208. <Form.Input
  209. label={'系统名称'}
  210. placeholder={'在此输入系统名称'}
  211. field={'SystemName'}
  212. onChange={handleInputChange}
  213. />
  214. <Button
  215. onClick={submitSystemName}
  216. loading={loadingInput['SystemName']}
  217. >
  218. 设置系统名称
  219. </Button>
  220. <Form.Input
  221. label={'Logo 图片地址'}
  222. placeholder={'在此输入 Logo 图片地址'}
  223. field={'Logo'}
  224. onChange={handleInputChange}
  225. />
  226. <Button onClick={submitLogo} loading={loadingInput['Logo']}>
  227. 设置 Logo
  228. </Button>
  229. <Form.TextArea
  230. label={'首页内容'}
  231. placeholder={
  232. '在此输入首页内容,支持 Markdown & HTML 代码,设置后首页的状态信息将不再显示。如果输入的是一个链接,则会使用该链接作为 iframe 的 src 属性,这允许你设置任意网页作为首页。'
  233. }
  234. field={'HomePageContent'}
  235. onChange={handleInputChange}
  236. style={{ fontFamily: 'JetBrains Mono, Consolas' }}
  237. autosize={{ minRows: 6, maxRows: 12 }}
  238. />
  239. <Button
  240. onClick={() => submitOption('HomePageContent')}
  241. loading={loadingInput['HomePageContent']}
  242. >
  243. 设置首页内容
  244. </Button>
  245. <Form.TextArea
  246. label={'关于'}
  247. placeholder={
  248. '在此输入新的关于内容,支持 Markdown & HTML 代码。如果输入的是一个链接,则会使用该链接作为 iframe 的 src 属性,这允许你设置任意网页作为关于页面。'
  249. }
  250. field={'About'}
  251. onChange={handleInputChange}
  252. style={{ fontFamily: 'JetBrains Mono, Consolas' }}
  253. autosize={{ minRows: 6, maxRows: 12 }}
  254. />
  255. <Button onClick={submitAbout} loading={loadingInput['About']}>
  256. 设置关于
  257. </Button>
  258. {/* */}
  259. <Banner
  260. fullMode={false}
  261. type='info'
  262. description='移除 One API 的版权标识必须首先获得授权,项目维护需要花费大量精力,如果本项目对你有意义,请主动支持本项目。'
  263. closeIcon={null}
  264. style={{ marginTop: 15 }}
  265. />
  266. <Form.Input
  267. label={'页脚'}
  268. placeholder={
  269. '在此输入新的页脚,留空则使用默认页脚,支持 HTML 代码'
  270. }
  271. field={'Footer'}
  272. onChange={handleInputChange}
  273. />
  274. <Button onClick={submitFooter} loading={loadingInput['Footer']}>
  275. 设置页脚
  276. </Button>
  277. </Form.Section>
  278. </Form>
  279. </Col>
  280. {/*<Modal*/}
  281. {/* onClose={() => setShowUpdateModal(false)}*/}
  282. {/* onOpen={() => setShowUpdateModal(true)}*/}
  283. {/* open={showUpdateModal}*/}
  284. {/*>*/}
  285. {/* <Modal.Header>新版本:{updateData.tag_name}</Modal.Header>*/}
  286. {/* <Modal.Content>*/}
  287. {/* <Modal.Description>*/}
  288. {/* <div dangerouslySetInnerHTML={{ __html: updateData.content }}></div>*/}
  289. {/* </Modal.Description>*/}
  290. {/* </Modal.Content>*/}
  291. {/* <Modal.Actions>*/}
  292. {/* <Button onClick={() => setShowUpdateModal(false)}>关闭</Button>*/}
  293. {/* <Button*/}
  294. {/* content='详情'*/}
  295. {/* onClick={() => {*/}
  296. {/* setShowUpdateModal(false);*/}
  297. {/* openGitHubRelease();*/}
  298. {/* }}*/}
  299. {/* />*/}
  300. {/* </Modal.Actions>*/}
  301. {/*</Modal>*/}
  302. </Row>
  303. );
  304. };
  305. export default OtherSetting;